/*++ Copyright (c) 1997 Microsoft Corporation. Module Name: rcastrm.c Abstract: RCA Streaming routines. Author: Richard Machin (RMachin) Revision History: Who When What -------- -------- ---------------------------------------------- RMachin 2-25-97 stolen/adapted from msfsread and mswaveio DChen 3-12-98 Bug fixing and cleanup JameelH 4-18-98 Cleanup SPATHER 5-20-99 Cleanup. Re-orged to separate KS / NDIS parts. Notes: --*/ #include #define MODULE_NUMBER MODULE_STRM #define _FILENUMBER 'MRTS' VOID RCAReceiveCallback( IN PVOID RcaVcContext, IN PVOID ClientReceiveContext, IN PNDIS_PACKET pPacket ) { NDIS_STATUS Status = NDIS_STATUS_SUCCESS; PPIN_INSTANCE_DEVIO pDevioPin; PPIN_INSTANCE_BRIDGE pBridgePin; PRCA_STREAM_HEADER StreamHdr; PMDL pMdl; ULONG ulBufferLength; PWORK_ITEM pWorkItem; RCADEBUGP(RCA_INFO, ("RCAReceiveCallback: Enter\n")); do { // // Check that all our pins exist. // pBridgePin = (PPIN_INSTANCE_BRIDGE) ClientReceiveContext; if (pBridgePin == NULL) { RCADEBUGP(RCA_WARNING, ("RCAReceiveCallback: Bridge pin was null, dumping\n")); Status = NDIS_STATUS_FAILURE; break; } pDevioPin = pBridgePin->FilterInstance->DevIoPin; if (pDevioPin == NULL) { RCADEBUGP(RCA_WARNING, ("RCAReceiveCallback: Devio pin was null, dumping\n")); Status = NDIS_STATUS_FAILURE; break; } // // Check that the device is in the running state. // if (pDevioPin->DeviceState != KSSTATE_RUN) { RCADEBUGP(RCA_WARNING, ("RCAReceiveCallback: Device is not running, dumping\n")); Status = NDIS_STATUS_FAILURE; break; } // // If we're connected as an IRP source, check that there is someone to send IRPs to. // if (!(pDevioPin->ConnectedAsSink)) { if (pDevioPin->FilterInstance->NextFileObject == NULL) { RCADEBUGP(RCA_ERROR, ("RCAReceiveCallback: No device to stream to, dumping\n")); Status = NDIS_STATUS_FAILURE; break; } } // // Get a stream header and fill it out. // StreamHdr = RCASHPoolGet(); if (StreamHdr == NULL) { RCADEBUGP(RCA_ERROR, ("RCAReceiveCallback: Could not get a stream header\n")); Status = NDIS_STATUS_FAILURE; break; } Status = RCACoNdisGetMdlFromPacket(pPacket, &pMdl); if (Status != NDIS_STATUS_SUCCESS) { RCADEBUGP(RCA_ERROR, ("RCAReceiveCallback: Could not get MDL from packet\n")); break; } ulBufferLength = MmGetMdlByteCount(pMdl); StreamHdr->Header.Size = sizeof (KSSTREAM_HEADER); StreamHdr->Header.TypeSpecificFlags = 0; StreamHdr->Header.PresentationTime.Time = 0; // FIXME: Fix this. StreamHdr->Header.PresentationTime.Numerator = 1; StreamHdr->Header.PresentationTime.Denominator = 1; StreamHdr->Header.DataUsed = ulBufferLength; StreamHdr->Header.FrameExtent = ulBufferLength; StreamHdr->Header.OptionsFlags = KSSTREAM_HEADER_OPTIONSF_TIMEVALID | KSSTREAM_HEADER_OPTIONSF_DURATIONVALID; StreamHdr->Header.Data = MmGetSystemAddressForMdl (pMdl); // data is in MDL address StreamHdr->Header.Duration = StreamHdr->Header.DataUsed; // just use all the data in the buffer StreamHdr->NdisPacket = pPacket; // // Make a worker thread stream the data. // pWorkItem = WORK_ITEM_FROM_PKT(pPacket); pWorkItem->StreamHeader = StreamHdr; RCA_ACQUIRE_BRIDGE_PIN_LOCK(pBridgePin); RcaGlobal.QueueSize++; InsertTailList(&pBridgePin->WorkQueue, &pWorkItem->ListEntry); if (!pBridgePin->bWorkItemQueued) { // // There is no work item pending, so we'll schedule one. // NdisInitializeWorkItem(&pBridgePin->WorkItem, RCAIoWorker, (PVOID)pBridgePin); NdisScheduleWorkItem(&pBridgePin->WorkItem); pBridgePin->bWorkItemQueued = TRUE; } RCA_RELEASE_BRIDGE_PIN_LOCK(pBridgePin); } while (FALSE); // // If something got botched, return the packet immediately. // if (Status != NDIS_STATUS_SUCCESS) { RCACoNdisReturnPacket(pPacket); } RCADEBUGP(RCA_INFO, ("RCAReceiveCallback: Exit\n")); } VOID RCASendCompleteCallback( IN PVOID RcaVcContext, IN PVOID ClientSendContext, IN PVOID PacketContext, IN PMDL pSentMdl, IN NDIS_STATUS Status ) { PIRP pIrp = (PIRP) PacketContext; PIO_STACK_LOCATION pIrpSp; PPIN_INSTANCE_BRIDGE pBridgePin = (PPIN_INSTANCE_BRIDGE) ClientSendContext; RCADEBUGP(RCA_INFO, ("RCASendCompleteCallback: Enter\n")); // // Complete the IRP. // pIrp->IoStatus.Status = Status; if (!NT_SUCCESS(Status)) { RCADEBUGP(RCA_ERROR, ("RCASendCompleteCallback: " "Send failed with status 0x%x\n", Status)); } pIrp->IoStatus.Information = 0; IoCompleteRequest(pIrp, IO_NO_INCREMENT); if (pBridgePin) { RCA_ACQUIRE_BRIDGE_PIN_LOCK(pBridgePin); pBridgePin->PendingSendsCount--; if ((pBridgePin->PendingSendsCount == 0) && pBridgePin->SignalWhenSendsComplete) { RCASignal(&pBridgePin->PendingSendsBlock, Status); } RCA_RELEASE_BRIDGE_PIN_LOCK(pBridgePin); } // // Free the MDL. // IoFreeMdl(pSentMdl); RCADEBUGP(RCA_INFO, ("RCASendCompleteCallback: Exit\n")); } VOID RCAVcCloseCallback( IN PVOID RcaVcContext, IN PVOID ClientReceiveContext, IN PVOID ClientSendContext ) { PPIN_INSTANCE_BRIDGE pBridgePin; PVOID VcContextToRelease; if (ClientReceiveContext) { pBridgePin = (PPIN_INSTANCE_BRIDGE) ClientReceiveContext; RCA_ACQUIRE_BRIDGE_PIN_LOCK(pBridgePin); ASSERT(RcaVcContext == pBridgePin->VcContext); VcContextToRelease = pBridgePin->VcContext; pBridgePin->VcContext = NULL; if (pBridgePin->FilterInstance->DevIoPin) pBridgePin->FilterInstance->DevIoPin->VcContext = NULL; RCA_RELEASE_BRIDGE_PIN_LOCK(pBridgePin); RCACoNdisReleaseReceiveVcContext(VcContextToRelease); } if (ClientSendContext) { pBridgePin = (PPIN_INSTANCE_BRIDGE) ClientSendContext; RCA_ACQUIRE_BRIDGE_PIN_LOCK(pBridgePin); ASSERT(RcaVcContext == pBridgePin->VcContext); VcContextToRelease = pBridgePin->VcContext; pBridgePin->VcContext = NULL; if (pBridgePin->FilterInstance->DevIoPin) pBridgePin->FilterInstance->DevIoPin->VcContext = NULL; RCA_RELEASE_BRIDGE_PIN_LOCK(pBridgePin); RCACoNdisReleaseSendVcContext(VcContextToRelease); } } NTSTATUS ReadStream( IN PIRP Irp, IN PPIN_INSTANCE_DEVIO PinInstance ) /*++ Routine Description: Handles IOCTL_KS_READ_STREAM by reading data from the open VC. Arguments: Irp - Streaming Irp. Return Values: Returns STATUS_SUCCESS if the request was fulfilled. Else returns STATUS_PORT_DISCONNECTED if the VC has been closed (no FILE PIN in this context). some read error, or some parameter validation error. --*/ { NTSTATUS Status = STATUS_UNSUCCESSFUL; PFILTER_INSTANCE FilterInstance = PinInstance->FilterInstance; RCADEBUGP(RCA_LOUD, ("ReadStream: enter\n")); if (FilterInstance->FilterType == FilterTypeCapture) { if ((PinInstance->DeviceState == KSSTATE_RUN) || (PinInstance->DeviceState == KSSTATE_PAUSE)) { PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); // Can be removed when the following debug print is removed RCADEBUGP(RCA_LOUD, ("ReadStream: Irp's output buffer length is: 0x%x\n", irpSp->Parameters.DeviceIoControl.OutputBufferLength)); Status = KsProbeStreamIrp(Irp, KSPROBE_STREAMREAD | (KSPROBE_ALLOCATEMDL | KSPROBE_PROBEANDLOCK), sizeof(KSSTREAM_HEADER)); if (NT_SUCCESS(Status)) { #if AUDIO_SINK_FLAG KsAddIrpToCancelableQueue(&PinInstance->ActiveQueue, &PinInstance->QueueLock, Irp, KsListEntryTail, NULL); #endif Status = STATUS_PENDING; } else { RCADEBUGP(RCA_ERROR, ("ReadStream: " "KsProbeStreamIrp failed with Status == 0x%x\n", Status)); } } } return(Status); } NTSTATUS WriteStream( IN PIRP Irp, IN PPIN_INSTANCE_DEVIO pDevIoPin ) /*++ Routine Description: Handles IOCTL_KS_WRITE_STREAM by writing data to the open VC. Arguments: Irp - Streaming Irp. Return Values: Returns STATUS_SUCCESS if the request was fulfilled (in which case the irp is pended until we complete it in our cosendcompletehandler.) Else returns an error, and the irp is completed back to the caller. --*/ { NTSTATUS Status = 0; PVOID VcContext; PNDIS_PACKET pNdisPacket; ULONG BufferLength; PUCHAR SystemBuffer; PMDL pMdl; PVOID pMdlVirtualAddress; UINT bLength = 0; RCADEBUGP(RCA_LOUD, ("WriteStream: enter\n")); RCAAssert( KeGetCurrentIrql() == PASSIVE_LEVEL ); RCA_ACQUIRE_BRIDGE_PIN_LOCK(pDevIoPin->FilterInstance->BridgePin); VcContext = pDevIoPin->VcContext; RCA_RELEASE_BRIDGE_PIN_LOCK(pDevIoPin->FilterInstance->BridgePin); // // FIXME: Now that we've released the lock, the VC could go away and // we'd be in trouble. Don't know how big this timing window // is. // do { ULONG BytesToCopy; if (VcContext == NULL) { RCADEBUGP(RCA_LOUD, ("WriteStream: no associated VC\n")); // // Bad sts will cause irp to be completed with sts in iostatus buffer // Status = STATUS_PORT_DISCONNECTED; break; } if (pDevIoPin->DeviceState != KSSTATE_RUN) { // // Device isn't "runnning". // Status = NDIS_STATUS_FAILURE; break; } // // Get the data in an MDL if it's not already. From the KsProbeStreamIrp code: // // Makes the specified modifications to the given IRP's input and output // buffers based on the specific streaming IOCTL in the current stack // location, and validates the stream header. The Irp end up in essentially // the METHOD_OUT_DIRECT or // METHOD_IN_DIRECT format, with the exception that the access to the data // buffer may be IoModifyAccess depending on the flags passed to this // function or in the stream header. If the stream buffers MDL's have been // allocated, they are available through the PIRP->MdlAddress. If extra data // has been requested, the copied list of headers with extra data area is // available in PIRP->Tail.Overlay.AuxiliaryBuffer. // Status = KsProbeStreamIrp(Irp, KSPROBE_STREAMWRITE | KSPROBE_ALLOCATEMDL | KSPROBE_PROBEANDLOCK, sizeof(KSSTREAM_HEADER)); if (Status != STATUS_SUCCESS) { RCADEBUGP(RCA_WARNING,("WriteStream: KsProbeStreamIrp failed sts %x\n", Status)); break; } if (!((PKSSTREAM_HEADER)Irp->AssociatedIrp.SystemBuffer)->DataUsed) { // // This IRP has no data, complete it immediately. // RCADEBUGP(RCA_WARNING, ("Irp %x has no data\n", Irp) ); Status = STATUS_UNSUCCESSFUL; break; } // // Build a partial MDL containing only the dataused portion of this MDL // RCADEBUGP(RCA_INFO,("WriteStream: allocating MDL\n")); pMdlVirtualAddress = MmGetMdlVirtualAddress (Irp->MdlAddress); RCADEBUGP(RCA_INFO,("WriteStream: going to alloc an mdl of length %lu\n", ((PKSSTREAM_HEADER)Irp->AssociatedIrp.SystemBuffer)->DataUsed)); pMdl = IoAllocateMdl(pMdlVirtualAddress, ((PKSSTREAM_HEADER)Irp->AssociatedIrp.SystemBuffer)->DataUsed, FALSE, FALSE, NULL); if (pMdl == NULL) { RCADEBUGP(RCA_WARNING,("WriteStream: STATUS_INSUFFICIENT_RESOURCES for MDL\n")); return(STATUS_INSUFFICIENT_RESOURCES); } RCADEBUGP(RCA_INFO,("WriteStream: building partial MDL\n")); BytesToCopy = ((PKSSTREAM_HEADER)Irp->AssociatedIrp.SystemBuffer)->DataUsed; // // For debugging only. // if (g_ulBufferSize > 0) { BytesToCopy = g_ulBufferSize; } IoBuildPartialMdl(Irp->MdlAddress, pMdl, pMdlVirtualAddress, BytesToCopy); // // TBS: wait for CSA soltion for passing header info across transform filters. // Now we're sure the header is in the system buffer. We also need to ship the header, since // we need the dataused number and (in future) CSA will specify timing and other info in there // that we need to get end-to-end. Allocate the MDL and put it on the end of the list // IoMarkIrpPending( Irp ); RCA_ACQUIRE_BRIDGE_PIN_LOCK(pDevIoPin->FilterInstance->BridgePin); pDevIoPin->FilterInstance->BridgePin->PendingSendsCount++; RCA_RELEASE_BRIDGE_PIN_LOCK(pDevIoPin->FilterInstance->BridgePin); Status = RCACoNdisSendFrame(VcContext, pMdl, (PVOID)Irp); if (Status != NDIS_STATUS_PENDING) { RCADEBUGP(RCA_ERROR, ("WriteStream: RCACoNdisSendFrame returned status 0x%x, " "manually calling send complete handler\n", Status)); RCASendCompleteCallback(VcContext, (PVOID) pDevIoPin->FilterInstance->BridgePin, (PVOID) Irp, pMdl, Status); } // // If status returned from RCACoNdisSendFrame was not STATUS_PENDING, then we // completed the IRP with that status. We need to set the status back to PENDING // here so that PinDispatchIoControl will not try to complete the IRP again. // Status = NDIS_STATUS_PENDING; } while (FALSE); return(Status); } NTSTATUS GetInterface( IN PIRP Irp, IN PKSPROPERTY Property, OUT PKSPIN_INTERFACE Interface ) /*++ Routine Description: Handles the KSPROPERTY_STREAM_INTERFACE property Get in the Stream property set. Returns the interface on the Dev I/O Pin so that positional translations can be performed. Arguments: Irp - Device control Irp. Property - Specific property request. Interface - The place in which to put the Interface. Return Values: Returns STATUS_SUCCESS. --*/ { RCADEBUGP(RCA_INFO, ("GetInterface: Enter\n")); Interface->Set = KSINTERFACESETID_Standard; // Interface->Id = KSINTERFACE_STANDARD_POSITION; Irp->IoStatus.Information = sizeof(KSPIN_INTERFACE); RCADEBUGP(RCA_INFO, ("GetInterface: Exit - Returning STATUS_NOT_IMPLEMENTED\n")); DbgBreakPoint(); return(STATUS_NOT_IMPLEMENTED); }