/*++ Copyright (c) 1989, 1990, 1991 Microsoft Corporation Module Name: action.c Abstract: This module contains support for the TdiAction handler. Author: David Beaver (dbeaver) 2-July-1991 Environment: Kernel mode Revision History: --*/ #include "precomp.h" #pragma hdrstop typedef struct _QUERY_INDICATION { UCHAR Command; USHORT Data2; UCHAR DestinationName[16]; UCHAR SourceName[16]; } QUERY_INDICATION, *PQUERY_INDICATION; typedef struct _ACTION_QUERY_INDICATION { TDI_ACTION_HEADER Header; QUERY_INDICATION QueryIndication; } ACTION_QUERY_INDICATION, *PACTION_QUERY_INDICATION; typedef struct _DATAGRAM_INDICATION { UCHAR DestinationName[16]; UCHAR SourceName[16]; USHORT DatagramBufferLength; UCHAR DatagramBuffer[1]; } DATAGRAM_INDICATION, *PDATAGRAM_INDICATION; typedef struct _ACTION_DATAGRAM_INDICATION { TDI_ACTION_HEADER Header; DATAGRAM_INDICATION DatagramIndication; } ACTION_DATAGRAM_INDICATION, *PACTION_DATAGRAM_INDICATION; #define QUERY_INDICATION_CODE 1 #define DATAGRAM_INDICATION_CODE 2 VOID NbfCancelAction( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); NTSTATUS NbfTdiAction( IN PDEVICE_CONTEXT DeviceContext, IN PIRP Irp ) /*++ Routine Description: This routine performs the TdiAction request for the transport provider. Arguments: DeviceContext - The device context for the operation Irp - the Irp for the requested operation. Return Value: NTSTATUS - status of operation. --*/ { NTSTATUS status; PIO_STACK_LOCATION irpSp; PTDI_ACTION_HEADER ActionHeader; LARGE_INTEGER timeout = {0,0}; PTP_REQUEST tpRequest; KIRQL oldirql, cancelirql; ULONG BytesRequired; // // what type of status do we want? // irpSp = IoGetCurrentIrpStackLocation (Irp); if ((!Irp->MdlAddress) || (MmGetMdlByteCount(Irp->MdlAddress) < sizeof(TDI_ACTION_HEADER))) { return STATUS_INVALID_PARAMETER; } ActionHeader = (PTDI_ACTION_HEADER)MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); if (!ActionHeader) { return STATUS_INSUFFICIENT_RESOURCES; } // // Make sure we have required number of bytes for this type of request // switch (ActionHeader->ActionCode) { case QUERY_INDICATION_CODE: BytesRequired = sizeof(ACTION_QUERY_INDICATION); break; case DATAGRAM_INDICATION_CODE: BytesRequired = sizeof(ACTION_DATAGRAM_INDICATION); break; default: return STATUS_NOT_IMPLEMENTED; } if (MmGetMdlByteCount(Irp->MdlAddress) < BytesRequired) { return STATUS_INVALID_PARAMETER; } // // Here the request is one of QUERY_INDICATION or DATAGRAM_INDICATION // // // These two requests are sent by RAS to "MABF" // if (!RtlEqualMemory ((PVOID)(&ActionHeader->TransportId), "MABF", 4)) { return STATUS_NOT_SUPPORTED; } // // They should be sent on the control channel // if (irpSp->FileObject->FsContext2 != UlongToPtr(NBF_FILE_TYPE_CONTROL)) { return STATUS_NOT_SUPPORTED; } // // Create a request to describe this. // status = NbfCreateRequest ( Irp, // IRP for this request. DeviceContext, // context. REQUEST_FLAGS_DC, // partial flags. Irp->MdlAddress, MmGetMdlByteCount(Irp->MdlAddress), timeout, &tpRequest); if (NT_SUCCESS (status)) { NbfReferenceDeviceContext ("Action", DeviceContext, DCREF_REQUEST); tpRequest->Owner = DeviceContextType; tpRequest->FrameContext = (USHORT)irpSp->FileObject->FsContext; IoAcquireCancelSpinLock(&cancelirql); ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock,&oldirql); // // Disallow these requests on a stopping device. // if (DeviceContext->State != DEVICECONTEXT_STATE_OPEN) { RELEASE_SPIN_LOCK (&DeviceContext->SpinLock,oldirql); IoReleaseCancelSpinLock(cancelirql); NbfCompleteRequest (tpRequest, STATUS_DEVICE_NOT_READY, 0); } else { if (ActionHeader->ActionCode == QUERY_INDICATION_CODE) { InsertTailList ( &DeviceContext->QueryIndicationQueue, &tpRequest->Linkage); } else { InsertTailList ( &DeviceContext->DatagramIndicationQueue, &tpRequest->Linkage); } DeviceContext->IndicationQueuesInUse = TRUE; // // If this IRP has been cancelled, then call the // cancel routine. // if (Irp->Cancel) { RELEASE_SPIN_LOCK (&DeviceContext->SpinLock,oldirql); Irp->CancelIrql = cancelirql; NbfCancelAction((PDEVICE_OBJECT)DeviceContext, Irp); return STATUS_PENDING; } IoSetCancelRoutine(Irp, NbfCancelAction); RELEASE_SPIN_LOCK (&DeviceContext->SpinLock,oldirql); IoReleaseCancelSpinLock(cancelirql); } status = STATUS_PENDING; } return status; } VOID NbfCancelAction( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is called by the I/O system to cancel an Action. What is done to cancel it is specific to each action. NOTE: This routine is called with the CancelSpinLock held and is responsible for releasing it. Arguments: DeviceObject - Pointer to the device object for this driver. Irp - Pointer to the request packet representing the I/O request. Return Value: none. --*/ { KIRQL oldirql; PIO_STACK_LOCATION IrpSp; PTP_REQUEST Request; PLIST_ENTRY p; BOOLEAN Found; PTDI_ACTION_HEADER ActionHeader; PLIST_ENTRY QueueHead, QueueEnd; PDEVICE_CONTEXT DeviceContext = (PDEVICE_CONTEXT)DeviceObject; // // Get a pointer to the current stack location in the IRP. This is where // the function codes and parameters are stored. // IrpSp = IoGetCurrentIrpStackLocation (Irp); ASSERT ((IrpSp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL) && (IrpSp->MinorFunction == TDI_ACTION)); ActionHeader = (PTDI_ACTION_HEADER)MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); if (!ActionHeader) { return; } switch (ActionHeader->ActionCode) { case QUERY_INDICATION_CODE: case DATAGRAM_INDICATION_CODE: // // Scan through the appropriate queue, looking for this IRP. // If we find it, we just remove it from the queue; there // is nothing else involved in cancelling. // ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); if (ActionHeader->ActionCode == QUERY_INDICATION_CODE) { QueueHead = DeviceContext->QueryIndicationQueue.Flink; QueueEnd = &DeviceContext->QueryIndicationQueue; } else { QueueHead = DeviceContext->DatagramIndicationQueue.Flink; QueueEnd = &DeviceContext->DatagramIndicationQueue; } Found = FALSE; for (p = QueueHead; p != QueueEnd; p = p->Flink) { Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); if (Request->IoRequestPacket == Irp) { // // Found it, remove it from the list here. // RemoveEntryList (p); Found = TRUE; break; } } RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); IoReleaseCancelSpinLock (Irp->CancelIrql); if (Found) { NbfCompleteRequest (Request, STATUS_CANCELLED, 0); } else { #if DBG DbgPrint("NBF: Tried to cancel action %lx on %lx, not found\n", Irp, DeviceContext); #endif } break; default: IoReleaseCancelSpinLock (Irp->CancelIrql); break; } } VOID NbfStopControlChannel( IN PDEVICE_CONTEXT DeviceContext, IN USHORT ChannelIdentifier ) /*++ Routine Description: This routine is called when an MJ_CLEANUP IRP is received on a control channel. It walks the device context's list of pending action requests and cancels those associated with this channel (as identified by ChannelIdentifier. Arguments: DeviceContext - Pointer to our device context. ChannelIdentifier - The identifier for this open of the control channel, which is stored in Request->FrameContext for requests made on this channel. Return Value: None --*/ { KIRQL oldirql, cancelirql; PTP_REQUEST Request; PLIST_ENTRY p; UINT i; BOOLEAN FoundRequest; PLIST_ENTRY QueueHead, QueueEnd; // // Scan both queues, looking for requests. Since the list // may change, we scan until we find one, then remove it // and complete it. We then start scanning at the beginning // again. We continue until we find none on the queue that // belong to this control channel. // // The outer loop only runs twice; the first time it // processes QueryIndicationQueue, the second time // DatagramIndicationQueue. // for (i = 0; i < 2; i++) { do { // // Loop until we do not find a request on this // pass through the queue. // FoundRequest = FALSE; IoAcquireCancelSpinLock(&cancelirql); ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); if (i == 0) { QueueHead = DeviceContext->QueryIndicationQueue.Flink; QueueEnd = &DeviceContext->QueryIndicationQueue; } else { QueueHead = DeviceContext->DatagramIndicationQueue.Flink; QueueEnd = &DeviceContext->DatagramIndicationQueue; } // // Scan the appropriate queue for a request on this // channel. // for (p = QueueHead; p != QueueEnd; p = p->Flink) { Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); if (Request->FrameContext == ChannelIdentifier) { // // Found it, remove it from the list here. // IoSetCancelRoutine(Request->IoRequestPacket, NULL); RemoveEntryList (p); FoundRequest = TRUE; break; } } RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); IoReleaseCancelSpinLock(cancelirql); // // If we found a request, then complete it and loop // back to the top of the while loop to rescan the // list. If not, then we will exit the while loop // now. // if (FoundRequest) { NbfCompleteRequest (Request, STATUS_CANCELLED, 0); } } while (FoundRequest); } } VOID NbfActionQueryIndication( IN PDEVICE_CONTEXT DeviceContext, IN PNBF_HDR_CONNECTIONLESS UiFrame ) /*++ Routine Description: This routine is called after a UI frame of type NAME_QUERY, ADD_NAME_QUERY, or ADD_GROUP_NAME_QUERY has been processed. It checks if there is a QUERY.INDICATION IRP waiting to be completed, and if so completes it. Arguments: DeviceContext - Pointer to our device context. UiFrame - Pointer to the incoming frame. The first byte of information is the first byte of the NetBIOS connectionless header. Return Value: None --*/ { KIRQL oldirql, cancelirql; PTP_REQUEST Request; PLIST_ENTRY p; PMDL Mdl; PACTION_QUERY_INDICATION ActionHeader; PQUERY_INDICATION QueryIndication; IoAcquireCancelSpinLock (&cancelirql); ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); if (!IsListEmpty (&DeviceContext->QueryIndicationQueue)) { p = RemoveHeadList (&DeviceContext->QueryIndicationQueue); RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); IoSetCancelRoutine(Request->IoRequestPacket,NULL); IoReleaseCancelSpinLock(cancelirql); Mdl = Request->Buffer2; ActionHeader = (PACTION_QUERY_INDICATION) (MmGetSystemAddressForMdl(Mdl)); QueryIndication = &ActionHeader->QueryIndication; // // Copy over data from frame (note that dest and source // address are copied with one call). // QueryIndication->Command = UiFrame->Command; RtlCopyMemory ((PUCHAR)(&QueryIndication->Data2), (PUCHAR)(&UiFrame->Data2Low), 2); RtlCopyMemory ((PUCHAR)(QueryIndication->DestinationName), (PUCHAR)(UiFrame->DestinationName), 2 * NETBIOS_NAME_LENGTH); NbfCompleteRequest (Request, STATUS_SUCCESS, sizeof(ACTION_QUERY_INDICATION)); } else { RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); IoReleaseCancelSpinLock(cancelirql); } } VOID NbfActionDatagramIndication( IN PDEVICE_CONTEXT DeviceContext, IN PNBF_HDR_CONNECTIONLESS UiFrame, IN ULONG Length ) /*++ Routine Description: This routine is called after a datagram frame has been received. It checks if there is a DATAGRAM.INDICATION IRP waiting to be completed, and if so completes it. Arguments: DeviceContext - Pointer to our device context. UiFrame - Pointer to the incoming frame. The first byte of information is the first byte of the NetBIOS connectionless header. Length - The length of the frame starting at UiFrame. Return Value: None --*/ { KIRQL oldirql, cancelirql; PTP_REQUEST Request; PLIST_ENTRY p; PACTION_DATAGRAM_INDICATION ActionHeader; PDATAGRAM_INDICATION DatagramIndication; ULONG CopyLength; PMDL Mdl; NTSTATUS Status; IoAcquireCancelSpinLock (&cancelirql); ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); if (!IsListEmpty (&DeviceContext->DatagramIndicationQueue)) { p = RemoveHeadList (&DeviceContext->DatagramIndicationQueue); RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); IoSetCancelRoutine(Request->IoRequestPacket, NULL); IoReleaseCancelSpinLock(cancelirql); Mdl = Request->Buffer2; ActionHeader = (PACTION_DATAGRAM_INDICATION) (MmGetSystemAddressForMdl(Mdl)); DatagramIndication = &ActionHeader->DatagramIndication; // // Copy over data from frame (note that dest and source // address are copied with one call). // RtlCopyMemory ((PUCHAR)(DatagramIndication->DestinationName), (PUCHAR)(UiFrame->DestinationName), 2 * NETBIOS_NAME_LENGTH); if ((Length-sizeof(NBF_HDR_CONNECTIONLESS)) <= (ULONG)DatagramIndication->DatagramBufferLength) { CopyLength = Length - sizeof(NBF_HDR_CONNECTIONLESS); Status = STATUS_SUCCESS; } else { CopyLength = DatagramIndication->DatagramBufferLength; Status = STATUS_BUFFER_OVERFLOW; } RtlCopyMemory( (PUCHAR)DatagramIndication->DatagramBuffer, ((PUCHAR)UiFrame) + sizeof(NBF_HDR_CONNECTIONLESS), CopyLength); DatagramIndication->DatagramBufferLength = (USHORT)CopyLength; NbfCompleteRequest (Request, Status, CopyLength + FIELD_OFFSET (ACTION_DATAGRAM_INDICATION, DatagramIndication.DatagramBuffer[0])); } else { RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); IoReleaseCancelSpinLock(cancelirql); } }