diff options
Diffstat (limited to 'private/ntos/tdi/nbf')
42 files changed, 55009 insertions, 0 deletions
diff --git a/private/ntos/tdi/nbf/action.c b/private/ntos/tdi/nbf/action.c new file mode 100644 index 000000000..84a51695a --- /dev/null +++ b/private/ntos/tdi/nbf/action.c @@ -0,0 +1,642 @@ +/*++ + +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; + + + // + // what type of status do we want? + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + ActionHeader = (PTDI_ACTION_HEADER)MmGetSystemAddressForMdl (Irp->MdlAddress); + + + // + // Handle the requests based on the action code. + // + + switch (ActionHeader->ActionCode) { + + case QUERY_INDICATION_CODE: + case DATAGRAM_INDICATION_CODE: + + // + // 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 != (PVOID)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; + + } + + break; + + default: + + status = STATUS_NOT_IMPLEMENTED; + + } + + + 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)MmGetSystemAddressForMdl (Irp->MdlAddress); + + 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); + + } +} + diff --git a/private/ntos/tdi/nbf/address.c b/private/ntos/tdi/nbf/address.c new file mode 100644 index 000000000..f5bda0cc7 --- /dev/null +++ b/private/ntos/tdi/nbf/address.c @@ -0,0 +1,3046 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + address.c + +Abstract: + + This module contains code which implements the TP_ADDRESS object. + Routines are provided to create, destroy, reference, and dereference, + transport address objects. + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#if DBG +#define NbfDbgShowAddr(TNA)\ + { \ + if ((TNA) == NULL) { \ + NbfPrint0("<NetBios broadcast>\n"); \ + } else { \ + NbfPrint6("%c %c %c %c %d (%c)\n", \ + (TNA)->NetbiosName[0], \ + (TNA)->NetbiosName[1], \ + (TNA)->NetbiosName[4], \ + (TNA)->NetbiosName[6], \ + (TNA)->NetbiosName[15], \ + (TNA)->NetbiosNameType + 'A'); \ + } \ + } +#else +#define NbfDbgShowAddr(TNA) +#endif + +// +// Map all generic accesses to the same one. +// + +STATIC GENERIC_MAPPING AddressGenericMapping = + { READ_CONTROL, READ_CONTROL, READ_CONTROL, READ_CONTROL }; + + +VOID +AddressTimeoutHandler( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) + +/*++ + +Routine Description: + + This routine is executed as a DPC at DISPATCH_LEVEL when the timeout + period for the ADD_NAME_QUERY/ADD_NAME_RECOGNIZED protocol expires. + The retry count in the Address object is decremented, and if it reaches 0, + the address is registered. If the retry count has not reached zero, + then the ADD NAME QUERY is retried. + +Arguments: + + Dpc - Pointer to a system DPC object. + + DeferredContext - Pointer to the TP_ADDRESS block representing the + address that is being registered. + + SystemArgument1 - Not used. + + SystemArgument2 - Not used. + +Return Value: + + none. + +--*/ + +{ + PTP_ADDRESS_FILE addressFile; + PTP_ADDRESS address; + PDEVICE_CONTEXT DeviceContext; + PLIST_ENTRY p; + LARGE_INTEGER timeout; + + Dpc, SystemArgument1, SystemArgument2; // prevent compiler warnings + + ENTER_NBF; + + + address = (PTP_ADDRESS)DeferredContext; + DeviceContext = address->Provider; + + // + // We are waiting for an ADD_NAME_RECOGNIZED indicating that there is a + // conflict. Decrement the retry count, and if it dropped to zero, + // then we've waited a sufficiently long time. If there was no conflict, + // complete all waiting file opens for the address. + // + + ACQUIRE_DPC_SPIN_LOCK (&address->SpinLock); + + if ((address->Flags & ADDRESS_FLAGS_QUICK_REREGISTER) != 0) { + + BOOLEAN DuplicateName; + PTP_CONNECTION Connection; + + DuplicateName = ((address->Flags & (ADDRESS_FLAGS_DUPLICATE_NAME|ADDRESS_FLAGS_CONFLICT)) != 0); + + for (p=address->ConnectionDatabase.Flink; + p != &address->ConnectionDatabase; + p=p->Flink) { + + Connection = CONTAINING_RECORD (p, TP_CONNECTION, AddressList); + + if ((Connection->Flags2 & CONNECTION_FLAGS2_STOPPING) != 0) { + continue; + } + + RELEASE_DPC_SPIN_LOCK (&address->SpinLock); + + if ((Connection->Flags2 & CONNECTION_FLAGS2_W_ADDRESS) != 0) { + + if (DuplicateName) { + + NbfStopConnection (Connection, STATUS_DUPLICATE_NAME); + + } else { + + // + // Continue with the connection attempt. + // + ULONG NameQueryTimeout; + + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + Connection->Flags2 &= ~CONNECTION_FLAGS2_W_ADDRESS; + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + KeQueryTickCount (&Connection->ConnectStartTime); + + NameQueryTimeout = Connection->Provider->NameQueryTimeout; + if (Connection->Provider->MacInfo.MediumAsync && + !Connection->Provider->MediumSpeedAccurate) { + NameQueryTimeout = NAME_QUERY_TIMEOUT / 10; + } + + NbfSendNameQuery ( + Connection, + TRUE); + + NbfStartConnectionTimer ( + Connection, + ConnectionEstablishmentTimeout, + NameQueryTimeout); + } + + } + + ACQUIRE_DPC_SPIN_LOCK (&address->SpinLock); + + } + + address->Flags &= ~ADDRESS_FLAGS_QUICK_REREGISTER; + + RELEASE_DPC_SPIN_LOCK (&address->SpinLock); + NbfDereferenceAddress ("Timer, registered", address, AREF_TIMER); + + } else if ((address->Flags & (ADDRESS_FLAGS_DUPLICATE_NAME|ADDRESS_FLAGS_CONFLICT)) != 0) { + + PIRP irp; + + // + // the address registration has failed. We signal the user in + // the normal way (by failing the open of the address). Now clean up + // the transport's data structures. + // + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint1 ("AddressTimeoutHandler %lx: duplicate\n", address); + } + + address->Flags &= ~ADDRESS_FLAGS_REGISTERING; +// address->Flags |= ADDRESS_FLAGS_STOPPING; + + // + // BUGBUG: This is probably all overkill, the + // uframes handler will already have called + // NbfStopAddress, which will tear off all + // the address files etc., and set the + // STOPPING flag which prevents further opens. + // + + p = address->AddressFileDatabase.Flink; + while (p != &address->AddressFileDatabase) { + addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); + p = p->Flink; + + if (addressFile->Irp != NULL) { + irp = addressFile->Irp; + addressFile->Irp = NULL; + RELEASE_DPC_SPIN_LOCK (&address->SpinLock); + irp->IoStatus.Information = 0; + irp->IoStatus.Status = STATUS_DUPLICATE_NAME; + LEAVE_NBF; + IoCompleteRequest (irp, IO_NETWORK_INCREMENT); + ENTER_NBF; + + NbfStopAddressFile (addressFile, address); + + ACQUIRE_DPC_SPIN_LOCK (&address->SpinLock); + } + + } + + RELEASE_DPC_SPIN_LOCK (&address->SpinLock); + + // + // There will be no more timer events happening, so we dereference the + // address to account for the timer. + // + + NbfStopAddress (address); + NbfDereferenceAddress ("Timer, dup address", address, AREF_TIMER); + + } else { + + // + // has the address registration succeeded? + // + + if (--(address->Retries) <= 0) { // if retry count exhausted. + PIRP irp; + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint1 ("AddressTimeoutHandler %lx: successful.\n", address); + } + + address->Flags &= ~ADDRESS_FLAGS_REGISTERING; + + p = address->AddressFileDatabase.Flink; + + while (p != &address->AddressFileDatabase) { + addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); + p = p->Flink; + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint3 ("AddressTimeoutHandler %lx: Completing IRP %lx for file %lx\n", + address, + addressFile->Irp, + addressFile); + } + + if (addressFile->Irp != NULL) { + irp = addressFile->Irp; + addressFile->Irp = NULL; + addressFile->State = ADDRESSFILE_STATE_OPEN; + RELEASE_DPC_SPIN_LOCK (&address->SpinLock); + irp->IoStatus.Information = 0; + irp->IoStatus.Status = STATUS_SUCCESS; + + LEAVE_NBF; + IoCompleteRequest (irp, IO_NETWORK_INCREMENT); + ENTER_NBF; + + ACQUIRE_DPC_SPIN_LOCK (&address->SpinLock); + } + + } + + RELEASE_DPC_SPIN_LOCK (&address->SpinLock); + + // + // Dereference the address if we're all done. + // + + NbfDereferenceAddress ("Timer, registered", address, AREF_TIMER); + + } else { + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint2 ("AddressTimeoutHandler %lx: step %x.\n", + address, + DeviceContext->AddNameQueryRetries - address->Retries); + } + + // + // restart the timer if we haven't yet completed registration + // + + RELEASE_DPC_SPIN_LOCK (&address->SpinLock); + + timeout.LowPart = (ULONG)(-(LONG)DeviceContext->AddNameQueryTimeout); + timeout.HighPart = -1; + KeSetTimer (&address->Timer,*(PTIME)&timeout, &address->Dpc); + (VOID)NbfSendAddNameQuery (address); // send another ADD_NAME_QUERY. + } + + } + + LEAVE_NBF; + return; + +} /* AddressTimeoutHandler */ + + +TDI_ADDRESS_NETBIOS UNALIGNED * +NbfParseTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN BOOLEAN BroadcastAddressOk +) + +/*++ + +Routine Description: + + This routine scans a TRANSPORT_ADDRESS, looking for an address + of type TDI_ADDRESS_TYPE_NETBIOS. + +Arguments: + + Transport - The generic TDI address. + + BroadcastAddressOk - TRUE if we should return the broadcast + address if found. If so, a value of (PVOID)-1 indicates + the broadcast address. + +Return Value: + + A pointer to the Netbios address, or NULL if none is found, + or (PVOID)-1 if the broadcast address is found. + +--*/ + +{ + TA_ADDRESS UNALIGNED * addressName; + INT i; + + addressName = &TransportAddress->Address[0]; + + // + // The name can be passed with multiple entries; we'll take and use only + // the Netbios one. + // + + for (i=0;i<TransportAddress->TAAddressCount;i++) { + if (addressName->AddressType == TDI_ADDRESS_TYPE_NETBIOS) { + if ((addressName->AddressLength == 0) && + BroadcastAddressOk) { + return (PVOID)-1; + } else if (addressName->AddressLength == + sizeof(TDI_ADDRESS_NETBIOS)) { + return((TDI_ADDRESS_NETBIOS UNALIGNED *)(addressName->Address)); + } + } + + addressName = (TA_ADDRESS UNALIGNED *)(addressName->Address + + addressName->AddressLength); + } + return NULL; + +} /* NbfParseTdiAddress */ + + +BOOLEAN +NbfValidateTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN ULONG TransportAddressLength +) + +/*++ + +Routine Description: + + This routine scans a TRANSPORT_ADDRESS, verifying that the + components of the address do not extend past the specified + length. + +Arguments: + + TransportAddress - The generic TDI address. + + TransportAddressLength - The specific length of TransportAddress. + +Return Value: + + TRUE if the address is valid, FALSE otherwise. + +--*/ + +{ + PUCHAR AddressEnd = ((PUCHAR)TransportAddress) + TransportAddressLength; + TA_ADDRESS UNALIGNED * addressName; + INT i; + + if (TransportAddressLength < sizeof(TransportAddress->TAAddressCount)) { + NbfPrint0 ("NbfValidateTdiAddress: runt address\n"); + return FALSE; + } + + addressName = &TransportAddress->Address[0]; + + for (i=0;i<TransportAddress->TAAddressCount;i++) { + if (addressName->Address > AddressEnd) { + NbfPrint0 ("NbfValidateTdiAddress: address too short\n"); + return FALSE; + } + addressName = (TA_ADDRESS UNALIGNED *)(addressName->Address + + addressName->AddressLength); + } + + if ((PUCHAR)addressName > AddressEnd) { + NbfPrint0 ("NbfValidateTdiAddress: address too short\n"); + return FALSE; + } + return TRUE; + +} /* NbfValidateTdiAddress */ + + +NTSTATUS +NbfOpenAddress( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine opens a file that points to an existing address object, or, if + the object doesn't exist, creates it (note that creation of the address + object includes registering the address, and may take many seconds to + complete, depending upon system configuration). + + If the address already exists, and it has an ACL associated with it, the + ACL is checked for access rights before allowing creation of the address. + +Arguments: + + DeviceObject - pointer to the device object describing the NBF transport. + + Irp - a pointer to the Irp used for the creation of the address. + + IrpSp - a pointer to the Irp stack location. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + NTSTATUS status; + PTP_ADDRESS address; + PTP_ADDRESS_FILE addressFile; + PNBF_NETBIOS_ADDRESS networkName; // Network name string. + PFILE_FULL_EA_INFORMATION ea; + TRANSPORT_ADDRESS UNALIGNED *name; + TDI_ADDRESS_NETBIOS UNALIGNED *netbiosName; + ULONG DesiredShareAccess; + KIRQL oldirql; + PACCESS_STATE AccessState; + ACCESS_MASK GrantedAccess; + BOOLEAN AccessAllowed; + BOOLEAN QuickAdd = FALSE; + + DeviceContext = (PDEVICE_CONTEXT)DeviceObject; + + // + // The network name is in the EA, passed in AssociatedIrp.SystemBuffer + // + + ea = (PFILE_FULL_EA_INFORMATION)Irp->AssociatedIrp.SystemBuffer; + if (ea == NULL) { + NbfPrint1("OpenAddress: IRP %lx has no EA\n", Irp); + return STATUS_INVALID_ADDRESS_COMPONENT; + } + + // + // this may be a valid name; parse the name from the EA and use it if OK. + // + + name = (TRANSPORT_ADDRESS UNALIGNED *)&ea->EaName[ea->EaNameLength+1]; + + if (!NbfValidateTdiAddress(name, ea->EaValueLength)) { + return STATUS_INVALID_ADDRESS_COMPONENT; + } + + // + // The name can have with multiple entries; we'll use the Netbios one. + // This call returns NULL if not Netbios address is found, (PVOID)-1 + // if it is the broadcast address, and a pointer to a Netbios + // address otherwise. + // + + netbiosName = NbfParseTdiAddress(name, TRUE); + + if (netbiosName != NULL) { + if (netbiosName != (PVOID)-1) { + networkName = (PNBF_NETBIOS_ADDRESS)ExAllocatePoolWithTag ( + NonPagedPool, + sizeof (NBF_NETBIOS_ADDRESS), + 'nFBN'); + if (networkName == NULL) { + PANIC ("NbfOpenAddress: PANIC! could not allocate networkName!\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 1, + sizeof(TA_NETBIOS_ADDRESS), + ADDRESS_RESOURCE_ID); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // get the name to local storage + // + + if ((netbiosName->NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_GROUP) || + (netbiosName->NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_QUICK_GROUP)) { + networkName->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_GROUP; + } else { + networkName->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + } + RtlCopyMemory (networkName->NetbiosName, netbiosName->NetbiosName, 16); + + if ((netbiosName->NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_QUICK_UNIQUE) || + (netbiosName->NetbiosNameType == TDI_ADDRESS_NETBIOS_TYPE_QUICK_GROUP)) { + QuickAdd = TRUE; + } + } else { + networkName = NULL; + } + + } else { + NbfPrint1("OpenAddress: IRP %lx has no NETBIOS address\n", Irp); + return STATUS_INVALID_ADDRESS_COMPONENT; + } + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint1 ("OpenAddress %s: ", + ((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + "shared" : "exclusive"); + NbfDbgShowAddr (networkName); + } + + // + // get an address file structure to represent this address. + // + + status = NbfCreateAddressFile (DeviceContext, &addressFile); + + if (!NT_SUCCESS (status)) { + if (networkName != NULL) { + ExFreePool (networkName); + } + return status; + } + + // + // See if this address is already established. This call automatically + // increments the reference count on the address so that it won't disappear + // from underneath us after this call but before we have a chance to use it. + // + // To ensure that we don't create two address objects for the + // same address, we hold the device context AddressResource until + // we have found the address or created a new one. + // + + ExAcquireResourceExclusive (&DeviceContext->AddressResource, TRUE); + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + address = NbfLookupAddress (DeviceContext, networkName); + + if (address == NULL) { + + // + // This address doesn't exist. Create it, and start the process of + // registering it. + // + + status = NbfCreateAddress ( + DeviceContext, + networkName, + &address); + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + if (NT_SUCCESS (status)) { + + // + // Initialize the shared access now. We use read access + // to control all access. + // + + DesiredShareAccess = (ULONG) + (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + IoSetShareAccess( + FILE_READ_DATA, + DesiredShareAccess, + IrpSp->FileObject, + &address->u.ShareAccess); + + + // + // Assign the security descriptor (need to do this with + // the spinlock released because the descriptor is not + // mapped. BUGBUG: Need to synchronize Assign and Access). + // + + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + status = SeAssignSecurity( + NULL, // parent descriptor + AccessState->SecurityDescriptor, + &address->SecurityDescriptor, + FALSE, // is directory + &AccessState->SubjectSecurityContext, + &AddressGenericMapping, + PagedPool); + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint3 ("Assign security A %lx AF %lx, status %lx\n", + address, + addressFile, + status); + } + + if (!NT_SUCCESS(status)) { + + // + // Error, return status. + // + + IoRemoveShareAccess (IrpSp->FileObject, &address->u.ShareAccess); + ExReleaseResource (&DeviceContext->AddressResource); + NbfDereferenceAddress ("Device context stopping", address, AREF_TEMP_CREATE); + NbfDereferenceAddressFile (addressFile); + return status; + + } + + ExReleaseResource (&DeviceContext->AddressResource); + + // + // if the adapter isn't ready, we can't do any of this; get out + // + + if (DeviceContext->State != DEVICECONTEXT_STATE_OPEN) { + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint3("OpenAddress A %lx AF %lx: DeviceContext %lx not open\n", + address, + addressFile, + DeviceContext); + } + NbfDereferenceAddressFile (addressFile); + status = STATUS_DEVICE_NOT_READY; + + } else { + + IrpSp->FileObject->FsContext = (PVOID)addressFile; + IrpSp->FileObject->FsContext2 = + (PVOID)TDI_TRANSPORT_ADDRESS_FILE; + addressFile->FileObject = IrpSp->FileObject; + addressFile->Irp = Irp; + addressFile->Address = address; + + NbfReferenceAddress("Opened new", address, AREF_OPEN); + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint2("OpenAddress A %lx AF %lx: created.\n", + address, + addressFile); + } + + ExInterlockedInsertTailList( + &address->AddressFileDatabase, + &addressFile->Linkage, + &address->SpinLock); + + + // + // Begin address registration unless this is the broadcast + // address (which is a "fake" address with no corresponding + // Netbios address) or the reserved address, which we know + // is unique since it is based on the adapter address. + // + // Also, for "quick" add names, do not register. + // + + if ((networkName != NULL) && + (!RtlEqualMemory (networkName->NetbiosName, + DeviceContext->ReservedNetBIOSAddress, + NETBIOS_NAME_LENGTH)) && + (!QuickAdd)) { + + NbfRegisterAddress (address); // begin address registration. + status = STATUS_PENDING; + + } else { + + address->Flags &= ~ADDRESS_FLAGS_NEEDS_REG; + addressFile->Irp = NULL; + addressFile->State = ADDRESSFILE_STATE_OPEN; + status = STATUS_SUCCESS; + + } + + } + + NbfDereferenceAddress("temp create", address, AREF_TEMP_CREATE); + + } else { + + ExReleaseResource (&DeviceContext->AddressResource); + + // + // If the address could not be created, and is not in the process of + // being created, then we can't open up an address. + // + + if (networkName != NULL) { + ExFreePool (networkName); + } + + NbfDereferenceAddressFile (addressFile); + + } + + } else { + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + // + // The address already exists. Check the ACL and see if we + // can access it. If so, simply use this address as our address. + // + + AccessState = IrpSp->Parameters.Create.SecurityContext->AccessState; + + AccessAllowed = SeAccessCheck( + address->SecurityDescriptor, + &AccessState->SubjectSecurityContext, + FALSE, // tokens locked + IrpSp->Parameters.Create.SecurityContext->DesiredAccess, + (ACCESS_MASK)0, // previously granted + NULL, // privileges + &AddressGenericMapping, + Irp->RequestorMode, + &GrantedAccess, + &status); + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint4 ("Access check A %lx AF %lx, %s (%lx)\n", + address, + addressFile, + AccessAllowed ? "allowed" : "not allowed", + status); + } + + if (AccessAllowed) { + + // + // Access was successful, make sure Status is right. + // + + status = STATUS_SUCCESS; + + // + // BUGBUG: Compare DesiredAccess to GrantedAccess? + // + + + // + // Check that the name is of the correct type (unique vs. group) + // We don't need to check this for the broadcast address. + // + // BUGBUG: This code is structured funny, the only reason + // this is inside this if is to avoid indenting too much. + // + + if (networkName != NULL) { + if (address->NetworkName->NetbiosNameType != + networkName->NetbiosNameType) { + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint2 ("Address types differ: old %c, new %c\n", + address->NetworkName->NetbiosNameType + 'A', + networkName->NetbiosNameType + 'A'); + } + + status = STATUS_DUPLICATE_NAME; + + } + } + + } + + + if (!NT_SUCCESS (status)) { + + ExReleaseResource (&DeviceContext->AddressResource); + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint2("OpenAddress A %lx AF %lx: ACL bad.\n", + address, + addressFile); + } + + NbfDereferenceAddressFile (addressFile); + + } else { + + // + // Now check that we can obtain the desired share + // access. We use read access to control all access. + // + + DesiredShareAccess = (ULONG) + (((IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_READ) || + (IrpSp->Parameters.Create.ShareAccess & FILE_SHARE_WRITE)) ? + FILE_SHARE_READ : 0); + + status = IoCheckShareAccess( + FILE_READ_DATA, + DesiredShareAccess, + IrpSp->FileObject, + &address->u.ShareAccess, + TRUE); + + if (!NT_SUCCESS (status)) { + + ExReleaseResource (&DeviceContext->AddressResource); + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint2("OpenAddress A %lx AF %lx: ShareAccess problem.\n", + address, + addressFile); + } + + NbfDereferenceAddressFile (addressFile); + + } else { + + ExReleaseResource (&DeviceContext->AddressResource); + + ACQUIRE_SPIN_LOCK (&address->SpinLock, &oldirql); + + // + // now, if the address registered, we simply return success after + // pointing the file object at the address file (which points to + // the address). If the address registration is pending, we mark + // the registration pending and let the registration completion + // routine complete the open. If the address is bad, we simply + // fail the open. + // + + if ((address->Flags & + (ADDRESS_FLAGS_CONFLICT | + ADDRESS_FLAGS_REGISTERING | + ADDRESS_FLAGS_DEREGISTERING | + ADDRESS_FLAGS_DUPLICATE_NAME | + ADDRESS_FLAGS_NEEDS_REG | + ADDRESS_FLAGS_STOPPING | + ADDRESS_FLAGS_BAD_ADDRESS | + ADDRESS_FLAGS_CLOSED)) == 0) { + + InsertTailList ( + &address->AddressFileDatabase, + &addressFile->Linkage); + + addressFile->Irp = NULL; + addressFile->Address = address; + addressFile->FileObject = IrpSp->FileObject; + addressFile->State = ADDRESSFILE_STATE_OPEN; + + NbfReferenceAddress("open ready", address, AREF_OPEN); + + IrpSp->FileObject->FsContext = (PVOID)addressFile; + IrpSp->FileObject->FsContext2 = + (PVOID)TDI_TRANSPORT_ADDRESS_FILE; + + RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint2("OpenAddress A %lx AF %lx: address ready.\n", + address, + addressFile); + } + + status = STATUS_SUCCESS; + + } else { + + // + // if the address is still registering, make the open pending. + // + + if ((address->Flags & (ADDRESS_FLAGS_REGISTERING | ADDRESS_FLAGS_NEEDS_REG)) != 0) { + + InsertTailList ( + &address->AddressFileDatabase, + &addressFile->Linkage); + + addressFile->Irp = Irp; + addressFile->Address = address; + addressFile->FileObject = IrpSp->FileObject; + + NbfReferenceAddress("open registering", address, AREF_OPEN); + + IrpSp->FileObject->FsContext = (PVOID)addressFile; + IrpSp->FileObject->FsContext2 = + (PVOID)TDI_TRANSPORT_ADDRESS_FILE; + + RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint2("OpenAddress A %lx AF %lx: address registering.\n", + address, + addressFile); + } + + status = STATUS_PENDING; + + } else { + + if ((address->Flags & ADDRESS_FLAGS_CONFLICT) != 0) { + status = STATUS_DUPLICATE_NAME; + } else { + status = STATUS_DRIVER_INTERNAL_ERROR; + } + + RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint3("OpenAddress A %lx AF %lx: address flags %lx.\n", + address, + addressFile, + address->Flags); + } + + NbfDereferenceAddressFile (addressFile); + + } + } + } + } + + + // + // This isn't needed since it was not used in the + // creation of the address. + // + + if (networkName != NULL) { + ExFreePool (networkName); + } + + // + // Remove the reference from NbfLookupAddress. + // + + NbfDereferenceAddress ("Done opening", address, AREF_LOOKUP); + } + + return status; +} /* NbfOpenAddress */ + + +VOID +NbfAllocateAddress( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_ADDRESS *TransportAddress + ) + +/*++ + +Routine Description: + + This routine allocates storage for a transport address. Some minimal + initialization is done on the address. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + Address - Pointer to a place where this routine will return a pointer + to a transport address structure. Returns NULL if no storage + can be allocated. + +Return Value: + + None. + +--*/ + +{ + PTP_ADDRESS Address; + PSEND_PACKET_TAG SendTag; + NDIS_STATUS NdisStatus; + PNDIS_PACKET NdisPacket; + PNDIS_BUFFER NdisBuffer; + + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + sizeof(TP_ADDRESS)) > + DeviceContext->MemoryLimit)) { + PANIC("NBF: Could not allocate address: limit\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_LIMIT, + 101, + sizeof(TP_ADDRESS), + ADDRESS_RESOURCE_ID); + *TransportAddress = NULL; + return; + } + + Address = (PTP_ADDRESS)ExAllocatePoolWithTag ( + NonPagedPool, + sizeof (TP_ADDRESS), + 'aFBN'); + if (Address == NULL) { + PANIC("NBF: Could not allocate address: no pool\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 201, + sizeof(TP_ADDRESS), + ADDRESS_RESOURCE_ID); + *TransportAddress = NULL; + return; + } + RtlZeroMemory (Address, sizeof(TP_ADDRESS)); + + NdisAllocatePacketPool( + &NdisStatus, + &Address->UIFramePoolHandle, + 1, + sizeof(SEND_PACKET_TAG)); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + PANIC("NBF: Could not allocate address UI frame pool: no pool\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 311, + sizeof(SEND_PACKET_TAG), + ADDRESS_RESOURCE_ID); + ExFreePool (Address); + *TransportAddress = NULL; + return; + } + + + // + // This code is similar to NbfAllocateUIFrame. + // + + Address->UIFrame = (PTP_UI_FRAME) ExAllocatePoolWithTag ( + NonPagedPool, + DeviceContext->UIFrameLength, + 'uFBN'); + if (Address->UIFrame == NULL) { + PANIC("NBF: Could not allocate address UI frame: no pool\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 411, + DeviceContext->UIFrameLength, + ADDRESS_RESOURCE_ID); + NdisFreePacketPool (Address->UIFramePoolHandle); + ExFreePool (Address); + *TransportAddress = NULL; + return; + } + RtlZeroMemory (Address->UIFrame, DeviceContext->UIFrameLength); + + + NdisAllocatePacket ( + &NdisStatus, + &NdisPacket, + Address->UIFramePoolHandle); + + ASSERT (NdisStatus == NDIS_STATUS_SUCCESS); + + Address->UIFrame->NdisPacket = NdisPacket; + Address->UIFrame->DataBuffer = NULL; + SendTag = (PSEND_PACKET_TAG)NdisPacket->ProtocolReserved; + SendTag->Type = TYPE_ADDRESS_FRAME; + SendTag->Owner = (PVOID)Address; + SendTag->Frame = Address->UIFrame; + + // + // Make the packet header known to the packet descriptor + // + + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + DeviceContext->NdisBufferPool, + Address->UIFrame->Header, + DeviceContext->UIFrameHeaderLength); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + + PANIC("NBF: Could not allocate address UI frame buffer: no pool\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_SPECIFIC, + 511, + 0, + UI_FRAME_RESOURCE_ID); + ExFreePool (Address->UIFrame); + NdisFreePacketPool (Address->UIFramePoolHandle); + ExFreePool (Address); + *TransportAddress = NULL; + return; + } + + NdisChainBufferAtFront (NdisPacket, NdisBuffer); + + DeviceContext->MemoryUsage += + sizeof(TP_ADDRESS) + + sizeof(NDIS_PACKET) + sizeof(SEND_PACKET_TAG) + + DeviceContext->UIFrameLength; + ++DeviceContext->AddressAllocated; + + Address->Type = NBF_ADDRESS_SIGNATURE; + Address->Size = sizeof (TP_ADDRESS); + + Address->Provider = DeviceContext; + KeInitializeSpinLock (&Address->SpinLock); +// KeInitializeSpinLock (&Address->Interlock); + + InitializeListHead (&Address->ConnectionDatabase); + InitializeListHead (&Address->AddressFileDatabase); + InitializeListHead (&Address->SendDatagramQueue); + + KeInitializeDpc (&Address->Dpc, AddressTimeoutHandler, (PVOID)Address); + KeInitializeTimer (&Address->Timer); + + // + // For each address, allocate a receive packet and a receive buffer. + // + + NbfAddReceivePacket (DeviceContext); + NbfAddReceiveBuffer (DeviceContext); + + *TransportAddress = Address; + +} /* NbfAllocateAddress */ + + +VOID +NbfDeallocateAddress( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS TransportAddress + ) + +/*++ + +Routine Description: + + This routine frees storage for a transport address. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + Address - Pointer to a transport address structure. + +Return Value: + + None. + +--*/ + +{ + PNDIS_BUFFER NdisBuffer; + + NdisUnchainBufferAtFront (TransportAddress->UIFrame->NdisPacket, &NdisBuffer); + if (NdisBuffer != NULL) { + NdisFreeBuffer (NdisBuffer); + } + ExFreePool (TransportAddress->UIFrame); + NdisFreePacketPool (TransportAddress->UIFramePoolHandle); + + ExFreePool (TransportAddress); + --DeviceContext->AddressAllocated; + + DeviceContext->MemoryUsage -= + sizeof(TP_ADDRESS) + + sizeof(NDIS_PACKET) + sizeof(SEND_PACKET_TAG) + + DeviceContext->UIFrameLength; + + // + // Remove the resources which allocating this caused. + // + + NbfRemoveReceivePacket (DeviceContext); + NbfRemoveReceiveBuffer (DeviceContext); + +} /* NbfDeallocateAddress */ + + +NTSTATUS +NbfCreateAddress( + IN PDEVICE_CONTEXT DeviceContext, + IN PNBF_NETBIOS_ADDRESS NetworkName, + OUT PTP_ADDRESS *Address + ) + +/*++ + +Routine Description: + + This routine creates a transport address and associates it with + the specified transport device context. The reference count in the + address is automatically set to 1, and the reference count of the + device context is incremented. + + NOTE: This routine must be called with the DeviceContext + spinlock held. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + NetworkName - Pointer to an NBF_NETBIOS_ADDRESS type containing the network + name to be associated with this address, if any. + NOTE: This has only the basic NetbiosNameType values, not the + QUICK_ ones. + + Address - Pointer to a place where this routine will return a pointer + to a transport address structure. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_ADDRESS pAddress; + PLIST_ENTRY p; + + + p = RemoveHeadList (&DeviceContext->AddressPool); + if (p == &DeviceContext->AddressPool) { + + if ((DeviceContext->AddressMaxAllocated == 0) || + (DeviceContext->AddressAllocated < DeviceContext->AddressMaxAllocated)) { + + NbfAllocateAddress (DeviceContext, &pAddress); + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint1 ("NBF: Allocated address at %lx\n", pAddress); + } + + } else { + + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_SPECIFIC, + 401, + sizeof(TP_ADDRESS), + ADDRESS_RESOURCE_ID); + pAddress = NULL; + + } + + if (pAddress == NULL) { + ++DeviceContext->AddressExhausted; + PANIC ("NbfCreateAddress: Could not allocate address object!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + } else { + + pAddress = CONTAINING_RECORD (p, TP_ADDRESS, Linkage); + + } + + ++DeviceContext->AddressInUse; + if (DeviceContext->AddressInUse > DeviceContext->AddressMaxInUse) { + ++DeviceContext->AddressMaxInUse; + } + + DeviceContext->AddressTotal += DeviceContext->AddressInUse; + ++DeviceContext->AddressSamples; + + + IF_NBFDBG (NBF_DEBUG_ADDRESS | NBF_DEBUG_UFRAMES) { + NbfPrint1 ("NbfCreateAddress %lx: ", pAddress); + NbfDbgShowAddr (NetworkName); + } + + // + // Initialize all of the static data for this address. + // + + pAddress->ReferenceCount = 1; + +#if DBG + { + UINT Counter; + for (Counter = 0; Counter < NUMBER_OF_AREFS; Counter++) { + pAddress->RefTypes[Counter] = 0; + } + + // This reference is removed by the caller. + + pAddress->RefTypes[AREF_TEMP_CREATE] = 1; + } +#endif + + pAddress->Flags = ADDRESS_FLAGS_NEEDS_REG; + InitializeListHead (&pAddress->AddressFileDatabase); + + pAddress->NetworkName = NetworkName; + if ((NetworkName != (PNBF_NETBIOS_ADDRESS)NULL) && + (NetworkName->NetbiosNameType == + TDI_ADDRESS_NETBIOS_TYPE_GROUP)) { + + pAddress->Flags |= ADDRESS_FLAGS_GROUP; + + } + + if (NetworkName != (PNBF_NETBIOS_ADDRESS)NULL) { + ++DeviceContext->AddressCounts[NetworkName->NetbiosName[0]]; + } + + // + // Now link this address into the specified device context's + // address database. To do this, we need to acquire the spin lock + // on the device context. + // + + InsertTailList (&DeviceContext->AddressDatabase, &pAddress->Linkage); + pAddress->Provider = DeviceContext; + NbfReferenceDeviceContext ("Create Address", DeviceContext, DCREF_ADDRESS); // count refs to the device context. + + *Address = pAddress; // return the address. + return STATUS_SUCCESS; // not finished yet. +} /* NbfCreateAddress */ + + +VOID +NbfRegisterAddress( + PTP_ADDRESS Address + ) + +/*++ + +Routine Description: + + This routine starts the registration process of the transport address + specified, if it has not already been started. + +Arguments: + + Address - Pointer to a transport address object to begin registering + on the network. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + LARGE_INTEGER Timeout; + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + if (!(Address->Flags & ADDRESS_FLAGS_NEEDS_REG)) { + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint1 ("NbfRegisterAddress %lx: NEEDS_REG 0.\n", Address); + } + + return; + } + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint1 ("NbfRegisterAddress %lx: registering.\n", Address); + } + + + Address->Flags &= ~ADDRESS_FLAGS_NEEDS_REG; + Address->Flags |= ADDRESS_FLAGS_REGISTERING; + + RtlZeroMemory (Address->UniqueResponseAddress, 6); + + // + // Keep a reference on this address until the registration process + // completes or is aborted. It will be aborted in UFRAMES.C, in + // either the NAME_IN_CONFLICT or ADD_NAME_RESPONSE frame handlers. + // + + NbfReferenceAddress ("start registration", Address, AREF_TIMER); + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + // + // Now start registration process by starting up a retransmission timer + // and begin sending ADD_NAME_QUERY NetBIOS frames. + // + // On an async line that is disconnected, we only send one packet + // with a short timeout. + // + + if (Address->Provider->MacInfo.MediumAsync && !Address->Provider->MediumSpeedAccurate) { + Address->Retries = 1; + Timeout.LowPart = (ULONG)(-(ADD_NAME_QUERY_TIMEOUT / 10)); + } else { + Address->Retries = Address->Provider->AddNameQueryRetries; + Timeout.LowPart = (ULONG)(-(LONG)Address->Provider->AddNameQueryTimeout); + } + Timeout.HighPart = -1; + KeSetTimer (&Address->Timer, *(PTIME)&Timeout, &Address->Dpc); + + (VOID)NbfSendAddNameQuery (Address); // send first ADD_NAME_QUERY. +} /* NbfRegisterAddress */ + + +NTSTATUS +NbfVerifyAddressObject ( + IN PTP_ADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine is called to verify that the pointer given us in a file + object is in fact a valid address file object. We also verify that the + address object pointed to by it is a valid address object, and reference + it to keep it from disappearing while we use it. + +Arguments: + + AddressFile - potential pointer to a TP_ADDRESS_FILE object + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INVALID_ADDRESS otherwise + +--*/ + +{ + KIRQL oldirql; + NTSTATUS status = STATUS_SUCCESS; + PTP_ADDRESS address; + + // + // try to verify the address file signature. If the signature is valid, + // verify the address pointed to by it and get the address spinlock. + // check the address's state, and increment the reference count if it's + // ok to use it. Note that the only time we return an error for state is + // if the address is closing. + // + + try { + + if ((AddressFile != (PTP_ADDRESS_FILE)NULL) && + (AddressFile->Size == sizeof (TP_ADDRESS_FILE)) && + (AddressFile->Type == NBF_ADDRESSFILE_SIGNATURE) ) { +// (AddressFile->State != ADDRESSFILE_STATE_CLOSING) ) { + + address = AddressFile->Address; + + if ((address != (PTP_ADDRESS)NULL) && + (address->Size == sizeof (TP_ADDRESS)) && + (address->Type == NBF_ADDRESS_SIGNATURE) ) { + + ACQUIRE_SPIN_LOCK (&address->SpinLock, &oldirql); + + if ((address->Flags & ADDRESS_FLAGS_STOPPING) == 0) { + + NbfReferenceAddress ("verify", address, AREF_VERIFY); + + } else { + + NbfPrint1("NbfVerifyAddress: A %lx closing\n", address); + status = STATUS_INVALID_ADDRESS; + } + + RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); + + } else { + + NbfPrint1("NbfVerifyAddress: A %lx bad signature\n", address); + status = STATUS_INVALID_ADDRESS; + } + + } else { + + NbfPrint1("NbfVerifyAddress: AF %lx bad signature\n", AddressFile); + status = STATUS_INVALID_ADDRESS; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + NbfPrint1("NbfVerifyAddress: AF %lx exception\n", address); + return GetExceptionCode(); + } + + return status; + +} + +VOID +NbfDestroyAddress( + IN PVOID Parameter + ) + +/*++ + +Routine Description: + + This routine destroys a transport address and removes all references + made by it to other objects in the transport. The address structure + is returned to nonpaged system pool or our lookaside list. It is assumed + that the caller has already removed all addressfile structures associated + with this address. + + The routine is called from a worker thread so that the security + descriptor can be accessed. + + This worked thread is only queued by NbfDerefAddress. The reason + for this is that there may be multiple streams of execution which are + simultaneously referencing the same address object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + Address - Pointer to a transport address structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PDEVICE_CONTEXT DeviceContext; + PTP_ADDRESS Address = (PTP_ADDRESS)Parameter; + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint1 ("NbfDestroyAddress %lx:.\n", Address); + } + + DeviceContext = Address->Provider; + + SeDeassignSecurity (&Address->SecurityDescriptor); + + // + // Delink this address from its associated device context's address + // database. To do this we must spin lock on the device context object, + // not on the address. + // + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + if (Address->NetworkName) { + --DeviceContext->AddressCounts[Address->NetworkName->NetbiosName[0]]; + } + + RemoveEntryList (&Address->Linkage); + + if (Address->NetworkName != NULL) { + ExFreePool (Address->NetworkName); + Address->NetworkName = NULL; + } + + // + // Now we can deallocate the transport address object. + // + + DeviceContext->AddressTotal += DeviceContext->AddressInUse; + ++DeviceContext->AddressSamples; + --DeviceContext->AddressInUse; + + if ((DeviceContext->AddressAllocated - DeviceContext->AddressInUse) > + DeviceContext->AddressInitAllocated) { + NbfDeallocateAddress (DeviceContext, Address); + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint1 ("NBF: Deallocated address at %lx\n", Address); + } + } else { + InsertTailList (&DeviceContext->AddressPool, &Address->Linkage); + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + NbfDereferenceDeviceContext ("Destroy Address", DeviceContext, DCREF_ADDRESS); // just housekeeping. + +} /* NbfDestroyAddress */ + + +#if DBG +VOID +NbfRefAddress( + IN PTP_ADDRESS Address + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport address. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + + ASSERT (Address->ReferenceCount > 0); // not perfect, but... + + (VOID)InterlockedIncrement (&Address->ReferenceCount); + +} /* NbfRefAddress */ +#endif + + +VOID +NbfDerefAddress( + IN PTP_ADDRESS Address + ) + +/*++ + +Routine Description: + + This routine dereferences a transport address by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + NbfDestroyAddress to remove it from the system. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&Address->ReferenceCount); + + // + // If we have deleted all references to this address, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + ASSERT (result >= 0); + + if (result == 0) { + + ExInitializeWorkItem( + &Address->u.DestroyAddressQueueItem, + NbfDestroyAddress, + (PVOID)Address); + ExQueueWorkItem(&Address->u.DestroyAddressQueueItem, DelayedWorkQueue); + } +} /* NbfDerefAddress */ + + + +VOID +NbfAllocateAddressFile( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_ADDRESS_FILE *TransportAddressFile + ) + +/*++ + +Routine Description: + + This routine allocates storage for an address file. Some + minimal initialization is done on the object. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + TransportAddressFile - Pointer to a place where this routine will return + a pointer to a transport address file structure. It returns NULL if no + storage can be allocated. + +Return Value: + + None. + +--*/ + +{ + + PTP_ADDRESS_FILE AddressFile; + + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + sizeof(TP_ADDRESS_FILE)) > + DeviceContext->MemoryLimit)) { + PANIC("NBF: Could not allocate address file: limit\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_LIMIT, + 102, + sizeof(TP_ADDRESS_FILE), + ADDRESS_FILE_RESOURCE_ID); + *TransportAddressFile = NULL; + return; + } + + AddressFile = (PTP_ADDRESS_FILE)ExAllocatePoolWithTag ( + NonPagedPool, + sizeof (TP_ADDRESS_FILE), + 'fFBN'); + if (AddressFile == NULL) { + PANIC("NBF: Could not allocate address file: no pool\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 202, + sizeof(TP_ADDRESS_FILE), + ADDRESS_FILE_RESOURCE_ID); + *TransportAddressFile = NULL; + return; + } + RtlZeroMemory (AddressFile, sizeof(TP_ADDRESS_FILE)); + + DeviceContext->MemoryUsage += sizeof(TP_ADDRESS_FILE); + ++DeviceContext->AddressFileAllocated; + + AddressFile->Type = NBF_ADDRESSFILE_SIGNATURE; + AddressFile->Size = sizeof (TP_ADDRESS_FILE); + + InitializeListHead (&AddressFile->ReceiveDatagramQueue); + InitializeListHead (&AddressFile->ConnectionDatabase); + + *TransportAddressFile = AddressFile; + +} /* NbfAllocateAddressFile */ + + +VOID +NbfDeallocateAddressFile( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS_FILE TransportAddressFile + ) + +/*++ + +Routine Description: + + This routine frees storage for an address file. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + TransportAddressFile - Pointer to a transport address file structure. + +Return Value: + + None. + +--*/ + +{ + + ExFreePool (TransportAddressFile); + --DeviceContext->AddressFileAllocated; + DeviceContext->MemoryUsage -= sizeof(TP_ADDRESS_FILE); + +} /* NbfDeallocateAddressFile */ + + +NTSTATUS +NbfCreateAddressFile( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_ADDRESS_FILE * AddressFile + ) + +/*++ + +Routine Description: + + This routine creates an address file from the pool of ther + specified device context. The reference count in the + address is automatically set to 1. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + AddressFile - Pointer to a place where this routine will return a pointer + to a transport address file structure. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PLIST_ENTRY p; + PTP_ADDRESS_FILE addressFile; + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + p = RemoveHeadList (&DeviceContext->AddressFilePool); + if (p == &DeviceContext->AddressFilePool) { + + if ((DeviceContext->AddressFileMaxAllocated == 0) || + (DeviceContext->AddressFileAllocated < DeviceContext->AddressFileMaxAllocated)) { + + NbfAllocateAddressFile (DeviceContext, &addressFile); + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint1 ("NBF: Allocated address file at %lx\n", addressFile); + } + + } else { + + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_SPECIFIC, + 402, + sizeof(TP_ADDRESS_FILE), + ADDRESS_FILE_RESOURCE_ID); + addressFile = NULL; + + } + + if (addressFile == NULL) { + ++DeviceContext->AddressFileExhausted; + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + PANIC ("NbfCreateAddressFile: Could not allocate address file object!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + } else { + + addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); + + } + + ++DeviceContext->AddressFileInUse; + if (DeviceContext->AddressFileInUse > DeviceContext->AddressFileMaxInUse) { + ++DeviceContext->AddressFileMaxInUse; + } + + DeviceContext->AddressFileTotal += DeviceContext->AddressFileInUse; + ++DeviceContext->AddressFileSamples; + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + + InitializeListHead (&addressFile->ConnectionDatabase); + addressFile->Address = NULL; + addressFile->FileObject = NULL; + addressFile->Provider = DeviceContext; + addressFile->State = ADDRESSFILE_STATE_OPENING; + addressFile->ConnectIndicationInProgress = FALSE; + addressFile->ReferenceCount = 1; + addressFile->CloseIrp = (PIRP)NULL; + + // + // Initialize the request handlers. + // + + addressFile->RegisteredConnectionHandler = FALSE; + addressFile->ConnectionHandler = TdiDefaultConnectHandler; + addressFile->ConnectionHandlerContext = NULL; + addressFile->RegisteredDisconnectHandler = FALSE; + addressFile->DisconnectHandler = TdiDefaultDisconnectHandler; + addressFile->DisconnectHandlerContext = NULL; + addressFile->RegisteredReceiveHandler = FALSE; + addressFile->ReceiveHandler = TdiDefaultReceiveHandler; + addressFile->ReceiveHandlerContext = NULL; + addressFile->RegisteredReceiveDatagramHandler = FALSE; + addressFile->ReceiveDatagramHandler = TdiDefaultRcvDatagramHandler; + addressFile->ReceiveDatagramHandlerContext = NULL; + addressFile->RegisteredExpeditedDataHandler = FALSE; + addressFile->ExpeditedDataHandler = TdiDefaultRcvExpeditedHandler; + addressFile->ExpeditedDataHandlerContext = NULL; + addressFile->RegisteredErrorHandler = FALSE; + addressFile->ErrorHandler = TdiDefaultErrorHandler; + addressFile->ErrorHandlerContext = NULL; + + + *AddressFile = addressFile; + return STATUS_SUCCESS; + +} /* NbfCreateAddress */ + + +NTSTATUS +NbfDestroyAddressFile( + IN PTP_ADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine destroys an address file and removes all references + made by it to other objects in the transport. + + This routine is only called by NbfDereferenceAddressFile. The reason + for this is that there may be multiple streams of execution which are + simultaneously referencing the same address file object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + AddressFile Pointer to a transport address file structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql, oldirql1; + PTP_ADDRESS address; + PDEVICE_CONTEXT DeviceContext; + PIRP CloseIrp; + + + address = AddressFile->Address; + DeviceContext = AddressFile->Provider; + + if (address) { + + // + // This addressfile was associated with an address. + // + + ACQUIRE_SPIN_LOCK (&address->SpinLock, &oldirql); + + // + // remove this addressfile from the address list and disassociate it from + // the file handle. + // + + RemoveEntryList (&AddressFile->Linkage); + InitializeListHead (&AddressFile->Linkage); + + if (address->AddressFileDatabase.Flink == &address->AddressFileDatabase) { + + // + // This is the last open of this address, it will close + // due to normal dereferencing but we have to set the + // CLOSING flag too to stop further references. + // + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql1); + address->Flags |= ADDRESS_FLAGS_STOPPING; + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql1); + + } + + AddressFile->Address = NULL; + + AddressFile->FileObject->FsContext = NULL; + AddressFile->FileObject->FsContext2 = NULL; + + RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); + + // + // We will already have been removed from the ShareAccess + // of the owning address. + // + + // + // Now dereference the owning address. + // + + NbfDereferenceAddress ("Close", address, AREF_OPEN); // remove the creation hold + + } + + // + // Save this for later completion. + // + + CloseIrp = AddressFile->CloseIrp; + + // + // return the addressFile to the pool of address files + // + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + DeviceContext->AddressFileTotal += DeviceContext->AddressFileInUse; + ++DeviceContext->AddressFileSamples; + --DeviceContext->AddressFileInUse; + + if ((DeviceContext->AddressFileAllocated - DeviceContext->AddressFileInUse) > + DeviceContext->AddressFileInitAllocated) { + NbfDeallocateAddressFile (DeviceContext, AddressFile); + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint1 ("NBF: Deallocated address file at %lx\n", AddressFile); + } + } else { + InsertTailList (&DeviceContext->AddressFilePool, &AddressFile->Linkage); + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + + if (CloseIrp != (PIRP)NULL) { + CloseIrp->IoStatus.Information = 0; + CloseIrp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest (CloseIrp, IO_NETWORK_INCREMENT); + } + + return STATUS_SUCCESS; + +} /* NbfDestroyAddressFile */ + + +VOID +NbfReferenceAddressFile( + IN PTP_ADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine increments the reference count on an address file. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + + ASSERT (AddressFile->ReferenceCount > 0); // not perfect, but... + + (VOID)InterlockedIncrement (&AddressFile->ReferenceCount); + +} /* NbfReferenceAddressFile */ + + +VOID +NbfDereferenceAddressFile( + IN PTP_ADDRESS_FILE AddressFile + ) + +/*++ + +Routine Description: + + This routine dereferences an address file by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + NbfDestroyAddressFile to remove it from the system. + +Arguments: + + AddressFile - Pointer to a transport address file object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&AddressFile->ReferenceCount); + + // + // If we have deleted all references to this address file, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the address any longer. + // + + ASSERT (result >= 0); + + if (result == 0) { + NbfDestroyAddressFile (AddressFile); + } +} /* NbfDerefAddressFile */ + + +PTP_ADDRESS +NbfLookupAddress( + IN PDEVICE_CONTEXT DeviceContext, + IN PNBF_NETBIOS_ADDRESS NetworkName + ) + +/*++ + +Routine Description: + + This routine scans the transport addresses defined for the given + device context and compares them with the specified NETWORK + NAME values. If an exact match is found, then a pointer to the + TP_ADDRESS object is returned, and as a side effect, the reference + count to the address object is incremented. If the address is not + found, then NULL is returned. + + NOTE: This routine must be called with the DeviceContext + spinlock held. + +Arguments: + + DeviceContext - Pointer to the device object and its extension. + NetworkName - Pointer to an NBF_NETBIOS_ADDRESS structure containing the + network name. + +Return Value: + + Pointer to the TP_ADDRESS object found, or NULL if not found. + +--*/ + +{ + PTP_ADDRESS address; + PLIST_ENTRY p; + ULONG i; + + + p = DeviceContext->AddressDatabase.Flink; + + for (p = DeviceContext->AddressDatabase.Flink; + p != &DeviceContext->AddressDatabase; + p = p->Flink) { + + address = CONTAINING_RECORD (p, TP_ADDRESS, Linkage); + + if ((address->Flags & ADDRESS_FLAGS_STOPPING) != 0) { + continue; + } + + // + // If the network name is specified and the network names don't match, + // then the addresses don't match. + // + + i = NETBIOS_NAME_LENGTH; // length of a Netbios name + + if (address->NetworkName != NULL) { + if (NetworkName != NULL) { + if (!RtlEqualMemory ( + address->NetworkName->NetbiosName, + NetworkName->NetbiosName, + i)) { + continue; + } + } else { + continue; + } + + } else { + if (NetworkName != NULL) { + continue; + } + } + + // + // We found the match. Bump the reference count on the address, and + // return a pointer to the address object for the caller to use. + // + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint2 ("NbfLookupAddress DC %lx: found %lx ", DeviceContext, address); + NbfDbgShowAddr (NetworkName); + } + + NbfReferenceAddress ("lookup", address, AREF_LOOKUP); + return address; + + } /* for */ + + // + // The specified address was not found. + // + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint1 ("NbfLookupAddress DC %lx: did not find ", address); + NbfDbgShowAddr (NetworkName); + } + + return NULL; + +} /* NbfLookupAddress */ + + +PTP_CONNECTION +NbfLookupRemoteName( + IN PTP_ADDRESS Address, + IN PUCHAR RemoteName, + IN UCHAR RemoteSessionNumber + ) + +/*++ + +Routine Description: + + + This routine scans the connections associated with the + given address, and determines if there is an connection + associated with the specific remote address and session + number which is becoming active. This is used + in determining whether name queries should be processed, + or ignored as duplicates. + +Arguments: + + Address - Pointer to the address object. + + RemoteName - The 16-character Netbios name of the remote. + + RemoteSessionNumber - The session number assigned to this + connection by the remote. + +Return Value: + + The connection if one is found, NULL otherwise. + +--*/ + +{ + KIRQL oldirql, oldirql1; + PLIST_ENTRY p; + PTP_CONNECTION connection; + BOOLEAN Found = FALSE; + + + // + // Hold the spinlock so the connection database doesn't + // change. + // + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + for (p=Address->ConnectionDatabase.Flink; + p != &Address->ConnectionDatabase; + p=p->Flink) { + + connection = CONTAINING_RECORD (p, TP_CONNECTION, AddressList); + + try { + + ACQUIRE_C_SPIN_LOCK (&connection->SpinLock, &oldirql1); + + if (((connection->Flags2 & CONNECTION_FLAGS2_REMOTE_VALID) != 0) && + ((connection->Flags & CONNECTION_FLAGS_READY) == 0)) { + + RELEASE_C_SPIN_LOCK (&connection->SpinLock, oldirql1); + + // + // If the remote names match, and the connection's RSN is + // the same (or zero, which is a temporary condition where + // we should err on the side of caution), then return the + // connection, which will cause the NAME_QUERY to be ignored. + // + + if ((RtlEqualMemory(RemoteName, connection->RemoteName, NETBIOS_NAME_LENGTH)) && + ((connection->Rsn == RemoteSessionNumber) || (connection->Rsn == 0))) { + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + NbfReferenceConnection ("Lookup found", connection, CREF_LISTENING); + Found = TRUE; + + } + + } else { + + RELEASE_C_SPIN_LOCK (&connection->SpinLock, oldirql1); + + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + DbgPrint ("NBF: Got exception in NbfLookupRemoteName\n"); + DbgBreakPoint(); + + RELEASE_C_SPIN_LOCK (&connection->SpinLock, oldirql1); + + return (PTP_CONNECTION)NULL; + } + + if (Found) { + return connection; + } + + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + return (PTP_CONNECTION)NULL; + +} + + +BOOLEAN +NbfMatchNetbiosAddress( + IN PTP_ADDRESS Address, + IN UCHAR NameType, + IN PUCHAR NetBIOSName + ) + +/*++ + +Routine Description: + + This routine is called to compare the addressing information in a + TP_ADDRESS object with the 16-byte NetBIOS name in a frame header. + If they match, then this routine returns TRUE, else it returns FALSE. + +Arguments: + + Address - Pointer to a TP_ADDRESS object. + + NameType - One of NETBIOS_NAME_TYPE_GROUP, NETBIOS_NAME_TYPE_UNIQUE, + or NETBIOS_NAME_TYPE_EITHER. Controls what type we are matching + on, if it matters. + + NetBIOSName - Pointer to a 16-byte character string (non-terminated), + or NULL if this is a received broadcast address. + +Return Value: + + BOOLEAN, TRUE if match, FALSE if not. + +--*/ + +{ + + PULONG AddressNamePointer; + ULONG UNALIGNED * NetbiosNamePointer; + + // + // If this is address is the Netbios broadcast address, the comparison + // succeeds only if the passed in address is also NULL. + // + + if (Address->NetworkName == NULL) { + + if (NetBIOSName == NULL) { + return TRUE; + } else { + return FALSE; + } + + } else if (NetBIOSName == NULL) { + + return FALSE; + + } + + // + // Do a quick check of the first character in the names. + // + + if (Address->NetworkName->NetbiosName[0] != NetBIOSName[0]) { + return FALSE; + } + + // + // If name type is important and it doesn't match + // this address' type, fail. + // + + if (NameType != NETBIOS_NAME_TYPE_EITHER) { + + if (Address->NetworkName->NetbiosNameType != (USHORT)NameType) { + + return FALSE; + } + } + + IF_NBFDBG (NBF_DEBUG_DATAGRAMS) { + NbfPrint2 ("MatchNetbiosAddress %lx: compare %.16s to ", Address, NetBIOSName); + NbfDbgShowAddr (Address->NetworkName); + } + + // + // Now compare the 16-character Netbios names as ULONGs + // for speed. We know the one stored in the address + // structure is aligned. + // + + AddressNamePointer = (PULONG)(Address->NetworkName->NetbiosName); + NetbiosNamePointer = (ULONG UNALIGNED *)NetBIOSName; + + if ((AddressNamePointer[0] == NetbiosNamePointer[0]) && + (AddressNamePointer[1] == NetbiosNamePointer[1]) && + (AddressNamePointer[2] == NetbiosNamePointer[2]) && + (AddressNamePointer[3] == NetbiosNamePointer[3])) { + return TRUE; + } else { + return FALSE; + } + +} /* NbfMatchNetbiosAddress */ + + +VOID +NbfStopAddress( + IN PTP_ADDRESS Address + ) + +/*++ + +Routine Description: + + This routine is called to terminate all activity on an address and + destroy the object. This is done in a graceful manner; i.e., all + outstanding addressfiles are removed from the addressfile database, and + all their activities are shut down. + +Arguments: + + Address - Pointer to a TP_ADDRESS object. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql, oldirql1; + PTP_ADDRESS_FILE addressFile; + PLIST_ENTRY p; + PDEVICE_CONTEXT DeviceContext; + + DeviceContext = Address->Provider; + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + // + // If we're already stopping this address, then don't try to do it again. + // + + if (!(Address->Flags & ADDRESS_FLAGS_STOPPING)) { + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint1 ("NbfStopAddress %lx: stopping\n", Address); + } + + NbfReferenceAddress ("Stopping", Address, AREF_TEMP_STOP); + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql1); + Address->Flags |= ADDRESS_FLAGS_STOPPING; + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql1); + + // + // Run down all addressfiles on this address. This + // will leave the address with no references + // potentially, but we don't need a temp one + // because every place that calls NbfStopAddress + // already has a temp reference. + // + + while (!IsListEmpty (&Address->AddressFileDatabase)) { + p = RemoveHeadList (&Address->AddressFileDatabase); + addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); + + addressFile->Address = NULL; + addressFile->FileObject->FsContext = NULL; + addressFile->FileObject->FsContext2 = NULL; + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + // + // Run-down this addressFile without the lock on. + // We don't care about removing ourselves from + // the address' ShareAccess because we are + // tearing it down. + // + + NbfStopAddressFile (addressFile, Address); + + // + // return the addressFile to the pool of address files + // + + NbfDereferenceAddressFile (addressFile); + + NbfDereferenceAddress ("stop address", Address, AREF_OPEN); + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + NbfDereferenceAddress ("Stopping", Address, AREF_TEMP_STOP); + + } else { + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint1 ("NbfStopAddress %lx: already stopping\n", Address); + } + + } + +} /* NbfStopAddress */ + + +NTSTATUS +NbfStopAddressFile( + IN PTP_ADDRESS_FILE AddressFile, + IN PTP_ADDRESS Address + ) + +/*++ + +Routine Description: + + This routine is called to terminate all activity on an AddressFile and + destroy the object. We remove every connection and datagram associated + with this addressfile from the address database and terminate their + activity. Then, if there are no other outstanding addressfiles open on + this address, the address will go away. + +Arguments: + + AddressFile - pointer to the addressFile to be stopped + + Address - the owning address for this addressFile (we do not depend upon + the pointer in the addressFile because we want this routine to be safe) + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the Irp does not + point to a real address. + +--*/ + +{ + KIRQL oldirql, oldirql1; + LIST_ENTRY localIrpList; + PLIST_ENTRY p, pFlink; + PIRP irp; + PTP_CONNECTION connection; + + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + if (AddressFile->State == ADDRESSFILE_STATE_CLOSING) { + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint1 ("NbfStopAddressFile %lx: already closing.\n", AddressFile); + } + return STATUS_SUCCESS; + } + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint1 ("NbfStopAddressFile %lx: closing.\n", AddressFile); + } + + + AddressFile->State = ADDRESSFILE_STATE_CLOSING; + InitializeListHead (&localIrpList); + + // + // Run down all connections on this addressfile, and + // preform the equivalent of NbfDestroyAssociation + // on them. + // + + while (!IsListEmpty (&AddressFile->ConnectionDatabase)) { + p = RemoveHeadList (&AddressFile->ConnectionDatabase); + connection = CONTAINING_RECORD (p, TP_CONNECTION, AddressFileList); + + try { + + ACQUIRE_C_SPIN_LOCK (&connection->SpinLock, &oldirql1); + + if ((connection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) == 0) { + + // + // It is in the process of being disassociated already. + // + + RELEASE_C_SPIN_LOCK (&connection->SpinLock, oldirql1); + continue; + } + + connection->Flags2 &= ~CONNECTION_FLAGS2_ASSOCIATED; + connection->Flags2 |= CONNECTION_FLAGS2_DESTROY; // BUGBUG: Is this needed? + RemoveEntryList (&connection->AddressList); + InitializeListHead (&connection->AddressList); + InitializeListHead (&connection->AddressFileList); + connection->AddressFile = NULL; + + NbfReferenceConnection ("Close AddressFile", connection, CREF_STOP_ADDRESS); + RELEASE_C_SPIN_LOCK (&connection->SpinLock, oldirql1); + + } except(EXCEPTION_EXECUTE_HANDLER) { + + DbgPrint ("NBF: Got exception in NbfStopAddressFile\n"); + DbgBreakPoint(); + + RELEASE_C_SPIN_LOCK (&connection->SpinLock, oldirql1); + continue; + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + +#if DBG + if (NbfDisconnectDebug) { + STRING remoteName, localName; + remoteName.Length = NETBIOS_NAME_LENGTH - 1; + remoteName.Buffer = connection->RemoteName; + localName.Length = NETBIOS_NAME_LENGTH - 1; + localName.Buffer = AddressFile->Address->NetworkName->NetbiosName; + NbfPrint2( "TpStopEndpoint stopping connection to %S from %S\n", + &remoteName, &localName ); + } +#endif + KeRaiseIrql (DISPATCH_LEVEL, &oldirql1); + NbfStopConnection (connection, STATUS_LOCAL_DISCONNECT); + KeLowerIrql (oldirql1); + NbfDereferenceConnection ("Close AddressFile", connection, CREF_STOP_ADDRESS); + + NbfDereferenceAddress ("Destroy association", Address, AREF_CONNECTION); + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + } + + // + // now remove all of the datagrams owned by this addressfile + // + + // + // If the address has a datagram send in progress, skip the + // first one, it will complete when the NdisSend completes. + // + + p = Address->SendDatagramQueue.Flink; + if (Address->Flags & ADDRESS_FLAGS_SEND_IN_PROGRESS) { + ASSERT (p != &Address->SendDatagramQueue); + p = p->Flink; + } + + for ( ; + p != &Address->SendDatagramQueue; + p = pFlink ) { + + pFlink = p->Flink; + irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + if (IoGetCurrentIrpStackLocation(irp)->FileObject->FsContext == AddressFile) { + RemoveEntryList (p); + InitializeListHead (p); + InsertTailList (&localIrpList, p); + } + + } + + for (p = AddressFile->ReceiveDatagramQueue.Flink; + p != &AddressFile->ReceiveDatagramQueue; + p = pFlink ) { + + pFlink = p->Flink; + RemoveEntryList (p); + InitializeListHead (p); + InsertTailList (&localIrpList, p); + } + + // + // and finally, signal failure if the address file was waiting for a + // registration to complete (Irp is set to NULL when this succeeds). + // + + if (AddressFile->Irp != NULL) { + PIRP irp=AddressFile->Irp; +#if DBG + if ((Address->Flags & ADDRESS_FLAGS_DUPLICATE_NAME) == 0) { + DbgPrint ("NBF: AddressFile %lx closed while opening!!\n", AddressFile); + DbgBreakPoint(); + } +#endif + AddressFile->Irp = NULL; + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + irp->IoStatus.Information = 0; + irp->IoStatus.Status = STATUS_DUPLICATE_NAME; + + LEAVE_NBF; + IoCompleteRequest (irp, IO_NETWORK_INCREMENT); + ENTER_NBF; + + } else { + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + } + + // + // cancel all the datagrams on this address file + // + + while (!IsListEmpty (&localIrpList)) { + KIRQL cancelIrql; + + p = RemoveHeadList (&localIrpList); + irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + + IoAcquireCancelSpinLock(&cancelIrql); + IoSetCancelRoutine(irp, NULL); + IoReleaseCancelSpinLock(cancelIrql); + irp->IoStatus.Information = 0; + irp->IoStatus.Status = STATUS_NETWORK_NAME_DELETED; + IoCompleteRequest (irp, IO_NETWORK_INCREMENT); + + NbfDereferenceAddress ("Datagram aborted", Address, AREF_REQUEST); + } + +} /* NbfStopAddressFile */ + + +NTSTATUS +NbfCloseAddress( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine is called to close the addressfile pointed to by a file + object. If there is any activity to be run down, we will run it down + before we terminate the addressfile. We remove every connection and + datagram associated with this addressfile from the address database + and terminate their activity. Then, if there are no other outstanding + addressfiles open on this address, the address will go away. + +Arguments: + + Irp - the Irp Address - Pointer to a TP_ADDRESS object. + +Return Value: + + STATUS_SUCCESS if all is well, STATUS_INVALID_HANDLE if the Irp does not + point to a real address. + +--*/ + +{ + PTP_ADDRESS address; + PTP_ADDRESS_FILE addressFile; + + addressFile = IrpSp->FileObject->FsContext; + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint1 ("NbfCloseAddress AF %lx:\n", addressFile); + } + + addressFile->CloseIrp = Irp; + + // + // We assume that addressFile has already been verified + // at this point. + // + + address = addressFile->Address; + ASSERT (address); + + // + // Remove us from the access info for this address. + // + + ExAcquireResourceExclusive (&addressFile->Provider->AddressResource, TRUE); + IoRemoveShareAccess (addressFile->FileObject, &address->u.ShareAccess); + ExReleaseResource (&addressFile->Provider->AddressResource); + + + NbfStopAddressFile (addressFile, address); + NbfDereferenceAddressFile (addressFile); + + // + // This removes a reference added by our caller. + // + + NbfDereferenceAddress ("IRP_MJ_CLOSE", address, AREF_VERIFY); + + return STATUS_PENDING; + +} /* NbfCloseAddress */ + + +NTSTATUS +NbfSendDatagramsOnAddress( + PTP_ADDRESS Address + ) + +/*++ + +Routine Description: + + This routine attempts to acquire a hold on the SendDatagramQueue of + the specified address, prepare the next datagram for shipment, and + call NbfSendUIMdlFrame to actually do the work. When NbfSendUIMdlFrame + is finished, it will cause an I/O completion routine in UFRAMES.C to + be called, at which time this routine is called again to handle the + next datagram in the pipeline. + + NOTE: This routine must be called at a point where the address + has another reference that will keep it around. + +Arguments: + + Address - a pointer to the address object to send the datagram on. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PLIST_ENTRY p; + PIRP Irp; + TDI_ADDRESS_NETBIOS UNALIGNED * remoteAddress; + PIO_STACK_LOCATION irpSp; + PDEVICE_CONTEXT DeviceContext; + PUCHAR SingleSR; + UINT SingleSRLength; + UINT HeaderLength; + PUCHAR LocalName; + + IF_NBFDBG (NBF_DEBUG_ADDRESS) { + NbfPrint1 ("NbfSendDatagramsOnAddress %lx:\n", Address); + } + + DeviceContext = Address->Provider; + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + if (!(Address->Flags & ADDRESS_FLAGS_SEND_IN_PROGRESS)) { + + // + // If the queue is empty, don't do anything. + // + + if (IsListEmpty (&Address->SendDatagramQueue)) { + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + return STATUS_SUCCESS; + } + + // + // Mark the address's send datagram queue as held so that the + // MDL and NBF header will not be used for two requests at the + // same time. + // + + Address->Flags |= ADDRESS_FLAGS_SEND_IN_PROGRESS; + + // + // We own the hold, and we've released the spinlock. So pick off the + // next datagram to be sent, and ship it. + // + + p = Address->SendDatagramQueue.Flink; + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + + // + // If there is no remote Address specified (the Address specified has + // length 0), this is a broadcast datagram. If anything is specified, it + // will be used as a netbios address. + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + remoteAddress = NbfParseTdiAddress ( + ((PTDI_REQUEST_KERNEL_SENDDG)(&irpSp->Parameters))-> + SendDatagramInformation->RemoteAddress, + TRUE); + ASSERT (remoteAddress != NULL); + + // + // Build the MAC header. DATAGRAM frames go out as + // single-route source routing. + // + + MacReturnSingleRouteSR( + &DeviceContext->MacInfo, + &SingleSR, + &SingleSRLength); + + MacConstructHeader ( + &DeviceContext->MacInfo, + Address->UIFrame->Header, + DeviceContext->NetBIOSAddress.Address, + DeviceContext->LocalAddress.Address, + sizeof (DLC_FRAME) + sizeof (NBF_HDR_CONNECTIONLESS) + + Irp->IoStatus.Information, + SingleSR, + SingleSRLength, + &HeaderLength); + + + // + // Build the DLC UI frame header. + // + + NbfBuildUIFrameHeader(&(Address->UIFrame->Header[HeaderLength])); + HeaderLength += sizeof(DLC_FRAME); + + + // + // Build the correct Netbios header. + // + + if (Address->NetworkName != NULL) { + LocalName = Address->NetworkName->NetbiosName; + } else { + LocalName = DeviceContext->ReservedNetBIOSAddress; + } + + if (remoteAddress == (PVOID)-1) { + + ConstructDatagramBroadcast ( + (PNBF_HDR_CONNECTIONLESS)&(Address->UIFrame->Header[HeaderLength]), + LocalName); + + } else { + + ConstructDatagram ( + (PNBF_HDR_CONNECTIONLESS)&(Address->UIFrame->Header[HeaderLength]), + (PNAME)remoteAddress->NetbiosName, + LocalName); + + } + + HeaderLength += sizeof(NBF_HDR_CONNECTIONLESS); + + + // + // Update our statistics for this datagram. + // + + ++DeviceContext->Statistics.DatagramsSent; + ADD_TO_LARGE_INTEGER( + &DeviceContext->Statistics.DatagramBytesSent, + Irp->IoStatus.Information); + + + // + // Munge the packet length, append the data, and send it. + // + + NbfSetNdisPacketLength(Address->UIFrame->NdisPacket, HeaderLength); + + if (Irp->MdlAddress) { + NdisChainBufferAtBack (Address->UIFrame->NdisPacket, (PNDIS_BUFFER)Irp->MdlAddress); + } + + NbfSendUIMdlFrame ( + Address); + + + // + // The hold will be released in the I/O completion handler in + // UFRAMES.C. At that time, if there is another outstanding datagram + // to send, it will reset the hold and call this routine again. + // + + + } else { + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + } + + return STATUS_SUCCESS; +} /* NbfSendDatagramsOnAddress */ diff --git a/private/ntos/tdi/nbf/autodial.c b/private/ntos/tdi/nbf/autodial.c new file mode 100644 index 000000000..85bea057e --- /dev/null +++ b/private/ntos/tdi/nbf/autodial.c @@ -0,0 +1,502 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + autodial.c + +Abstract: + + This module contains code that interacts with the + automatic connection driver (rasacd.sys): + + o NbfNoteNewConnection + o NbfAcdBind + o NbfAcdUnbind + +Author: + + Anthony Discolo (adiscolo) 6 September 1995 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef RASAUTODIAL + +#include <acd.h> +#include <acdapi.h> + +// +// Global variables. +// +BOOLEAN fAcdLoadedG; +ACD_DRIVER AcdDriverG; +ULONG ulDriverIdG = 'Nbf '; + + + +BOOLEAN +NbfCancelAutoDialRequest( + IN PVOID pArg, + IN ULONG ulFlags, + IN ACD_CONNECT_CALLBACK pProc, + IN USHORT nArgs, + IN PVOID *pArgs + ) +{ + if (nArgs != 1) + return FALSE; + + return (pArgs[0] == pArg); +} // NbfCancelAutoDialRequest + + + +VOID +NbfRetryTdiConnect( + IN BOOLEAN fSuccess, + IN PVOID *pArgs + ) + +/*++ + +Routine Description: + + This routine is called indirectly by the automatic + connection driver to continue the connection process + after an automatic connection has been made. + +Arguments: + + fSuccess - TRUE if the connection attempt was successful. + + pArgs - a pointer to the argument vector + +Return Value: + + None. + +--*/ + +{ + NTSTATUS status; + PTP_CONNECTION pConnection = pArgs[0]; + KIRQL irql; + BOOL fStopping; + + // + // Check for a destroyed connection. + // + if (pConnection == NULL) + return; + status = NbfVerifyConnectionObject(pConnection); + if (status != STATUS_SUCCESS) { + DbgPrint( + "NbfRetryTdiConnect: NbfVerifyConnectionObject failed (status=0x%x)\n", + status); + return; + } +#ifdef notdef // DBG + DbgPrint( + "NbfRetryTdiConnect: fSuccess=%d, pConnection=0x%x, STOPPING=%d\n", + fSuccess, + pConnection, + pConnection->Flags2 & CONNECTION_FLAGS2_STOPPING); +#endif + KeRaiseIrql(DISPATCH_LEVEL, &irql); + // + // Check to see if the connection + // is closing. + // + ACQUIRE_DPC_SPIN_LOCK(&pConnection->SpinLock); + fStopping = (pConnection->Flags2 & CONNECTION_FLAGS2_STOPPING); + // + // Clear the automatic connection + // in-progress flag, and set the + // autoconnected flag. + // + pConnection->Flags2 &= ~CONNECTION_FLAGS2_AUTOCONNECTING; + pConnection->Flags2 |= CONNECTION_FLAGS2_AUTOCONNECTED; + RELEASE_DPC_SPIN_LOCK(&pConnection->SpinLock); + if (!fStopping) { + if (fSuccess) { + // + // Restart the name query. + // + pConnection->Retries = + (USHORT)pConnection->Provider->NameQueryRetries; + NbfSendNameQuery ( + pConnection, + TRUE); + NbfStartConnectionTimer ( + pConnection, + ConnectionEstablishmentTimeout, + pConnection->Provider->NameQueryTimeout); + } + else { + // + // Terminate the connection with an error. + // + NbfStopConnection(pConnection, STATUS_BAD_NETWORK_PATH); + } + } + KeLowerIrql(irql); + NbfDereferenceConnection ("NbfRetryTdiConnect", pConnection, CREF_BY_ID); +} /* NbfRetryTdiConnect */ + + + +BOOLEAN +NbfCancelTdiConnect( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ) + +/*++ + +DESCRIPTION + This routine is called by the I/O system to cancel a connection + when we are attempting to restore an automatic connection. + +ARGUMENTS + pDeviceObject: a pointer to the device object for this driver + + pIrp: a pointer to the irp to be cancelled + +RETURN VALUE + TRUE if the request was canceled; FALSE otherwise. + +--*/ + +{ + PIO_STACK_LOCATION pIrpSp; + PTP_CONNECTION pConnection; + ACD_ADDR addr; + + UNREFERENCED_PARAMETER(pDeviceObject); + // + // Get a pointer to the current stack location in the IRP. This is where + // the function codes and parameters are stored. + // + pIrpSp = IoGetCurrentIrpStackLocation(pIrp); + pConnection = pIrpSp->FileObject->FsContext; + if (pConnection == NULL) + return FALSE; +#ifdef notdef // DBG + DbgPrint( + "NbfCancelTdiConnect: pIrp=0x%x, pConnection=0x%x\n", + pIrp, + pConnection); +#endif + // + // Get the address of the connection. + // + addr.fType = ACD_ADDR_NB; + RtlCopyMemory(&addr.cNetbios, pConnection->CalledAddress.NetbiosName, 15); + // + // Cancel the autodial request. + // + return (*AcdDriverG.lpfnCancelConnection)( + ulDriverIdG, + &addr, + NbfCancelAutoDialRequest, + pConnection); +} // NbfCancelTdiConnect + + + +BOOLEAN +NbfAttemptAutoDial( + IN PTP_CONNECTION pConnection, + IN ULONG ulFlags, + IN ACD_CONNECT_CALLBACK pProc, + IN PVOID pArg + ) + +/*++ + +Routine Description: + + Call the automatic connection driver to attempt an + automatic connection. + +Arguments: + + pConnection - a pointer to the TP_CONNECTION block for this connection + + ulFlags - connection flags to pass to the automatic + connection driver + + pProc - a callback procedure when the automatic connection completes + + pArg - the single parameter to the callback procedure + +Return Value: + + TRUE if the automatic connection was started successfully, + FALSE otherwise. + +--*/ + +{ + ACD_ADDR addr; + PVOID pArgs[1]; + BOOLEAN bSuccess; + + // + // If we've already attempted an automatic + // connection on this connection, then + // don't try again. + // + if (pConnection->Flags2 & CONNECTION_FLAGS2_AUTOCONNECTED) + return FALSE; + // + // Get the address of the connection. + // + addr.fType = ACD_ADDR_NB; + RtlCopyMemory(&addr.cNetbios, pConnection->CalledAddress.NetbiosName, 15); +#ifdef notdef // DBG + DbgPrint("NbfAttemptAutoDial: szAddr=%15.15s\n", addr.cNetbios); +#endif + // + // Attempt to start the connection. + // NbfRetryTdiConnect() will be called + // when the connection process has completed. + // + pArgs[0] = pArg; + bSuccess = (*AcdDriverG.lpfnStartConnection)( + ulDriverIdG, + &addr, + ulFlags, + pProc, + 1, + pArgs); + if (bSuccess) { + // + // Set the CONNECTION_FLAGS2_AUTOCONNECTING flag on + // the connection. This will prevent it from being + // aborted during the automatic connection process. + // + pConnection->Flags2 |= CONNECTION_FLAGS2_AUTOCONNECTING; + } + + return bSuccess; +} // NbfAttemptAutoDial + + + +VOID +NbfNoteNewConnection( + PTP_CONNECTION pConnection, + PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + Inform the automatic connection driver of a successful + new connection. + +Arguments: + Connection - a pointer to a connection object + + DeviceContext - a pointer to the device context + +Return Value: + None. + +--*/ + +{ + KIRQL irql; + ACD_ADDR addr; + ACD_ADAPTER adapter; + ULONG ulcChars; + + addr.fType = ACD_ADDR_NB; + RtlCopyMemory(&addr.cNetbios, pConnection->CalledAddress.NetbiosName, 15); +#ifdef notdef // DBG + DbgPrint("NbfNoteNewConnection: szAddr=%15.15s\n", addr.cNetbios); +#endif + // + // Eliminate the "/Device/Nbf_" prefix when + // copying the adapter name. + // + adapter.fType = ACD_ADAPTER_NAME; + ulcChars = DeviceContext->DeviceNameLength - 12; + if (ulcChars >= ACD_ADAPTER_NAME_LEN) + ulcChars = ACD_ADAPTER_NAME_LEN - 1; + RtlCopyMemory( + adapter.szName, + &DeviceContext->DeviceName[12], + ulcChars * sizeof (WCHAR)); + adapter.szName[ulcChars] = L'\0'; + // + // Simply notify the automatic connection driver + // that a successful connection has been made. + // + (*AcdDriverG.lpfnNewConnection)( + &addr, + &adapter); +} // NbfNoteNewConnection + + + +VOID +NbfAcdBind() +{ + NTSTATUS status; + UNICODE_STRING nameString; + IO_STATUS_BLOCK ioStatusBlock; + PIRP pIrp; + PFILE_OBJECT pAcdFileObject; + PDEVICE_OBJECT pAcdDeviceObject; + PACD_DRIVER pDriver = &AcdDriverG; + + // + // Initialize the name of the automatic + // connection device. + // + RtlInitUnicodeString(&nameString, ACD_DEVICE_NAME); + // + // Get the file and device objects for the + // device. + // + status = IoGetDeviceObjectPointer( + &nameString, + SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE, + &pAcdFileObject, + &pAcdDeviceObject); + if (status != STATUS_SUCCESS) + return; + // + // Reference the device object. + // + ObReferenceObject(pAcdDeviceObject); + // + // Remove the reference IoGetDeviceObjectPointer() + // put on the file object. + // + ObDereferenceObject(pAcdFileObject); + // + // Initialize our part of the ACD_DRIVER + // structure. + // + KeInitializeSpinLock(&AcdDriverG.SpinLock); + AcdDriverG.ulDriverId = ulDriverIdG; + AcdDriverG.fEnabled = FALSE; + // + // Build a request to get the automatic + // connection driver entry points. + // + pIrp = IoBuildDeviceIoControlRequest( + IOCTL_INTERNAL_ACD_BIND, + pAcdDeviceObject, + (PVOID)&pDriver, + sizeof (pDriver), + NULL, + 0, + TRUE, + NULL, + &ioStatusBlock); + if (pIrp == NULL) { + ObDereferenceObject(pAcdDeviceObject); + return; + } + // + // Submit the request to the + // automatic connection driver. + // + status = IoCallDriver(pAcdDeviceObject, pIrp); + fAcdLoadedG = (status == STATUS_SUCCESS); + // + // Close the device. + // + ObDereferenceObject(pAcdDeviceObject); +} // NbfAcdBind + + + +VOID +NbfAcdUnbind() +{ + NTSTATUS status; + UNICODE_STRING nameString; + IO_STATUS_BLOCK ioStatusBlock; + PIRP pIrp; + PFILE_OBJECT pAcdFileObject; + PDEVICE_OBJECT pAcdDeviceObject; + PACD_DRIVER pDriver = &AcdDriverG; + + // + // Don't bother to unbind if we + // didn't successfully bind in the + // first place. + // + if (!fAcdLoadedG) + return; + // + // Initialize the name of the automatic + // connection device. + // + RtlInitUnicodeString(&nameString, ACD_DEVICE_NAME); + // + // Get the file and device objects for the + // device. + // + status = IoGetDeviceObjectPointer( + &nameString, + SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE, + &pAcdFileObject, + &pAcdDeviceObject); + if (status != STATUS_SUCCESS) + return; + // + // Reference the device object. + // + ObReferenceObject(pAcdDeviceObject); + // + // Remove the reference IoGetDeviceObjectPointer() + // put on the file object. + // + ObDereferenceObject(pAcdFileObject); + // + // Build a request to unbind from + // the automatic connection driver. + // + pIrp = IoBuildDeviceIoControlRequest( + IOCTL_INTERNAL_ACD_UNBIND, + pAcdDeviceObject, + (PVOID)&pDriver, + sizeof (pDriver), + NULL, + 0, + TRUE, + NULL, + &ioStatusBlock); + if (pIrp == NULL) { + ObDereferenceObject(pAcdDeviceObject); + return; + } + // + // Submit the request to the + // automatic connection driver. + // + status = IoCallDriver(pAcdDeviceObject, pIrp); + // + // Close the device. + // + ObDereferenceObject(pAcdDeviceObject); +} // NbfAcdUnbind + +#endif // RASAUTODIAL + diff --git a/private/ntos/tdi/nbf/connect.c b/private/ntos/tdi/nbf/connect.c new file mode 100644 index 000000000..af2aec64b --- /dev/null +++ b/private/ntos/tdi/nbf/connect.c @@ -0,0 +1,1700 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + connect.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiAccept + o TdiListen + o TdiConnect + o TdiDisconnect + o TdiAssociateAddress + o TdiDisassociateAddress + o OpenConnection + o CloseConnection + +Author: + + David Beaver (dbeaver) 1 July 1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef notdef // RASAUTODIAL +#include <acd.h> +#include <acdapi.h> +#endif // RASAUTODIAL + +#ifdef notdef // RASAUTODIAL +extern BOOLEAN fAcdLoadedG; +extern ACD_DRIVER AcdDriverG; + +// +// Imported functions. +// +VOID +NbfRetryPreTdiConnect( + IN BOOLEAN fSuccess, + IN PVOID *pArgs + ); + +BOOLEAN +NbfAttemptAutoDial( + IN PTP_CONNECTION Connection, + IN ULONG ulFlags, + IN ACD_CONNECT_CALLBACK pProc, + IN PVOID pArg + ); + +VOID +NbfCancelPreTdiConnect( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ); +#endif // RASAUTODIAL + +NTSTATUS +NbfTdiConnectCommon( + IN PIRP Irp + ); + + + +NTSTATUS +NbfTdiAccept( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiAccept request for the transport provider. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_CONNECTION connection; + PIO_STACK_LOCATION irpSp; + KIRQL oldirql; + NTSTATUS status; + + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint0 ("NbfTdiAccept: Entered.\n"); + } + + // + // Get the connection this is associated with; if there is none, get out. + // This adds a connection reference of type BY_ID if successful. + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + connection = irpSp->FileObject->FsContext; + + // + // This adds a connection reference of type BY_ID if successful. + // + + status = NbfVerifyConnectionObject (connection); + + if (!NT_SUCCESS (status)) { + return status; + } + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + + // + // just set the connection flags to allow reads and writes to proceed. + // + + ACQUIRE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + // + // Turn off the stopping flag for this connection. + // + + connection->Flags2 &= ~CONNECTION_FLAGS2_STOPPING; + connection->Status = STATUS_PENDING; + + connection->Flags2 |= CONNECTION_FLAGS2_ACCEPTED; + + + if (connection->AddressFile->ConnectIndicationInProgress) { + connection->Flags2 |= CONNECTION_FLAGS2_INDICATING; + } + + if ((connection->Flags2 & CONNECTION_FLAGS2_WAITING_SC) != 0) { + + // + // We previously completed a listen, now the user is + // coming back with an accept, Set this flag to allow + // the connection to proceed. + // + // BUGBUG: If the connection has gone down in the + // meantime, we have just reenabled it. + // + + ACQUIRE_DPC_SPIN_LOCK (connection->LinkSpinLock); + connection->Flags |= CONNECTION_FLAGS_READY; + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + + INCREMENT_COUNTER (connection->Provider, OpenConnections); + + // + // Set this flag to enable disconnect indications; once + // the client has accepted he expects those. + // + + connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED; + + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + NbfSendSessionConfirm (connection); + + } else { + + // + // This accept is being called at some point before + // the link is up; directly from the connection handler + // or at some point slightly later. We don't set + // FLAGS2_REQ_COMPLETED now because the reference + // count is not high enough; we set it when we get + // the session initialize. + // + // BUGBUG: If the connection goes down in the meantime, + // we won't indicate the disconnect. + // + + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + } + + NbfDereferenceConnection ("Temp TdiAccept", connection, CREF_BY_ID); + + KeLowerIrql (oldirql); + + return STATUS_SUCCESS; + +} /* NbfTdiAccept */ + + +NTSTATUS +NbfTdiAssociateAddress( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the association of the connection and the address for + the user. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PFILE_OBJECT fileObject; + PTP_ADDRESS_FILE addressFile; + PTP_ADDRESS oldAddress; + PTP_CONNECTION connection; + PIO_STACK_LOCATION irpSp; + PTDI_REQUEST_KERNEL_ASSOCIATE parameters; + PDEVICE_CONTEXT deviceContext; + + KIRQL oldirql, oldirql2; + + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint0 ("TdiAssociateAddress: Entered.\n"); + } + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + // + // verify that the operation is taking place on a connection. At the same + // time we do this, we reference the connection. This ensures it does not + // get removed out from under us. Note also that we do the connection + // lookup within a try/except clause, thus protecting ourselves against + // really bogus handles + // + + connection = irpSp->FileObject->FsContext; + status = NbfVerifyConnectionObject (connection); + if (!NT_SUCCESS (status)) { + return status; + } + + + // + // Make sure this connection is ready to be associated. + // + + oldAddress = (PTP_ADDRESS)NULL; + + try { + + ACQUIRE_C_SPIN_LOCK (&connection->SpinLock, &oldirql2); + + if ((connection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) && + ((connection->Flags2 & CONNECTION_FLAGS2_DISASSOCIATED) == 0)) { + + // + // The connection is already associated with + // an active connection...bad! + // + + RELEASE_C_SPIN_LOCK (&connection->SpinLock, oldirql2); + NbfDereferenceConnection ("Temp Ref Associate", connection, CREF_BY_ID); + + return STATUS_INVALID_CONNECTION; + + } else { + + // + // See if there is an old association hanging around... + // this happens if the connection has been disassociated, + // but not closed. + // + + if (connection->Flags2 & CONNECTION_FLAGS2_DISASSOCIATED) { + + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint0 ("NbfTdiAssociateAddress: removing association.\n"); + } + + // + // Save this; since it is non-null this address + // will be dereferenced after the connection + // spinlock is released. + // + + oldAddress = connection->AddressFile->Address; + + // + // Remove the old association. + // + + connection->Flags2 &= ~CONNECTION_FLAGS2_ASSOCIATED; + RemoveEntryList (&connection->AddressList); + RemoveEntryList (&connection->AddressFileList); + InitializeListHead (&connection->AddressList); + InitializeListHead (&connection->AddressFileList); + connection->AddressFile = NULL; + + } + + } + + RELEASE_C_SPIN_LOCK (&connection->SpinLock, oldirql2); + + } except(EXCEPTION_EXECUTE_HANDLER) { + + DbgPrint ("NBF: Got exception 1 in NbfTdiAssociateAddress\n"); + DbgBreakPoint(); + + RELEASE_C_SPIN_LOCK (&connection->SpinLock, oldirql2); + NbfDereferenceConnection ("Temp Ref Associate", connection, CREF_BY_ID); + return GetExceptionCode(); + } + + + // + // If we removed an old association, dereference the + // address. + // + + if (oldAddress != (PTP_ADDRESS)NULL) { + + NbfDereferenceAddress("Removed old association", oldAddress, AREF_CONNECTION); + + } + + + deviceContext = connection->Provider; + + parameters = (PTDI_REQUEST_KERNEL_ASSOCIATE)&irpSp->Parameters; + + // + // get a pointer to the address File Object, which points us to the + // transport's address object, which is where we want to put the + // connection. + // + + status = ObReferenceObjectByHandle ( + parameters->AddressHandle, + 0L, + 0, + KernelMode, + (PVOID *) &fileObject, + NULL); + + if (NT_SUCCESS(status)) { + + // + // we might have one of our address objects; verify that. + // + + addressFile = fileObject->FsContext; + + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint3 ("NbfTdiAssociateAddress: Connection %lx Irp %lx AddressFile %lx \n", + connection, Irp, addressFile); + } + + if (NT_SUCCESS (NbfVerifyAddressObject (addressFile))) { + + // + // have an address and connection object. Add the connection to the + // address object database. Also add the connection to the address + // file object db (used primarily for cleaning up). Reference the + // address to account for one more reason for it staying open. + // + + ACQUIRE_SPIN_LOCK (&addressFile->Address->SpinLock, &oldirql); + if ((addressFile->Address->Flags & ADDRESS_FLAGS_STOPPING) == 0) { + + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint2 ("NbfTdiAssociateAddress: Valid Address %lx %lx\n", + addressFile->Address, addressFile); + } + + try { + + ACQUIRE_C_SPIN_LOCK (&connection->SpinLock, &oldirql2); + + if ((connection->Flags2 & CONNECTION_FLAGS2_CLOSING) == 0) { + + NbfReferenceAddress ( + "Connection associated", + addressFile->Address, + AREF_CONNECTION); + +#if DBG + if (!(IsListEmpty(&connection->AddressList))) { + DbgPrint ("NBF: C %lx, new A %lx, in use\n", + connection, addressFile->Address); + DbgBreakPoint(); + } +#endif + InsertTailList ( + &addressFile->Address->ConnectionDatabase, + &connection->AddressList); + +#if DBG + if (!(IsListEmpty(&connection->AddressFileList))) { + DbgPrint ("NBF: C %lx, new AF %lx, in use\n", + connection, addressFile); + DbgBreakPoint(); + } +#endif + InsertTailList ( + &addressFile->ConnectionDatabase, + &connection->AddressFileList); + + connection->AddressFile = addressFile; + connection->Flags2 |= CONNECTION_FLAGS2_ASSOCIATED; + connection->Flags2 &= ~CONNECTION_FLAGS2_DISASSOCIATED; + + if (addressFile->ConnectIndicationInProgress) { + connection->Flags2 |= CONNECTION_FLAGS2_INDICATING; + } + + status = STATUS_SUCCESS; + + } else { + + // + // The connection is closing, stop the + // association. + // + + status = STATUS_INVALID_CONNECTION; + + } + + RELEASE_C_SPIN_LOCK (&connection->SpinLock, oldirql2); + + } except(EXCEPTION_EXECUTE_HANDLER) { + + DbgPrint ("NBF: Got exception 2 in NbfTdiAssociateAddress\n"); + DbgBreakPoint(); + + RELEASE_C_SPIN_LOCK (&connection->SpinLock, oldirql2); + + status = GetExceptionCode(); + } + + } else { + + status = STATUS_INVALID_HANDLE; //BUGBUG: should this be more informative? + } + + RELEASE_SPIN_LOCK (&addressFile->Address->SpinLock, oldirql); + + NbfDereferenceAddress ("Temp associate", addressFile->Address, AREF_VERIFY); + + } else { + + status = STATUS_INVALID_HANDLE; + } + + // + // Note that we don't keep a reference to this file object around. + // That's because the IO subsystem manages the object for us; we simply + // want to keep the association. We only use this association when the + // IO subsystem has asked us to close one of the file object, and then + // we simply remove the association. + // + + ObDereferenceObject (fileObject); + + } else { + status = STATUS_INVALID_HANDLE; + } + + NbfDereferenceConnection ("Temp Ref Associate", connection, CREF_BY_ID); + + return status; + +} /* TdiAssociateAddress */ + + +NTSTATUS +NbfTdiDisassociateAddress( + IN PIRP Irp + ) +/*++ + +Routine Description: + + This routine performs the disassociation of the connection and the address + for the user. If the connection has not been stopped, it will be stopped + here. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + KIRQL oldirql; + PIO_STACK_LOCATION irpSp; + PTP_CONNECTION connection; + NTSTATUS status; +// PDEVICE_CONTEXT DeviceContext; + + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint0 ("TdiDisassociateAddress: Entered.\n"); + } + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + connection = irpSp->FileObject->FsContext; + + // + // If successful this adds a reference of type BY_ID. + // + + status = NbfVerifyConnectionObject (connection); + + if (!NT_SUCCESS (status)) { + return status; + } + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + + ACQUIRE_DPC_C_SPIN_LOCK (&connection->SpinLock); + if ((connection->Flags2 & CONNECTION_FLAGS2_STOPPING) == 0) { + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + NbfStopConnection (connection, STATUS_LOCAL_DISCONNECT); + } else { + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + } + + // + // and now we disassociate the address. This only removes + // the appropriate reference for the connection, the + // actually disassociation will be done later. + // + // The DISASSOCIATED flag is used to make sure that + // only one person removes this reference. + // + + ACQUIRE_DPC_C_SPIN_LOCK (&connection->SpinLock); + if ((connection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) && + ((connection->Flags2 & CONNECTION_FLAGS2_DISASSOCIATED) == 0)) { + connection->Flags2 |= CONNECTION_FLAGS2_DISASSOCIATED; + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + } else { + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + } + + KeLowerIrql (oldirql); + + NbfDereferenceConnection ("Temp use in Associate", connection, CREF_BY_ID); + + return STATUS_SUCCESS; + +} /* TdiDisassociateAddress */ + + + +NTSTATUS +NbfTdiConnect( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiConnect request for the transport provider. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PTP_CONNECTION connection; + KIRQL oldirql; + PIO_STACK_LOCATION irpSp; + PTDI_REQUEST_KERNEL parameters; + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteAddress; + + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint0 ("NbfTdiConnect: Entered.\n"); + } + + // + // is the file object a connection? + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + connection = irpSp->FileObject->FsContext; + + // + // If successful this adds a reference of type BY_ID. + // + + status = NbfVerifyConnectionObject (connection); + + if (!NT_SUCCESS (status)) { + return status; + } + + parameters = (PTDI_REQUEST_KERNEL)(&irpSp->Parameters); + + // + // Check that the remote is a Netbios address. + // + + if (!NbfValidateTdiAddress( + parameters->RequestConnectionInformation->RemoteAddress, + parameters->RequestConnectionInformation->RemoteAddressLength)) { + + NbfDereferenceConnection ("Invalid Address", connection, CREF_BY_ID); + return STATUS_BAD_NETWORK_PATH; + } + + RemoteAddress = NbfParseTdiAddress((PTRANSPORT_ADDRESS)(parameters->RequestConnectionInformation->RemoteAddress), FALSE); + + if (RemoteAddress == NULL) { + + NbfDereferenceConnection ("Not Netbios", connection, CREF_BY_ID); + return STATUS_BAD_NETWORK_PATH; + + } + + // + // copy the called address someplace we can use it. + // + + connection->CalledAddress.NetbiosNameType = + RemoteAddress->NetbiosNameType; + + RtlCopyMemory( + connection->CalledAddress.NetbiosName, + RemoteAddress->NetbiosName, + 16); + +#ifdef notdef // RASAUTODIAL + if (fAcdLoadedG) { + KIRQL adirql; + BOOLEAN fEnabled; + + // + // See if the automatic connection driver knows + // about this address before we search the + // network. If it does, we return STATUS_PENDING, + // and we will come back here via NbfRetryTdiConnect(). + // + ACQUIRE_SPIN_LOCK(&AcdDriverG.SpinLock, &adirql); + fEnabled = AcdDriverG.fEnabled; + RELEASE_SPIN_LOCK(&AcdDriverG.SpinLock, adirql); + if (fEnabled && NbfAttemptAutoDial( + connection, + ACD_NOTIFICATION_PRECONNECT, + NbfRetryPreTdiConnect, + Irp)) + { + ACQUIRE_SPIN_LOCK(&connection->SpinLock, &oldirql); + connection->Flags2 |= CONNECTION_FLAGS2_AUTOCONNECT; + connection->Status = STATUS_PENDING; + RELEASE_SPIN_LOCK(&connection->SpinLock, oldirql); + NbfDereferenceConnection ("Automatic connection", connection, CREF_BY_ID); + // + // Set a special cancel routine on the irp + // in case we get cancelled during the + // automatic connection. + // + IoSetCancelRoutine(Irp, NbfCancelPreTdiConnect); + return STATUS_PENDING; + } + } +#endif // RASAUTODIAL + + return NbfTdiConnectCommon(Irp); +} // NbfTdiConnect + + + +NTSTATUS +NbfTdiConnectCommon( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiConnect request for the transport provider. + Note: the caller needs to call NbfVerifyConnectionObject(pConnection) + before calling this routine. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PTP_CONNECTION connection; + LARGE_INTEGER timeout = {0,0}; + KIRQL oldirql, cancelirql; + PTP_REQUEST tpRequest; + PIO_STACK_LOCATION irpSp; + PTDI_REQUEST_KERNEL parameters; + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteAddress; + ULONG NameQueryTimeout; + + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint0 ("NbfTdiConnectCommon: Entered.\n"); + } + + // + // is the file object a connection? + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + connection = irpSp->FileObject->FsContext; + parameters = (PTDI_REQUEST_KERNEL)(&irpSp->Parameters); + + // + // fix up the timeout if required; no connect request should take more + // than 15 seconds if there is someone out there. We'll assume that's + // what the user wanted if they specify -1 as the timer length. + // + + if (parameters->RequestSpecific != NULL) { + if ((((PLARGE_INTEGER)(parameters->RequestSpecific))->LowPart == -1) && + (((PLARGE_INTEGER)(parameters->RequestSpecific))->HighPart == -1)) { + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint1 ("TdiConnect: Modifying user timeout to %lx seconds.\n", + TDI_TIMEOUT_CONNECT); + } + + timeout.LowPart = (ULONG)(-TDI_TIMEOUT_CONNECT * 10000000L); // n * 10 ** 7 => 100ns units + if (timeout.LowPart != 0) { + timeout.HighPart = -1L; + } else { + timeout.HighPart = 0; + } + + } else { + + timeout.LowPart = ((PLARGE_INTEGER)(parameters->RequestSpecific))->LowPart; + timeout.HighPart = ((PLARGE_INTEGER)(parameters->RequestSpecific))->HighPart; + } + } + + // + // We need a request object to keep track of this TDI request. + // Attach this request to the new connection object. + // + + status = NbfCreateRequest ( + Irp, // IRP for this request. + connection, // context. + REQUEST_FLAGS_CONNECTION, // partial flags. + NULL, + 0, + timeout, + &tpRequest); + + if (!NT_SUCCESS (status)) { // couldn't make the request. + NbfDereferenceConnection ("Throw away", connection, CREF_BY_ID); + return status; // return with failure. + } else { + + // Reference the connection since NbfDestroyRequest derefs it. + + NbfReferenceConnection("For connect request", connection, CREF_REQUEST); + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + + tpRequest->Owner = ConnectionType; + IoAcquireCancelSpinLock (&cancelirql); + ACQUIRE_DPC_C_SPIN_LOCK (&connection->SpinLock); + if ((connection->Flags2 & CONNECTION_FLAGS2_STOPPING) != 0) { + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + IoReleaseCancelSpinLock (cancelirql); + NbfCompleteRequest ( + tpRequest, + connection->Status, + 0); + KeLowerIrql (oldirql); + NbfDereferenceConnection("Temporary Use 1", connection, CREF_BY_ID); + return STATUS_PENDING; + } else { + InsertTailList (&connection->InProgressRequest,&tpRequest->Linkage); + + // + // Initialize this now, we cut these down on an async medium + // that is disconnected. + // + + connection->Retries = (USHORT)connection->Provider->NameQueryRetries; + NameQueryTimeout = connection->Provider->NameQueryTimeout; + + if (connection->Provider->MacInfo.MediumAsync) { + + // + // If we are on an async medium, then we need to send out + // a committed NAME_QUERY frame right from the start, since + // the FIND_NAME frames are not forwarded by the gateway. + // + + connection->Flags2 |= (CONNECTION_FLAGS2_CONNECTOR | // we're the initiator. + CONNECTION_FLAGS2_WAIT_NR); // wait for NAME_RECOGNIZED. + + // + // Because we may call NbfTdiConnect twice + // via an automatic connection, check to see + // if an LSN has already been assigned. + // + if (!(connection->Flags2 & CONNECTION_FLAGS2_GROUP_LSN)) { + connection->Flags2 |= CONNECTION_FLAGS2_GROUP_LSN; + + if (NbfAssignGroupLsn(connection) != STATUS_SUCCESS) { + + // + // Could not find an empty LSN; have to fail. + // + RemoveEntryList(&tpRequest->Linkage); + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + IoReleaseCancelSpinLock (cancelirql); + NbfCompleteRequest ( + tpRequest, + connection->Status, + 0); + KeLowerIrql (oldirql); + NbfDereferenceConnection("Temporary Use 1", connection, CREF_BY_ID); + return STATUS_PENDING; + + } + } + + if (!connection->Provider->MediumSpeedAccurate) { + + // + // The link is not up, so we cut our timeouts down. + // We still send one frame so that loopback works. + // + + connection->Retries = 1; + NameQueryTimeout = NAME_QUERY_TIMEOUT / 10; + + } + + } else { + + // + // Normal connection, we send out a FIND_NAME first. + // + + connection->Flags2 |= (CONNECTION_FLAGS2_CONNECTOR | // we're the initiator. + CONNECTION_FLAGS2_WAIT_NR_FN); // wait for NAME_RECOGNIZED. + + } + + connection->Flags2 &= ~(CONNECTION_FLAGS2_STOPPING | + CONNECTION_FLAGS2_INDICATING); + connection->Status = STATUS_PENDING; + + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + // + // Check if the IRP has been cancelled. + // + + if (Irp->Cancel) { + Irp->CancelIrql = cancelirql; + NbfCancelConnection((PDEVICE_OBJECT)(connection->Provider), Irp); + KeLowerIrql (oldirql); + NbfDereferenceConnection ("IRP cancelled", connection, CREF_BY_ID); // release lookup hold. + return STATUS_PENDING; + } + + IoSetCancelRoutine(Irp, NbfCancelConnection); + IoReleaseCancelSpinLock(cancelirql); + + } + } + + + // + // On "easily disconnected" networks, quick reregister + // (one ADD_NAME_QUERY) the address if NEED_REREGISTER + // is set (happens when we get a five-second period + // with no multicast activity). If we are currently + // quick reregistering, wait for it to complete. + // + + if (connection->Provider->EasilyDisconnected) { + + PTP_ADDRESS Address; + LARGE_INTEGER Timeout; + + // + // If the address needs (or is) reregistering, then do wait, + // setting a flag so the connect will be resumed when the + // reregistration times out. + // + + Address = connection->AddressFile->Address; + + ACQUIRE_DPC_SPIN_LOCK (&Address->SpinLock); + + if ((Address->Flags & + (ADDRESS_FLAGS_NEED_REREGISTER | ADDRESS_FLAGS_QUICK_REREGISTER)) != 0) { + + connection->Flags2 |= CONNECTION_FLAGS2_W_ADDRESS; + + if (Address->Flags & ADDRESS_FLAGS_NEED_REREGISTER) { + + Address->Flags &= ~ADDRESS_FLAGS_NEED_REREGISTER; + Address->Flags |= ADDRESS_FLAGS_QUICK_REREGISTER; + + NbfReferenceAddress ("start registration", Address, AREF_TIMER); + RELEASE_DPC_SPIN_LOCK (&Address->SpinLock); + + // + // Now start reregistration process by starting up a retransmission timer + // and begin sending ADD_NAME_QUERY NetBIOS frames. + // + + Address->Retries = 1; + Timeout.LowPart = (ULONG)(-(LONG)Address->Provider->AddNameQueryTimeout); + Timeout.HighPart = -1; + KeSetTimer (&Address->Timer, *(PTIME)&Timeout, &Address->Dpc); + + (VOID)NbfSendAddNameQuery (Address); // send first ADD_NAME_QUERY. + + } else { + + RELEASE_DPC_SPIN_LOCK (&Address->SpinLock); + + } + KeLowerIrql (oldirql); + + NbfDereferenceConnection("Temporary Use 4", connection, CREF_BY_ID); + + return STATUS_PENDING; // things are started. + + } else { + + RELEASE_DPC_SPIN_LOCK (&Address->SpinLock); + + } + + } + + // + // Send the NAME_QUERY frame as a FIND.NAME to get a NAME_RECOGNIZED + // frame back. The first time we send this frame, we are just looking + // for the data link information to decide whether we already have + // a link with the remote NetBIOS name. If we do, we can reuse it. + // If we don't, then we make one first, and then use it. A consequence + // of this is that we really engage in an extra non-committal NQ/NR + // exchange up front to locate the remote guy, and then commit to an actual + // LSN with a second NQ/NR sequence to establish the transport connection + // + + NbfSendNameQuery ( + connection, + TRUE); + + // + // Start the connection timer (do this at the end, so that + // if we get delayed in this routine the connection won't + // get into an unexpected state). + // + + NbfStartConnectionTimer ( + connection, + ConnectionEstablishmentTimeout, + NameQueryTimeout); + + KeLowerIrql (oldirql); + + NbfDereferenceConnection("Temporary Use 3", connection, CREF_BY_ID); + + return STATUS_PENDING; // things are started. + +} /* TdiConnect */ + + + +NTSTATUS +NbfTdiDisconnect( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiDisconnect request for the transport provider. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_CONNECTION connection; + LARGE_INTEGER timeout; + PIO_STACK_LOCATION irpSp; + PTDI_REQUEST_KERNEL parameters; + KIRQL oldirql; + NTSTATUS status; + + + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint0 ("TdiDisconnect: Entered.\n"); + } + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + connection = irpSp->FileObject->FsContext; + + // + // If successful this adds a reference of type BY_ID. + // + + status = NbfVerifyConnectionObject (connection); + if (!NT_SUCCESS (status)) { + return status; + } + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + + ACQUIRE_DPC_C_SPIN_LOCK (&connection->SpinLock); + +#if DBG + if (NbfDisconnectDebug) { + STRING remoteName; + STRING localName; + remoteName.Length = NETBIOS_NAME_LENGTH - 1; + remoteName.Buffer = connection->RemoteName; + localName.Length = NETBIOS_NAME_LENGTH - 1; + localName.Buffer = connection->AddressFile->Address->NetworkName->NetbiosName; + NbfPrint2( "TdiDisconnect entered for connection to %S from %S\n", + &remoteName, &localName ); + } +#endif + + // + // if the connection is currently stopping, there's no reason to blow + // it away... + // + + if ((connection->Flags2 & CONNECTION_FLAGS2_STOPPING) != 0) { +#if 0 + NbfPrint2 ("TdiDisconnect: ignoring disconnect %lx, connection stopping (%lx)\n", + connection, connection->Status); +#endif + + // + // In case a connect indication is in progress. + // + + connection->Flags2 |= CONNECTION_FLAGS2_DISCONNECT; + + // + // If possible, queue the disconnect. This flag is cleared + // when the indication is about to be done. + // + + if ((connection->Flags2 & CONNECTION_FLAGS2_REQ_COMPLETED) && + (connection->Flags2 & CONNECTION_FLAGS2_LDISC) == 0) { +#if DBG + DbgPrint ("NBF: Queueing disconnect irp %lx\n", Irp); +#endif + connection->Flags2 |= CONNECTION_FLAGS2_LDISC; + status = STATUS_SUCCESS; + } else { + status = connection->Status; + } + + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + KeLowerIrql (oldirql); + NbfDereferenceConnection ("Ignoring disconnect", connection, CREF_BY_ID); // release our lookup reference. + return status; + + } + + connection->Flags2 &= ~ (CONNECTION_FLAGS2_ACCEPTED | + CONNECTION_FLAGS2_PRE_ACCEPT | + CONNECTION_FLAGS2_WAITING_SC); + + connection->Flags2 |= CONNECTION_FLAGS2_DISCONNECT; + connection->Flags2 |= CONNECTION_FLAGS2_LDISC; + + // + // Set this flag so the disconnect IRP is completed. + // + // BUGBUG: If the connection goes down before we can + // call NbfStopConnection with STATUS_LOCAL_DISCONNECT, + // the disconnect IRP won't get completed. + // + + connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED; + +// connection->DisconnectIrp = Irp; + + // + // fix up the timeout if required; no disconnect request should take very + // long. However, the user can cause the timeout to not happen if they + // desire that. + // + + parameters = (PTDI_REQUEST_KERNEL)(&irpSp->Parameters); + + // + // fix up the timeout if required; no disconnect request should take more + // than 15 seconds. We'll assume that's what the user wanted if they + // specify -1 as the timer. + // + + if (parameters->RequestSpecific != NULL) { + if ((((PLARGE_INTEGER)(parameters->RequestSpecific))->LowPart == -1) && + (((PLARGE_INTEGER)(parameters->RequestSpecific))->HighPart == -1)) { + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint1 ("TdiDisconnect: Modifying user timeout to %lx seconds.\n", + TDI_TIMEOUT_CONNECT); + } + + timeout.LowPart = (ULONG)(-TDI_TIMEOUT_DISCONNECT * 10000000L); // n * 10 ** 7 => 100ns units + if (timeout.LowPart != 0) { + timeout.HighPart = -1L; + } else { + timeout.HighPart = 0; + } + + } else { + timeout.LowPart = ((PLARGE_INTEGER)(parameters->RequestSpecific))->LowPart; + timeout.HighPart = ((PLARGE_INTEGER)(parameters->RequestSpecific))->HighPart; + } + } + + // + // Now the reason for the disconnect + // + + if ((ULONG)(parameters->RequestFlags) & (ULONG)TDI_DISCONNECT_RELEASE) { + connection->Flags2 |= CONNECTION_FLAGS2_DESTROY; + } else if ((ULONG)(parameters->RequestFlags) & (ULONG)TDI_DISCONNECT_ABORT) { + connection->Flags2 |= CONNECTION_FLAGS2_ABORT; + } else if ((ULONG)(parameters->RequestFlags) & (ULONG)TDI_DISCONNECT_WAIT) { + connection->Flags2 |= CONNECTION_FLAGS2_ORDREL; + } + + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint1 ("TdiDisconnect calling NbfStopConnection %lx\n", connection); + } + + // + // This will get passed to IoCompleteRequest during TdiDestroyConnection + // + + NbfStopConnection (connection, STATUS_LOCAL_DISCONNECT); // starts the abort sequence. + + KeLowerIrql (oldirql); + + NbfDereferenceConnection ("Disconnecting", connection, CREF_BY_ID); // release our lookup reference. + + // + // This request will be completed by TdiDestroyConnection once + // the connection reference count drops to 0. + // + + return STATUS_SUCCESS; +} /* TdiDisconnect */ + + +NTSTATUS +NbfTdiListen( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiListen request for the transport provider. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PTP_CONNECTION connection; + LARGE_INTEGER timeout = {0,0}; + KIRQL oldirql, cancelirql; + PTP_REQUEST tpRequest; + PIO_STACK_LOCATION irpSp; + PTDI_REQUEST_KERNEL_LISTEN parameters; + PTDI_CONNECTION_INFORMATION ListenInformation; + TDI_ADDRESS_NETBIOS UNALIGNED * ListenAddress; + PVOID RequestBuffer2; + ULONG RequestBuffer2Length; + + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint0 ("TdiListen: Entered.\n"); + } + + // + // validate this connection + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + connection = irpSp->FileObject->FsContext; + + // + // If successful this adds a reference of type BY_ID. + // + + status = NbfVerifyConnectionObject (connection); + + if (!NT_SUCCESS (status)) { + return status; + } + + parameters = (PTDI_REQUEST_KERNEL_LISTEN)&irpSp->Parameters; + + // + // Record the remote address if there is one. + // + + ListenInformation = parameters->RequestConnectionInformation; + + if ((ListenInformation != NULL) && + (ListenInformation->RemoteAddress != NULL)) { + + if ((NbfValidateTdiAddress( + ListenInformation->RemoteAddress, + ListenInformation->RemoteAddressLength)) && + ((ListenAddress = NbfParseTdiAddress(ListenInformation->RemoteAddress, FALSE)) != NULL)) { + + RequestBuffer2 = (PVOID)ListenAddress->NetbiosName, + RequestBuffer2Length = NETBIOS_NAME_LENGTH; + + } else { + + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint0 ("TdiListen: Create Request Failed, bad address.\n"); + } + + NbfDereferenceConnection ("Bad address", connection, CREF_BY_ID); + return STATUS_BAD_NETWORK_PATH; + } + + } else { + + RequestBuffer2 = NULL; + RequestBuffer2Length = 0; + } + + // + // We need a request object to keep track of this TDI request. + // Attach this request to the new connection object. + // + + status = NbfCreateRequest ( + Irp, // IRP for this request. + connection, // context. + REQUEST_FLAGS_CONNECTION, // partial flags. + RequestBuffer2, + RequestBuffer2Length, + timeout, // timeout value (can be 0). + &tpRequest); + + + if (!NT_SUCCESS (status)) { // couldn't make the request. + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint1 ("TdiListen: Create Request Failed, reason: %lx.\n", status); + } + + NbfDereferenceConnection ("For create", connection, CREF_BY_ID); + return status; // return with failure. + } + + // Reference the connection since NbfDestroyRequest derefs it. + + IoAcquireCancelSpinLock (&cancelirql); + ACQUIRE_C_SPIN_LOCK (&connection->SpinLock, &oldirql); + tpRequest->Owner = ConnectionType; + + NbfReferenceConnection("For listen request", connection, CREF_REQUEST); + + if ((connection->Flags2 & CONNECTION_FLAGS2_STOPPING) != 0) { + + RELEASE_C_SPIN_LOCK (&connection->SpinLock,oldirql); + IoReleaseCancelSpinLock(cancelirql); + + NbfCompleteRequest ( + tpRequest, + connection->Status, + 0); + NbfDereferenceConnection("Temp create", connection, CREF_BY_ID); + return STATUS_PENDING; + + } else { + + InsertTailList (&connection->InProgressRequest,&tpRequest->Linkage); + connection->Flags2 |= (CONNECTION_FLAGS2_LISTENER | // we're the passive one. + CONNECTION_FLAGS2_WAIT_NQ); // wait for NAME_QUERY. + connection->Flags2 &= ~(CONNECTION_FLAGS2_INDICATING | + CONNECTION_FLAGS2_STOPPING); + connection->Status = STATUS_PENDING; + + // + // If TDI_QUERY_ACCEPT is not set, then we set PRE_ACCEPT to + // indicate that when the listen completes we do not have to + // wait for a TDI_ACCEPT to continue. + // + + if ((parameters->RequestFlags & TDI_QUERY_ACCEPT) == 0) { + connection->Flags2 |= CONNECTION_FLAGS2_PRE_ACCEPT; + } + + RELEASE_C_SPIN_LOCK (&connection->SpinLock,oldirql); + + // + // Check if the IRP has been cancelled. + // + + if (Irp->Cancel) { + Irp->CancelIrql = cancelirql; + NbfCancelConnection((PDEVICE_OBJECT)(connection->Provider), Irp); + NbfDereferenceConnection ("IRP cancelled", connection, CREF_BY_ID); // release lookup hold. + return STATUS_PENDING; + } + + IoSetCancelRoutine(Irp, NbfCancelConnection); + IoReleaseCancelSpinLock(cancelirql); + + } + + // + // Wait for an incoming NAME_QUERY frame. The remainder of the + // connectionless protocol to set up a connection is processed + // in the NAME_QUERY frame handler. + // + + NbfDereferenceConnection("Temp create", connection, CREF_BY_ID); + + return STATUS_PENDING; // things are started. +} /* TdiListen */ + + +NTSTATUS +NbfOpenConnection ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine is called to open a connection. Note that the connection that + is open is of little use until associated with an address; until then, + the only thing that can be done with it is close it. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + + IrpSp - Pointer to current IRP stack frame. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + NTSTATUS status; + PTP_CONNECTION connection; + PFILE_FULL_EA_INFORMATION ea; + + UNREFERENCED_PARAMETER (Irp); + + // + // BUGBUG: check ea to make sure it's correct + // + + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint0 ("NbfOpenConnection: Entered.\n"); + } + + DeviceContext = (PDEVICE_CONTEXT)DeviceObject; + + // + // First, try to make a connection object to represent this pending + // connection. Then fill in the relevant fields. + // In addition to the creation, if successful NbfCreateConnection + // will create a second reference which is removed once the request + // references the connection, or if the function exits before that. + + status = NbfCreateConnection (DeviceContext, &connection); + if (!NT_SUCCESS (status)) { + return status; // sorry, we couldn't make one. + } + + // + // set the connection context so we can connect the user to this data + // structure + // + + ea = (PFILE_FULL_EA_INFORMATION)Irp->AssociatedIrp.SystemBuffer; + RtlCopyMemory ( + &connection->Context, + &ea->EaName[ea->EaNameLength+1], + sizeof (PVOID)); + + // + // let file object point at connection and connection at file object + // + + IrpSp->FileObject->FsContext = (PVOID)connection; + IrpSp->FileObject->FsContext2 = (PVOID)TDI_CONNECTION_FILE; + connection->FileObject = IrpSp->FileObject; + + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint1 ("NBFOpenConnection: Opened Connection %lx.\n", + connection); + } + + return status; + +} /* NbfOpenConnection */ + +#if DBG +VOID +ConnectionCloseTimeout( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) + +{ + PTP_CONNECTION Connection; + + Dpc, SystemArgument1, SystemArgument2; // prevent compiler warnings + + Connection = (PTP_CONNECTION)DeferredContext; + + DbgPrint ("NBF: Close of connection %lxpending for 2 minutes\n", Connection); + DbgBreakPoint(); + +} +#endif + + +NTSTATUS +NbfCloseConnection ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine is called to close a connection. There may be actions in + progress on this connection, so we note the closing IRP, mark the + connection as closing, and complete it somewhere down the road (when all + references have been removed). + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + + IrpSp - Pointer to current IRP stack frame. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS status; + KIRQL oldirql; + PTP_CONNECTION connection; + + UNREFERENCED_PARAMETER (DeviceObject); + UNREFERENCED_PARAMETER (Irp); + + IF_NBFDBG (NBF_DEBUG_CONNECT) { + NbfPrint0 ("NbfCloseConnection: Entered.\n"); + } + + // + // is the file object a connection? + // + + connection = IrpSp->FileObject->FsContext; + + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + + // + // We duplicate the code from VerifyConnectionObject, + // although we don't actually call that since it does + // a reference, which we don't want (to avoid bouncing + // the reference count up from 0 if this is a dead + // link). + // + + try { + + if ((connection->Size == sizeof (TP_CONNECTION)) && + (connection->Type == NBF_CONNECTION_SIGNATURE)) { + + ACQUIRE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + if ((connection->Flags2 & CONNECTION_FLAGS2_CLOSING) == 0) { + + status = STATUS_SUCCESS; + + } else { + + status = STATUS_INVALID_CONNECTION; + } + + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + } else { + + status = STATUS_INVALID_CONNECTION; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + KeLowerIrql (oldirql); + return GetExceptionCode(); + } + + if (!NT_SUCCESS (status)) { + KeLowerIrql (oldirql); + return status; + } + + // + // We recognize it; is it closing already? + // + + ACQUIRE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + if ((connection->Flags2 & CONNECTION_FLAGS2_CLOSING) != 0) { + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + KeLowerIrql (oldirql); +#if DBG + NbfPrint1("Closing already-closing connection %lx\n", connection); +#endif + return STATUS_INVALID_CONNECTION; + } + + connection->Flags2 |= CONNECTION_FLAGS2_CLOSING; + + // + // if there is activity on the connection, tear it down. + // + + if ((connection->Flags2 & CONNECTION_FLAGS2_STOPPING) == 0) { + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + NbfStopConnection (connection, STATUS_LOCAL_DISCONNECT); + ACQUIRE_DPC_C_SPIN_LOCK (&connection->SpinLock); + } + + // + // If the connection is still associated, disassociate it. + // + + if ((connection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) && + ((connection->Flags2 & CONNECTION_FLAGS2_DISASSOCIATED) == 0)) { + connection->Flags2 |= CONNECTION_FLAGS2_DISASSOCIATED; + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + } else { + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + } + + // + // Save this to complete the IRP later. + // + + connection->CloseIrp = Irp; + +#if 0 + // + // make it impossible to use this connection from the file object + // + + IrpSp->FileObject->FsContext = NULL; + IrpSp->FileObject->FsContext2 = NULL; + connection->FileObject = NULL; +#endif + +#if DBG + { + LARGE_INTEGER Timeout; + BOOLEAN AlreadyInserted; + + Timeout.LowPart = (ULONG)(-(120*SECONDS)); + Timeout.HighPart = -1; + + ACQUIRE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + AlreadyInserted = KeCancelTimer (&connection->Timer); + + KeInitializeDpc ( + &connection->Dpc, + ConnectionCloseTimeout, + (PVOID)connection); + + KeSetTimer ( + &connection->Timer, + Timeout, + &connection->Dpc); + + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + if (AlreadyInserted) { + DbgPrint ("NBF: Cancelling connection timer for debug %lx\n", connection); + NbfDereferenceConnection ("debug", connection, CREF_TIMER); + } + + } +#endif + + KeLowerIrql (oldirql); + + // + // dereference for the creation. Note that this dereference + // here won't have any effect until the regular reference count + // hits zero. + // + + NbfDereferenceConnectionSpecial (" Closing Connection", connection, CREF_SPECIAL_CREATION); + + return STATUS_PENDING; + +} /* NbfCloseConnection */ + + diff --git a/private/ntos/tdi/nbf/connobj.c b/private/ntos/tdi/nbf/connobj.c new file mode 100644 index 000000000..1e7e9221b --- /dev/null +++ b/private/ntos/tdi/nbf/connobj.c @@ -0,0 +1,2413 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + connobj.c + +Abstract: + + This module contains code which implements the TP_CONNECTION object. + Routines are provided to create, destroy, reference, and dereference, + transport connection objects. + + A word about connection reference counts: + + With TDI version 2, connections live on even after they have been stopped. + This necessitated changing the way NBF handles connection reference counts, + making the stopping of a connection only another way station in the life + of a connection, rather than its demise. Reference counts now work like + this: + + Connection State Reference Count Flags + ------------------ ----------------- -------- + Opened, no activity 1 0 + Open, Associated 2 FLAGS2_ASSOCIATED + Open, Assoc., Connected 3 FLAGS_READY + Above + activity >3 varies + Open, Assoc., Stopping >3 FLAGS_STOPPING + Open, Assoc., Stopped 3 FLAGS_STOPPING + Open, Disassoc. Complete 2 FLAGS_STOPPING + FLAGS2_ASSOCIATED == 0 + Closing 1 FLAGS2_CLOSING + Closed 0 FLAGS2_CLOSING + + Note that keeping the stopping flag set when the connection has fully + stopped avoids using the connection until it is connected again; the + CLOSING flag serves the same purpose. This allows a connection to run + down in its own time. + + +Author: + + David Beaver (dbeaver) 1 July 1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef RASAUTODIAL +#include <acd.h> +#include <acdapi.h> +#endif // RASAUTODIAL + +#ifdef RASAUTODIAL +extern BOOLEAN fAcdLoadedG; +extern ACD_DRIVER AcdDriverG; + +// +// Imported routines +// +BOOLEAN +NbfAttemptAutoDial( + IN PTP_CONNECTION Connection, + IN ULONG ulFlags, + IN ACD_CONNECT_CALLBACK pProc, + IN PVOID pArg + ); + +VOID +NbfRetryTdiConnect( + IN BOOLEAN fSuccess, + IN PVOID *pArgs + ); + +BOOLEAN +NbfCancelTdiConnect( + IN PDEVICE_OBJECT pDeviceObject, + IN PIRP pIrp + ); +#endif // RASAUTODIAL + + + +VOID +ConnectionEstablishmentTimeout( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) + +/*++ + +Routine Description: + + This routine is executed as a DPC at DISPATCH_LEVEL when the timeout + period for the NAME_QUERY/NAME_RECOGNIZED protocol expires. The retry + count in the Connection object is decremented, and if it reaches 0, + the connection is aborted. If the retry count has not reached zero, + then the NAME QUERY is retried. The following cases are covered by + this routine: + + 1. Initial NAME_QUERY timeout for find_name portion of connection setup. + + NQ(find_name) -------------------> + [TIMEOUT] + NQ(find_name) -------------------> + <------------------- NR(find_name) + + 2. Secondary NAME_QUERY timeout for connection setup. + + NQ(connection) -------------------> + [TIMEOUT] + NQ(connection) -------------------> + <------------------- NR(connection) + + There is another case where the data link connection does not get + established within a reasonable amount of time. This is handled by + the link layer routines. + +Arguments: + + Dpc - Pointer to a system DPC object. + + DeferredContext - Pointer to the TP_CONNECTION block representing the + request that has timed out. + + SystemArgument1 - Not used. + + SystemArgument2 - Not used. + +Return Value: + + none. + +--*/ + +{ + PTP_CONNECTION Connection; + + Dpc, SystemArgument1, SystemArgument2; // prevent compiler warnings + + ENTER_NBF; + + Connection = (PTP_CONNECTION)DeferredContext; + + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint1 ("ConnectionEstablishmentTimeout: Entered for connection %lx.\n", + Connection); + } + + // + // If this connection is being run down, then we can't do anything. + // + + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + if (Connection->Flags2 & CONNECTION_FLAGS2_STOPPING) { + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + NbfDereferenceConnection ("Connect timed out", Connection, CREF_TIMER); + LEAVE_NBF; + return; + } + + + if (Connection->Flags2 & (CONNECTION_FLAGS2_WAIT_NR_FN | CONNECTION_FLAGS2_WAIT_NR)) { + + // + // We are waiting for a commital or non-commital NAME_RECOGNIZED frame. + // Decrement the retry count, and possibly resend the NAME_QUERY. + // + + if (--(Connection->Retries) == 0) { // if retry count exhausted. + + NTSTATUS StopStatus; + + // + // See if we got a no listens response, or just + // nothing. + // + + if ((Connection->Flags2 & CONNECTION_FLAGS2_NO_LISTEN) != 0) { + + Connection->Flags2 &= ~CONNECTION_FLAGS2_NO_LISTEN; + StopStatus = STATUS_REMOTE_NOT_LISTENING; // no listens + + } else { + + StopStatus = STATUS_BAD_NETWORK_PATH; // name not found. + + } + +#ifdef RASAUTODIAL + // + // If this is a connect operation that has + // returned with STATUS_BAD_NETWORK_PATH, then + // attempt to create an automatic connection. + // + if (fAcdLoadedG && + StopStatus == STATUS_BAD_NETWORK_PATH) + { + KIRQL adirql; + BOOLEAN fEnabled; + + ACQUIRE_SPIN_LOCK(&AcdDriverG.SpinLock, &adirql); + fEnabled = AcdDriverG.fEnabled; + RELEASE_SPIN_LOCK(&AcdDriverG.SpinLock, adirql); + if (fEnabled && NbfAttemptAutoDial( + Connection, + 0, + NbfRetryTdiConnect, + Connection)) + { + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + goto done; + } + } +#endif // RASAUTODIAL + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + NbfStopConnection (Connection, StopStatus); + + } else { + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + // + // We make source routing optional on every second + // name query (whenever Retries is even). + // + + NbfSendNameQuery ( + Connection, + (BOOLEAN)((Connection->Retries & 1) ? FALSE : TRUE)); + + NbfStartConnectionTimer ( + Connection, + ConnectionEstablishmentTimeout, + Connection->Provider->NameQueryTimeout); + + } + + } else { + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + } + + + // + // Dereference the connection, to account for the fact that the + // timer went off. Note that if we restarted the timer using + // NbfStartConnectionTimer, the reference count has already been + // incremented to account for the new timer. + // + +done: + NbfDereferenceConnection ("Timer timed out",Connection, CREF_TIMER); + + LEAVE_NBF; + return; + +} /* ConnectionEstablishmentTimeout */ + + +VOID +NbfAllocateConnection( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_CONNECTION *TransportConnection + ) + +/*++ + +Routine Description: + + This routine allocates storage for a transport connection. Some + minimal initialization is done. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - the device context for this connection to be + associated with. + + TransportConnection - Pointer to a place where this routine will + return a pointer to a transport connection structure. Returns + NULL if the storage cannot be allocated. + +Return Value: + + None. + +--*/ + +{ + + PTP_CONNECTION Connection; + + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + sizeof(TP_CONNECTION)) > + DeviceContext->MemoryLimit)) { + PANIC("NBF: Could not allocate connection: limit\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_LIMIT, + 103, + sizeof(TP_CONNECTION), + CONNECTION_RESOURCE_ID); + *TransportConnection = NULL; + return; + } + + Connection = (PTP_CONNECTION)ExAllocatePoolWithTag ( + NonPagedPool, + sizeof (TP_CONNECTION), + 'cFBN'); + if (Connection == NULL) { + PANIC("NBF: Could not allocate connection: no pool\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 203, + sizeof(TP_CONNECTION), + CONNECTION_RESOURCE_ID); + *TransportConnection = NULL; + return; + } + RtlZeroMemory (Connection, sizeof(TP_CONNECTION)); + + DeviceContext->MemoryUsage += sizeof(TP_CONNECTION); + ++DeviceContext->ConnectionAllocated; + + Connection->Type = NBF_CONNECTION_SIGNATURE; + Connection->Size = sizeof (TP_CONNECTION); + + Connection->Provider = DeviceContext; + Connection->ProviderInterlock = &DeviceContext->Interlock; + KeInitializeSpinLock (&Connection->SpinLock); + KeInitializeDpc ( + &Connection->Dpc, + ConnectionEstablishmentTimeout, + (PVOID)Connection); + KeInitializeTimer (&Connection->Timer); + + + InitializeListHead (&Connection->LinkList); + InitializeListHead (&Connection->AddressFileList); + InitializeListHead (&Connection->AddressList); + InitializeListHead (&Connection->PacketWaitLinkage); + InitializeListHead (&Connection->PacketizeLinkage); + InitializeListHead (&Connection->SendQueue); + InitializeListHead (&Connection->ReceiveQueue); + InitializeListHead (&Connection->InProgressRequest); + InitializeListHead (&Connection->DeferredQueue); + + NbfAddSendPacket (DeviceContext); + NbfAddSendPacket (DeviceContext); + NbfAddUIFrame (DeviceContext); + + *TransportConnection = Connection; + +} /* NbfAllocateConnection */ + + +VOID +NbfDeallocateConnection( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_CONNECTION TransportConnection + ) + +/*++ + +Routine Description: + + This routine frees storage for a transport connection. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - the device context for this connection to be + associated with. + + TransportConnection - Pointer to a transport connection structure. + +Return Value: + + None. + +--*/ + +{ + + ExFreePool (TransportConnection); + --DeviceContext->ConnectionAllocated; + DeviceContext->MemoryUsage -= sizeof(TP_CONNECTION); + + NbfRemoveSendPacket (DeviceContext); + NbfRemoveSendPacket (DeviceContext); + NbfRemoveUIFrame (DeviceContext); + +} /* NbfDeallocateConnection */ + + +NTSTATUS +NbfCreateConnection( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_CONNECTION *TransportConnection + ) + +/*++ + +Routine Description: + + This routine creates a transport connection. The reference count in the + connection is automatically set to 1, and the reference count in the + DeviceContext is incremented. + +Arguments: + + Address - the address for this connection to be associated with. + + TransportConnection - Pointer to a place where this routine will + return a pointer to a transport connection structure. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_CONNECTION Connection; + KIRQL oldirql; + PLIST_ENTRY p; + + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint0 ("NbfCreateConnection: Entered.\n"); + } + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + p = RemoveHeadList (&DeviceContext->ConnectionPool); + if (p == &DeviceContext->ConnectionPool) { + + if ((DeviceContext->ConnectionMaxAllocated == 0) || + (DeviceContext->ConnectionAllocated < DeviceContext->ConnectionMaxAllocated)) { + + NbfAllocateConnection (DeviceContext, &Connection); + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint1 ("NBF: Allocated connection at %lx\n", Connection); + } + + } else { + + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_SPECIFIC, + 403, + sizeof(TP_CONNECTION), + CONNECTION_RESOURCE_ID); + Connection = NULL; + + } + + if (Connection == NULL) { + ++DeviceContext->ConnectionExhausted; + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + PANIC ("NbfCreateConnection: Could not allocate connection object!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + } else { + + Connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList); +#if DBG + InitializeListHead (p); +#endif + + } + + ++DeviceContext->ConnectionInUse; + if (DeviceContext->ConnectionInUse > DeviceContext->ConnectionMaxInUse) { + ++DeviceContext->ConnectionMaxInUse; + } + + DeviceContext->ConnectionTotal += DeviceContext->ConnectionInUse; + ++DeviceContext->ConnectionSamples; + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint1 ("NbfCreateConnection: Connection at %lx.\n", Connection); + } + + // + // We have two references; one is for creation, and the + // other is a temporary one so that the connection won't + // go away before the creator has a chance to access it. + // + + Connection->SpecialRefCount = 1; + Connection->ReferenceCount = -1; // this is -1 based + +#if DBG + { + UINT Counter; + for (Counter = 0; Counter < NUMBER_OF_CREFS; Counter++) { + Connection->RefTypes[Counter] = 0; + } + + // This reference is removed by NbfCloseConnection + + Connection->RefTypes[CREF_SPECIAL_CREATION] = 1; + } +#endif + + // + // Initialize the request queues & components of this connection. + // + + InitializeListHead (&Connection->SendQueue); + InitializeListHead (&Connection->ReceiveQueue); + InitializeListHead (&Connection->InProgressRequest); + InitializeListHead (&Connection->AddressList); + InitializeListHead (&Connection->AddressFileList); + Connection->SpecialReceiveIrp = (PIRP)NULL; + Connection->Flags = 0; + Connection->Flags2 = 0; + Connection->DeferredFlags = 0; + Connection->Lsn = 0; + Connection->Rsn = 0; + Connection->Retries = 0; // no retries yet. + Connection->MessageBytesReceived = 0; // no data yet + Connection->MessageBytesAcked = 0; + Connection->MessageInitAccepted = 0; + Connection->ReceiveBytesUnaccepted = 0; + Connection->CurrentReceiveAckQueueable = FALSE; + Connection->CurrentReceiveSynchronous = FALSE; + Connection->ConsecutiveSends = 0; + Connection->ConsecutiveReceives = 0; + Connection->Link = NULL; // no datalink connection yet. + Connection->LinkSpinLock = NULL; + Connection->Context = NULL; // no context yet. + Connection->Status = STATUS_PENDING; // default NbfStopConnection status. + Connection->SendState = CONNECTION_SENDSTATE_IDLE; + Connection->CurrentReceiveIrp = (PIRP)NULL; + Connection->DisconnectIrp = (PIRP)NULL; + Connection->CloseIrp = (PIRP)NULL; + Connection->AddressFile = NULL; + Connection->IndicationInProgress = FALSE; + Connection->OnDataAckQueue = FALSE; + Connection->OnPacketWaitQueue = FALSE; + Connection->TransferBytesPending = 0; + Connection->TotalTransferBytesPending = 0; + + RtlZeroMemory (&Connection->NetbiosHeader, sizeof(NBF_HDR_CONNECTION)); + +#if DBG + Connection->Destroyed = FALSE; + Connection->TotalReferences = 0; + Connection->TotalDereferences = 0; + Connection->NextRefLoc = 0; + ExInterlockedInsertHeadList (&NbfGlobalConnectionList, &Connection->GlobalLinkage, &NbfGlobalInterlock); + StoreConnectionHistory (Connection, TRUE); +#endif + + // + // Now assign this connection an ID. This is used later to identify the + // connection across multiple processes. + // + // The high bit of the ID is not user, it is off for connection + // initiating NAME.QUERY frames and on for ones that are the result + // of a FIND.NAME request. + // + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + Connection->ConnectionId = DeviceContext->UniqueIdentifier; + ++DeviceContext->UniqueIdentifier; + if (DeviceContext->UniqueIdentifier == 0x8000) { + DeviceContext->UniqueIdentifier = 1; + } + + NbfReferenceDeviceContext ("Create Connection", DeviceContext, DCREF_CONNECTION); + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + *TransportConnection = Connection; // return the connection. + + return STATUS_SUCCESS; +} /* NbfCreateConnection */ + + +NTSTATUS +NbfVerifyConnectionObject ( + IN PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called to verify that the pointer given us in a file + object is in fact a valid connection object. + +Arguments: + + Connection - potential pointer to a TP_CONNECTION object. + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INVALID_CONNECTION otherwise + +--*/ + +{ + KIRQL oldirql; + NTSTATUS status = STATUS_SUCCESS; + + // + // try to verify the connection signature. If the signature is valid, + // get the connection spinlock, check its state, and increment the + // reference count if it's ok to use it. Note that being in the stopping + // state is an OK place to be and reference the connection; we can + // disassociate the address while running down. + // + + try { + + if ((Connection != (PTP_CONNECTION)NULL) && + (Connection->Size == sizeof (TP_CONNECTION)) && + (Connection->Type == NBF_CONNECTION_SIGNATURE)) { + + ACQUIRE_C_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + if ((Connection->Flags2 & CONNECTION_FLAGS2_CLOSING) == 0) { + + NbfReferenceConnection ("Verify Temp Use", Connection, CREF_BY_ID); + + } else { + + status = STATUS_INVALID_CONNECTION; + } + + RELEASE_C_SPIN_LOCK (&Connection->SpinLock, oldirql); + + } else { + + status = STATUS_INVALID_CONNECTION; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + return GetExceptionCode(); + } + + return status; + +} + + +NTSTATUS +NbfDestroyAssociation( + IN PTP_CONNECTION TransportConnection + ) + +/*++ + +Routine Description: + + This routine destroys the association between a transport connection and + the address it was formerly associated with. The only action taken is + to disassociate the address and remove the connection from all address + queues. + + This routine is only called by NbfDereferenceConnection. The reason for + this is that there may be multiple streams of execution which are + simultaneously referencing the same connection object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + TransportConnection - Pointer to a transport connection structure to + be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql, oldirql2; + PTP_ADDRESS_FILE addressFile; + BOOLEAN NotAssociated = FALSE; + + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint1 ("NbfDestroyAssociation: Entered for connection %lx.\n", + TransportConnection); + } + + try { + + ACQUIRE_C_SPIN_LOCK (&TransportConnection->SpinLock, &oldirql2); + if ((TransportConnection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) == 0) { +#if DBG + if (!(IsListEmpty(&TransportConnection->AddressList)) || + !(IsListEmpty(&TransportConnection->AddressFileList))) { + DbgPrint ("NBF: C %lx, AF %lx, freed while still queued\n", + TransportConnection, TransportConnection->AddressFile); + DbgBreakPoint(); + } +#endif + RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql2); + NotAssociated = TRUE; + } else { + TransportConnection->Flags2 &= ~CONNECTION_FLAGS2_ASSOCIATED; + RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql2); + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + DbgPrint ("NBF: Got exception 1 in NbfDestroyAssociation\n"); + DbgBreakPoint(); + + RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql2); + } + + if (NotAssociated) { + return STATUS_SUCCESS; + } + + addressFile = TransportConnection->AddressFile; + + // + // Delink this connection from its associated address connection + // database. To do this we must spin lock on the address object as + // well as on the connection, + // + + ACQUIRE_SPIN_LOCK (&addressFile->Address->SpinLock, &oldirql); + + try { + + ACQUIRE_C_SPIN_LOCK (&TransportConnection->SpinLock, &oldirql2); + RemoveEntryList (&TransportConnection->AddressFileList); + RemoveEntryList (&TransportConnection->AddressList); + + InitializeListHead (&TransportConnection->AddressList); + InitializeListHead (&TransportConnection->AddressFileList); + + // + // remove the association between the address and the connection. + // + + TransportConnection->AddressFile = NULL; + + RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql2); + + } except(EXCEPTION_EXECUTE_HANDLER) { + + DbgPrint ("NBF: Got exception 2 in NbfDestroyAssociation\n"); + DbgBreakPoint(); + + RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql2); + } + + RELEASE_SPIN_LOCK (&addressFile->Address->SpinLock, oldirql); + + // + // and remove a reference to the address + // + + NbfDereferenceAddress ("Destroy association", addressFile->Address, AREF_CONNECTION); + + + return STATUS_SUCCESS; + +} /* NbfDestroyAssociation */ + + +NTSTATUS +NbfIndicateDisconnect( + IN PTP_CONNECTION TransportConnection + ) + +/*++ + +Routine Description: + + This routine indicates a remote disconnection on this connection if it + is necessary to do so. No other action is taken here. + + This routine is only called by NbfDereferenceConnection. The reason for + this is that there may be multiple streams of execution which are + simultaneously referencing the same connection object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + TransportConnection - Pointer to a transport connection structure to + be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_ADDRESS_FILE addressFile; + PDEVICE_CONTEXT DeviceContext; + ULONG DisconnectReason; + PIRP DisconnectIrp; + KIRQL oldirql; + ULONG Lflags2; + + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint1 ("NbfIndicateDisconnect: Entered for connection %lx.\n", + TransportConnection); + } + + try { + + ACQUIRE_C_SPIN_LOCK (&TransportConnection->SpinLock, &oldirql); + + if (((TransportConnection->Flags2 & CONNECTION_FLAGS2_REQ_COMPLETED) != 0)) { + + ASSERT (TransportConnection->Lsn == 0); + + // + // Turn off all but the still-relevant bits in the flags. + // + + Lflags2 = TransportConnection->Flags2; + TransportConnection->Flags2 &= + (CONNECTION_FLAGS2_ASSOCIATED | + CONNECTION_FLAGS2_DISASSOCIATED | + CONNECTION_FLAGS2_CLOSING); + TransportConnection->Flags2 |= CONNECTION_FLAGS2_STOPPING; + + // + // Clean up other stuff -- basically everything gets + // done here except for the flags and the status, since + // they are used to block other requests. When the connection + // is given back to us (in Accept, Connect, or Listen) + // they are cleared. + // + + TransportConnection->NetbiosHeader.TransmitCorrelator = 0; + TransportConnection->Retries = 0; // no retries yet. + TransportConnection->MessageBytesReceived = 0; // no data yet + TransportConnection->MessageBytesAcked = 0; + TransportConnection->MessageInitAccepted = 0; + TransportConnection->ReceiveBytesUnaccepted = 0; + TransportConnection->ConsecutiveSends = 0; + TransportConnection->ConsecutiveReceives = 0; + TransportConnection->SendState = CONNECTION_SENDSTATE_IDLE; + + TransportConnection->TransmittedTsdus = 0; + TransportConnection->ReceivedTsdus = 0; + + TransportConnection->CurrentReceiveIrp = (PIRP)NULL; + + DisconnectIrp = TransportConnection->DisconnectIrp; + TransportConnection->DisconnectIrp = (PIRP)NULL; + + if ((TransportConnection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) != 0) { + addressFile = TransportConnection->AddressFile; + } else { + addressFile = NULL; + } + + RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql); + + + DeviceContext = TransportConnection->Provider; + + + // + // If this connection was stopped by a call to TdiDisconnect, + // we have to complete that. We save the Irp so we can return + // the connection to the pool before we complete the request. + // + + + if (DisconnectIrp != (PIRP)NULL || + (Lflags2 & CONNECTION_FLAGS2_LDISC) != 0) { + + if (DisconnectIrp != (PIRP)NULL) { + IF_NBFDBG (NBF_DEBUG_SETUP) { + NbfPrint1("IndicateDisconnect %lx, complete IRP\n", TransportConnection); + } + + // + // Now complete the IRP if needed. This will be non-null + // only if TdiDisconnect was called, and we have not + // yet completed it. + // + + DisconnectIrp->IoStatus.Information = 0; + DisconnectIrp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest (DisconnectIrp, IO_NETWORK_INCREMENT); + } + + } else if ((TransportConnection->Status != STATUS_LOCAL_DISCONNECT) && + (addressFile != NULL) && + (addressFile->RegisteredDisconnectHandler == TRUE)) { + + // + // This was a remotely spawned disconnect, so indicate that + // to our client. Note that in the comparison above we + // check the status first, since if it is LOCAL_DISCONNECT + // addressFile may be NULL (BUGBUG: This is sort of a hack + // for PDK2, we should really indicate the disconnect inside + // NbfStopConnection, where we know addressFile is valid). + // + + IF_NBFDBG (NBF_DEBUG_SETUP) { + NbfPrint1("IndicateDisconnect %lx, indicate\n", TransportConnection); + } + + // + // if the disconnection was remotely spawned, then indicate + // disconnect. In the case that a disconnect was issued at + // the same time as the connection went down remotely, we + // won't do this because DisconnectIrp will be non-NULL. + // + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint1 ("NbfIndicateDisconnect calling DisconnectHandler, refcnt=%ld\n", + TransportConnection->ReferenceCount); + } + + // + // Invoke the user's disconnection event handler, if any. We do this here + // so that any outstanding sends will complete before we tear down the + // connection. + // + + DisconnectReason = 0; + if (TransportConnection->Flags2 & CONNECTION_FLAGS2_ABORT) { + DisconnectReason |= TDI_DISCONNECT_ABORT; + } + if (TransportConnection->Flags2 & CONNECTION_FLAGS2_DESTROY) { + DisconnectReason |= TDI_DISCONNECT_RELEASE; + } + + (*addressFile->DisconnectHandler)( + addressFile->DisconnectHandlerContext, + TransportConnection->Context, + 0, + NULL, + 0, + NULL, + TDI_DISCONNECT_ABORT); + +#if MAGIC + if (NbfEnableMagic) { + extern VOID NbfSendMagicBullet (PDEVICE_CONTEXT, PTP_LINK); + NbfSendMagicBullet (DeviceContext, NULL); + } +#endif + } + + } else { + + // + // The client does not yet think that this connection + // is up...generally this happens due to request count + // fluctuation during connection setup. + // + + RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql); + + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + DbgPrint ("NBF: Got exception in NbfIndicateDisconnect\n"); + DbgBreakPoint(); + + RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql); + } + + + return STATUS_SUCCESS; + +} /* NbfIndicateDisconnect */ + + +NTSTATUS +NbfDestroyConnection( + IN PTP_CONNECTION TransportConnection + ) + +/*++ + +Routine Description: + + This routine destroys a transport connection and removes all references + made by it to other objects in the transport. The connection structure + is returned to our lookaside list. It is assumed that the caller + has removed all IRPs from the connections's queues first. + + This routine is only called by NbfDereferenceConnection. The reason for + this is that there may be multiple streams of execution which are + simultaneously referencing the same connection object, and it should + not be deleted out from under an interested stream of execution. + +Arguments: + + TransportConnection - Pointer to a transport connection structure to + be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PDEVICE_CONTEXT DeviceContext; + PIRP CloseIrp; + + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint1 ("NbfDestroyConnection: Entered for connection %lx.\n", + TransportConnection); + } + +#if DBG + if (TransportConnection->Destroyed) { + NbfPrint1 ("attempt to destroy already-destroyed connection 0x%lx\n", TransportConnection); + DbgBreakPoint (); + } + if (!(TransportConnection->Flags2 & CONNECTION_FLAGS2_STOPPING)) { + NbfPrint1 ("attempt to destroy unstopped connection 0x%lx\n", TransportConnection); + DbgBreakPoint (); + } + TransportConnection->Destroyed = TRUE; + ACQUIRE_SPIN_LOCK (&NbfGlobalInterlock, &oldirql); + RemoveEntryList (&TransportConnection->GlobalLinkage); + RELEASE_SPIN_LOCK (&NbfGlobalInterlock, oldirql); +#endif + + DeviceContext = TransportConnection->Provider; + + // + // Destroy any association that this connection has. + // + + NbfDestroyAssociation (TransportConnection); + + // + // Clear out any associated nasties hanging around the connection. Note + // that the current flags are set to STOPPING; this way anyone that may + // maliciously try to use the connection after it's dead and gone will + // just get ignored. + // + + ASSERT (TransportConnection->Lsn == 0); + + TransportConnection->Flags = 0; + TransportConnection->Flags2 = CONNECTION_FLAGS2_CLOSING; + TransportConnection->NetbiosHeader.TransmitCorrelator = 0; + TransportConnection->Retries = 0; // no retries yet. + TransportConnection->MessageBytesReceived = 0; // no data yet + TransportConnection->MessageBytesAcked = 0; + TransportConnection->MessageInitAccepted = 0; + TransportConnection->ReceiveBytesUnaccepted = 0; + + + // + // Now complete the close IRP. This will be set to non-null + // when CloseConnection was called. + // + + CloseIrp = TransportConnection->CloseIrp; + + if (CloseIrp != (PIRP)NULL) { + + TransportConnection->CloseIrp = (PIRP)NULL; + CloseIrp->IoStatus.Information = 0; + CloseIrp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest (CloseIrp, IO_NETWORK_INCREMENT); + + } else { + +#if DBG + NbfPrint1("Connection %x destroyed, no CloseIrp!!\n", TransportConnection); +#endif + + } + + // + // Return the connection to the provider's pool. + // + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + DeviceContext->ConnectionTotal += DeviceContext->ConnectionInUse; + ++DeviceContext->ConnectionSamples; + --DeviceContext->ConnectionInUse; + + if ((DeviceContext->ConnectionAllocated - DeviceContext->ConnectionInUse) > + DeviceContext->ConnectionInitAllocated) { + NbfDeallocateConnection (DeviceContext, TransportConnection); + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint1 ("NBF: Deallocated connection at %lx\n", TransportConnection); + } + } else { + InsertTailList (&DeviceContext->ConnectionPool, &TransportConnection->LinkList); + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + NbfDereferenceDeviceContext ("Destroy Connection", DeviceContext, DCREF_CONNECTION); + + return STATUS_SUCCESS; + +} /* NbfDestroyConnection */ + + +#if DBG +VOID +NbfRefConnection( + IN PTP_CONNECTION TransportConnection + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport connection. + +Arguments: + + TransportConnection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint2 ("NbfReferenceConnection: entered for connection %lx, " + "current level=%ld.\n", + TransportConnection, + TransportConnection->ReferenceCount); + } + +#if DBG + StoreConnectionHistory( TransportConnection, TRUE ); +#endif + + result = InterlockedIncrement (&TransportConnection->ReferenceCount); + + if (result == 0) { + + // + // The first increment causes us to increment the + // "ref count is not zero" special ref. + // + + ExInterlockedAddUlong( + (PULONG)(&TransportConnection->SpecialRefCount), + 1, + TransportConnection->ProviderInterlock); + +#if DBG + ++TransportConnection->RefTypes[CREF_SPECIAL_TEMP]; +#endif + + } + + ASSERT (result >= 0); + +} /* NbfRefConnection */ +#endif + + +VOID +NbfDerefConnection( + IN PTP_CONNECTION TransportConnection + ) + +/*++ + +Routine Description: + + This routine dereferences a transport connection by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + NbfDestroyConnection to remove it from the system. + +Arguments: + + TransportConnection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint2 ("NbfDereferenceConnection: entered for connection %lx, " + "current level=%ld.\n", + TransportConnection, + TransportConnection->ReferenceCount); + } + +#if DBG + StoreConnectionHistory( TransportConnection, FALSE ); +#endif + + result = InterlockedDecrement (&TransportConnection->ReferenceCount); + + // + // If all the normal references to this connection are gone, then + // we can remove the special reference that stood for + // "the regular ref count is non-zero". + // + + if (result < 0) { + + // + // If the refcount is -1, then we need to disconnect from + // the link and indicate disconnect. However, we need to + // do this before we actually do the special deref, since + // otherwise the connection might go away while we + // are doing that. + // + // Note that both these routines are protected in that if they + // are called twice, the second call will have no effect. + // + + + // + // If both the connection and its link are active, then they have + // mutual references to each other. We remove the link's + // reference to the connection in NbfStopConnection, now + // the reference count has fallen enough that we know it + // is okay to remove the connection's reference to the + // link. + // + + if (NbfDisconnectFromLink (TransportConnection, TRUE)) { + + // + // if the reference count goes to one, we can safely indicate the + // user about disconnect states. That reference should + // be for the connection's creation. + // + + NbfIndicateDisconnect (TransportConnection); + + } + + // + // Now it is OK to let the connection go away. + // + + NbfDereferenceConnectionSpecial ("Regular ref gone", TransportConnection, CREF_SPECIAL_TEMP); + + } + +} /* NbfDerefConnection */ + + +VOID +NbfDerefConnectionSpecial( + IN PTP_CONNECTION TransportConnection + ) + +/*++ + +Routine Description: + + This routines completes the dereferencing of a connection. + It may be called any time, but it does not do its work until + the regular reference count is also 0. + +Arguments: + + TransportConnection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql; + + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint3 ("NbfDereferenceConnectionSpecial: entered for connection %lx, " + "current level=%ld (%ld).\n", + TransportConnection, + TransportConnection->ReferenceCount, + TransportConnection->SpecialRefCount); + } + +#if DBG + StoreConnectionHistory( TransportConnection, FALSE ); +#endif + + + ACQUIRE_SPIN_LOCK (TransportConnection->ProviderInterlock, &oldirql); + + --TransportConnection->SpecialRefCount; + + if ((TransportConnection->SpecialRefCount == 0) && + (TransportConnection->ReferenceCount == -1)) { + + // + // If we have deleted all references to this connection, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the connection any longer. + // + +#if DBG + { + BOOLEAN TimerCancelled; + TimerCancelled = KeCancelTimer (&TransportConnection->Timer); + ASSERT (TimerCancelled); + } +#endif + + RELEASE_SPIN_LOCK (TransportConnection->ProviderInterlock, oldirql); + + NbfDestroyConnection (TransportConnection); + + } else { + + RELEASE_SPIN_LOCK (TransportConnection->ProviderInterlock, oldirql); + + } + +} /* NbfDerefConnectionSpecial */ + + +VOID +NbfClearConnectionLsn( + IN PTP_CONNECTION TransportConnection + ) + +/*++ + +Routine Description: + + This routine clears the LSN field in a connection. To do this is + acquires the device context lock, and modifies the table value + for that LSN depending on the type of the connection. + + NOTE: This routine is called with the connection spinlock held, + or in a state where nobody else will be accessing the + connection. + +Arguments: + + TransportConnection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + KIRQL oldirql; + + DeviceContext = TransportConnection->Provider; + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + if (TransportConnection->Lsn != 0) { + + if (TransportConnection->Flags2 & CONNECTION_FLAGS2_GROUP_LSN) { + + // + // It was to a group address; the count should be + // LSN_TABLE_MAX. + // + + ASSERT(DeviceContext->LsnTable[TransportConnection->Lsn] == LSN_TABLE_MAX); + + DeviceContext->LsnTable[TransportConnection->Lsn] = 0; + + TransportConnection->Flags2 &= ~CONNECTION_FLAGS2_GROUP_LSN; + + } else { + + ASSERT(DeviceContext->LsnTable[TransportConnection->Lsn] > 0); + + --(DeviceContext->LsnTable[TransportConnection->Lsn]); + + } + + TransportConnection->Lsn = 0; + + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + +} + + +PTP_CONNECTION +NbfLookupConnectionById( + IN PTP_ADDRESS Address, + IN USHORT ConnectionId + ) + +/*++ + +Routine Description: + + This routine accepts a connection identifier and an address and + returns a pointer to the connection object, TP_CONNECTION. If the + connection identifier is not found on the address, then NULL is returned. + This routine automatically increments the reference count of the + TP_CONNECTION structure if it is found. It is assumed that the + TP_ADDRESS structure is already held with a reference count. + +Arguments: + + Address - Pointer to a transport address object. + + ConnectionId - Identifier of the connection for this address. + +Return Value: + + A pointer to the connection we found + +--*/ + +{ + KIRQL oldirql, oldirql1; + PLIST_ENTRY p; + PTP_CONNECTION Connection; + BOOLEAN Found = FALSE; + + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint2 ("NbfLookupConnectionById: entered, Address: %lx ID: %lx\n", + Address, ConnectionId); + } + + // + // Currently, this implementation is inefficient, but brute force so + // that a system can get up and running. Later, a cache of the mappings + // of popular connection id's and pointers to their TP_CONNECTION structures + // will be searched first. + // + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + for (p=Address->ConnectionDatabase.Flink; + p != &Address->ConnectionDatabase; + p=p->Flink) { + + + Connection = CONTAINING_RECORD (p, TP_CONNECTION, AddressList); + + try { + + ACQUIRE_C_SPIN_LOCK (&Connection->SpinLock, &oldirql1); + + if (((Connection->Flags2 & CONNECTION_FLAGS2_STOPPING) == 0) && + (Connection->ConnectionId == ConnectionId)) { + + // This reference is removed by the calling function + NbfReferenceConnection ("Lookup up for request", Connection, CREF_BY_ID); + Found = TRUE; + } + + RELEASE_C_SPIN_LOCK (&Connection->SpinLock, oldirql1); + + } except(EXCEPTION_EXECUTE_HANDLER) { + + DbgPrint ("NBF: Got exception in NbfLookupConnectionById\n"); + DbgBreakPoint(); + + RELEASE_C_SPIN_LOCK (&Connection->SpinLock, oldirql1); + } + + if (Found) { + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + return Connection; + } + + + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + return NULL; + +} /* NbfLookupConnectionById */ + + +PTP_CONNECTION +NbfLookupConnectionByContext( + IN PTP_ADDRESS Address, + IN CONNECTION_CONTEXT ConnectionContext + ) + +/*++ + +Routine Description: + + This routine accepts a connection identifier and an address and + returns a pointer to the connection object, TP_CONNECTION. If the + connection identifier is not found on the address, then NULL is returned. + This routine automatically increments the reference count of the + TP_CONNECTION structure if it is found. It is assumed that the + TP_ADDRESS structure is already held with a reference count. + + BUGBUG: Should the ConnectionDatabase go in the address file? + +Arguments: + + Address - Pointer to a transport address object. + + ConnectionContext - Connection Context for this address. + +Return Value: + + A pointer to the connection we found + +--*/ + +{ + KIRQL oldirql, oldirql1; + PLIST_ENTRY p; + BOOLEAN Found = FALSE; + PTP_CONNECTION Connection; + + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint2 ("NbfLookupConnectionByContext: entered, Address: %lx Context: %lx\n", + Address, ConnectionContext); + } + + // + // Currently, this implementation is inefficient, but brute force so + // that a system can get up and running. Later, a cache of the mappings + // of popular connection id's and pointers to their TP_CONNECTION structures + // will be searched first. + // + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + for (p=Address->ConnectionDatabase.Flink; + p != &Address->ConnectionDatabase; + p=p->Flink) { + + Connection = CONTAINING_RECORD (p, TP_CONNECTION, AddressList); + + try { + + ACQUIRE_C_SPIN_LOCK (&Connection->SpinLock, &oldirql1); + + if (Connection->Context == ConnectionContext) { + // This reference is removed by the calling function + NbfReferenceConnection ("Lookup up for request", Connection, CREF_LISTENING); + Found = TRUE; + } + + RELEASE_C_SPIN_LOCK (&Connection->SpinLock, oldirql1); + + } except(EXCEPTION_EXECUTE_HANDLER) { + + DbgPrint ("NBF: Got exception in NbfLookupConnectionById\n"); + DbgBreakPoint(); + + RELEASE_C_SPIN_LOCK (&Connection->SpinLock, oldirql1); + } + + if (Found) { + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + return Connection; + } + + + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + return NULL; + +} /* NbfLookupConnectionByContext */ + + +PTP_CONNECTION +NbfLookupListeningConnection( + IN PTP_ADDRESS Address, + IN PUCHAR RemoteName + ) + +/*++ + +Routine Description: + + This routine scans the connection database on an address to find + a TP_CONNECTION object which has LSN=0 and CONNECTION_FLAGS_WAIT_NQ + flag set. It returns a pointer to the found connection object (and + simultaneously resets the flag) or NULL if it could not be found. + The reference count is also incremented atomically on the connection. + + The list is scanned for listens posted to this specific remote + name, or to those with no remote name specified. + +Arguments: + + Address - Pointer to a transport address object. + + RemoteName - The name of the remote. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql, oldirql1; + PTP_CONNECTION Connection; + PLIST_ENTRY p, q; + PTP_REQUEST ListenRequest; + + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint0 ("NbfLookupListeningConnection: Entered.\n"); + } + + // + // Currently, this implementation is inefficient, but brute force so + // that a system can get up and running. Later, a cache of the mappings + // of popular connection id's and pointers to their TP_CONNECTION structures + // will be searched first. + // + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + for (p=Address->ConnectionDatabase.Flink; + p != &Address->ConnectionDatabase; + p=p->Flink) { + + Connection = CONTAINING_RECORD (p, TP_CONNECTION, AddressList); + if ((Connection->Lsn == 0) && + (Connection->Flags2 & CONNECTION_FLAGS2_WAIT_NQ)) { + + q = Connection->InProgressRequest.Flink; + if (q != &Connection->InProgressRequest) { + ListenRequest = CONTAINING_RECORD (q, TP_REQUEST, Linkage); + if ((ListenRequest->Buffer2 != NULL) && + (!RtlEqualMemory( + ListenRequest->Buffer2, + RemoteName, + NETBIOS_NAME_LENGTH))) { + continue; + } + } else { + continue; + } + // This reference is removed by the calling function + NbfReferenceConnection ("Found Listening", Connection, CREF_LISTENING); + ACQUIRE_C_SPIN_LOCK (&Connection->SpinLock, &oldirql1); + Connection->Flags2 &= ~CONNECTION_FLAGS2_WAIT_NQ; + RELEASE_C_SPIN_LOCK (&Connection->SpinLock, oldirql1); + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint1 ("NbfLookupListeningConnection: Found Connection %lx\n",Connection); + } + return Connection; + } + + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint0 ("NbfLookupListeningConnection: Found No Connection!\n"); + } + + return NULL; + +} /* NbfLookupListeningConnection */ + + +VOID +NbfStopConnection( + IN PTP_CONNECTION Connection, + IN NTSTATUS Status + ) + +/*++ + +Routine Description: + + This routine is called to terminate all activity on a connection and + destroy the object. This is done in a graceful manner; i.e., all + outstanding requests are terminated by cancelling them, etc. It is + assumed that the caller has a reference to this connection object, + but this routine will do the dereference for the one issued at creation + time. + + Orderly release is a function of this routine, but it is not a provided + service of this transport provider, so there is no code to do it here. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + + Status - The status that caused us to stop the connection. This + will determine what status pending requests are aborted with, + and also how we proceed during the stop (whether to send a + session end, and whether to indicate disconnect). + +Return Value: + + none. + +--*/ + +{ + KIRQL cancelirql; + PLIST_ENTRY p; + PIRP Irp; + PTP_REQUEST Request; + BOOLEAN TimerWasCleared; + ULONG DisconnectReason; + PULONG StopCounter; + PDEVICE_CONTEXT DeviceContext; + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint3 ("NbfStopConnection: Entered for connection %lx LSN %x RSN %x.\n", + Connection, Connection->Lsn, Connection->Rsn); + } + + ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); + + DeviceContext = Connection->Provider; + + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + if (!(Connection->Flags2 & CONNECTION_FLAGS2_STOPPING)) { + + // + // We are stopping the connection, record statistics + // about it. + // + + if (Connection->Flags & CONNECTION_FLAGS_READY) { + DECREMENT_COUNTER (DeviceContext, OpenConnections); + } + + Connection->Flags2 |= CONNECTION_FLAGS2_STOPPING; + Connection->Flags2 &= ~CONNECTION_FLAGS2_REMOTE_VALID; + Connection->Status = Status; + + if (Connection->Link != NULL) { + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + Connection->Flags &= ~(CONNECTION_FLAGS_READY| + CONNECTION_FLAGS_WAIT_SI| + CONNECTION_FLAGS_WAIT_SC); // no longer open for business + Connection->SendState = CONNECTION_SENDSTATE_IDLE; + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + } + + // + // If this flag was on, turn it off. + // + Connection->Flags &= ~CONNECTION_FLAGS_W_RESYNCH; + + // + // Stop the timer if it was running. + // + + TimerWasCleared = KeCancelTimer (&Connection->Timer); + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint2 ("NbfStopConnection: Timer for connection %lx " + "%s canceled.\n", Connection, + TimerWasCleared ? "was" : "was NOT" ); + } + + + switch (Status) { + + case STATUS_LOCAL_DISCONNECT: + StopCounter = &DeviceContext->Statistics.LocalDisconnects; + break; + case STATUS_REMOTE_DISCONNECT: + StopCounter = &DeviceContext->Statistics.RemoteDisconnects; + break; + case STATUS_LINK_FAILED: + StopCounter = &DeviceContext->Statistics.LinkFailures; + break; + case STATUS_IO_TIMEOUT: + StopCounter = &DeviceContext->Statistics.SessionTimeouts; + break; + case STATUS_CANCELLED: + StopCounter = &DeviceContext->Statistics.CancelledConnections; + break; + case STATUS_REMOTE_RESOURCES: + StopCounter = &DeviceContext->Statistics.RemoteResourceFailures; + break; + case STATUS_INSUFFICIENT_RESOURCES: + StopCounter = &DeviceContext->Statistics.LocalResourceFailures; + break; + case STATUS_BAD_NETWORK_PATH: + StopCounter = &DeviceContext->Statistics.NotFoundFailures; + break; + case STATUS_REMOTE_NOT_LISTENING: + StopCounter = &DeviceContext->Statistics.NoListenFailures; + break; + + default: + StopCounter = NULL; + break; + + } + + if (StopCounter != NULL) { + (*StopCounter)++; + } + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + // + // Run down all TdiConnect/TdiDisconnect/TdiListen requests. + // + + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + while (TRUE) { + p = RemoveHeadList (&Connection->InProgressRequest); + if (p == &Connection->InProgressRequest) { + break; + } + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + IoSetCancelRoutine(Request->IoRequestPacket, NULL); + IoReleaseCancelSpinLock(cancelirql); +#if DBG + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + LARGE_INTEGER MilliSeconds, Time; + ULONG junk; + KeQuerySystemTime (&Time); + MilliSeconds.LowPart = Time.LowPart; + MilliSeconds.HighPart = Time.HighPart; + MilliSeconds.QuadPart = MilliSeconds.QuadPart - + (Request->Time).QuadPart; + MilliSeconds = RtlExtendedLargeIntegerDivide (MilliSeconds, 10000L, &junk); + NbfPrint3 ("NbfStopConnection: Canceling pending CONNECT, Irp: %lx Time Pending: %ld%ld msec\n", + Request->IoRequestPacket, MilliSeconds.HighPart, MilliSeconds.LowPart); + } +#endif + + NbfCompleteRequest (Request, Connection->Status, 0); + + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + } + + + if (Connection->Link == NULL) { + + // + // We are stopping early on. + // + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + IoReleaseCancelSpinLock (cancelirql); + + if (TimerWasCleared) { + NbfDereferenceConnection ("Stopping timer", Connection, CREF_TIMER); // account for timer reference. + } + + + ASSERT (Connection->SendState == CONNECTION_SENDSTATE_IDLE); + ASSERT (!Connection->OnPacketWaitQueue); + ASSERT (!Connection->OnDataAckQueue); + ASSERT (!(Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_ACK)); + ASSERT (IsListEmpty(&Connection->SendQueue)); + ASSERT (IsListEmpty(&Connection->ReceiveQueue)); + + return; + + } + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + IoReleaseCancelSpinLock (cancelirql); + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + // + // If this connection is waiting to packetize, + // remove it from the device context queue it is on. + // + // NOTE: If the connection is currently in the + // packetize queue, it will eventually go to get + // packetized and at that point it will get + // removed. + // + + if (Connection->OnPacketWaitQueue) { + + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint1("Stop waiting connection, flags %lx\n", + Connection->Flags); + } + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + Connection->OnPacketWaitQueue = FALSE; + ASSERT ((Connection->Flags & CONNECTION_FLAGS_SEND_SE) == 0); + Connection->Flags &= ~(CONNECTION_FLAGS_STARVED|CONNECTION_FLAGS_W_PACKETIZE); + RemoveEntryList (&Connection->PacketWaitLinkage); + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + } + + + // + // If we are on the data ack queue, then take ourselves off. + // + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + if (Connection->OnDataAckQueue) { + RemoveEntryList (&Connection->DataAckLinkage); + Connection->OnDataAckQueue = FALSE; + DeviceContext->DataAckQueueChanged = TRUE; + } + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + // + // If this connection is waiting to send a piggyback ack, + // remove it from the device context queue for that, and + // send a data ack (which will get there before the + // SessionEnd). + // + + if ((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_ACK) != 0) { + +#if DBG + { + extern ULONG NbfDebugPiggybackAcks; + if (NbfDebugPiggybackAcks) { + NbfPrint1("Stop waiting connection, deferred flags %lx\n", + Connection->DeferredFlags); + } + } +#endif + + Connection->DeferredFlags &= + ~(CONNECTION_FLAGS_DEFERRED_ACK | CONNECTION_FLAGS_DEFERRED_NOT_Q); + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + NbfSendDataAck (Connection); + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + } + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + if (TimerWasCleared) { + NbfDereferenceConnection ("Stopping timer", Connection, CREF_TIMER); // account for timer reference. + } + + + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + + // + // Run down all TdiSend requests on this connection. + // + + while (TRUE) { + p = RemoveHeadList (&Connection->SendQueue); + if (p == &Connection->SendQueue) { + break; + } + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + IoSetCancelRoutine(Irp, NULL); + IoReleaseCancelSpinLock(cancelirql); +#if DBG + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint1("NbfStopConnection: Canceling pending SEND, Irp: %lx\n", + Irp); + } +#endif + NbfCompleteSendIrp (Irp, Connection->Status, 0); + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + ++Connection->TransmissionErrors; + } + + // + // NOTE: We hold the connection spinlock AND the + // cancel spinlock here. + // + + Connection->Flags &= ~CONNECTION_FLAGS_ACTIVE_RECEIVE; + + // + // Run down all TdiReceive requests on this connection. + // + + while (TRUE) { + p = RemoveHeadList (&Connection->ReceiveQueue); + if (p == &Connection->ReceiveQueue) { + break; + } + Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + IoSetCancelRoutine(Irp, NULL); +#if DBG + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint1 ("NbfStopConnection: Canceling pending RECEIVE, Irp: %lx\n", + Irp); + } +#endif + + // + // It is OK to call this with the locks held. + // + NbfCompleteReceiveIrp (Irp, Connection->Status, 0); + + ++Connection->ReceiveErrors; + } + + + // + // NOTE: We hold the connection spinlock AND the + // cancel spinlock here. + // + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + IoReleaseCancelSpinLock(cancelirql); + + // + // If we aren't DESTROYing the link, then send a SESSION_END frame + // to the remote side. When the SESSION_END frame is acknowleged, + // we will decrement the connection's reference count by one, removing + // its creation reference. This will cause the connection object to + // be disposed of, and will begin running down the link. + // DGB: add logic to avoid blowing away link if one doesn't exist yet. + // + + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + DisconnectReason = 0; + if (Connection->Flags2 & CONNECTION_FLAGS2_ABORT) { + DisconnectReason |= TDI_DISCONNECT_ABORT; + } + if (Connection->Flags2 & CONNECTION_FLAGS2_DESTROY) { + DisconnectReason |= TDI_DISCONNECT_RELEASE; + } + + if (Connection->Link != NULL) { + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + if ((Status == STATUS_LOCAL_DISCONNECT) || + (Status == STATUS_CANCELLED)) { + + // + // (note that a connection should only get stopped + // with STATUS_INSUFFICIENT_RESOURCES if it is not + // yet connected to the remote). + // + + // + // If this is done, when this packet is destroyed + // it will dereference the connection for CREF_LINK. + // + + NbfSendSessionEnd ( + Connection, + (BOOLEAN)((DisconnectReason & TDI_DISCONNECT_ABORT) != 0)); + + } else { + + // + // Not attached to the link anymore; this dereference + // will allow our reference to fall below 3, which + // will cause NbfDisconnectFromLink to be called. + // + + NbfDereferenceConnection("Stopped", Connection, CREF_LINK); + + } + + } else { + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + } + + + // + // Note that we've blocked all new requests being queued during the + // time we have been in this teardown code; NbfDestroyConnection also + // sets the connection flags to STOPPING when returning the + // connection to the queue. This avoids lingerers using non-existent + // connections. + // + + } else { + + // + // The connection was already stopping; it may have a + // SESSION_END pending in which case we should kill + // it. + // + + if ((Status != STATUS_LOCAL_DISCONNECT) && + (Status != STATUS_CANCELLED)) { + + if (Connection->Flags & CONNECTION_FLAGS_SEND_SE) { + + ASSERT (Connection->Link != NULL); + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + Connection->Flags &= ~CONNECTION_FLAGS_SEND_SE; + + if (Connection->OnPacketWaitQueue) { + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); +#if DBG + DbgPrint ("NBF: Removing connection %lx from PacketWait for SESSION_END\n", Connection); +#endif + Connection->OnPacketWaitQueue = FALSE; + RemoveEntryList (&Connection->PacketWaitLinkage); + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + } + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + NbfDereferenceConnection("Stopped again", Connection, CREF_LINK); + return; + + } + } + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + } +} /* NbfStopConnection */ + + +VOID +NbfCancelConnection( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a connect + or a listen. It is simple since there can only be one of these + active on a connection; we just stop the connection, the IRP + will get completed as part of normal session teardown. + + 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_CONNECTION Connection; + PTP_REQUEST Request; + PLIST_ENTRY p; + BOOLEAN fCanceled = TRUE; + + UNREFERENCED_PARAMETER (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_CONNECT || IrpSp->MinorFunction == TDI_LISTEN)); + + Connection = IrpSp->FileObject->FsContext; + + // + // Since this IRP is still in the cancellable state, we know + // that the connection is still around (although it may be in + // the process of being torn down). + // + + ACQUIRE_C_SPIN_LOCK (&Connection->SpinLock, &oldirql); + NbfReferenceConnection ("Cancelling Send", Connection, CREF_TEMP); + + p = RemoveHeadList (&Connection->InProgressRequest); + ASSERT (p != &Connection->InProgressRequest); + + RELEASE_C_SPIN_LOCK (&Connection->SpinLock, oldirql); + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + ASSERT (Request->IoRequestPacket == Irp); +#ifdef RASAUTODIAL + // + // If there's an automatic connection in + // progress, cancel it. + // + if (Connection->Flags2 & CONNECTION_FLAGS2_AUTOCONNECTING) + fCanceled = NbfCancelTdiConnect(NULL, Irp); +#endif // RASAUTODIAL + + if (fCanceled) + IoSetCancelRoutine(Request->IoRequestPacket, NULL); + + IoReleaseCancelSpinLock(Irp->CancelIrql); + + if (fCanceled) { + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint2("NBF: Cancelled in-progress connect/listen %lx on %lx\n", + Request->IoRequestPacket, Connection); + } + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + NbfCompleteRequest (Request, STATUS_CANCELLED, 0); + NbfStopConnection (Connection, STATUS_LOCAL_DISCONNECT); // prevent indication to clients + KeLowerIrql (oldirql); + } + + NbfDereferenceConnection ("Cancel done", Connection, CREF_TEMP); + +} + +#if 0 +VOID +NbfWaitConnectionOnLink( + IN PTP_CONNECTION Connection, + IN ULONG Flags + ) + +/*++ + +Routine Description: + + This routine is called to suspend a connection's activities because + the specified session-oriented frame could not be sent due to link + problems. Routines in FRAMESND.C call this. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + + Flags - Field containing bitflag set to indicate starved frame to be sent. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint0 ("NbfWaitConnectionOnLink: Entered.\n"); + } + + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + if (((Connection->Flags2 & CONNECTION_FLAGS2_STOPPING) == 0) || + (Flags == CONNECTION_FLAGS_SEND_SE)) { + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + Connection->Flags |= Flags; + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + } + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); +} /* NbfWaitConnectionOnLink */ +#endif + + +VOID +NbfStartConnectionTimer( + IN PTP_CONNECTION Connection, + IN PKDEFERRED_ROUTINE TimeoutFunction, + IN ULONG WaitTime + ) + +/*++ + +Routine Description: + + This routine is called to start a timeout on NAME_QUERY/NAME_RECOGNIZED + activities on a connection. + +Arguments: + + TransportConnection - Pointer to a TP_CONNECTION object. + + TimeoutFunction - The function to call when the timer fires. + + WaitTime - a longword containing the low order time to wait. + +Return Value: + + none. + +--*/ + +{ + LARGE_INTEGER Timeout; + BOOLEAN AlreadyInserted; + + IF_NBFDBG (NBF_DEBUG_CONNOBJ) { + NbfPrint1 ("NbfStartConnectionTimer: Entered for connection %lx.\n", + Connection ); + } + + // + // Start the timer. Unlike the link timers, this is simply a kernel- + // managed object. + // + + Timeout.LowPart = (ULONG)(-(LONG)WaitTime); + Timeout.HighPart = -1; + + // + // Take the lock so we synchronize the cancelling with + // restarting the timer. This is so two threads won't + // both fail to cancel and then start the timer at the + // same time (it messes up the reference count). + // + + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + AlreadyInserted = KeCancelTimer (&Connection->Timer); + + KeInitializeDpc ( + &Connection->Dpc, + TimeoutFunction, + (PVOID)Connection); + + KeSetTimer ( + &Connection->Timer, + Timeout, + &Connection->Dpc); + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + // + // If the timer wasn't already running, reference the connection to + // account for the new timer. If the timer was already started, + // then KeCancelTimer will have returned TRUE. In this + // case, the prior call to NbfStartConnectionTimer referenced the + // connection, so we don't do it again here. + // + + if ( !AlreadyInserted ) { + + // This reference is removed in ConnectionEstablishmentTimeout, + // or when the timer is cancelled. + + NbfReferenceConnection ("starting timer", Connection, CREF_TIMER); + } + +} /* NbfStartConnectionTimer */ + diff --git a/private/ntos/tdi/nbf/devctx.c b/private/ntos/tdi/nbf/devctx.c new file mode 100644 index 000000000..0d6179e60 --- /dev/null +++ b/private/ntos/tdi/nbf/devctx.c @@ -0,0 +1,408 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + devctx.c + +Abstract: + + This module contains code which implements the DEVICE_CONTEXT object. + Routines are provided to reference, and dereference transport device + context objects. Currently, there is no need to create them or destroy + them, as this is handled at configuration time. If it is later required + to dynamically load/unload the transport provider's device object and + associated context, then we can add the create and destroy functions. + + The transport device context object is a structure which contains a + system-defined DEVICE_OBJECT followed by information which is maintained + by the transport provider, called the context. + +Author: + + David Beaver (dbeaver) 1 -July 1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +#ifdef ALLOC_PRAGMA +#ifndef _PNP_POWER +#pragma alloc_text(INIT,NbfCreateDeviceContext) +#else +#pragma alloc_text(PAGE,NbfCreateDeviceContext) +#endif + +#endif + + +VOID +NbfRefDeviceContext( + IN PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a device context. + +Arguments: + + DeviceContext - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_DEVCTX) { + NbfPrint0 ("NbfRefDeviceContext: Entered.\n"); + } + + ASSERT (DeviceContext->ReferenceCount > 0); // not perfect, but... + + (VOID)InterlockedIncrement (&DeviceContext->ReferenceCount); + +} /* NbfRefDeviceContext */ + + +VOID +NbfDerefDeviceContext( + IN PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + + This routine dereferences a device context by decrementing the + reference count contained in the structure. Currently, we don't + do anything special when the reference count drops to zero, but + we could dynamically unload stuff then. + +Arguments: + + DeviceContext - Pointer to a transport device context object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + IF_NBFDBG (NBF_DEBUG_DEVCTX) { + NbfPrint0 ("NbfDerefDeviceContext: Entered.\n"); + } + + result = InterlockedDecrement (&DeviceContext->ReferenceCount); + + ASSERT (result >= 0); + + if (result == 0) { + NbfDestroyDeviceContext (DeviceContext); + } + +} /* NbfDerefDeviceContext */ + + + +NTSTATUS +NbfCreateDeviceContext( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING DeviceName, + IN OUT PDEVICE_CONTEXT *DeviceContext + ) + +/*++ + +Routine Description: + + This routine creates and initializes a device context structure. + +Arguments: + + + DriverObject - pointer to the IO subsystem supplied driver object. + + DeviceContext - Pointer to a pointer to a transport device context object. + + DeviceName - pointer to the name of the device this device object points to. + +Return Value: + + STATUS_SUCCESS if all is well; STATUS_INSUFFICIENT_RESOURCES otherwise. + +--*/ + +{ + NTSTATUS status; + PDEVICE_OBJECT deviceObject; + PDEVICE_CONTEXT deviceContext; + USHORT i; + + + // + // Create the device object for NETBEUI. + // + + status = IoCreateDevice( + DriverObject, + sizeof (DEVICE_CONTEXT) - sizeof (DEVICE_OBJECT) + + (DeviceName->Length + sizeof(UNICODE_NULL)), + DeviceName, + FILE_DEVICE_TRANSPORT, + 0, + FALSE, + &deviceObject); + + if (!NT_SUCCESS(status)) { + return status; + } + + deviceObject->Flags |= DO_DIRECT_IO; + + deviceContext = (PDEVICE_CONTEXT)deviceObject; + + // + // Initialize our part of the device context. + // + + RtlZeroMemory( + ((PUCHAR)deviceContext) + sizeof(DEVICE_OBJECT), + sizeof(DEVICE_CONTEXT) - sizeof(DEVICE_OBJECT)); + + // + // Copy over the device name. + // + + deviceContext->DeviceNameLength = DeviceName->Length + sizeof(WCHAR); + deviceContext->DeviceName = (PWCHAR)(deviceContext+1); + RtlCopyMemory( + deviceContext->DeviceName, + DeviceName->Buffer, + DeviceName->Length); + deviceContext->DeviceName[DeviceName->Length/sizeof(WCHAR)] = UNICODE_NULL; + + // + // Initialize device context fields. + // + + deviceContext->NetmanVariables = NULL; // no variables yet. + + // + // Initialize the reference count. + // + + deviceContext->ReferenceCount = 1; + +#if DBG + { + UINT Counter; + for (Counter = 0; Counter < NUMBER_OF_DCREFS; Counter++) { + deviceContext->RefTypes[Counter] = 0; + } + + // This reference is removed by the caller. + + deviceContext->RefTypes[DCREF_CREATION] = 1; + } +#endif + + // + // initialize the various fields in the device context + // + + KeInitializeSpinLock (&deviceContext->Interlock); + KeInitializeSpinLock (&deviceContext->SpinLock); + KeInitializeSpinLock (&deviceContext->LinkSpinLock); + KeInitializeSpinLock (&deviceContext->TimerSpinLock); + KeInitializeSpinLock (&deviceContext->LoopbackSpinLock); + KeInitializeSpinLock (&deviceContext->SendPoolListLock); + KeInitializeSpinLock (&deviceContext->RcvPoolListLock); + + deviceContext->LinkTreeRoot = NULL; + deviceContext->LastLink = NULL; + deviceContext->LinkTreeElements = 0; + + deviceContext->LoopbackLinks[0] = NULL; + deviceContext->LoopbackLinks[1] = NULL; + deviceContext->LoopbackInProgress = FALSE; + KeInitializeDpc( + &deviceContext->LoopbackDpc, + NbfProcessLoopbackQueue, + (PVOID)deviceContext + ); + + deviceContext->WanThreadQueued = FALSE; + ExInitializeWorkItem( + &deviceContext->WanDelayedQueueItem, + NbfProcessWanDelayedQueue, + (PVOID)deviceContext); + + + deviceContext->UniqueIdentifier = 1; + deviceContext->ControlChannelIdentifier = 1; + + InitializeListHead (&deviceContext->ConnectionPool); + InitializeListHead (&deviceContext->AddressPool); + InitializeListHead (&deviceContext->AddressFilePool); + InitializeListHead (&deviceContext->AddressDatabase); + InitializeListHead (&deviceContext->LinkPool); + InitializeListHead (&deviceContext->LinkDeferred); + InitializeListHead (&deviceContext->PacketWaitQueue); + InitializeListHead (&deviceContext->PacketizeQueue); + InitializeListHead (&deviceContext->DataAckQueue); + InitializeListHead (&deviceContext->DeferredRrQueue); + InitializeListHead (&deviceContext->RequestPool); + InitializeListHead (&deviceContext->UIFramePool); + deviceContext->PacketPool.Next = NULL; + deviceContext->ReceivePacketPool.Next = NULL; + deviceContext->ReceiveBufferPool.Next = NULL; + InitializeListHead (&deviceContext->ReceiveInProgress); + InitializeListHead (&deviceContext->ShortList); + InitializeListHead (&deviceContext->LongList); + InitializeListHead (&deviceContext->PurgeList); + InitializeListHead (&deviceContext->LoopbackQueue); + InitializeListHead (&deviceContext->FindNameQueue); + InitializeListHead (&deviceContext->StatusQueryQueue); + InitializeListHead (&deviceContext->IrpCompletionQueue); + + InitializeListHead (&deviceContext->QueryIndicationQueue); + InitializeListHead (&deviceContext->DatagramIndicationQueue); + deviceContext->IndicationQueuesInUse = FALSE; + + // + // Equivalent to setting ShortListActive, DataAckQueueActive, + // and LinkDeferredActive to FALSE. + // + + deviceContext->a.AnyActive = 0; + + deviceContext->ProcessingShortTimer = FALSE; + deviceContext->DataAckQueueChanged = FALSE; + deviceContext->StalledConnectionCount = (USHORT)0; + + deviceContext->EasilyDisconnected = FALSE; + + // + // Initialize provider statistics. + // + + deviceContext->Statistics.Version = 0x0100; + +#if 0 + deviceContext->Information.Version = 2; // BUGBUG: define TDI_VERSION in TDI.H. + deviceContext->Information.MaxTsduSize = NBF_MAX_TSDU_SIZE; + deviceContext->Information.MaxDatagramSize = NBF_MAX_DATAGRAM_SIZE; + deviceContext->Information.MaxConnectionUserData = NBF_MAX_CONNECTION_USER_DATA; + deviceContext->Information.ServiceFlags = NBF_SERVICE_FLAGS; + deviceContext->Information.TransmittedTsdus = 0; + deviceContext->Information.ReceivedTsdus = 0; + deviceContext->Information.TransmissionErrors = 0; + deviceContext->Information.ReceiveErrors = 0; + deviceContext->Information.MinimumLookaheadData = NBF_MIN_LOOKAHEAD_DATA; + deviceContext->Information.MaximumLookaheadData = NBF_MAX_LOOKAHEAD_DATA; + deviceContext->Information.DiscardedFrames = 0; + deviceContext->Information.OversizeTsdusReceived = 0; + deviceContext->Information.UndersizeTsdusReceived = 0; + deviceContext->Information.MulticastTsdusReceived = 0; + deviceContext->Information.BroadcastTsdusReceived = 0; + deviceContext->Information.MulticastTsdusTransmitted = 0; + deviceContext->Information.BroadcastTsdusTransmitted = 0; + deviceContext->Information.SendTimeouts = 0; + deviceContext->Information.ReceiveTimeouts = 0; + deviceContext->Information.ConnectionIndicationsReceived = 0; + deviceContext->Information.ConnectionIndicationsAccepted = 0; + deviceContext->Information.ConnectionsInitiated = 0; + deviceContext->Information.ConnectionsAccepted = 0; +#endif + + deviceContext->State = DEVICECONTEXT_STATE_OPENING; + + // + // Loopback buffer is not allocated. + // + + deviceContext->LookaheadContiguous = NULL; + + // + // Initialize the resource that guards address ACLs. + // + + ExInitializeResource (&deviceContext->AddressResource); + + // + // No LSNs are in use. + // + + for (i=0; i<(NETBIOS_SESSION_LIMIT+1); i++) { + deviceContext->LsnTable[i] = 0; + } + deviceContext->NextLsnStart = 1; + + // + // No addresses are in use. + // + + for (i=0; i<256; i++) { + deviceContext->AddressCounts[i] = 0; + } + + // + // set the netbios multicast address for this network type + // + + for (i=0; i<HARDWARE_ADDRESS_LENGTH; i++) { + deviceContext->LocalAddress.Address [i] = 0; // set later + deviceContext->NetBIOSAddress.Address [i] = 0; + } + + deviceContext->Type = NBF_DEVICE_CONTEXT_SIGNATURE; + deviceContext->Size = sizeof (DEVICE_CONTEXT); + + *DeviceContext = deviceContext; + return STATUS_SUCCESS; +} + + +VOID +NbfDestroyDeviceContext( + IN PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + + This routine destroys a device context structure. + +Arguments: + + DeviceContext - Pointer to a pointer to a transport device context object. + +Return Value: + + None. + +--*/ + +{ + ExDeleteResource (&DeviceContext->AddressResource); + IoDeleteDevice ((PDEVICE_OBJECT)DeviceContext); + return; +} diff --git a/private/ntos/tdi/nbf/dlc.c b/private/ntos/tdi/nbf/dlc.c new file mode 100644 index 000000000..893bffd49 --- /dev/null +++ b/private/ntos/tdi/nbf/dlc.c @@ -0,0 +1,3270 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + dlc.c + +Abstract: + + This module contains code which implements the data link layer for the + transport provider. + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// Macros + + +// +// These two functions are used by the loopback indicator. +// + +STATIC +VOID +NbfCopyFromPacketToBuffer( + IN PNDIS_PACKET Packet, + IN UINT Offset, + IN UINT BytesToCopy, + OUT PCHAR Buffer, + OUT PUINT BytesCopied + ); + + +VOID +NbfProcessSabme( + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PTP_LINK Link, + IN PDLC_U_FRAME Header, + IN PVOID MacHeader, + IN PHARDWARE_ADDRESS SourceAddress, + IN PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + + This routine processes a received SABME frame. + +Arguments: + + Command - Boolean set to TRUE if command, else FALSE if response. + + PollFinal - Boolean set to TRUE if Poll or Final. + + Link - Pointer to a transport link object. + + Header - Pointer to a DLC U-type frame. + + MacHeader - Pointer to the MAC header of the packet. + + DeviceContext - The device context of this adapter. + +Return Value: + + none. + +--*/ + +{ + PUCHAR SourceRouting; + UINT SourceRoutingLength; + UCHAR TempSR[MAX_SOURCE_ROUTING]; + PUCHAR ResponseSR; + +#if DBG + UCHAR *s; +#endif + + Header; // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessSabme: Entered.\n"); + } + + // + // Format must be: SABME-c/x, on a real link object. + // + + if (!Command || (Link == NULL)) { + return; + } + + // + // Build response routing information. We do this on the SABME, even + // though we already did in on the Name Query, because the client may + // choose a different route (if there were multiple routes) than we + // did. + // + + MacReturnSourceRouting( + &DeviceContext->MacInfo, + MacHeader, + &SourceRouting, + &SourceRoutingLength); + + if (SourceRouting != NULL) { + + RtlCopyMemory( + TempSR, + SourceRouting, + SourceRoutingLength); + + MacCreateNonBroadcastReplySR( + &DeviceContext->MacInfo, + TempSR, + SourceRoutingLength, + &ResponseSR); + + } else { + + ResponseSR = NULL; + + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); // keep state stable + + MacConstructHeader ( + &DeviceContext->MacInfo, + Link->Header, + SourceAddress->Address, + DeviceContext->LocalAddress.Address, + 0, // PacketLength, filled in later + ResponseSR, + SourceRoutingLength, + (PUINT)&(Link->HeaderLength)); + + // + // We optimize for fourteen-byte headers by putting + // the correct Dsap/Ssap at the end, so we can fill + // in new packets as one 16-byte move. + // + + if (Link->HeaderLength <= 14) { + Link->Header[Link->HeaderLength] = DSAP_NETBIOS_OVER_LLC; + Link->Header[Link->HeaderLength+1] = DSAP_NETBIOS_OVER_LLC; + } + + // + // Process the SABME. + // + + Link->LinkBusy = FALSE; // he's cleared his busy condition. + + switch (Link->State) { + + case LINK_STATE_ADM: + + // + // Remote station is initiating this link. Send UA and wait for + // his checkpoint before setting READY state. + // + + // Moving out of ADM, add special reference + NbfReferenceLinkSpecial("Waiting for Poll", Link, LREF_NOT_ADM); + + Link->State = LINK_STATE_W_POLL; // wait for RR-c/p. + + // Don't start T1, but prepare for timing the response + FakeStartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); + + NbfResetLink (Link); + NbfSendUa (Link, PollFinal); // releases lock + IF_NBFDBG (NBF_DEBUG_SETUP) { + NbfPrint4("ADM SABME on %lx %x-%x-%x\n", + Link, + Link->HardwareAddress.Address[3], + Link->HardwareAddress.Address[4], + Link->HardwareAddress.Address[5]); + } + StartTi (Link); +#if DBG + s = "ADM"; +#endif + break; + + case LINK_STATE_CONNECTING: + + // + // We've sent a SABME and are waiting for a UA. He's sent a + // SABME at the same time, so we tried to do it at the same time. + // The only balanced thing we can do at this time is to respond + // with UA and duplicate the effort. To not send anything would + // be bad because the link would never complete. + // + + // + // BUGBUG: What about timers here? + // + + Link->State = LINK_STATE_W_POLL; // wait for RR-c/p. + NbfSendUa (Link, PollFinal); // releases lock + StartTi (Link); +#if DBG + s = "CONNECTING"; +#endif + break; + + case LINK_STATE_W_POLL: + + // + // We're waiting for his initial poll on a RR-c/p. Instead, we + // got a SABME, so this is really a link reset. + // + // Unfortunately, if we happen to get two SABMEs + // and respond to the second one with another UA + // (when he has sent the RR-c/p and is expecting + // an RR-r/f), he will send a FRMR. So, we ignore + // this frame. + // + + // Link->State = LINK_STATE_W_POLL; // wait for RR-c/p. + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + StartTi(Link); +#if DBG + s = "W_POLL"; +#endif + break; + + case LINK_STATE_READY: + + // + // Link is already balanced. He's resetting the link. + // + + case LINK_STATE_W_FINAL: + + // + // We're waiting for a RR-r/f from the remote guy but instead + // he sent this SABME. We have to assume he wants to reset the link. + // + + case LINK_STATE_W_DISC_RSP: + + // + // We're waiting for a response from our DISC-c/p but instead of + // a UA-r/f, we got this SABME. He wants to initiate the link + // again because a transport connection has been initiated while + // we were taking the link down. + // + + Link->State = LINK_STATE_W_POLL; // wait for RR-c/p. + + // Don't start T1, but prepare for timing the response + FakeStartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); + + NbfResetLink (Link); // reset this connection. + NbfSendUa (Link, PollFinal); // releases lock. + StartTi(Link); +#if DBG + s = "READY/W_FINAL/W_DISC_RSP"; +#endif + break; + + default: + + ASSERT (FALSE); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "Unknown link state"; +#endif + + } /* switch */ + +#if DBG + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 (" NbfProcessSabme: Processed, State was %s.\n", s); + } +#endif + +} /* NbfProcessSabme */ + + +VOID +NbfProcessUa( + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PTP_LINK Link, + IN PDLC_U_FRAME Header + ) + +/*++ + +Routine Description: + + This routine processes a received UA frame. + +Arguments: + + Command - Boolean set to TRUE if command, else FALSE if response. + + PollFinal - Boolean set to TRUE if Poll or Final. + + Link - Pointer to a transport link object. + + Header - Pointer to a DLC U-type frame. + +Return Value: + + none. + +--*/ + +{ +#if DBG + UCHAR *s; +#endif + + PollFinal, Header; // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessUa: Entered.\n"); + } + + // + // Format must be: UA-r/x, on a real link object. + // + + if (Command || (Link == NULL)) { + return; + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); // keep state stable + + Link->LinkBusy = FALSE; // he's cleared his busy condition. + + switch (Link->State) { + + case LINK_STATE_ADM: + + // + // Received an unnumbered acknowlegement while in ADM. Somehow + // the remote station is confused, so tell him we're disconnected. + // + + NbfSendDm (Link, FALSE); // indicate we're disconnected, release lock +#if DBG + s = "ADM"; +#endif + break; + + case LINK_STATE_CONNECTING: + + // + // We've sent a SABME and have just received the UA. + // + + UpdateBaseT1Timeout (Link); // got response to poll. + + Link->State = LINK_STATE_W_FINAL; // wait for RR-r/f. + Link->SendRetries = (UCHAR)Link->LlcRetries; + NbfSendRr (Link, TRUE, TRUE); // send RR-c/p, StartT1, release lock +#if DBG + s = "CONNECTING"; +#endif + break; + + case LINK_STATE_READY: + + // + // Link is already balanced. He's confused; throw it away. + // + + case LINK_STATE_W_POLL: + + // + // We're waiting for his initial poll on a RR-c/p. Instead, we + // got a UA, so he is confused. Throw it away. + // + + case LINK_STATE_W_FINAL: + + // + // We're waiting for a RR-r/f from the remote guy but instead + // he sent this UA. He is confused. Throw it away. + // + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "READY/W_POLL/W_FINAL"; +#endif + break; + + case LINK_STATE_W_DISC_RSP: + + // + // We've sent a DISC-c/p and have received the correct response. + // Disconnect this link. + // + + Link->State = LINK_STATE_ADM; // completed disconnection. + + // + // This is the normal "clean" disconnect path, so we stop + // all the timers here since we won't call NbfStopLink. + // + + StopT1 (Link); + StopT2 (Link); + StopTi (Link); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + // Moving to ADM, dereference link + NbfDereferenceLinkSpecial ("Got UA for DISC", Link, LREF_NOT_ADM); // decrement link's last ref. + +#if DBG + s = "W_DISC_RSP"; +#endif + break; + + default: + + ASSERT (FALSE); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "Unknown link state"; +#endif + + } /* switch */ + +} /* NbfProcessUa */ + + +VOID +NbfProcessDisc( + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PTP_LINK Link, + IN PDLC_U_FRAME Header + ) + +/*++ + +Routine Description: + + This routine processes a received DISC frame. + +Arguments: + + Command - Boolean set to TRUE if command, else FALSE if response. + + PollFinal - Boolean set to TRUE if Poll or Final. + + Link - Pointer to a transport link object. + + Header - Pointer to a DLC U-type frame. + +Return Value: + + none. + +--*/ + +{ +#if DBG + UCHAR *s; +#endif + + Header; // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessDisc: Entered.\n"); + } + + // + // Format must be: DISC-c/x, on a real link object. + // + + if (!Command || (Link == NULL)) { + return; + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); // keep state stable + + Link->LinkBusy = FALSE; // he's cleared his busy condition. + + switch (Link->State) { + + case LINK_STATE_ADM: + + // + // Received a DISC while in ADM. Simply report disconnected mode. + // + +#if DBG + s = "ADM"; +#endif + NbfSendDm (Link, PollFinal); // indicate we're disconnected, release lock + break; + + case LINK_STATE_READY: + + // + // Link is balanced. Kill the link. + // + + Link->State = LINK_STATE_ADM; // we're reset now. + NbfSendUa (Link, PollFinal); // Send UA-r/x, release lock +#if DBG + if (NbfDisconnectDebug) { + NbfPrint0( "NbfProcessDisc calling NbfStopLink\n" ); + } +#endif + NbfStopLink (Link); // say goodnight, gracie + + // Moving to ADM, remove reference + NbfDereferenceLinkSpecial("Stopping link", Link, LREF_NOT_ADM); + +#if DBG + s = "READY"; +#endif + break; + + case LINK_STATE_CONNECTING: + + // + // We've sent a SABME and have just received a DISC. That means + // we have crossed a disconnection and reconnection. Throw away + // the disconnect. + // + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "CONNECTING"; +#endif + break; + + case LINK_STATE_W_POLL: + + // + // We're waiting for his initial poll on a RR-c/p. Instead, we + // got a DISC, so he wants to drop the link. + // + + case LINK_STATE_W_FINAL: + + // + // We're waiting for a RR-r/f from the remote guy but instead + // he sent DISC, so he wants to drop the link. + // + + case LINK_STATE_W_DISC_RSP: + + // + // We've sent a DISC-c/p and have received a DISC from him as well. + // Disconnect this link. + // + + Link->State = LINK_STATE_ADM; // we're reset now. + NbfSendUa (Link, PollFinal); // Send UA-r/x, release lock. + + NbfStopLink (Link); + + // moving to ADM, remove reference + NbfDereferenceLinkSpecial ("Got DISC on W_DIS_RSP", Link, LREF_NOT_ADM); // remove its "alive" ref. + +#if DBG + s = "W_POLL/W_FINAL/W_DISC_RSP"; +#endif + break; + + default: + + ASSERT (FALSE); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "Unknown link state"; +#endif + + } /* switch */ + +} /* NbfProcessDisc */ + + +VOID +NbfProcessDm( + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PTP_LINK Link, + IN PDLC_U_FRAME Header + ) + +/*++ + +Routine Description: + + This routine processes a received DM frame. + +Arguments: + + Command - Boolean set to TRUE if command, else FALSE if response. + + PollFinal - Boolean set to TRUE if Poll or Final. + + Link - Pointer to a transport link object. + + Header - Pointer to a DLC U-type frame. + +Return Value: + + none. + +--*/ + +{ +#if DBG + UCHAR *s; +#endif + + Header; // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessDm: Entered.\n"); + } + + // + // Format must be: DM-r/x, on a real link object. + // + + if (Command || (Link == NULL)) { + return; + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); // keep state stable + + Link->LinkBusy = FALSE; // he's cleared his busy condition. + + switch (Link->State) { + + case LINK_STATE_ADM: + + // + // Received a DM while in ADM. Do nothing. + // + + case LINK_STATE_CONNECTING: + + // + // We've sent a SABME and have just received a DM. That means + // we have crossed a disconnection and reconnection. Throw away + // the disconnect mode indicator, we will reconnect in time. + // + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "ADM/CONNECTING"; +#endif + break; + + case LINK_STATE_READY: + + // + // Link is balanced and he is in ADM, so we have to shut down. + // + +#if DBG + if (NbfDisconnectDebug) { + NbfPrint0( "NbfProcessDm calling NbfStopLink\n" ); + } +#endif + + case LINK_STATE_W_POLL: + + // + // We're waiting for his initial poll on a RR-c/p. Instead, we + // got a DM, so he has dropped the link. + // + + case LINK_STATE_W_FINAL: + + // + // We're waiting for a RR-r/f from the remote guy but instead + // he sent DM, so he has already dropped the link. + // + + case LINK_STATE_W_DISC_RSP: + + // + // We've sent a DISC-c/p and have received a DM from him, indicating + // that he is now in ADM. While technically not what we expected, + // this protocol is commonly used in place of UA-r/f, so just treat + // as though we got a UA-r/f. Disconnect the link normally. + // + + Link->State = LINK_STATE_ADM; // we're reset now. + NbfSendDm (Link, FALSE); // indicate disconnected, release lock + + NbfStopLink (Link); + + // moving to ADM, remove reference + NbfDereferenceLinkSpecial ("Got DM in W_DISC_RSP", Link, LREF_NOT_ADM); // remove its "alive" ref. + +#if DBG + s = "READY/W_FINAL/W_POLL/W_DISC_RSP"; +#endif + break; + + default: + + ASSERT (FALSE); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "Unknown link state"; +#endif + + } /* switch */ + +} /* NbfProcessDm */ + + +VOID +NbfProcessFrmr( + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PTP_LINK Link, + IN PDLC_U_FRAME Header + ) + +/*++ + +Routine Description: + + This routine processes a received FRMR response frame. + +Arguments: + + Command - Boolean set to TRUE if command, else FALSE if response. + + PollFinal - Boolean set to TRUE if Poll or Final. + + Link - Pointer to a transport link object. + + Header - Pointer to a DLC U-type frame. + +Return Value: + + none. + +--*/ + +{ +#if DBG + UCHAR *s; +#endif + ULONG DumpData[6]; + + PollFinal, Header; // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessFrmr: Entered.\n"); + } + + // + // Log an error, this shouldn't happen. + // + + DumpData[0] = Link->State; + DumpData[1] = Link->Flags; + DumpData[2] = (Header->Information.FrmrInfo.Command << 8) + + (Header->Information.FrmrInfo.Ctrl); + DumpData[3] = (Header->Information.FrmrInfo.Vs << 16) + + (Header->Information.FrmrInfo.Vr << 8) + + (Header->Information.FrmrInfo.Reason); + DumpData[4] = (Link->SendState << 24) + + (Link->NextSend << 16) + + (Link->LastAckReceived << 8) + + (Link->SendWindowSize); + DumpData[5] = (Link->ReceiveState << 24) + + (Link->NextReceive << 16) + + (Link->LastAckSent << 8) + + (Link->ReceiveWindowSize); + + NbfWriteGeneralErrorLog( + Link->Provider, + EVENT_TRANSPORT_BAD_PROTOCOL, + 1, + STATUS_LINK_FAILED, + L"FRMR", + 6, + DumpData); + + + ++Link->Provider->FrmrReceived; + + // + // Format must be: FRMR-r/x, on a real link object. + // + + if (Command || (Link == NULL)) { + return; + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); // keep state stable + + switch (Link->State) { + + case LINK_STATE_ADM: + + // + // Received a FRMR while in ADM. Report disconnected mode. + // + +#if DBG + s = "ADM"; +#endif + NbfSendDm (Link, FALSE); // indicate disconnected, release lock + break; + + case LINK_STATE_READY: + + // + // Link is balanced and he reported a protocol error. + // +#if 0 + // We want to reset the link, but not quite as fully + // as NbfResetLink. This code is the same as what + // is in there, except for: + // + // - resetting the send/receive numbers + // - removing packets from the WackQ + // + + StopT1 (Link); + StopT2 (Link); + Link->Flags &= LINK_FLAGS_DEFERRED_MASK; // keep deferred operations + + Link->SendWindowSize = 1; + Link->LinkBusy = FALSE; + + Link->ReceiveWindowSize = 1; + Link->WindowErrors = 0; + Link->BestWindowSize = 1; + Link->WorstWindowSize = Link->MaxWindowSize; + Link->Flags |= LINK_FLAGS_JUMP_START; + + Link->CurrentT1Timeout = Link->Provider->DefaultT1Timeout; + Link->BaseT1Timeout = Link->Provider->DefaultT1Timeout << DLC_TIMER_ACCURACY; + Link->CurrentPollRetransmits = 0; + Link->CurrentPollOutstanding = FALSE; + Link->T2Timeout = Link->Provider->DefaultT2Timeout; + Link->TiTimeout = Link->Provider->DefaultTiTimeout; + Link->LlcRetries = Link->Provider->LlcRetries; + Link->MaxWindowSize = Link->Provider->LlcMaxWindowSize; + + // + // The rest is similar to NbfActivateLink + // + + Link->State = LINK_STATE_CONNECTING; + Link->SendState = SEND_STATE_DOWN; + Link->ReceiveState = RECEIVE_STATE_DOWN; + Link->SendRetries = (UCHAR)Link->LlcRetries; + + NbfSendSabme (Link, TRUE); // send SABME/p, StartT1, release lock +#else + Link->State = LINK_STATE_ADM; // we're reset now. + NbfSendDm (Link, FALSE); // indicate disconnected, release lock + + NbfStopLink (Link); + + // moving to ADM, remove reference + NbfDereferenceLinkSpecial("Got DM in W_POLL", Link, LREF_NOT_ADM); +#endif + +#if DBG + NbfPrint1("Received FRMR on link %lx\n", Link); +#endif + +#if DBG + s = "READY"; +#endif + break; + + case LINK_STATE_CONNECTING: + + // + // We've sent a SABME and have just received a FRMR. + // + + case LINK_STATE_W_POLL: + + // + // We're waiting for his initial poll on a RR-c/p. Instead, we + // got a FRMR. + // + + case LINK_STATE_W_FINAL: + + // + // We're waiting for a RR-r/f from the remote guy but instead + // he sent FRMR. + // + + case LINK_STATE_W_DISC_RSP: + + // + // We've sent a DISC-c/p and have received a FRMR. + // + + Link->State = LINK_STATE_ADM; // we're reset now. + NbfSendDm (Link, FALSE); // indicate disconnected, release lock + + // moving to ADM, remove reference + NbfDereferenceLinkSpecial("Got DM in W_POLL", Link, LREF_NOT_ADM); + +#if DBG + s = "CONN/W_POLL/W_FINAL/W_DISC_RSP"; +#endif + break; + + default: + + ASSERT (FALSE); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "Unknown link state"; +#endif + + } /* switch */ + +} /* NbfProcessFrmr */ + + +VOID +NbfProcessTest( + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PTP_LINK Link, + IN PDLC_U_FRAME Header + ) + +/*++ + +Routine Description: + + This routine processes a received TEST frame. + +Arguments: + + Command - Boolean set to TRUE if command, else FALSE if response. + + PollFinal - Boolean set to TRUE if Poll or Final. + + Link - Pointer to a transport link object. + + Header - Pointer to a DLC U-type frame. + +Return Value: + + none. + +--*/ + +{ + Header, PollFinal; // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessTest: Entered.\n"); + } + + // + // Process only: TEST-c/x. + // + + // BUGBUG: respond to TEST on a link that is NULL. + + if (!Command || (Link == NULL)) { + return; + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); // keep state stable + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + +#if DBG + PANIC ("NbfSendTest: Received Test Packet, not processing....\n"); +#endif + //NbfSendTest (Link, FALSE, PollFinal, Psdu); + +} /* NbfProcessTest */ + + +VOID +NbfProcessXid( + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PTP_LINK Link, + IN PDLC_U_FRAME Header + ) + +/*++ + +Routine Description: + + This routine processes a received XID frame. + +Arguments: + + Command - Boolean set to TRUE if command, else FALSE if response. + + PollFinal - Boolean set to TRUE if Poll or Final. + + Link - Pointer to a transport link object. + + Header - Pointer to a DLC U-type frame. + +Return Value: + + none. + +--*/ + +{ + Header; // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessXid: Entered.\n"); + } + + // + // Process only: XID-c/x. + // + + // BUGBUG: respond to XID with a link that is NULL. + + if (!Command || (Link == NULL)) { + return; + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); // keep state stable + + NbfSendXid (Link, FALSE, PollFinal); // releases lock + +} /* NbfProcessXid */ + + +VOID +NbfProcessSFrame( + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PTP_LINK Link, + IN PDLC_S_FRAME Header, + IN UCHAR CommandByte + ) + +/*++ + +Routine Description: + + This routine processes a received RR, RNR, or REJ frame. + +Arguments: + + Command - Boolean set to TRUE if command, else FALSE if response. + + PollFinal - Boolean set to TRUE if Poll or Final. + + Link - Pointer to a transport link object. + + Header - Pointer to a DLC S-type frame. + + CommandByte - The command byte of the frame (RR, RNR, or REJ). + +Return Value: + + none. + +--*/ + +{ +#if DBG + UCHAR *s; +#endif + BOOLEAN Resend; + BOOLEAN AckedPackets; + UCHAR AckSequenceNumber; + UCHAR OldLinkSendRetries; + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint2 (" NbfProcessSFrame %s: Entered, Link: %lx\n", Link, + Command == DLC_CMD_RR ? "RR" : (Command == DLC_CMD_RNR ? "RNR" : "REJ")); + } + + // + // Process any of: RR-x/x, RNR-x/x, REJ-x/x + // + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); // keep state stable + + if (CommandByte == DLC_CMD_RNR) { + Link->LinkBusy = TRUE; // he's set a busy condition. + } else { + Link->LinkBusy = FALSE; // busy condition cleared. + } + + switch (Link->State) { + + case LINK_STATE_ADM: + + // + // We're disconnected. Tell him. + // + +#if DBG + s = "ADM"; +#endif + NbfSendDm (Link, PollFinal); // releases lock + break; + + case LINK_STATE_READY: + + // + // Link is balanced. Note that the sections below surrounded by + // if (Command && PollFinal) variants are all disjoint sets. + // That's the only reason the Spinlock stuff works right. DO NOT + // attempt to fiddle this unless you figure out the locking first! + // + + // + // If the AckSequenceNumber is not valid, ignore it. The + // number should be between the first packet on the WackQ + // and one more than the last packet. These correspond to + // Link->LastAckReceived and Link->NextSend. + // + + AckSequenceNumber = (UCHAR)(Header->RcvSeq >> 1); + + if (Link->NextSend >= Link->LastAckReceived) { + + // + // There is no 127 -> 0 wrap between the two... + // + + if ((AckSequenceNumber < Link->LastAckReceived) || + (AckSequenceNumber > Link->NextSend)) { + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + DbgPrint("NbfResendLlcPackets: %.2x-%.2x-%.2x-%.2x-%.2x-%.2x Unexpected N(R) %d, LastAck %d NextSend %d\n", + Link->HardwareAddress.Address[0], + Link->HardwareAddress.Address[1], + Link->HardwareAddress.Address[2], + Link->HardwareAddress.Address[3], + Link->HardwareAddress.Address[4], + Link->HardwareAddress.Address[5], + AckSequenceNumber, Link->LastAckReceived, Link->NextSend); +#endif + break; + + } + + } else { + + // + // There is a 127 -> 0 wrap between the two... + // + + if ((AckSequenceNumber < Link->LastAckReceived) && + (AckSequenceNumber > Link->NextSend)) { + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + DbgPrint("NbfResendLlcPackets: %.2x-%.2x-%.2x-%.2x-%.2x-%.2x Unexpected N(R) %d, LastAck %d NextSend %d\n", + Link->HardwareAddress.Address[0], + Link->HardwareAddress.Address[1], + Link->HardwareAddress.Address[2], + Link->HardwareAddress.Address[3], + Link->HardwareAddress.Address[4], + Link->HardwareAddress.Address[5], + AckSequenceNumber, Link->LastAckReceived, Link->NextSend); +#endif + break; + + } + + } + + + // + // We always resend on a REJ, and never on an RNR; + // for an RR we may change Resend to TRUE below. + // If we get a REJ on a WAN line (T1 is more than + // five seconds) then we pretend this was a final + // so we will resend even if a poll was outstanding. + // + + if (CommandByte == DLC_CMD_REJ) { + Resend = TRUE; + if (Link->CurrentT1Timeout >= ((5 * SECONDS) / SHORT_TIMER_DELTA)) { + PollFinal = TRUE; + } + OldLinkSendRetries = (UCHAR)Link->SendRetries; + } else { + Resend = FALSE; + } + + +#if 0 + // + // If we've got a request with no poll, must have fired T2 on + // the other side (or, if the other side is OS/2, he lost a + // packet and knows it or is telling me to lower the window size). + // In the T2 case, we've Acked current stuff, mark the window as + // needing adjustment at some future time. In the OS/2 cases, this + // is also the right thing to do. + // + + if ((!Command) && (!PollFinal)) { + ; + } +#endif + + if (PollFinal) { + + if (Command) { + + // + // If he is checkpointing, then we must respond with RR-r/f to + // update him on the status of our reception of his I-frames. + // + + StopT2 (Link); // we acked some I-frames. + NbfSendRr (Link, FALSE, PollFinal); // releases lock + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + } else { + + // + // If we were checkpointing, and he has sent an RR-r/f, we + // can clear the checkpoint condition. Any packets which + // are still waiting for acknowlegement at this point must + // now be resent. + // + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessRr: he's responded to our checkpoint.\n"); + } + if (Link->SendState != SEND_STATE_CHECKPOINTING) { + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessRr: BUGBUG: not ckpting, but final received.\n"); + } + } else if (CommandByte == DLC_CMD_RR) { + OldLinkSendRetries = (UCHAR)Link->SendRetries; + Resend = TRUE; + UpdateBaseT1Timeout (Link); // gor response to poll + } + StopT1 (Link); // checkpointing finished. + Link->SendRetries = (UCHAR)Link->LlcRetries; + Link->SendState = SEND_STATE_READY; + StartTi (Link); + } + } + + // + // NOTE: The link spinlock is held here. + // + + // + // The N(R) in this frame acknowleges some (or all) of our packets. + // We'll ack packets on our send queue if this is a final when we + // call Resend. This call must come after the checkpoint + // acknowlegement check so that an RR-r/f is always sent BEFORE + // any new I-frames. This allows us to always send I-frames as + // commands. + // + + if (Link->WackQ.Flink != &Link->WackQ) { + + // + // NOTE: ResendLlcPackets may release and reacquire + // the link spinlock. + // + + AckedPackets = ResendLlcPackets( + Link, + AckSequenceNumber, + Resend); + + if (Resend && (!AckedPackets) && (Link->State == LINK_STATE_READY)) { + + // + // To prevent stalling, pretend this RR wasn't + // received. + // + + if (OldLinkSendRetries == 1) { + + CancelT1Timeout (Link); // we are stopping a polling state + + Link->State = LINK_STATE_W_DISC_RSP; // we are awaiting a DISC/f. + Link->SendState = SEND_STATE_DOWN; + Link->ReceiveState = RECEIVE_STATE_DOWN; + Link->SendRetries = (UCHAR)Link->LlcRetries; + +#if DBG + DbgPrint ("NBF: No ack teardown of %lx\n", Link); +#endif + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + NbfStopLink (Link); + + StartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); // retransmit timer. + NbfSendDisc (Link, TRUE); // send DISC-c/p. + + } else { + + StopTi (Link); + Link->SendRetries = OldLinkSendRetries-1; + + if (Link->SendState != SEND_STATE_CHECKPOINTING) { + Link->SendState = SEND_STATE_CHECKPOINTING; + NbfSendRr (Link, TRUE, TRUE);// send RR-c/p, StartT1, release lock + } else { + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + } + + } +#if DBG + s = "READY"; +#endif + break; // No need to RestartLinkTraffic + + } else if (AckedPackets) { + + Link->SendRetries = (UCHAR)Link->LlcRetries; + + } + + } + + + // + // If the link send state is READY, get the link going + // again. + // + // NOTE: RestartLinkTraffic releases the link spinlock. + // + + if (Link->SendState == SEND_STATE_READY) { + RestartLinkTraffic (Link); + } else { + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + } +#if DBG + s = "READY"; +#endif + break; + + case LINK_STATE_CONNECTING: + + // + // We've sent a SABME and are waiting for a UA. He's sent a + // RR too early, so just let the SABME timeout. + // + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "CONNECTING"; +#endif + break; + + case LINK_STATE_W_POLL: + + // + // We're waiting for his initial poll on a RR-c/p. If he just + // sends something without a poll, we'll let that get by. + // + + if (!Command) { + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "W_POLL"; +#endif + break; // don't allow this protocol. + } + Link->State = LINK_STATE_READY; // we're up! + + FakeUpdateBaseT1Timeout (Link); + NbfSendRr (Link, FALSE, PollFinal); // send RR-r/x, release lock + NbfCompleteLink (Link); // fire up the connections. + IF_NBFDBG (NBF_DEBUG_SETUP) { + NbfPrint4("W_POLL RR on %lx %x-%x-%x\n", + Link, + Link->HardwareAddress.Address[3], + Link->HardwareAddress.Address[4], + Link->HardwareAddress.Address[5]); + } + StartTi (Link); + +#if DBG + s = "W_POLL"; +#endif + break; + + case LINK_STATE_W_FINAL: + + // + // We're waiting for a RR-r/f from the remote guy. + // + + if (Command || !PollFinal) { // wait for final. + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "W_FINAL"; +#endif + break; // we sent RR-c/p. + } + Link->State = LINK_STATE_READY; // we're up. + StopT1 (Link); // poll was acknowleged. + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + NbfCompleteLink (Link); // fire up the connections. + StartTi (Link); +#if DBG + s = "W_FINAL"; +#endif + break; + + case LINK_STATE_W_DISC_RSP: + + // + // We're waiting for a response from our DISC-c/p but instead of + // a UA-r/f, we got this RR. Throw the packet away. + // + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "W_DISC_RSP"; +#endif + break; + + default: + + ASSERT (FALSE); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "Unknown link state"; +#endif + + } /* switch */ + +#if DBG + if (CommandByte == DLC_CMD_REJ) { + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 (" NbfProcessRej: (%s) REJ received.\n", s); + } + } +#endif + +} /* NbfProcessSFrame */ + + +VOID +NbfInsertInLoopbackQueue ( + IN PDEVICE_CONTEXT DeviceContext, + IN PNDIS_PACKET NdisPacket, + IN UCHAR LinkIndex + ) + +/*++ + +Routine Description: + + This routine places a packet on the loopback queue, and + queues a DPC to do the indication if needed. + +Arguments: + + DeviceContext - The device context in question. + + NdisPacket - The packet to place on the loopback queue. + + LinkIndex - The index of the loopback link to indicate to. + +Return Value: + + None: + +--*/ + +{ + PSEND_PACKET_TAG SendPacketTag; + KIRQL oldirql; + + SendPacketTag = (PSEND_PACKET_TAG)NdisPacket->ProtocolReserved; + SendPacketTag->OnLoopbackQueue = TRUE; + + SendPacketTag->LoopbackLinkIndex = LinkIndex; + + ACQUIRE_SPIN_LOCK(&DeviceContext->LoopbackSpinLock, &oldirql); + + InsertTailList(&DeviceContext->LoopbackQueue, &SendPacketTag->Linkage); + + if (!DeviceContext->LoopbackInProgress) { + + KeInsertQueueDpc(&DeviceContext->LoopbackDpc, NULL, NULL); + DeviceContext->LoopbackInProgress = TRUE; + + } + + RELEASE_SPIN_LOCK (&DeviceContext->LoopbackSpinLock, oldirql); + +} + + +VOID +NbfProcessLoopbackQueue ( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) + +/*++ + +Routine Description: + + This is the DPC routine which processes items on the + loopback queue. It processes a single request off the + queue (if there is one), then if there is another request + it requeues itself. + +Arguments: + + Dpc - The system DPC object. + + DeferredContext - A pointer to the device context. + + SystemArgument1, SystemArgument2 - Not used. + +Return Value: + + None. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + PNDIS_PACKET NdisPacket; + PNDIS_BUFFER FirstBuffer; + PVOID LookaheadBuffer; + UINT LookaheadBufferSize; + UINT BytesCopied; + UINT PacketSize; + ULONG HeaderLength; + PTP_LINK Link; + PSEND_PACKET_TAG SendPacketTag; + PLIST_ENTRY p; + + + UNREFERENCED_PARAMETER(Dpc); + UNREFERENCED_PARAMETER(SystemArgument1); + UNREFERENCED_PARAMETER(SystemArgument2); + + + DeviceContext = (PDEVICE_CONTEXT)DeferredContext; + + ACQUIRE_DPC_SPIN_LOCK(&DeviceContext->LoopbackSpinLock); + + if (!IsListEmpty(&DeviceContext->LoopbackQueue)) { + + p = RemoveHeadList(&DeviceContext->LoopbackQueue); + + // + // BUGBUG: This depends on the fact that the Linkage field is + // the first one in ProtocolReserved. + // + + NdisPacket = CONTAINING_RECORD(p, NDIS_PACKET, ProtocolReserved[0]); + + SendPacketTag = (PSEND_PACKET_TAG)NdisPacket->ProtocolReserved; + SendPacketTag->OnLoopbackQueue = FALSE; + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LoopbackSpinLock); + + + // + // Determine the data needed to indicate. We don't guarantee + // that we will have the correct lookahead length, but since + // we know that any header we prepend is a single piece, + // and that's all we'll have to look at in an indicated packet, + // that's not a concern. + // + // Unfortunately that last paragraph is bogus since for + // indications to our client we need more data... + // + + NdisQueryPacket(NdisPacket, NULL, NULL, &FirstBuffer, &PacketSize); + + NdisQueryBuffer(FirstBuffer, &LookaheadBuffer, &LookaheadBufferSize); + + if ((LookaheadBufferSize != PacketSize) && + (LookaheadBufferSize < NBF_MAX_LOOPBACK_LOOKAHEAD)) { + + // + // There is not enough contiguous data in the + // packet's first buffer, so we merge it into + // DeviceContext->LookaheadContiguous. + // + + if (PacketSize > NBF_MAX_LOOPBACK_LOOKAHEAD) { + LookaheadBufferSize = NBF_MAX_LOOPBACK_LOOKAHEAD; + } else { + LookaheadBufferSize = PacketSize; + } + + NbfCopyFromPacketToBuffer( + NdisPacket, + 0, + LookaheadBufferSize, + DeviceContext->LookaheadContiguous, + &BytesCopied); + + ASSERT (BytesCopied == LookaheadBufferSize); + + LookaheadBuffer = DeviceContext->LookaheadContiguous; + + } + + + // + // Now determine which link to loop it back to; + // UI frames are not indicated on any link. + // + + SendPacketTag = (PSEND_PACKET_TAG)NdisPacket->ProtocolReserved; + + // + // Hold DeviceContext->LinkSpinLock until we get a + // reference. + // + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + switch (SendPacketTag->LoopbackLinkIndex) { + + case LOOPBACK_TO_CONNECTOR: + + Link = DeviceContext->LoopbackLinks[CONNECTOR_LINK]; + break; + + case LOOPBACK_TO_LISTENER: + + Link = DeviceContext->LoopbackLinks[LISTENER_LINK]; + break; + + case LOOPBACK_UI_FRAME: + default: + + Link = (PTP_LINK)NULL; + break; + + } + + // + // For non-null links, we have to reference them. + // We use LREF_TREE since that is what + // NbfGeneralReceiveHandler expects. + // + + if (Link != (PTP_LINK)NULL) { + NbfReferenceLink("loopback indication", Link, LREF_TREE); + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + MacReturnHeaderLength( + &DeviceContext->MacInfo, + LookaheadBuffer, + &HeaderLength); + + DeviceContext->LoopbackHeaderLength = HeaderLength; + + // + // Process the receive like any other. We don't have to + // worry about frame padding since we construct the + // frame ourselves. + // + + NbfGeneralReceiveHandler( + DeviceContext, + (NDIS_HANDLE)NdisPacket, + &DeviceContext->LocalAddress, // since it is loopback + Link, + LookaheadBuffer, // header + PacketSize - HeaderLength, // total packet size + (PDLC_FRAME)((PUCHAR)LookaheadBuffer + HeaderLength), // l/a data + LookaheadBufferSize - HeaderLength, // lookahead data length + TRUE + ); + + + // + // Now complete the send. + // + + NbfSendCompletionHandler( + DeviceContext->NdisBindingHandle, + NdisPacket, + NDIS_STATUS_SUCCESS + ); + + + ACQUIRE_DPC_SPIN_LOCK(&DeviceContext->LoopbackSpinLock); + + if (!IsListEmpty(&DeviceContext->LoopbackQueue)) { + + KeInsertQueueDpc(&DeviceContext->LoopbackDpc, NULL, NULL); + + // + // Remove these two lines if it is decided thet + // NbfReceiveComplete should be called after every + // loopback indication. + // + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LoopbackSpinLock); + return; + + } else { + + DeviceContext->LoopbackInProgress = FALSE; + + } + + } else { + + // + // This shouldn't happen! + // + + DeviceContext->LoopbackInProgress = FALSE; + +#if DBG + NbfPrint1("Loopback queue empty for device context %x\n", DeviceContext); +#endif + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LoopbackSpinLock); + + NbfReceiveComplete( + (NDIS_HANDLE)DeviceContext + ); + +} /* NbfProcessLoopbackQueue */ + + +NDIS_STATUS +NbfReceiveIndication ( + IN NDIS_HANDLE BindingContext, + IN NDIS_HANDLE ReceiveContext, + IN PVOID HeaderBuffer, + IN UINT HeaderBufferSize, + IN PVOID LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT PacketSize + ) + +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that a frame has been received on the physical link. + This routine is time critical, so we only allocate a + buffer and copy the packet into it. We also perform minimal + validation on this packet. It gets queued to the device context + to allow for processing later. + +Arguments: + + BindingContext - The Adapter Binding specified at initialization time. + + ReceiveContext - A magic cookie for the MAC. + + HeaderBuffer - pointer to a buffer containing the packet header. + + HeaderBufferSize - the size of the header. + + LookaheadBuffer - pointer to a buffer containing the negotiated minimum + amount of buffer I get to look at (not including header). + + LookaheadBufferSize - the size of the above. May be less than asked + for, if that's all there is. + + PacketSize - Overall size of the packet (not including header). + +Return Value: + + NDIS_STATUS - status of operation, one of: + + NDIS_STATUS_SUCCESS if packet accepted, + NDIS_STATUS_NOT_RECOGNIZED if not recognized by protocol, + NDIS_any_other_thing if I understand, but can't handle. + +--*/ +{ + PDEVICE_CONTEXT DeviceContext; + KIRQL oldirql; + PTP_LINK Link; + HARDWARE_ADDRESS SourceAddressBuffer; + PHARDWARE_ADDRESS SourceAddress; + UINT RealPacketSize; + PDLC_FRAME DlcHeader; + BOOLEAN Multicast; + + ENTER_NBF; + + IF_NBFDBG (NBF_DEBUG_NDIS) { + PUCHAR p; + SHORT i; + NbfPrint2 ("NbfReceiveIndication: Packet, Size: 0x0%lx LookaheadSize: 0x0%lx\n 00:", + PacketSize, LookaheadBufferSize); + p = (PUCHAR)LookaheadBuffer; + for (i=0;i<25;i++) { + NbfPrint1 (" %2x",p[i]); + } + NbfPrint0 ("\n"); + } + + DeviceContext = (PDEVICE_CONTEXT)BindingContext; + + RealPacketSize = 0; + + // + // Obtain the packet length; this may optionally adjust + // the lookahead buffer forward if the header we wish + // to remove spills over into what the MAC considers + // data. If it determines that the header is not + // valid, it keeps RealPacketSize at 0. + // + + MacReturnPacketLength( + &DeviceContext->MacInfo, + HeaderBuffer, + HeaderBufferSize, + PacketSize, + &RealPacketSize, + &LookaheadBuffer, + &LookaheadBufferSize + ); + + if (RealPacketSize < 2) { + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint1 ("NbfReceiveIndication: Discarding packet, bad length %lx\n", + HeaderBuffer); + } + return NDIS_STATUS_NOT_RECOGNIZED; + } + + // + // We've negotiated at least a contiguous DLC header passed back in the + // lookahead buffer. Check it to see if we want this packet. + // + + DlcHeader = (PDLC_FRAME)LookaheadBuffer; + + if (((*(USHORT UNALIGNED *)(&DlcHeader->Dsap)) & + (USHORT)((DLC_SSAP_MASK << 8) | DLC_DSAP_MASK)) != + (USHORT)((DSAP_NETBIOS_OVER_LLC << 8) | DSAP_NETBIOS_OVER_LLC)) { + + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint1 ("NbfReceiveIndication: Discarding lookahead data, not NetBIOS: %lx\n", + LookaheadBuffer); + } + LEAVE_NBF; + return NDIS_STATUS_NOT_RECOGNIZED; // packet was processed. + } + + + // + // Check that the packet is not too long. + // + + if (PacketSize > DeviceContext->MaxReceivePacketSize) { +#if DBG + NbfPrint2("NbfReceiveIndication: Ignoring packet length %d, max %d\n", + PacketSize, DeviceContext->MaxReceivePacketSize); +#endif + return NDIS_STATUS_NOT_RECOGNIZED; + } + + MacReturnSourceAddress( + &DeviceContext->MacInfo, + HeaderBuffer, + &SourceAddressBuffer, + &SourceAddress, + &Multicast + ); + + // + // Record how many multicast packets we get, to monitor + // general network activity. + // + + if (Multicast) { + ++DeviceContext->MulticastPacketCount; + } + + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + + // + // Unless this is a UI frame find the Link this packet belongs to. + // If there is not a recognized link, pass the frame on to be handled + // by the receive complete code. + // + + if ((((PDLC_U_FRAME)LookaheadBuffer)->Command) != DLC_CMD_UI) { + + // This adds a link reference if it is found + + Link = NbfFindLink (DeviceContext, SourceAddress->Address); + + if (Link != NULL) { + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 ("NbfReceiveIndication: Found link, Link: %lx\n", Link); + } + + } + + } else { + + Link = NULL; + + } + + + NbfGeneralReceiveHandler( + DeviceContext, + ReceiveContext, + SourceAddress, + Link, + HeaderBuffer, // header + RealPacketSize, // total data length in packet + (PDLC_FRAME)LookaheadBuffer, // lookahead data + LookaheadBufferSize, // lookahead data length + FALSE // not loopback + ); + + KeLowerIrql (oldirql); + + return STATUS_SUCCESS; + +} /* NbfReceiveIndication */ + + +VOID +NbfGeneralReceiveHandler ( + IN PDEVICE_CONTEXT DeviceContext, + IN NDIS_HANDLE ReceiveContext, + IN PHARDWARE_ADDRESS SourceAddress, + IN PTP_LINK Link, + IN PVOID HeaderBuffer, + IN UINT PacketSize, + IN PDLC_FRAME DlcHeader, + IN UINT DlcSize, + IN BOOLEAN Loopback + ) + +/*++ + +Routine Description: + + This routine receives control from either NbfReceiveIndication + or NbfProcessLoopbackQueue. It continues the processing of + indicated data once the link has been determined. + + This routine is time critical, so we only allocate a + buffer and copy the packet into it. We also perform minimal + validation on this packet. It gets queued to the device context + to allow for processing later. + +Arguments: + + DeviceContext - The device context of this adapter. + + ReceiveContext - A magic cookie for the MAC. + + SourceAddress - The source address of the packet. + + Link - The link that this packet was received on, may be NULL + if the link was not found. If not NULL, Link will have + a reference of type LREF_TREE. + + HeaderBuffer - pointer to the packet header. + + PacketSize - Overall size of the packet (not including header). + + DlcHeader - Points to the DLC header of the packet. + + DlcSize - The length of the packet indicated, starting from DlcHeader. + + Loopback - TRUE if this was called by NbfProcessLoopbackQueue; + used to determine whether to call NdisTransferData or + NbfTransferLoopbackData. + +Return Value: + + None. + +--*/ +{ + + PNDIS_PACKET NdisPacket; + NTSTATUS Status; + PNDIS_BUFFER NdisBuffer; + NDIS_STATUS NdisStatus; + PSINGLE_LIST_ENTRY linkage; + UINT BytesTransferred; + BOOLEAN Command; + BOOLEAN PollFinal; + PRECEIVE_PACKET_TAG ReceiveTag; + PBUFFER_TAG BufferTag; + PUCHAR SourceRouting; + UINT SourceRoutingLength; + PDLC_I_FRAME IHeader; + PDLC_U_FRAME UHeader; + PDLC_S_FRAME SHeader; + PTP_ADDRESS DatagramAddress; + UINT NdisBufferLength; + PVOID BufferPointer; + + ENTER_NBF; + + + INCREMENT_COUNTER (DeviceContext, PacketsReceived); + + Command = (BOOLEAN)!(DlcHeader->Ssap & DLC_SSAP_RESPONSE); + + if (Link == (PTP_LINK)NULL) { + UHeader = (PDLC_U_FRAME)DlcHeader; + if (((UHeader->Command & ~DLC_U_PF) == DLC_CMD_UI) && Command) { + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfGeneralReceiveHandler: Processing packet as UI frame.\n"); + } + + MacReturnSourceRouting( + &DeviceContext->MacInfo, + HeaderBuffer, + &SourceRouting, + &SourceRoutingLength); + + if (SourceRoutingLength > MAX_SOURCE_ROUTING) { + Status = STATUS_ABANDONED; + } + else { + Status = NbfProcessUi ( + DeviceContext, + SourceAddress, + HeaderBuffer, + (PUCHAR)UHeader, + DlcSize, + SourceRouting, + SourceRoutingLength, + &DatagramAddress); + } + } else { + + // + // or drop on the floor. (BUGBUG: Note that state tables say that + // we'll always handle a DM with a DM response. This should change.) + // + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfReceiveIndication: it's not a UI frame!\n"); + } + Status = STATUS_SUCCESS; + } + + if (Status != STATUS_MORE_PROCESSING_REQUIRED) { + + LEAVE_NBF; + return; + + } else if (((PNBF_HDR_CONNECTIONLESS)((PUCHAR)UHeader + 3))->Command == + NBF_CMD_STATUS_RESPONSE) { + + (VOID)NbfProcessStatusResponse( + DeviceContext, + ReceiveContext, + (PNBF_HDR_CONNECTIONLESS)((PUCHAR)UHeader + 3), + SourceAddress, + SourceRouting, + SourceRoutingLength); + return; + + } else { + goto HandleAtComplete; // only datagrams will get through this + } + } + + + // + // At this point we have a link reference count of type LREF_TREE + // + + ++Link->PacketsReceived; + + // + // deal with I-frames first; they are what we expect the most of... + // + + if (!(DlcHeader->Byte1 & DLC_I_INDICATOR)) { + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 ("NbfReceiveIndication: I-frame encountered.\n"); + } + if (DlcSize >= 4 + sizeof (NBF_HDR_CONNECTION)) { + IHeader = (PDLC_I_FRAME)DlcHeader; + NbfProcessIIndicate ( + Command, + (BOOLEAN)(IHeader->RcvSeq & DLC_I_PF), + Link, + (PUCHAR)DlcHeader, + DlcSize, + PacketSize, + ReceiveContext, + Loopback); + } else { +#if DBG +// IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 ("NbfReceiveIndication: Runt I-frame, discarded!\n"); +// } +#endif + ; + } + + } else if (((DlcHeader->Byte1 & DLC_U_INDICATOR) == DLC_U_INDICATOR)) { + + // + // different case switches for S and U frames, because structures + // are different. + // + + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint0 ("NbfReceiveIndication: U-frame encountered.\n"); + } + + UHeader = (PDLC_U_FRAME)DlcHeader; + PollFinal = (BOOLEAN)(UHeader->Command & DLC_U_PF); + switch (UHeader->Command & ~DLC_U_PF) { + + case DLC_CMD_SABME: + NbfProcessSabme (Command, PollFinal, Link, UHeader, + HeaderBuffer, SourceAddress, DeviceContext); + break; + + case DLC_CMD_DISC: + NbfProcessDisc (Command, PollFinal, Link, UHeader); + break; + + case DLC_CMD_UA: + NbfProcessUa (Command, PollFinal, Link, UHeader); + break; + + case DLC_CMD_DM: + NbfProcessDm (Command, PollFinal, Link, UHeader); + break; + + case DLC_CMD_FRMR: + NbfProcessFrmr (Command, PollFinal, Link, UHeader); + break; + + case DLC_CMD_UI: + + ASSERT (FALSE); + break; + + case DLC_CMD_XID: + PANIC ("ReceiveIndication: XID!\n"); + NbfProcessXid (Command, PollFinal, Link, UHeader); + break; + + case DLC_CMD_TEST: + PANIC ("NbfReceiveIndication: TEST!\n"); + NbfProcessTest (Command, PollFinal, Link, UHeader); + break; + + default: + PANIC ("NbfReceiveIndication: bad U-frame, packet dropped.\n"); + + } /* switch */ + + } else { + + // + // We have an S-frame. + // + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 ("NbfReceiveIndication: S-frame encountered.\n"); + } + SHeader = (PDLC_S_FRAME)DlcHeader; + PollFinal = (BOOLEAN)(SHeader->RcvSeq & DLC_S_PF); + switch (SHeader->Command) { + + case DLC_CMD_RR: + case DLC_CMD_RNR: + case DLC_CMD_REJ: + NbfProcessSFrame (Command, PollFinal, Link, SHeader, SHeader->Command); + break; + + default: + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfReceiveIndication: bad S-frame.\n"); + } + + } /* switch */ + + } // if U-frame or S-frame + + // + // If we reach here, the packet has been processed. If it needs + // to be copied, we will jump to HandleAtComplete. + // + + NbfDereferenceLinkMacro ("Done with Indicate frame", Link, LREF_TREE); + LEAVE_NBF; + return; + +HandleAtComplete:; + + // + // At this point we DO NOT have any link references added in + // this function. + // + + linkage = ExInterlockedPopEntryList( + &DeviceContext->ReceivePacketPool, + &DeviceContext->Interlock); + + if (linkage != NULL) { + NdisPacket = CONTAINING_RECORD( linkage, NDIS_PACKET, ProtocolReserved[0] ); + } else { + // PANIC ("NbfReceiveIndicate: Discarding Packet, no receive packets.\n"); + DeviceContext->ReceivePacketExhausted++; + LEAVE_NBF; + return; + } + ReceiveTag = (PRECEIVE_PACKET_TAG)(NdisPacket->ProtocolReserved); + + linkage = ExInterlockedPopEntryList( + &DeviceContext->ReceiveBufferPool, + &DeviceContext->Interlock); + + if (linkage != NULL) { + BufferTag = CONTAINING_RECORD( linkage, BUFFER_TAG, Linkage); + } else { + ExInterlockedPushEntryList( + &DeviceContext->ReceivePacketPool, + &ReceiveTag->Linkage, + &DeviceContext->Interlock); + // PANIC ("NbfReceiveIndicate: Discarding Packet, no receive buffers.\n"); + DeviceContext->ReceiveBufferExhausted++; + LEAVE_NBF; + return; + } + + NdisAdjustBufferLength (BufferTag->NdisBuffer, PacketSize); + NdisChainBufferAtFront (NdisPacket, BufferTag->NdisBuffer); + + // + // DatagramAddress has a reference of type AREF_PROCESS_DATAGRAM, + // unless this is a datagram intended for RAS only, in which case + // it is NULL. + // + + BufferTag->Address = DatagramAddress; + + // + // set up async return status so we can tell when it has happened; + // can never get return of NDIS_STATUS_PENDING in synch completion routine + // for NdisTransferData, so we know it has completed when this status + // changes + // + + BufferTag->NdisStatus = NDIS_STATUS_PENDING; + ReceiveTag->PacketType = TYPE_AT_COMPLETE; + + ExInterlockedInsertTailList( + &DeviceContext->ReceiveInProgress, + &BufferTag->Linkage, + &DeviceContext->SpinLock); + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 ("NbfReceiveIndicate: Packet on Queue: %lx\n",NdisPacket); + } + + // + // receive packet is mapped at initalize + // + + // + // Determine how to handle the data transfer. + // + + if (Loopback) { + + NbfTransferLoopbackData( + &NdisStatus, + DeviceContext, + ReceiveContext, + DeviceContext->MacInfo.TransferDataOffset, + PacketSize, + NdisPacket, + &BytesTransferred + ); + + } else { + + NdisTransferData ( + &NdisStatus, + DeviceContext->NdisBindingHandle, + ReceiveContext, + DeviceContext->MacInfo.TransferDataOffset, + PacketSize, + NdisPacket, + &BytesTransferred); + + } + + // + // handle the various error codes + // + + switch (NdisStatus) { + case NDIS_STATUS_SUCCESS: // received packet + BufferTag->NdisStatus = NDIS_STATUS_SUCCESS; + + if (BytesTransferred == PacketSize) { // Did we get the entire packet? + ReceiveTag->PacketType = TYPE_AT_INDICATE; + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + ExInterlockedPushEntryList( + &DeviceContext->ReceivePacketPool, + &ReceiveTag->Linkage, + &DeviceContext->Interlock); + LEAVE_NBF; + return; + } + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint2 ("NbfReceiveIndicate: Discarding Packet, Partial transfer: 0x0%lx of 0x0%lx transferred\n", + BytesTransferred, PacketSize); + } + break; + + case NDIS_STATUS_PENDING: // waiting async complete from NdisTransferData + LEAVE_NBF; + return; + break; + + default: // something broke; certainly we'll never get NdisTransferData + // asynch completion with this error status... + break; + } + + // + // receive failed, for some reason; cleanup and fail return + // BUGBUG: Statistics go here + // + +#if DBG + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 ("NbfReceiveIndicate: Discarding Packet, transfer failed: %s\n", + NbfGetNdisStatus (NdisStatus)); + } +#endif + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + RemoveEntryList (&BufferTag->Linkage); + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + ReceiveTag->PacketType = TYPE_AT_INDICATE; + + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + ExInterlockedPushEntryList( + &DeviceContext->ReceivePacketPool, + &ReceiveTag->Linkage, + &DeviceContext->Interlock); + + NdisQueryBuffer (NdisBuffer, &BufferPointer, &NdisBufferLength); + BufferTag = CONTAINING_RECORD ( + BufferPointer, + BUFFER_TAG, + Buffer[0] + ); + NdisAdjustBufferLength (NdisBuffer, BufferTag->Length); // reset to good value + + ExInterlockedPushEntryList( + &DeviceContext->ReceiveBufferPool, + (PSINGLE_LIST_ENTRY)&BufferTag->Linkage, + &DeviceContext->Interlock); + + if (DatagramAddress) { + NbfDereferenceAddress ("DG TransferData failed", DatagramAddress, AREF_PROCESS_DATAGRAM); + } + + LEAVE_NBF; + return; + +} /* NbfGeneralReceiveHandler */ + + + +VOID +NbfTransferDataComplete ( + IN NDIS_HANDLE BindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus, + IN UINT BytesTransferred + ) + +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that an NdisTransferData has completed. We use this indication + to start stripping buffers from the receive queue. + +Arguments: + + BindingContext - The Adapter Binding specified at initialization time. + + NdisPacket/RequestHandle - An identifier for the request that completed. + + NdisStatus - The completion status for the request. + + BytesTransferred - Number of bytes actually transferred. + + +Return Value: + + None. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext = (PDEVICE_CONTEXT)BindingContext; + PRECEIVE_PACKET_TAG ReceiveTag; + PTP_CONNECTION Connection; + KIRQL oldirql1; + PTP_REQUEST Request; + PNDIS_BUFFER NdisBuffer; + UINT NdisBufferLength; + PVOID BufferPointer; + PBUFFER_TAG BufferTag; + + // + // Put the NDIS status into a place we can use in packet processing. + // Note that this complete indication may be occuring during the call + // to NdisTransferData in the receive indication. + // + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint2 (" NbfTransferDataComplete: Entered, Packet: %lx bytes transferred: 0x0%x\n", + NdisPacket, BytesTransferred); + } + ReceiveTag = (PRECEIVE_PACKET_TAG)(NdisPacket->ProtocolReserved); + + // + // note that the processing below depends on having only one packet + // transfer outstanding at a time. NDIS is supposed to guarentee this. + // + + switch (ReceiveTag->PacketType) { + + case TYPE_AT_COMPLETE: // datagrams + + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + NdisQueryBuffer (NdisBuffer, &BufferPointer, &NdisBufferLength); + BufferTag = CONTAINING_RECORD( BufferPointer, BUFFER_TAG, Buffer[0]); + BufferTag->NdisStatus = NdisStatus; + + ReceiveTag->PacketType = TYPE_AT_INDICATE; + + ExInterlockedPushEntryList( + &DeviceContext->ReceivePacketPool, + &ReceiveTag->Linkage, + &DeviceContext->Interlock); + + break; + + case TYPE_AT_INDICATE: // I-frames + + // + // The transfer for this packet is complete. Was it successful?? + // + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql1); + + Connection = ReceiveTag->Connection; + + // + // rip all of the NDIS_BUFFERs we've used off the chain and return them. + // + + if (ReceiveTag->AllocatedNdisBuffer) { + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + while (NdisBuffer != NULL) { + NdisFreeBuffer (NdisBuffer); + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + } + } else { + NdisReinitializePacket (NdisPacket); + } + + + if ((NdisStatus != NDIS_STATUS_SUCCESS) || + (!DeviceContext->MacInfo.SingleReceive)) { + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + + ULONG DumpData[2]; + DumpData[0] = BytesTransferred; + DumpData[1] = ReceiveTag->BytesToTransfer; + + NbfWriteGeneralErrorLog( + DeviceContext, + EVENT_TRANSPORT_TRANSFER_DATA, + 603, + NdisStatus, + NULL, + 2, + DumpData); + + // Drop the packet. +#if DBG + NbfPrint1 ("NbfTransferDataComplete: status %s\n", + NbfGetNdisStatus (NdisStatus)); +#endif + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + Connection->Flags |= CONNECTION_FLAGS_TRANSFER_FAIL; + + } else { + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + } + + Connection->TransferBytesPending -= ReceiveTag->BytesToTransfer; + + if ((Connection->TransferBytesPending == 0) && + (Connection->Flags & CONNECTION_FLAGS_TRANSFER_FAIL)) { + + Connection->CurrentReceiveMdl = Connection->SavedCurrentReceiveMdl; + Connection->ReceiveByteOffset = Connection->SavedReceiveByteOffset; + Connection->MessageBytesReceived -= Connection->TotalTransferBytesPending; + Connection->Flags &= ~CONNECTION_FLAGS_TRANSFER_FAIL; + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + if ((Connection->Flags & CONNECTION_FLAGS_VERSION2) == 0) { + NbfSendNoReceive (Connection); + } + NbfSendReceiveOutstanding (Connection); + + ReceiveTag->CompleteReceive = FALSE; + + } else { + + // + // BUGBUG: If we have more data outstanding, this can't + // be the last piece; i.e. we can't handle having + // the last piece complete asynchronously before + // an earlier piece. + // +#if DBG + if (Connection->TransferBytesPending > 0) { + ASSERT (!ReceiveTag->CompleteReceive); + } +#endif + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + } + + if (!Connection->CurrentReceiveSynchronous) { + NbfDereferenceReceiveIrp ("TransferData complete", IoGetCurrentIrpStackLocation(Connection->CurrentReceiveIrp), RREF_RECEIVE); + } + + + // + // dereference the connection to say we've done the I frame processing. + // This reference was done before calling NdisTransferData. + // + + if (ReceiveTag->TransferDataPended) { + NbfDereferenceConnection("TransferData done", Connection, CREF_TRANSFER_DATA); + } + + + } else { + + ASSERT (NdisStatus == STATUS_SUCCESS); + ASSERT (!ReceiveTag->TransferDataPended); + ASSERT (Connection->CurrentReceiveSynchronous); + + if (!Connection->SpecialReceiveIrp) { + Connection->CurrentReceiveIrp->IoStatus.Information += BytesTransferred; + } + + } + + + // + // see if we've completed the current receive. If so, move to the next one. + // + + if (ReceiveTag->CompleteReceive) { + CompleteReceive (Connection, ReceiveTag->EndOfMessage, (ULONG)BytesTransferred); + } + + ExInterlockedPushEntryList( + &DeviceContext->ReceivePacketPool, + &ReceiveTag->Linkage, + &DeviceContext->Interlock); + + KeLowerIrql (oldirql1); + + break; + + case TYPE_STATUS_RESPONSE: // response to remote adapter status + + // + // BUGBUG: Handle failure. + // + +#if DBG + if (NdisStatus != NDIS_STATUS_SUCCESS) { + DbgPrint ("NBF: STATUS_RESPONSE TransferData failed\n"); + } +#endif + + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + ASSERT (NdisBuffer); + NdisFreeBuffer (NdisBuffer); + + Request = (PTP_REQUEST)ReceiveTag->Connection; + + if (ReceiveTag->CompleteReceive) { + NbfCompleteRequest( + Request, + ReceiveTag->EndOfMessage ? STATUS_SUCCESS : STATUS_BUFFER_OVERFLOW, + Request->BytesWritten); + } + + NbfDereferenceRequest("Status xfer done", Request, RREF_STATUS); + + ReceiveTag->PacketType = TYPE_AT_INDICATE; + + ExInterlockedPushEntryList( + &DeviceContext->ReceivePacketPool, + &ReceiveTag->Linkage, + &DeviceContext->Interlock); + + break; + + default: +#if DBG + NbfPrint1 ("NbfTransferDataComplete: Bang! Packet Transfer failed, unknown packet type: %ld\n", + ReceiveTag->PacketType); + DbgBreakPoint (); +#endif + break; + } + + // + // BUGBUG: Statistics need to be kept here. + // + + return; + +} // NbfTransferDataComplete + + + +VOID +NbfReceiveComplete ( + IN NDIS_HANDLE BindingContext + ) + +/*++ + +Routine Description: + + This routine receives control from the physical provider as an + indication that a connection(less) frame has been received on the + physical link. We dispatch to the correct packet handler here. + +Arguments: + + BindingContext - The Adapter Binding specified at initialization time. + Nbf uses the DeviceContext for this parameter. + +Return Value: + + None + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + UINT i; + NTSTATUS Status; + KIRQL oldirql2; + BOOLEAN Command; + PDLC_U_FRAME UHeader; + PDLC_FRAME DlcHeader; + PLIST_ENTRY linkage; + UINT NdisBufferLength; + PVOID BufferPointer; + PBUFFER_TAG BufferTag; + PTP_ADDRESS Address; + PIRP Irp; + PIO_STACK_LOCATION IrpSp; + PTP_CONNECTION Connection; + PTP_LINK Link; + + ENTER_NBF; + + // + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfReceiveComplete: Entered.\n"); + } + + DeviceContext = (PDEVICE_CONTEXT) BindingContext; + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql2); + + // + // Complete all pending receives. Do a quick check + // without the lock. + // + + while (!IsListEmpty (&DeviceContext->IrpCompletionQueue)) { + + linkage = ExInterlockedRemoveHeadList( + &DeviceContext->IrpCompletionQueue, + &DeviceContext->Interlock); + + if (linkage != NULL) { + + Irp = CONTAINING_RECORD (linkage, IRP, Tail.Overlay.ListEntry); + IrpSp = IoGetCurrentIrpStackLocation (Irp); + + Connection = IRP_RECEIVE_CONNECTION(IrpSp); + + if (Connection == NULL) { +#if DBG + DbgPrint ("Connection of Irp %lx is NULL\n", Irp); + DbgBreakPoint(); +#endif + } + + IRP_RECEIVE_REFCOUNT(IrpSp) = 0; + IRP_RECEIVE_IRP (IrpSp) = NULL; + + LEAVE_NBF; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + ENTER_NBF; + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + if (Connection->Flags & CONNECTION_FLAGS_RC_PENDING) { + + Connection->Flags &= ~CONNECTION_FLAGS_RC_PENDING; + + if (Connection->Flags & CONNECTION_FLAGS_PEND_INDICATE) { + + Connection->Flags &= ~CONNECTION_FLAGS_PEND_INDICATE; + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + // + // We got an indicate (and sent a NO_RECEIVE) while + // this was in progress, so send the receive outstanding + // now. + // + + NbfSendReceiveOutstanding (Connection); + + } else { + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + } + + } else { + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + } + + NbfDereferenceConnectionMacro ("receive completed", Connection, CREF_RECEIVE_IRP); + + } else { + + // + // ExInterlockedRemoveHeadList returned NULL, so don't + // bother looping back. + // + + break; + + } + + } + + + // + // Packetize all waiting connections + // + + if (!IsListEmpty(&DeviceContext->PacketizeQueue)) { + + PacketizeConnections (DeviceContext); + + } + + if (!IsListEmpty (&DeviceContext->DeferredRrQueue)) { + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->Interlock); + + while (!IsListEmpty(&DeviceContext->DeferredRrQueue)) { + + linkage = RemoveHeadList (&DeviceContext->DeferredRrQueue); + + Link = CONTAINING_RECORD (linkage, TP_LINK, DeferredRrLinkage); + + Link->OnDeferredRrQueue = FALSE; + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->Interlock); + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + StopT2 (Link); // we're acking, so no delay req'd. + NbfSendRr (Link, FALSE, FALSE); // releases lock + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->Interlock); + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->Interlock); + + } + + + // + // Get every waiting packet, in order... + // + + + if (!IsListEmpty (&DeviceContext->ReceiveInProgress)) { + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + while (!IsListEmpty (&DeviceContext->ReceiveInProgress)) { + + linkage = RemoveHeadList (&DeviceContext->ReceiveInProgress); + BufferTag = CONTAINING_RECORD( linkage, BUFFER_TAG, Linkage); + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 (" NbfReceiveComplete: Processing Buffer %lx\n", BufferTag); + } + + // + // NdisTransferData may have failed at async completion; check and + // see. If it did, then we discard this packet. If we're still waiting + // for transfer to complete, go back to sleep and hope (no guarantee!) + // we get waken up later. + // + +#if DBG + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 (" NbfReceiveComplete: NdisStatus: %s.\n", + NbfGetNdisStatus(BufferTag->NdisStatus)); + } +#endif + if (BufferTag->NdisStatus == NDIS_STATUS_PENDING) { + InsertHeadList (&DeviceContext->ReceiveInProgress, linkage); + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfReceiveComplete: Status pending, returning packet to queue.\n"); + } + KeLowerIrql (oldirql2); + LEAVE_NBF; + return; + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + if (BufferTag->NdisStatus != NDIS_STATUS_SUCCESS) { +#if DBG + NbfPrint1 (" NbfReceiveComplete: Failed return from Transfer data, reason: %s\n", + NbfGetNdisStatus (BufferTag->NdisStatus)); +#endif + // BUGBUG: some statistics go here for lost packet + + goto FreeBuffer; // skip the buffer, continue with while loop + } + + // + // Have a buffer. Since I allocated the storage for it, I know it's + // virtually contiguous and can treat it that way, which I will + // henceforth. + // + + NdisQueryBuffer (BufferTag->NdisBuffer, &BufferPointer, &NdisBufferLength); + + IF_NBFDBG (NBF_DEBUG_DLC) { + PUCHAR pc; + NbfPrint0 (" NbfRC Packet: \n "); + pc = (PUCHAR)BufferPointer; + for (i=0;i<25;i++) { + NbfPrint1 (" %2x",pc[i]); + } + NbfPrint0 ("\n"); + } + + // + // Determine what address this is for, which is stored + // in the buffer tag header. + // + + Address = BufferTag->Address; + + // + // Process the frame as a UI frame; only datagrams should + // be processed here. If Address is NULL then this datagram + // is not needed for any bound address and should be given + // to RAS only. + // + + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfReceiveComplete: Delivering this frame manually.\n"); + } + DlcHeader = (PDLC_FRAME)BufferPointer; + Command = (BOOLEAN)!(DlcHeader->Ssap & DLC_SSAP_RESPONSE); + UHeader = (PDLC_U_FRAME)DlcHeader; + + BufferPointer = (PUCHAR)BufferPointer + 3; + NdisBufferLength -= 3; // 3 less bytes. + + if (Address != NULL) { + + // + // Indicate it or complete posted datagrams. + // + + Status = NbfIndicateDatagram ( + DeviceContext, + Address, + BufferPointer, // the Dsdu, with some tweaking + NdisBufferLength); + + // + // Dereference the address. + // + + NbfDereferenceAddress ("Datagram done", Address, AREF_PROCESS_DATAGRAM); + + } + + // + // Let the RAS clients have a crack at this if they want + // (they only want directed datagrams, not broadcast). + // + + if ((((PNBF_HDR_CONNECTIONLESS)BufferPointer)->Command == NBF_CMD_DATAGRAM) && + (DeviceContext->IndicationQueuesInUse)) { + + NbfActionDatagramIndication( + DeviceContext, + (PNBF_HDR_CONNECTIONLESS)BufferPointer, + NdisBufferLength); + + } + + BufferPointer = (PUCHAR)BufferPointer - 3; + NdisBufferLength += 3; // 3 more bytes. + + // + // Finished with buffer; return to pool. + // + +FreeBuffer:; + + NdisAdjustBufferLength (BufferTag->NdisBuffer, BufferTag->Length); + ExInterlockedPushEntryList( + &DeviceContext->ReceiveBufferPool, + (PSINGLE_LIST_ENTRY)&BufferTag->Linkage, + &DeviceContext->Interlock); + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + } // if queue not empty + + KeLowerIrql (oldirql2); + LEAVE_NBF; + return; + +} /* NbfReceiveComplete */ + + +VOID +NbfTransferLoopbackData ( + OUT PNDIS_STATUS NdisStatus, + IN PDEVICE_CONTEXT DeviceContext, + IN NDIS_HANDLE ReceiveContext, + IN UINT ByteOffset, + IN UINT BytesToTransfer, + IN PNDIS_PACKET Packet, + OUT PUINT BytesTransferred + ) + +/*++ + +Routine Description: + + This routine is called instead of NdisTransferData for + loopback indications. It copies the data from the + source packet to the receive packet. + +Arguments: + + NdisStatus - Returns the status of the operation. + + DeviceContext - The device context. + + ReceiveContext - A pointer to the source packet. + + ByteOffset - The offset from the start of the source packet + that the transfer should begin at. + + BytesToTransfer - The number of bytes to transfer. + + Packet - A pointer to the receive packet. + + BytesTransferred - Returns the number of bytes copied. + +Return Value: + + None. + +--*/ + +{ + PNDIS_PACKET SourcePacket = (PNDIS_PACKET)ReceiveContext; + + NdisCopyFromPacketToPacket( + Packet, + 0, + BytesToTransfer, + SourcePacket, + DeviceContext->LoopbackHeaderLength + ByteOffset, + BytesTransferred + ); + + *NdisStatus = NDIS_STATUS_SUCCESS; // BUGBUG: what if BytesTransferred is too small + +} + + +VOID +NbfCopyFromPacketToBuffer( + IN PNDIS_PACKET Packet, + IN UINT Offset, + IN UINT BytesToCopy, + OUT PCHAR Buffer, + OUT PUINT BytesCopied + ) + +/*++ + +Routine Description: + + Copy from an ndis packet into a buffer. + +Arguments: + + Packet - The packet to copy from. + + Offset - The offset from which to start the copy. + + BytesToCopy - The number of bytes to copy from the packet. + + Buffer - The destination of the copy. + + BytesCopied - The number of bytes actually copied. Can be less then + BytesToCopy if the packet is shorter than BytesToCopy. + +Return Value: + + None + +--*/ + +{ + + // + // Holds the number of ndis buffers comprising the packet. + // + UINT NdisBufferCount; + + // + // Points to the buffer from which we are extracting data. + // + PNDIS_BUFFER CurrentBuffer; + + // + // Holds the virtual address of the current buffer. + // + PVOID VirtualAddress; + + // + // Holds the length of the current buffer of the packet. + // + UINT CurrentLength; + + // + // Keep a local variable of BytesCopied so we aren't referencing + // through a pointer. + // + UINT LocalBytesCopied = 0; + + // + // Take care of boundary condition of zero length copy. + // + + *BytesCopied = 0; + if (!BytesToCopy) return; + + // + // Get the first buffer. + // + + NdisQueryPacket( + Packet, + NULL, + &NdisBufferCount, + &CurrentBuffer, + NULL + ); + + // + // Could have a null packet. + // + + if (!NdisBufferCount) return; + + NdisQueryBuffer( + CurrentBuffer, + &VirtualAddress, + &CurrentLength + ); + + while (LocalBytesCopied < BytesToCopy) { + + if (!CurrentLength) { + + NdisGetNextBuffer( + CurrentBuffer, + &CurrentBuffer + ); + + // + // We've reached the end of the packet. We return + // with what we've done so far. (Which must be shorter + // than requested. + // + + if (!CurrentBuffer) break; + + NdisQueryBuffer( + CurrentBuffer, + &VirtualAddress, + &CurrentLength + ); + continue; + + } + + // + // Try to get us up to the point to start the copy. + // + + if (Offset) { + + if (Offset > CurrentLength) { + + // + // What we want isn't in this buffer. + // + + Offset -= CurrentLength; + CurrentLength = 0; + continue; + + } else { + + VirtualAddress = (PCHAR)VirtualAddress + Offset; + CurrentLength -= Offset; + Offset = 0; + + } + + } + + // + // Copy the data. + // + + + { + + // + // Holds the amount of data to move. + // + UINT AmountToMove; + + AmountToMove = + ((CurrentLength <= (BytesToCopy - LocalBytesCopied))? + (CurrentLength):(BytesToCopy - LocalBytesCopied)); + + RtlCopyMemory( + Buffer, + VirtualAddress, + AmountToMove + ); + + Buffer = (PCHAR)Buffer + AmountToMove; + VirtualAddress = (PCHAR)VirtualAddress + AmountToMove; + + LocalBytesCopied += AmountToMove; + CurrentLength -= AmountToMove; + + } + + } + + *BytesCopied = LocalBytesCopied; + +} + diff --git a/private/ntos/tdi/nbf/event.c b/private/ntos/tdi/nbf/event.c new file mode 100644 index 000000000..6cb072d8a --- /dev/null +++ b/private/ntos/tdi/nbf/event.c @@ -0,0 +1,190 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + event.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiSetEventHandler + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +NTSTATUS +NbfTdiSetEventHandler( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiSetEventHandler request for the + transport provider. The caller (request dispatcher) verifies + that this routine will not be executed on behalf of a user-mode + client, as this request enables direct callouts at DISPATCH_LEVEL. + +Arguments: + + Irp - Pointer to the IRP for this request + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS rc=STATUS_SUCCESS; + KIRQL oldirql; + PTDI_REQUEST_KERNEL_SET_EVENT parameters; + PIO_STACK_LOCATION irpSp; + PTP_ADDRESS address; + PTP_ADDRESS_FILE addressFile; + NTSTATUS status; + + // + // Get the Address this is associated with; if there is none, get out. + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + addressFile = irpSp->FileObject->FsContext; + status = NbfVerifyAddressObject (addressFile); + if (!NT_SUCCESS (status)) { + return status; + } + + address = addressFile->Address; + + ACQUIRE_SPIN_LOCK (&address->SpinLock, &oldirql); + + parameters = (PTDI_REQUEST_KERNEL_SET_EVENT)&irpSp->Parameters; + + switch (parameters->EventType) { + + case TDI_EVENT_RECEIVE: + + if (parameters->EventHandler == NULL) { + addressFile->ReceiveHandler = + (PTDI_IND_RECEIVE)TdiDefaultReceiveHandler; + addressFile->ReceiveHandlerContext = NULL; + addressFile->RegisteredReceiveHandler = FALSE; + } else { + addressFile->ReceiveHandler = + (PTDI_IND_RECEIVE)parameters->EventHandler; + addressFile->ReceiveHandlerContext = parameters->EventContext; + addressFile->RegisteredReceiveHandler = TRUE; + } + + break; + + case TDI_EVENT_RECEIVE_EXPEDITED: + + if (parameters->EventHandler == NULL) { + addressFile->ExpeditedDataHandler = + (PTDI_IND_RECEIVE_EXPEDITED)TdiDefaultRcvExpeditedHandler; + addressFile->ExpeditedDataHandlerContext = NULL; + addressFile->RegisteredExpeditedDataHandler = FALSE; + } else { + addressFile->ExpeditedDataHandler = + (PTDI_IND_RECEIVE_EXPEDITED)parameters->EventHandler; + addressFile->ExpeditedDataHandlerContext = parameters->EventContext; + addressFile->RegisteredExpeditedDataHandler = TRUE; + } + + break; + + case TDI_EVENT_RECEIVE_DATAGRAM: + + if (parameters->EventHandler == NULL) { + addressFile->ReceiveDatagramHandler = + (PTDI_IND_RECEIVE_DATAGRAM)TdiDefaultRcvDatagramHandler; + addressFile->ReceiveDatagramHandlerContext = NULL; + addressFile->RegisteredReceiveDatagramHandler = FALSE; + } else { + addressFile->ReceiveDatagramHandler = + (PTDI_IND_RECEIVE_DATAGRAM)parameters->EventHandler; + addressFile->ReceiveDatagramHandlerContext = parameters->EventContext; + addressFile->RegisteredReceiveDatagramHandler = TRUE; + } + + break; + + case TDI_EVENT_ERROR: + + if (parameters->EventHandler == NULL) { + addressFile->ErrorHandler = + (PTDI_IND_ERROR)TdiDefaultErrorHandler; + addressFile->ErrorHandlerContext = NULL; + addressFile->RegisteredErrorHandler = FALSE; + } else { + addressFile->ErrorHandler = + (PTDI_IND_ERROR)parameters->EventHandler; + addressFile->ErrorHandlerContext = parameters->EventContext; + addressFile->RegisteredErrorHandler = TRUE; + } + + break; + + case TDI_EVENT_DISCONNECT: + + if (parameters->EventHandler == NULL) { + addressFile->DisconnectHandler = + (PTDI_IND_DISCONNECT)TdiDefaultDisconnectHandler; + addressFile->DisconnectHandlerContext = NULL; + addressFile->RegisteredDisconnectHandler = FALSE; + } else { + addressFile->DisconnectHandler = + (PTDI_IND_DISCONNECT)parameters->EventHandler; + addressFile->DisconnectHandlerContext = parameters->EventContext; + addressFile->RegisteredDisconnectHandler = TRUE; + } + + break; + + case TDI_EVENT_CONNECT: + + if (parameters->EventHandler == NULL) { + addressFile->ConnectionHandler = + (PTDI_IND_CONNECT)TdiDefaultConnectHandler; + addressFile->ConnectionHandlerContext = NULL; + addressFile->RegisteredConnectionHandler = FALSE; + } else { + addressFile->ConnectionHandler = + (PTDI_IND_CONNECT)parameters->EventHandler; + addressFile->ConnectionHandlerContext = parameters->EventContext; + addressFile->RegisteredConnectionHandler = TRUE; + } + break; + + default: + + rc = STATUS_INVALID_PARAMETER; + + } /* switch */ + + RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); + + NbfDereferenceAddress ("Set event handler", address, AREF_VERIFY); + + return rc; +} /* TdiSetEventHandler */ diff --git a/private/ntos/tdi/nbf/framecon.c b/private/ntos/tdi/nbf/framecon.c new file mode 100644 index 000000000..6905d9668 --- /dev/null +++ b/private/ntos/tdi/nbf/framecon.c @@ -0,0 +1,1087 @@ + +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + framecon.c + +Abstract: + + This module contains routines which build NetBIOS Frames Protocol frames, + both connection-oriented and connectionless. The following frames are + constructed by routines in this module: + + o NBF_CMD_ADD_GROUP_NAME_QUERY + o NBF_CMD_ADD_NAME_QUERY + o NBF_CMD_NAME_IN_CONFLICT + o NBF_CMD_STATUS_QUERY + o NBF_CMD_TERMINATE_TRACE + o NBF_CMD_DATAGRAM + o NBF_CMD_DATAGRAM_BROADCAST + o NBF_CMD_NAME_QUERY + o NBF_CMD_ADD_NAME_RESPONSE + o NBF_CMD_NAME_RECOGNIZED + o NBF_CMD_STATUS_RESPONSE + o NBF_CMD_TERMINATE_TRACE2 + o NBF_CMD_DATA_ACK + o NBF_CMD_DATA_FIRST_MIDDLE + o NBF_CMD_DATA_ONLY_LAST + o NBF_CMD_SESSION_CONFIRM + o NBF_CMD_SESSION_END + o NBF_CMD_SESSION_INITIALIZE + o NBF_CMD_NO_RECEIVE + o NBF_CMD_RECEIVE_OUTSTANDING + o NBF_CMD_RECEIVE_CONTINUE + o NBF_CMD_SESSION_ALIVE + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +VOID +ConstructAddGroupNameQuery( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN USHORT Correlator, // correlator for ADD_NAME_RESPONSE. + IN PNAME GroupName // NetBIOS group name to be added. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_ADD_GROUP_NAME_QUERY connectionless + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 44-byte connectionless frame buffer. + + Correlator - Correlator for ADD_NAME_RESPONSE frame. + + GroupName - Pointer to NetBIOS group name to be added. + +Return Value: + + none. + +--*/ + +{ + USHORT i; + + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructAddGroupNameQuery: Entered.\n"); + } + + RawFrame->Command = NBF_CMD_ADD_GROUP_NAME_QUERY; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTIONLESS); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = 0; // reserved field, MBZ. + RawFrame->Data2Low = 0; + RawFrame->Data2High = 0; + TRANSMIT_CORR(RawFrame) = Correlator; + RESPONSE_CORR(RawFrame) = (USHORT)0; + for (i=0; i<NETBIOS_NAME_LENGTH; i++) { + RawFrame->DestinationName [i] = 0; + RawFrame->SourceName [i] = GroupName [i]; + } +} /* ConstructAddGroupNameQuery */ + + +VOID +ConstructAddNameQuery( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN USHORT Correlator, // correlator for ADD_NAME_RESPONSE. + IN PNAME Name // NetBIOS name to be added. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_ADD_NAME_QUERY connectionless + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 44-byte connectionless frame buffer. + + Correlator - Correlator for ADD_NAME_RESPONSE frame. + + Name - Pointer to NetBIOS name to be added. + +Return Value: + + none. + +--*/ + +{ + USHORT i; + + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructAddNameQuery: Entered.\n"); + } + + RawFrame->Command = NBF_CMD_ADD_NAME_QUERY; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTIONLESS); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = 0; // reserved field, MBZ. + RawFrame->Data2Low = 0; + RawFrame->Data2High = 0; + TRANSMIT_CORR(RawFrame) = Correlator; + RESPONSE_CORR(RawFrame) = (USHORT)0; + for (i=0; i<NETBIOS_NAME_LENGTH; i++) { + RawFrame->DestinationName [i] = 0; + RawFrame->SourceName [i] = Name [i]; + } +} /* ConstructAddNameQuery */ + + +VOID +ConstructNameInConflict( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN PNAME ConflictingName, // NetBIOS name that is conflicting. + IN PNAME SendingPermanentName // NetBIOS permanent node name of sender. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_NAME_IN_CONFLICT connectionless + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 44-byte connectionless frame buffer. + + Conflictingname - Pointer to NetBIOS name that is conflicting. + + SendingPermanentName - Pointer to NetBIOS permanent node name of sender. + +Return Value: + + none. + +--*/ + +{ + USHORT i; + + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructNameInConflict: Entered (BUGBUG).\n"); + } + + RawFrame->Command = NBF_CMD_NAME_IN_CONFLICT; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTIONLESS); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = 0; // reserved field, MBZ. + RawFrame->Data2Low = 0; + RawFrame->Data2High = 0; + TRANSMIT_CORR(RawFrame) = (USHORT)0; + RESPONSE_CORR(RawFrame) = (USHORT)0; + for (i=0; i<NETBIOS_NAME_LENGTH; i++) { + RawFrame->DestinationName [i] = ConflictingName[i]; + RawFrame->SourceName [i] = SendingPermanentName[i]; + } + +} /* ConstructNameInConflict */ + + +VOID +ConstructStatusQuery( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN UCHAR RequestType, // type of request. + IN USHORT BufferLength, // length of user's status buffer. + IN USHORT Correlator, // correlator for STATUS_RESPONSE. + IN PNAME ReceiverName, // NetBIOS name of receiver. + IN PNAME SendingPermanentName // NetBIOS permanent node name of sender. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_STATUS_QUERY connectionless + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 44-byte connectionless frame buffer. + + RequestType - Type of request. One of: + 0 - request is 1.x or 2.0. + 1 - first request, 2.1 or above. + >1 - subsequent request, 2.1 or above. + + BufferLength - Length of user's status buffer. + + Correlator - Correlator for STATUS_RESPONSE frame. + + ReceiverName - Pointer to NetBIOS name of receiver. + + SendingPermanentName - Pointer to NetBIOS permanent node name of sender. + +Return Value: + + none. + +--*/ + +{ + SHORT i; + + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint1 ("ConstructStatusQuery: Entered, frame: %lx\n", RawFrame); + } + + RawFrame->Command = NBF_CMD_STATUS_QUERY; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTIONLESS); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = RequestType; + RawFrame->Data2Low = (UCHAR)(BufferLength & 0xff); + RawFrame->Data2High = (UCHAR)(BufferLength >> 8); + TRANSMIT_CORR(RawFrame) = (USHORT)0; + RESPONSE_CORR(RawFrame) = Correlator; + for (i=0; i<NETBIOS_NAME_LENGTH; i++) { + RawFrame->DestinationName [i] = ReceiverName [i]; + RawFrame->SourceName [i] = SendingPermanentName [i]; + } + +} /* ConstructStatusQuery */ + + +VOID +ConstructDatagram( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN PNAME ReceiverName, // NetBIOS name of receiver. + IN PNAME SenderName // NetBIOS name of sender. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_DATAGRAM connectionless + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 44-byte connectionless frame buffer. + + ReceiverName - Pointer to a NetBIOS name of the receiver. + + SenderName - Pointer to a NetBIOS name of the sender. + +Return Value: + + none. + +--*/ + +{ + USHORT i; + + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructDatagram: Entered.\n"); + } + + RawFrame->Command = NBF_CMD_DATAGRAM; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTIONLESS); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = 0; // reserved field, MBZ. + RawFrame->Data2Low = 0; + RawFrame->Data2High = 0; + TRANSMIT_CORR(RawFrame) = (USHORT)0; + RESPONSE_CORR(RawFrame) = (USHORT)0; + for (i=0; i<NETBIOS_NAME_LENGTH; i++) { + RawFrame->DestinationName [i] = ReceiverName [i]; + RawFrame->SourceName [i] = SenderName [i]; + } +} /* ConstructDatagram */ + + +VOID +ConstructDatagramBroadcast( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN PNAME SenderName // NetBIOS name of sender. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_DATAGRAM_BROADCAST connectionless + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 44-byte connectionless frame buffer. + + SenderName - Pointer to a NetBIOS name of the sender. + +Return Value: + + none. + +--*/ + +{ + USHORT i; + + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructDatagramBroadcast: Entered.\n"); + } + + RawFrame->Command = NBF_CMD_DATAGRAM_BROADCAST; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTIONLESS); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = 0; // reserved field, MBZ. + RawFrame->Data2Low = 0; + RawFrame->Data2High = 0; + TRANSMIT_CORR(RawFrame) = (USHORT)0; + RESPONSE_CORR(RawFrame) = (USHORT)0; + for (i=0; i<NETBIOS_NAME_LENGTH; i++) { + RawFrame->DestinationName [i] = 0; + RawFrame->SourceName [i] = SenderName [i]; + } +} /* ConstructDatagramBroadcast */ + + +VOID +ConstructNameQuery( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN UCHAR NameType, // type of name. + IN UCHAR LocalSessionNumber, // LSN assigned to session (0=FIND_NAME). + IN USHORT Correlator, // correlator in NAME_RECOGNIZED. + IN PNAME SenderName, // NetBIOS name of sender. + IN PNAME ReceiverName // NetBIOS name of receiver. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_NAME_QUERY connectionless + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 44-byte connectionless frame buffer. + + NameType - Type of name, one of the following: + NAME_QUERY_LSN_FIND_NAME + + LocalSessionNumber - LSN assigned to session (0=FIND.NAME). + + Correlator - Correlator in NAME_RECOGNIZED. + + SenderName - Pointer to a NetBIOS name of the sender. + + ReceiverName - Pointer to a NetBIOS name of the receiver. + +Return Value: + + none. + +--*/ + +{ + SHORT i; + + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint1 ("ConstructNameQuery: Entered, frame: %lx\n", RawFrame); + } + + RawFrame->Command = NBF_CMD_NAME_QUERY; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTIONLESS); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = 0; // reserved field, MBZ. + RawFrame->Data2Low = LocalSessionNumber; + RawFrame->Data2High = NameType; + TRANSMIT_CORR(RawFrame) = (USHORT)0; + RESPONSE_CORR(RawFrame) = Correlator; + for (i=0; i<NETBIOS_NAME_LENGTH; i++) { + RawFrame->DestinationName [i] = ReceiverName [i]; + RawFrame->SourceName [i] = SenderName [i]; + } +} /* ConstructNameQuery */ + + +VOID +ConstructAddNameResponse( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN UCHAR NameType, // type of name. + IN USHORT Correlator, // correlator from ADD_[GROUP_]NAME_QUERY. + IN PNAME Name // NetBIOS name being responded to. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_ADD_NAME_RESPONSE connectionless + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 44-byte connectionless frame buffer. + + NameType - Type of name, either group or unique. + + Correlator - Correlator from ADD_[GROUP]NAME_QUERY. + + Name - Pointer to NetBIOS name being responded to. + +Return Value: + + none. + +--*/ + +{ + USHORT i; + + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructAddNameResponse: Entered (BUGBUG).\n"); + } + + RawFrame->Command = NBF_CMD_ADD_NAME_RESPONSE; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTIONLESS); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = 0; // reserved field, MBZ. + RawFrame->Data2Low = NameType; + RawFrame->Data2High = 0; + TRANSMIT_CORR(RawFrame) = Correlator; + RESPONSE_CORR(RawFrame) = (USHORT)0; + for (i=0; i<NETBIOS_NAME_LENGTH; i++) { + RawFrame->DestinationName [i] = Name [i]; + RawFrame->SourceName [i] = Name [i]; + } +} /* ConstructAddNameResponse */ + + +VOID +ConstructNameRecognized( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN UCHAR NameType, // type of name. + IN UCHAR LocalSessionNumber, // LSN assigned to session. + IN USHORT NameQueryCorrelator, // correlator from NAME_QUERY. + IN USHORT Correlator, // correlator expected from next response. + IN PNAME SenderName, // NetBIOS name of sender. + IN PNAME ReceiverName // NetBIOS name of receiver. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_NAME_RECOGNIZED connectionless + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 44-byte connectionless frame buffer. + + NameType - Type of name, either group or unique. + + LocalSessionNumber - LSN assigned to session. Special values are: + NAME_RECOGNIZED_LSN_NO_LISTENS // no listens available. + NAME_RECOGNIZED_LSN_FIND_NAME // this is a find name response. + NAME_RECOGNIZED_LSN_NO_RESOURCE // listen available, but no resources. + + NameQueryCorrelator - Correlator from NAME_QUERY. + + Correlator - Correlator expected from next response. + + SenderName - Pointer to a NetBIOS name of the sender. + + ReceiverName - Pointer to a NetBIOS name of the receiver. + +Return Value: + + none. + +--*/ + +{ + USHORT i; + + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructNameRecognized: Entered.\n"); + } + + RawFrame->Command = NBF_CMD_NAME_RECOGNIZED; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTIONLESS); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = 0; // reserved field, MBZ. + RawFrame->Data2Low = LocalSessionNumber; + RawFrame->Data2High = NameType; + TRANSMIT_CORR(RawFrame) = NameQueryCorrelator; + RESPONSE_CORR(RawFrame) = Correlator; + for (i=0; i<NETBIOS_NAME_LENGTH; i++) { + RawFrame->DestinationName [i] = ReceiverName [i]; + RawFrame->SourceName [i] = SenderName [i]; + } +} /* ConstructNameRecognized */ + + +VOID +ConstructStatusResponse( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN UCHAR RequestType, // type of request, defined below. + IN BOOLEAN Truncated, // data is truncated. + IN BOOLEAN DataOverflow, // too much data for user's buffer. + IN USHORT DataLength, // length of data sent. + IN USHORT Correlator, // correlator from STATUS_QUERY. + IN PNAME ReceivingPermanentName, // NetBIOS permanent node name of receiver. + IN PNAME SenderName // NetBIOS name of sender. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_STATUS_RESPONSE connectionless + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 44-byte connectionless frame buffer. + + RequestType - type of request, one of the below: + 0 - request is 1.x or 2.0. + >0 - number of names, 2.1 or above. + + Truncated - TRUE if there are more names. + + DataOverflow - TRUE if the total data is larger than the user's buffer. + + DataLength - The length of the data in Buffer. + + Correlator - Correlator from STATUS_QUERY. + + ReceivingPermanentName - Pointer to the NetBIOS permanent node name of the receiver, + as passed in the STATUS_QUERY frame. + + SenderName - Pointer to a NetBIOS name of the sender. + +Return Value: + + none. + +--*/ + +{ + SHORT i; + + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructStatusResponse: Entered (BUGBUG).\n"); + } + + RawFrame->Command = NBF_CMD_STATUS_RESPONSE; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTIONLESS); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = RequestType; + RawFrame->Data2Low = (UCHAR)(DataLength & 0xff); + RawFrame->Data2High = (UCHAR)((DataLength >> 8) + + (Truncated << 7) + + (DataOverflow << 6)); + TRANSMIT_CORR(RawFrame) = Correlator; + RESPONSE_CORR(RawFrame) = 0; + for (i=0; i<NETBIOS_NAME_LENGTH; i++) { + RawFrame->DestinationName [i] = ReceivingPermanentName [i]; + RawFrame->SourceName [i] = SenderName [i]; + } + +} /* ConstructStatusResponse */ + + +VOID +ConstructDataAck( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN USHORT Correlator, // correlator from DATA_ONLY_LAST. + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_DATA_ACK connection-oriented + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 14-byte connection-oriented buffer. + + Correlator - Correlator from DATA_ONLY_LAST being acked. + + LocalSessionNumber - Session number of SENDER. + + RemoteSessionNumber - Session number of RECEIVER. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructDataAck: Entered.\n"); + } + + RawFrame->Command = NBF_CMD_DATA_ACK; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTION); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = 0; + RawFrame->Data2Low = 0; + RawFrame->Data2High = 0; + TRANSMIT_CORR(RawFrame) = Correlator; + RESPONSE_CORR(RawFrame) = (USHORT)0; + RawFrame->SourceSessionNumber = LocalSessionNumber; + RawFrame->DestinationSessionNumber = RemoteSessionNumber; +} /* ConstructDataAck */ + + +VOID +ConstructDataOnlyLast( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN BOOLEAN Resynched, // TRUE if we are resynching. + IN USHORT Correlator, // correlator for RECEIVE_CONTINUE. + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_DATA_ONLY_LAST connection-oriented + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 14-byte connection-oriented buffer. + + Resynched - TRUE if we are resynching and should set the + correct bit in the frame. + + Correlator - Correlator for RECEIVE_CONTINUE, if any. + + LocalSessionNumber - Session number of SENDER. + + RemoteSessionNumber - Session number of RECEIVER. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructDataOnlyLast: Entered.\n"); + } + + RawFrame->Command = NBF_CMD_DATA_ONLY_LAST; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTION); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = 0; + ASSERT (TRUE == (UCHAR)1); + RawFrame->Data2Low = Resynched; + RawFrame->Data2High = (UCHAR)0; + TRANSMIT_CORR(RawFrame) = (USHORT)0; + RESPONSE_CORR(RawFrame) = Correlator; + RawFrame->SourceSessionNumber = LocalSessionNumber; + RawFrame->DestinationSessionNumber = RemoteSessionNumber; +} /* ConstructDataOnlyLast */ + + +VOID +ConstructSessionConfirm( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN UCHAR Options, // bitflag options, defined below. + IN USHORT MaximumUserBufferSize, // max size of user frame on session. + IN USHORT Correlator, // correlator from SESSION_INITIALIZE. + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_SESSION_CONFIRM connection-oriented + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 14-byte connection-oriented buffer. + + Options - Bitflag options, any of the following: + SESSION_CONFIRM_OPTIONS_20 // set if NETBIOS 2.0 or above. + SESSION_CONFIRM_NO_ACK // set if NO.ACK protocol supported. + + MaximumUserBufferSize - Maximum size of user data per frame on this + session, in bytes. This is limited by the following constant: + SESSION_CONFIRM_MAXIMUM_FRAME_SIZE // defined limit of this field. + + Correlator - Correlator from SESSION_INITIALIZE. + + LocalSessionNumber - Session number of SENDER. + + RemoteSessionNumber - Session number of RECEIVER. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructSessionConfirm: Entered.\n"); + } + + RawFrame->Command = NBF_CMD_SESSION_CONFIRM; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTION); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = Options; + RawFrame->Data2Low = (UCHAR)(MaximumUserBufferSize & 0xff); + RawFrame->Data2High = (UCHAR)(MaximumUserBufferSize >> 8); + TRANSMIT_CORR(RawFrame) = Correlator; + RESPONSE_CORR(RawFrame) = (USHORT)0; + RawFrame->SourceSessionNumber = LocalSessionNumber; + RawFrame->DestinationSessionNumber = RemoteSessionNumber; +} /* ConstructSessionConfirm */ + + +VOID +ConstructSessionEnd( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN USHORT Reason, // reason for termination, defined below. + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_SESSION_END connection-oriented + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 14-byte connection-oriented buffer. + + Reason - Reason code for termination, any of the following: + SESSION_END_REASON_HANGUP // normal termination via HANGUP. + SESSION_END_REASON_ABEND // abnormal session termination. + + LocalSessionNumber - Session number of SENDER. + + RemoteSessionNumber - Session number of RECEIVER. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructSessionEnd: Entered (BUGBUG).\n"); + } + + RawFrame->Command = NBF_CMD_SESSION_END; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTION); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = 0; + RawFrame->Data2Low = (UCHAR)(Reason & 0xff); + RawFrame->Data2High = (UCHAR)(Reason >> 8); + TRANSMIT_CORR(RawFrame) = (USHORT)0; + RESPONSE_CORR(RawFrame) = (USHORT)0; + RawFrame->SourceSessionNumber = LocalSessionNumber; + RawFrame->DestinationSessionNumber = RemoteSessionNumber; +} /* ConstructSessionEnd */ + + +VOID +ConstructSessionInitialize( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN UCHAR Options, // bitflag options, defined below. + IN USHORT MaximumUserBufferSize, // max size of user frame on session. + IN USHORT NameRecognizedCorrelator, // correlator from NAME_RECOGNIZED. + IN USHORT Correlator, // correlator for SESSION_CONFIRM. + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_SESSION_INITIALIZE connection-oriented + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 14-byte connection-oriented buffer. + + Options - Bitflag options, any of the following: + SESSION_INITIALIZE_OPTIONS_20 // set if NETBIOS 2.0 or above. + SESSION_INITIALIZE_NO_ACK // set if NO.ACK protocol supported. + + MaximumUserBufferSize - Maximum size of user data per frame on this + session, in bytes. This is limited by the following constant: + SESSION_INITIALIZE_MAXIMUM_FRAME_SIZE // defined limit of this field. + + NameRecognizedCorrelator - Correlator from NAME_RECOGNIZED. + + Correlator - Correlator for SESSION_CONFIRM. + + LocalSessionNumber - Session number of SENDER. + + RemoteSessionNumber - Session number of RECEIVER. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructSessionInitialize: Entered.\n"); + } + + RawFrame->Command = NBF_CMD_SESSION_INITIALIZE; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTION); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = Options; + RawFrame->Data2Low = (UCHAR)(MaximumUserBufferSize & 0xff); + RawFrame->Data2High = (UCHAR)(MaximumUserBufferSize >> 8); + TRANSMIT_CORR(RawFrame) = NameRecognizedCorrelator; + RESPONSE_CORR(RawFrame) = Correlator; + RawFrame->SourceSessionNumber = LocalSessionNumber; + RawFrame->DestinationSessionNumber = RemoteSessionNumber; +} /* ConstructSessionInitialize */ + + +VOID +ConstructNoReceive( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN USHORT Options, // option bitflags, defined below. + IN USHORT BytesAccepted, // number of bytes accepted. + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_NO_RECEIVE connection-oriented + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 14-byte connection-oriented buffer. + + Options - Bitflag options, any of the following: + NO_RECEIVE_OPTIONS_PARTIAL_NO_ACK // NO.ACK data partially received. + + BytesAccepted - Number of bytes accepted, current outstanding message. + + LocalSessionNumber - Session number of SENDER. + + RemoteSessionNumber - Session number of RECEIVER. + +Return Value: + + none. + +--*/ + +{ +// Options; // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructNoReceive: Entered.\n"); + } + + RawFrame->Command = NBF_CMD_NO_RECEIVE; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTION); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + if (Options == NO_RECEIVE_PARTIAL_NO_ACK) { + RawFrame->Data1 = NO_RECEIVE_PARTIAL_NO_ACK; + } else { + RawFrame->Data1 = 0; + } + RawFrame->Data2Low = (UCHAR)(BytesAccepted & 0xff); + RawFrame->Data2High = (UCHAR)(BytesAccepted >> 8); + TRANSMIT_CORR(RawFrame) = (USHORT)0; + RESPONSE_CORR(RawFrame) = (USHORT)0; + RawFrame->SourceSessionNumber = LocalSessionNumber; + RawFrame->DestinationSessionNumber = RemoteSessionNumber; +} /* ConstructNoReceive */ + + +VOID +ConstructReceiveOutstanding( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN USHORT BytesAccepted, // number of bytes accepted. + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_RECEIVE_OUTSTANDING connection-oriented + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 14-byte connection-oriented buffer. + + BytesAccepted - Number of bytes accepted, current outstanding message. + + LocalSessionNumber - Session number of SENDER. + + RemoteSessionNumber - Session number of RECEIVER. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructReceiveOutstanding: Entered.\n"); + } + + RawFrame->Command = NBF_CMD_RECEIVE_OUTSTANDING; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTION); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = 0; + RawFrame->Data2Low = (UCHAR)(BytesAccepted & 0xff); + RawFrame->Data2High = (UCHAR)(BytesAccepted >> 8); + TRANSMIT_CORR(RawFrame) = (USHORT)0; + RESPONSE_CORR(RawFrame) = (USHORT)0; + RawFrame->SourceSessionNumber = LocalSessionNumber; + RawFrame->DestinationSessionNumber = RemoteSessionNumber; +} /* ConstructReceiveOutstanding */ + + +VOID +ConstructReceiveContinue( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN USHORT Correlator, // correlator from DATA_FIRST_MIDDLE + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_RECEIVE_CONTINUE connection-oriented + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 14-byte connection-oriented buffer. + + Correlator - The correlator from the DATA_FIRST_MIDDLE frame. + + LocalSessionNumber - Session number of SENDER. + + RemoteSessionNumber - Session number of RECEIVER. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructReceiveContinue: Entered.\n"); + } + + RawFrame->Command = NBF_CMD_RECEIVE_CONTINUE; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTION); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = 0; + RawFrame->Data2Low = 0; + RawFrame->Data2High = 0; + TRANSMIT_CORR(RawFrame) = Correlator; + RESPONSE_CORR(RawFrame) = (USHORT)0; + RawFrame->SourceSessionNumber = LocalSessionNumber; + RawFrame->DestinationSessionNumber = RemoteSessionNumber; +} /* ConstructReceiveContinue */ + +#if 0 + +VOID +ConstructSessionAlive( + IN PNBF_HDR_CONNECTION RawFrame // frame buffer to format. + ) + +/*++ + +Routine Description: + + This routine constructs an NBF_CMD_SESSION_ALIVE connection-oriented + NetBIOS Frame. + +Arguments: + + RawFrame - Pointer to an unformatted 14-byte connection-oriented buffer. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_FRAMECON) { + NbfPrint0 ("ConstructSessionAlive: Entered.\n"); + } + + RawFrame->Command = NBF_CMD_SESSION_ALIVE; + HEADER_LENGTH(RawFrame) = sizeof(NBF_HDR_CONNECTION); + HEADER_SIGNATURE(RawFrame) = NETBIOS_SIGNATURE; + RawFrame->Data1 = 0; + RawFrame->Data2Low = 0; + RawFrame->Data2High = 0; + TRANSMIT_CORR(RawFrame) = (USHORT)0; + RESPONSE_CORR(RawFrame) = (USHORT)0; + RawFrame->SourceSessionNumber = 0; + RawFrame->DestinationSessionNumber = 0; +} /* ConstructSessionAlive */ + +#endif diff --git a/private/ntos/tdi/nbf/framesnd.c b/private/ntos/tdi/nbf/framesnd.c new file mode 100644 index 000000000..e4c356d99 --- /dev/null +++ b/private/ntos/tdi/nbf/framesnd.c @@ -0,0 +1,2504 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + framesnd.c + +Abstract: + + This module contains routines which build and send NetBIOS Frames Protocol + frames and data link frames for other modules. These routines call on the + ones in FRAMECON.C to do the construction work. + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#if DBG +ULONG NbfSendsIssued = 0; +ULONG NbfSendsCompletedInline = 0; +ULONG NbfSendsCompletedOk = 0; +ULONG NbfSendsCompletedFail = 0; +ULONG NbfSendsPended = 0; +ULONG NbfSendsCompletedAfterPendOk = 0; +ULONG NbfSendsCompletedAfterPendFail = 0; + +ULONG NbfPacketPanic = 0; +#endif + + +NTSTATUS +NbfSendAddNameQuery( + IN PTP_ADDRESS Address + ) + +/*++ + +Routine Description: + + This routine sends a ADD_NAME_QUERY frame to register the specified + address. + +Arguments: + + Address - Pointer to a transport address object. + + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PDEVICE_CONTEXT DeviceContext; + PTP_UI_FRAME RawFrame; + PUCHAR SingleSR; + UINT SingleSRLength; + UINT HeaderLength; + + DeviceContext = Address->Provider; + + + // + // Allocate a UI frame from the pool. + // + + Status = NbfCreateConnectionlessFrame (DeviceContext, &RawFrame); + if (!NT_SUCCESS (Status)) { // couldn't make frame. + return STATUS_INSUFFICIENT_RESOURCES; + } + + IF_NBFDBG (NBF_DEBUG_FRAMESND) { + NbfPrint3 ("NbfSendAddNameQuery: Sending Frame: %lx, NdisPacket: %lx MacHeader: %lx\n", + RawFrame, RawFrame->NdisPacket, RawFrame->Header); + } + + + // + // Build the MAC header. ADD_NAME_QUERY frames go out as + // single-route source routing. + // + + MacReturnSingleRouteSR( + &DeviceContext->MacInfo, + &SingleSR, + &SingleSRLength); + + MacConstructHeader ( + &DeviceContext->MacInfo, + RawFrame->Header, + DeviceContext->NetBIOSAddress.Address, + DeviceContext->LocalAddress.Address, + sizeof (DLC_FRAME) + sizeof (NBF_HDR_CONNECTIONLESS), + SingleSR, + SingleSRLength, + &HeaderLength); + + + // + // Build the DLC UI frame header. + // + + NbfBuildUIFrameHeader(&RawFrame->Header[HeaderLength]); + HeaderLength += sizeof(DLC_FRAME); + + + // + // Build the appropriate Netbios header based on the type + // of the address. + // + + if ((Address->Flags & ADDRESS_FLAGS_GROUP) != 0) { + + ConstructAddGroupNameQuery ( + (PNBF_HDR_CONNECTIONLESS)&(RawFrame->Header[HeaderLength]), + 0, // correlator we don't use. + Address->NetworkName->NetbiosName); + + } else { + + ConstructAddNameQuery ( + (PNBF_HDR_CONNECTIONLESS)&(RawFrame->Header[HeaderLength]), + 0, // correlator we don't use. + Address->NetworkName->NetbiosName); + + } + + HeaderLength += sizeof(NBF_HDR_CONNECTIONLESS); + + + // + // Munge the packet length and send the it. + // + + NbfSetNdisPacketLength(RawFrame->NdisPacket, HeaderLength); + + NbfSendUIFrame ( + DeviceContext, + RawFrame, + FALSE); // no loopback (MC frame). + + return STATUS_SUCCESS; +} /* NbfSendAddNameQuery */ + + +VOID +NbfSendNameQuery( + IN PTP_CONNECTION Connection, + IN BOOLEAN SourceRoutingOptional + ) + +/*++ + +Routine Description: + + This routine sends a NAME_QUERY frame of the appropriate type given the + state of the specified connection. + +Arguments: + + Connection - Pointer to a transport connection object. + + SourceRoutingOptional - TRUE if source routing should be removed if + we are configured that way. + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PDEVICE_CONTEXT DeviceContext; + PTP_ADDRESS Address; + PTP_UI_FRAME RawFrame; + PUCHAR NameQuerySR; + UINT NameQuerySRLength; + PUCHAR NameQueryAddress; + UINT HeaderLength; + UCHAR Lsn; + UCHAR NameType; + + Address = Connection->AddressFile->Address; + DeviceContext = Address->Provider; + + + // + // Allocate a UI frame from the pool. + // + + Status = NbfCreateConnectionlessFrame(DeviceContext, &RawFrame); + if (!NT_SUCCESS (Status)) { // couldn't make frame. + return; + } + + IF_NBFDBG (NBF_DEBUG_FRAMESND) { + NbfPrint2 ("NbfSendNameQuery: Sending Frame: %lx, NdisPacket: %lx\n", + RawFrame, RawFrame->NdisPacket); + } + + + // + // Build the MAC header. + // + + if (((Connection->Flags2 & CONNECTION_FLAGS2_WAIT_NR) != 0) && + ((Connection->Flags2 & CONNECTION_FLAGS2_GROUP_LSN) == 0)) { + + // + // This is the second find name to a unique name; this + // means that we already have a link and we can send this + // frame directed to it. + // + + ASSERT (Connection->Link != NULL); + + MacReturnSourceRouting( + &DeviceContext->MacInfo, + Connection->Link->Header, + &NameQuerySR, + &NameQuerySRLength); + + NameQueryAddress = Connection->Link->HardwareAddress.Address; + + } else { + + // + // Standard NAME_QUERY frames go out as + // single-route source routing, except if + // it is optional and we are configured + // that way. + // + + if (SourceRoutingOptional && + Connection->Provider->MacInfo.QueryWithoutSourceRouting) { + + NameQuerySR = NULL; + NameQuerySRLength = 0; + + } else { + + MacReturnSingleRouteSR( + &DeviceContext->MacInfo, + &NameQuerySR, + &NameQuerySRLength); + + } + + NameQueryAddress = DeviceContext->NetBIOSAddress.Address; + + } + + MacConstructHeader ( + &DeviceContext->MacInfo, + RawFrame->Header, + NameQueryAddress, + DeviceContext->LocalAddress.Address, + sizeof (DLC_FRAME) + sizeof (NBF_HDR_CONNECTIONLESS), + NameQuerySR, + NameQuerySRLength, + &HeaderLength); + + + // + // Build the DLC UI frame header. + // + + NbfBuildUIFrameHeader(&RawFrame->Header[HeaderLength]); + HeaderLength += sizeof(DLC_FRAME); + + + // + // Build the Netbios header. + // + + Lsn = (UCHAR)((Connection->Flags2 & CONNECTION_FLAGS2_WAIT_NR_FN) ? + NAME_QUERY_LSN_FIND_NAME : Connection->Lsn); + + NameType = (UCHAR)((Connection->AddressFile->Address->Flags & ADDRESS_FLAGS_GROUP) ? + NETBIOS_NAME_TYPE_GROUP : NETBIOS_NAME_TYPE_UNIQUE); + + ConstructNameQuery ( + (PNBF_HDR_CONNECTIONLESS)&(RawFrame->Header[HeaderLength]), + NameType, // type of our name. + Lsn, // calculated, above. + (USHORT)Connection->ConnectionId, // corr. in 1st NAME_RECOGNIZED. + Address->NetworkName->NetbiosName, // NetBIOS name of sender. + Connection->CalledAddress.NetbiosName); // NetBIOS name of receiver. + + HeaderLength += sizeof(NBF_HDR_CONNECTIONLESS); + + + // + // Munge the packet length and send the it. + // + + NbfSetNdisPacketLength(RawFrame->NdisPacket, HeaderLength); + + NbfSendUIFrame ( + DeviceContext, + RawFrame, + FALSE); // no loopback (MC frame) + +} /* NbfSendNameQuery */ + + +VOID +NbfSendNameRecognized( + IN PTP_ADDRESS Address, + IN UCHAR LocalSessionNumber, // LSN assigned to session. + IN PNBF_HDR_CONNECTIONLESS Header, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ) + +/*++ + +Routine Description: + + This routine sends a NAME_RECOGNIZED frame of the appropriate type + in response to the NAME_QUERY pointed to by Header. + +Arguments: + + Address - Pointer to a transport address object. + + LocalSessionNumber - The LSN to use in the frame. + + Header - Pointer to the connectionless NetBIOS header of the + NAME_QUERY frame. + + SourceAddress - Pointer to the source hardware address in the received + frame. + + SourceRoutingInformation - Pointer to source routing information, if any. + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PDEVICE_CONTEXT DeviceContext; + PTP_UI_FRAME RawFrame; + UINT HeaderLength; + PUCHAR ReplySR; + UINT ReplySRLength; + UCHAR TempSR[MAX_SOURCE_ROUTING]; + UCHAR NameType; + + DeviceContext = Address->Provider; + + + // + // Allocate a UI frame from the pool. + // + + Status = NbfCreateConnectionlessFrame (DeviceContext, &RawFrame); + if (!NT_SUCCESS (Status)) { // couldn't make frame. + return; + } + + IF_NBFDBG (NBF_DEBUG_FRAMESND) { + NbfPrint2 ("NbfSendNameRecognized: Sending Frame: %lx, NdisPacket: %lx\n", + RawFrame, RawFrame->NdisPacket); + } + + + // + // Build the MAC header. NAME_RECOGNIZED frames go out as + // directed source routing unless configured for general-route. + // + + if (DeviceContext->MacInfo.AllRoutesNameRecognized) { + + MacReturnGeneralRouteSR( + &DeviceContext->MacInfo, + &ReplySR, + &ReplySRLength); + + } else { + + if (SourceRouting != NULL) { + + RtlCopyMemory( + TempSR, + SourceRouting, + SourceRoutingLength); + + MacCreateNonBroadcastReplySR( + &DeviceContext->MacInfo, + TempSR, + SourceRoutingLength, + &ReplySR); + + ReplySRLength = SourceRoutingLength; + + } else { + + ReplySR = NULL; + } + } + + + MacConstructHeader ( + &DeviceContext->MacInfo, + RawFrame->Header, + SourceAddress->Address, + DeviceContext->LocalAddress.Address, + sizeof (DLC_FRAME) + sizeof (NBF_HDR_CONNECTIONLESS), + ReplySR, + ReplySRLength, + &HeaderLength); + + + // + // Build the DLC UI frame header. + // + + NbfBuildUIFrameHeader(&RawFrame->Header[HeaderLength]); + HeaderLength += sizeof(DLC_FRAME); + + + // + // Build the Netbios header. + // + + NameType = (UCHAR)((Address->Flags & ADDRESS_FLAGS_GROUP) ? + NETBIOS_NAME_TYPE_GROUP : NETBIOS_NAME_TYPE_UNIQUE); + + ConstructNameRecognized ( // build a good response. + (PNBF_HDR_CONNECTIONLESS)&(RawFrame->Header[HeaderLength]), + NameType, // type of local name. + LocalSessionNumber, // return our LSN. + RESPONSE_CORR(Header), // new xmit corr. + 0, // our response correlator (unused). + Header->DestinationName, // our NetBIOS name. + Header->SourceName); // his NetBIOS name. + + // + // BUGBUG: Use Address->NetworkName->Address[0].Address[0].NetbiosName + // instead of Header->DestinationName? + // + + HeaderLength += sizeof(NBF_HDR_CONNECTIONLESS); + + + // + // Munge the packet length and send the it. + // + + NbfSetNdisPacketLength(RawFrame->NdisPacket, HeaderLength); + + NbfSendUIFrame ( + DeviceContext, + RawFrame, + FALSE); // no loopback (MC frame) + +} /* NbfSendNameRecognized */ + + +VOID +NbfSendNameInConflict( + IN PTP_ADDRESS Address, + IN PUCHAR ConflictingName + ) + +/*++ + +Routine Description: + + This routine sends a NAME_IN_CONFLICT frame. + +Arguments: + + Address - Pointer to a transport address object. + + ConflictingName - The NetBIOS name which is in conflict. + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PDEVICE_CONTEXT DeviceContext; + PTP_UI_FRAME RawFrame; + UINT HeaderLength; + PUCHAR SingleSR; + UINT SingleSRLength; + + DeviceContext = Address->Provider; + + + // + // Allocate a UI frame from the pool. + // + + Status = NbfCreateConnectionlessFrame (DeviceContext, &RawFrame); + if (!NT_SUCCESS (Status)) { // couldn't make frame. + return; + } + + IF_NBFDBG (NBF_DEBUG_FRAMESND) { + NbfPrint2 ("NbfSendNameRecognized: Sending Frame: %lx, NdisPacket: %lx\n", + RawFrame, RawFrame->NdisPacket); + } + + + // + // Build the MAC header. ADD_NAME_QUERY frames go out as + // single-route source routing. + // + + MacReturnSingleRouteSR( + &DeviceContext->MacInfo, + &SingleSR, + &SingleSRLength); + + MacConstructHeader ( + &DeviceContext->MacInfo, + RawFrame->Header, + DeviceContext->NetBIOSAddress.Address, + DeviceContext->LocalAddress.Address, + sizeof (DLC_FRAME) + sizeof (NBF_HDR_CONNECTIONLESS), + SingleSR, + SingleSRLength, + &HeaderLength); + + + // + // Build the DLC UI frame header. + // + + NbfBuildUIFrameHeader(&RawFrame->Header[HeaderLength]); + HeaderLength += sizeof(DLC_FRAME); + + + // + // Build the Netbios header. + // + + ConstructNameInConflict ( + (PNBF_HDR_CONNECTIONLESS)&(RawFrame->Header[HeaderLength]), + ConflictingName, // his NetBIOS name. + DeviceContext->ReservedNetBIOSAddress); // our reserved NetBIOS name. + + HeaderLength += sizeof(NBF_HDR_CONNECTIONLESS); + + + // + // Munge the packet length and send the it. + // + + NbfSetNdisPacketLength(RawFrame->NdisPacket, HeaderLength); + + NbfSendUIFrame ( + DeviceContext, + RawFrame, + FALSE); // no loopback (MC frame) + +} /* NbfSendNameInConflict */ + + +VOID +NbfSendSessionInitialize( + IN PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine sends a SESSION_INITIALIZE frame on the specified connection. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Connection - Pointer to a transport connection object. + + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PTP_PACKET Packet; + PDEVICE_CONTEXT DeviceContext; + PTP_LINK Link; + + NbfReferenceConnection("send Session Initialize", Connection, CREF_FRAME_SEND); + + DeviceContext = Connection->Provider; + Link = Connection->Link; + Status = NbfCreatePacket (DeviceContext, Connection->Link, &Packet); + + if (!NT_SUCCESS (Status)) { // if we couldn't make frame. +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfSendSessionInitialize: NbfCreatePacket failed.\n"); + } +#endif + NbfWaitPacket (Connection, CONNECTION_FLAGS_SEND_SI); + NbfDereferenceConnection("Couldn't get SI packet", Connection, CREF_FRAME_SEND); + return; + } + + + // + // Initialize the Netbios header. + // + + ConstructSessionInitialize ( + (PNBF_HDR_CONNECTION)&(Packet->Header[Link->HeaderLength + sizeof(DLC_I_FRAME)]), + SESSION_INIT_OPTIONS_20 | SESSION_INIT_NO_ACK | + SESSION_INIT_OPTIONS_LF, // supported options BUGBUG: Set LF correctly. + (USHORT)(Connection->Link->MaxFrameSize - sizeof(NBF_HDR_CONNECTION) - sizeof(DLC_I_FRAME)), + // maximum frame size/this session. + Connection->NetbiosHeader.TransmitCorrelator, // correlator from NAME_RECOGNIZED. + 0, // correlator for expected SESSION_CONFIRM. + Connection->Lsn, // our local session number. + Connection->Rsn); // his session number (our RSN). + + // + // Now send the packet on the connection via the link. If there are + // conditions on the link which make it impossible to send the packet, + // then the packet will be queued to the WackQ, and then timeouts will + // restart the link. This is acceptable when the traffic level is so + // high that we encounter this condition. + // + + // + // Set this so NbfDestroyPacket will dereference the connection. + // + + Packet->Owner = Connection; + Packet->Action = PACKET_ACTION_CONNECTION; + + Packet->NdisIFrameLength = + Link->HeaderLength + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION); + + MacModifyHeader( + &DeviceContext->MacInfo, + Packet->Header, + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION)); + + NbfSetNdisPacketLength( + Packet->NdisPacket, + Packet->NdisIFrameLength); + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + Status = SendOnePacket (Connection, Packet, FALSE, NULL); // fire and forget. + + if (Status == STATUS_LINK_FAILED) { + NbfDereferencePacket (Packet); // destroy the packet. + } + + return; +} /* NbfSendSessionInitialize */ + + +VOID +NbfSendSessionConfirm( + IN PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine sends a SESSION_CONFIRM frame on the specified connection. + +Arguments: + + Connection - Pointer to a transport connection object. + + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PTP_PACKET Packet; + PDEVICE_CONTEXT DeviceContext; + PTP_LINK Link; + + NbfReferenceConnection("send Session Confirm", Connection, CREF_FRAME_SEND); + + DeviceContext = Connection->Provider; + Link = Connection->Link; + Status = NbfCreatePacket (DeviceContext, Connection->Link, &Packet); + + if (!NT_SUCCESS (Status)) { // if we couldn't make frame. +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfSendSessionConfirm: NbfCreatePacket failed.\n"); + } +#endif + NbfWaitPacket (Connection, CONNECTION_FLAGS_SEND_SC); + NbfDereferenceConnection("Couldn't get SC packet", Connection, CREF_FRAME_SEND); + return; + } + + + // + // Initialize the Netbios header. + // + + ConstructSessionConfirm ( + (PNBF_HDR_CONNECTION)&(Packet->Header[Link->HeaderLength + sizeof(DLC_I_FRAME)]), + SESSION_CONFIRM_OPTIONS_20 | SESSION_CONFIRM_NO_ACK, // supported options. + (USHORT)(Connection->Link->MaxFrameSize - sizeof(NBF_HDR_CONNECTION) - sizeof(DLC_I_FRAME)), + // maximum frame size/this session. + Connection->NetbiosHeader.TransmitCorrelator, // correlator from NAME_RECOGNIZED. + Connection->Lsn, // our local session number. + Connection->Rsn); // his session number (our RSN). + + // + // Now send the packet on the connection via the link. If there are + // conditions on the link which make it impossible to send the packet, + // then the packet will be queued to the WackQ, and then timeouts will + // restart the link. This is acceptable when the traffic level is so + // high that we encounter this condition. + // + + // + // Set this so NbfDestroyPacket will dereference the connection. + // + + Packet->Owner = Connection; + Packet->Action = PACKET_ACTION_CONNECTION; + + Packet->NdisIFrameLength = + Link->HeaderLength + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION); + + MacModifyHeader( + &DeviceContext->MacInfo, + Packet->Header, + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION)); + + NbfSetNdisPacketLength( + Packet->NdisPacket, + Packet->NdisIFrameLength); + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + Status = SendOnePacket (Connection, Packet, FALSE, NULL); // fire and forget. + + if (Status == STATUS_LINK_FAILED) { + NbfDereferencePacket (Packet); // destroy the packet. + } + + return; +} /* NbfSendSessionConfirm */ + + +VOID +NbfSendSessionEnd( + IN PTP_CONNECTION Connection, + IN BOOLEAN Abort + ) + +/*++ + +Routine Description: + + This routine sends a SESSION_END frame on the specified connection. + +Arguments: + + Connection - Pointer to a transport connection object. + + Abort - Boolean set to TRUE if the connection is abnormally terminating. + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PTP_PACKET Packet; + PDEVICE_CONTEXT DeviceContext; + PTP_LINK Link; + + NbfReferenceConnection("send Session End", Connection, CREF_FRAME_SEND); + + DeviceContext = Connection->Provider; + Link = Connection->Link; + + Status = NbfCreatePacket (DeviceContext, Connection->Link, &Packet); + + if (!NT_SUCCESS (Status)) { // if we couldn't make frame. +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfSendSessionEnd: NbfCreatePacket failed.\n"); + } +#endif + NbfWaitPacket (Connection, CONNECTION_FLAGS_SEND_SE); + NbfDereferenceConnection("Couldn't get SE packet", Connection, CREF_FRAME_SEND); + return; + } + + // + // The following statements instruct the packet destructor to run + // down this connection when the packet is acknowleged. + // + + Packet->Owner = Connection; + Packet->Action = PACKET_ACTION_END; + + + // + // Initialize the Netbios header. + // + + ConstructSessionEnd ( + (PNBF_HDR_CONNECTION)&(Packet->Header[Link->HeaderLength + sizeof(DLC_I_FRAME)]), + (USHORT)(Abort ? // reason for termination. + SESSION_END_REASON_ABEND : + SESSION_END_REASON_HANGUP), + Connection->Lsn, // our local session number. + Connection->Rsn); // his session number (our RSN). + + // + // Now send the packet on the connection via the link. If there are + // conditions on the link which make it impossible to send the packet, + // then the packet will be queued to the WackQ, and then timeouts will + // restart the link. This is acceptable when the traffic level is so + // high that we encounter this condition. + // + // Note that we force an ack for this packet, as we want to make sure we + // run down the connection and link correctly. + // + + Packet->NdisIFrameLength = + Link->HeaderLength + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION); + + MacModifyHeader( + &DeviceContext->MacInfo, + Packet->Header, + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION)); + + NbfSetNdisPacketLength( + Packet->NdisPacket, + Packet->NdisIFrameLength); + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + Status = SendOnePacket (Connection, Packet, TRUE, NULL); // fire and forget. + + if (Status == STATUS_LINK_FAILED) { + NbfDereferencePacket (Packet); // destroy the packet. + } + + return; +} /* NbfSendSessionEnd */ + + +VOID +NbfSendNoReceive( + IN PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine sends a NO_RECEIVE frame on the specified connection. + +Arguments: + + Connection - Pointer to a transport connection object. + + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PTP_PACKET Packet; + PDEVICE_CONTEXT DeviceContext; + PTP_LINK Link; + USHORT MessageBytesToAck; + + NbfReferenceConnection("send No Receive", Connection, CREF_FRAME_SEND); + + DeviceContext = Connection->Provider; + Link = Connection->Link; + Status = NbfCreatePacket (DeviceContext, Connection->Link, &Packet); + + if (!NT_SUCCESS (Status)) { // if we couldn't make frame. +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfSendNoReceive: NbfCreatePacket failed.\n"); + } +#endif + NbfDereferenceConnection("Couldn't get NR packet", Connection, CREF_FRAME_SEND); + return; + } + + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + MessageBytesToAck = (USHORT) + (Connection->MessageBytesReceived + Connection->MessageInitAccepted - Connection->MessageBytesAcked); + Connection->Flags |= CONNECTION_FLAGS_W_RESYNCH; + + // + // Initialize the Netbios header. + // + + ConstructNoReceive ( + (PNBF_HDR_CONNECTION)&(Packet->Header[Link->HeaderLength + sizeof(DLC_I_FRAME)]), + (USHORT)0, // options + MessageBytesToAck, // number of bytes accepted. + Connection->Lsn, // our local session number. + Connection->Rsn); // his session number (our RSN). + + // + // Now send the packet on the connection via the link. If there are + // conditions on the link which make it impossible to send the packet, + // then the packet will be queued to the WackQ, and then timeouts will + // restart the link. This is acceptable when the traffic level is so + // high that we encounter this condition. + // + + // + // Set this so NbfDestroyPacket will dereference the connection. + // + + Packet->Owner = Connection; + Packet->Action = PACKET_ACTION_CONNECTION; + + Packet->NdisIFrameLength = + Link->HeaderLength + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION); + + MacModifyHeader( + &DeviceContext->MacInfo, + Packet->Header, + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION)); + + NbfSetNdisPacketLength( + Packet->NdisPacket, + Packet->NdisIFrameLength); + + Status = SendOnePacket (Connection, Packet, FALSE, NULL); // fire and forget. + + if (Status != STATUS_LINK_FAILED) { + ExInterlockedAddUlong( + &Connection->MessageBytesAcked, + MessageBytesToAck, + Connection->LinkSpinLock); + } else { + NbfDereferencePacket (Packet); // destroy the packet. + } + + return; +} /* NbfSendNoReceive */ + + +VOID +NbfSendReceiveContinue( + IN PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine sends a RECEIVE_CONTINUE frame on the specified connection. + +Arguments: + + Connection - Pointer to a transport connection object. + + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PTP_PACKET Packet; + PDEVICE_CONTEXT DeviceContext; + PTP_LINK Link; + USHORT MessageBytesToAck; + + NbfReferenceConnection("send Receive Continue", Connection, CREF_FRAME_SEND); + + DeviceContext = Connection->Provider; + Link = Connection->Link; + Status = NbfCreatePacket (DeviceContext, Connection->Link, &Packet); + + if (!NT_SUCCESS (Status)) { // if we couldn't make frame. +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfSendReceiveContinue: NbfCreatePacket failed.\n"); + } +#endif + NbfWaitPacket (Connection, CONNECTION_FLAGS_SEND_RC); + NbfDereferenceConnection("Couldn't get RC packet", Connection, CREF_FRAME_SEND); + return; + } + + // + // Save this variable now since it is what we are implicitly ack'ing. + // + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + MessageBytesToAck = (USHORT) + (Connection->MessageBytesReceived + Connection->MessageInitAccepted - Connection->MessageBytesAcked); + + // + // Initialize the Netbios header. + // + + ConstructReceiveContinue ( + (PNBF_HDR_CONNECTION)&(Packet->Header[Link->HeaderLength + sizeof(DLC_I_FRAME)]), + Connection->NetbiosHeader.TransmitCorrelator, // correlator from DFM + Connection->Lsn, // our local session number. + Connection->Rsn); // his session number (our RSN). + + // + // Now send the packet on the connection via the link. If there are + // conditions on the link which make it impossible to send the packet, + // then the packet will be queued to the WackQ, and then timeouts will + // restart the link. This is acceptable when the traffic level is so + // high that we encounter this condition. + // + + // + // Set this so NbfDestroyPacket will dereference the connection. + // + + Packet->Owner = Connection; + Packet->Action = PACKET_ACTION_CONNECTION; + + Packet->NdisIFrameLength = + Link->HeaderLength + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION); + + MacModifyHeader( + &DeviceContext->MacInfo, + Packet->Header, + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION)); + + NbfSetNdisPacketLength( + Packet->NdisPacket, + Packet->NdisIFrameLength); + + Status = SendOnePacket (Connection, Packet, FALSE, NULL); // fire and forget. + + if (Status != STATUS_LINK_FAILED) { + ExInterlockedAddUlong( + &Connection->MessageBytesAcked, + MessageBytesToAck, + Connection->LinkSpinLock); + } else { + NbfDereferencePacket (Packet); // destroy the packet. + } + + return; +} /* NbfSendReceiveContinue */ + + +VOID +NbfSendReceiveOutstanding( + IN PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine sends a RECEIVE_OUTSTANDING frame on the specified connection. + +Arguments: + + Connection - Pointer to a transport connection object. + + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PTP_PACKET Packet; + PDEVICE_CONTEXT DeviceContext; + PTP_LINK Link; + USHORT MessageBytesToAck; + + NbfReferenceConnection("send Receive Outstanding", Connection, CREF_FRAME_SEND); + + DeviceContext = Connection->Provider; + Link = Connection->Link; + Status = NbfCreatePacket (DeviceContext, Connection->Link, &Packet); + + if (!NT_SUCCESS (Status)) { // if we couldn't make frame. +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfSendReceiveOutstanding: NbfCreatePacket failed.\n"); + } +#endif + NbfWaitPacket (Connection, CONNECTION_FLAGS_SEND_RO); + NbfDereferenceConnection("Couldn't get RO packet", Connection, CREF_FRAME_SEND); + return; + } + + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + MessageBytesToAck = (USHORT) + (Connection->MessageBytesReceived + Connection->MessageInitAccepted - Connection->MessageBytesAcked); + Connection->Flags |= CONNECTION_FLAGS_W_RESYNCH; + + // + // Initialize the Netbios header. + // + + ConstructReceiveOutstanding ( + (PNBF_HDR_CONNECTION)&(Packet->Header[Link->HeaderLength + sizeof(DLC_I_FRAME)]), + MessageBytesToAck, // number of bytes accepted. + Connection->Lsn, // our local session number. + Connection->Rsn); // his session number (our RSN). + + + // + // Now send the packet on the connection via the link. If there are + // conditions on the link which make it impossible to send the packet, + // then the packet will be queued to the WackQ, and then timeouts will + // restart the link. This is acceptable when the traffic level is so + // high that we encounter this condition. + // + + // + // Set this so NbfDestroyPacket will dereference the connection. + // + + Packet->Owner = Connection; + Packet->Action = PACKET_ACTION_CONNECTION; + + Packet->NdisIFrameLength = + Link->HeaderLength + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION); + + MacModifyHeader( + &DeviceContext->MacInfo, + Packet->Header, + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION)); + + NbfSetNdisPacketLength( + Packet->NdisPacket, + Packet->NdisIFrameLength); + + Status = SendOnePacket (Connection, Packet, FALSE, NULL); // fire and forget. + + if (Status != STATUS_LINK_FAILED) { + ExInterlockedAddUlong( + &Connection->MessageBytesAcked, + MessageBytesToAck, + Connection->LinkSpinLock); + } else { + NbfDereferencePacket (Packet); // destroy the packet. + } + + return; +} /* NbfSendReceiveOutstanding */ + + +VOID +NbfSendDataAck( + IN PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine sends a DATA_ACK frame on the specified connection. + +Arguments: + + Connection - Pointer to a transport connection object. + + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PTP_PACKET Packet; + PDEVICE_CONTEXT DeviceContext; + PTP_LINK Link; + + NbfReferenceConnection("send Data Ack", Connection, CREF_FRAME_SEND); + + DeviceContext = Connection->Provider; + Link = Connection->Link; + Status = NbfCreatePacket (DeviceContext, Connection->Link, &Packet); + + if (!NT_SUCCESS (Status)) { // if we couldn't make frame. +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfSendDataAck: NbfCreatePacket failed.\n"); + } +#endif + NbfWaitPacket (Connection, CONNECTION_FLAGS_SEND_DA); + NbfDereferenceConnection("Couldn't get DA packet", Connection, CREF_FRAME_SEND); + return; + } + + + // + // Initialize the Netbios header. + // + + ConstructDataAck ( + (PNBF_HDR_CONNECTION)&(Packet->Header[Link->HeaderLength + sizeof(DLC_I_FRAME)]), + Connection->NetbiosHeader.TransmitCorrelator, // correlator from DATA_ONLY_LAST. + Connection->Lsn, // our local session number. + Connection->Rsn); // his session number (our RSN). + + // + // Now send the packet on the connection via the link. If there are + // conditions on the link which make it impossible to send the packet, + // then the packet will be queued to the WackQ, and then timeouts will + // restart the link. This is acceptable when the traffic level is so + // high that we encounter this condition. Note that Data Ack will be + // seeing this condition frequently when send windows close after large + // sends. + // + + // + // Set this so NbfDestroyPacket will dereference the connection. + // + + Packet->Owner = Connection; + Packet->Action = PACKET_ACTION_CONNECTION; + + Packet->NdisIFrameLength = + Link->HeaderLength + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION); + + MacModifyHeader( + &DeviceContext->MacInfo, + Packet->Header, + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION)); + + NbfSetNdisPacketLength( + Packet->NdisPacket, + Packet->NdisIFrameLength); + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + Status = SendOnePacket (Connection, Packet, FALSE, NULL); // fire and forget. + + if (Status == STATUS_LINK_FAILED) { + NbfDereferencePacket (Packet); // destroy the packet. + } + + return; +} /* NbfSendDataAck */ + + +VOID +NbfSendDm( + IN PTP_LINK Link, + IN BOOLEAN PollFinal + ) + +/*++ + +Routine Description: + + This routine sends a DM-r/x DLC frame on the specified link. + + NOTE: This routine is called with the link spinlock held, + and returns with it released. IT MUST BE CALLED AT DPC + LEVEL. + +Arguments: + + Link - Pointer to a transport link object. + + PollFinal - TRUE if poll/final bit should be set. + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PTP_PACKET RawFrame; + PDLC_U_FRAME DlcHeader; // S-format frame alias. + + Status = NbfCreatePacket (Link->Provider, Link, &RawFrame); + if (NT_SUCCESS (Status)) { + + RawFrame->Owner = NULL; + RawFrame->Action = PACKET_ACTION_NULL; + + // + // set the packet length correctly (Note that the NDIS_BUFFER + // gets returned to the proper length in NbfDestroyPacket) + // + + MacModifyHeader(&Link->Provider->MacInfo, RawFrame->Header, sizeof(DLC_FRAME)); + NbfSetNdisPacketLength (RawFrame->NdisPacket, Link->HeaderLength + sizeof(DLC_FRAME)); + + // + // Format LLC DM-r/x header. + // + + DlcHeader = (PDLC_U_FRAME)&(RawFrame->Header[Link->HeaderLength]); + DlcHeader->Dsap = DSAP_NETBIOS_OVER_LLC; + DlcHeader->Ssap = DSAP_NETBIOS_OVER_LLC | DLC_SSAP_RESPONSE; + DlcHeader->Command = (UCHAR)(DLC_CMD_DM | (PollFinal ? DLC_U_PF : 0)); + + // + // This releases the spin lock. + // + + SendControlPacket (Link, RawFrame); + + } else { + RELEASE_DPC_SPIN_LOCK(&Link->SpinLock); +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfSendDm: packet not sent.\n"); + } +#endif + } +} /* NbfSendDm */ + + +VOID +NbfSendUa( + IN PTP_LINK Link, + IN BOOLEAN PollFinal + ) + +/*++ + +Routine Description: + + This routine sends a UA-r/x DLC frame on the specified link. + + NOTE: This routine is called with the link spinlock held, + and returns with it released. IT MUST BE CALLED AT DPC + LEVEL. + +Arguments: + + Link - Pointer to a transport link object. + + PollFinal - TRUE if poll/final bit should be set. + + OldIrql - The IRQL at which Link->SpinLock was acquired. + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PTP_PACKET RawFrame; + PDLC_U_FRAME DlcHeader; // U-format frame alias. + + Status = NbfCreatePacket (Link->Provider, Link, &RawFrame); + if (NT_SUCCESS (Status)) { + + RawFrame->Owner = NULL; + RawFrame->Action = PACKET_ACTION_NULL; + + // + // set the packet length correctly (Note that the NDIS_BUFFER + // gets returned to the proper length in NbfDestroyPacket) + // + + MacModifyHeader(&Link->Provider->MacInfo, RawFrame->Header, sizeof(DLC_FRAME)); + NbfSetNdisPacketLength (RawFrame->NdisPacket, Link->HeaderLength + sizeof(DLC_FRAME)); + + // Format LLC UA-r/x header. + // + + DlcHeader = (PDLC_U_FRAME)&(RawFrame->Header[Link->HeaderLength]); + DlcHeader->Dsap = DSAP_NETBIOS_OVER_LLC; + DlcHeader->Ssap = DSAP_NETBIOS_OVER_LLC | DLC_SSAP_RESPONSE; + DlcHeader->Command = (UCHAR)(DLC_CMD_UA | (PollFinal ? DLC_U_PF : 0)); + + // + // This releases the spin lock. + // + + SendControlPacket (Link, RawFrame); + + } else { + RELEASE_DPC_SPIN_LOCK(&Link->SpinLock); +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfSendUa: packet not sent.\n"); + } +#endif + } +} /* NbfSendUa */ + + +VOID +NbfSendSabme( + IN PTP_LINK Link, + IN BOOLEAN PollFinal + ) + +/*++ + +Routine Description: + + This routine sends a SABME-c/x DLC frame on the specified link. + + NOTE: This routine is called with the link spinlock held, + and returns with it released. + +Arguments: + + Link - Pointer to a transport link object. + + PollFinal - TRUE if poll/final bit should be set. + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PLIST_ENTRY p; + PTP_PACKET RawFrame, packet; + PDLC_U_FRAME DlcHeader; // S-format frame alias. + + Status = NbfCreatePacket (Link->Provider, Link, &RawFrame); + if (NT_SUCCESS (Status)) { + + RawFrame->Owner = NULL; + RawFrame->Action = PACKET_ACTION_NULL; + + // + // set the packet length correctly (Note that the NDIS_BUFFER + // gets returned to the proper length in NbfDestroyPacket) + // + + MacModifyHeader(&Link->Provider->MacInfo, RawFrame->Header, sizeof(DLC_FRAME)); + NbfSetNdisPacketLength (RawFrame->NdisPacket, Link->HeaderLength + sizeof(DLC_FRAME)); + + // + // Format LLC SABME-c/x header. + // + + DlcHeader = (PDLC_U_FRAME)&(RawFrame->Header[Link->HeaderLength]); + DlcHeader->Dsap = DSAP_NETBIOS_OVER_LLC; + DlcHeader->Ssap = DSAP_NETBIOS_OVER_LLC; + DlcHeader->Command = (UCHAR)(DLC_CMD_SABME | (PollFinal ? DLC_U_PF : 0)); + + // + // Set up so that T1 will be started when the send + // completes. + // + + if (PollFinal) { + if (Link->Provider->MacInfo.MediumAsync) { + RawFrame->NdisIFrameLength = Link->HeaderLength + sizeof(DLC_S_FRAME); + RawFrame->Link = Link; + NbfReferenceLink ("Sabme/p", Link, LREF_START_T1); + } else { + StartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); + } + } + + // + // This releases the spin lock. + // + + SendControlPacket (Link, RawFrame); + + // + // Reset the link state based on having sent this packet.. + // Note that a SABME can be sent under some conditions on an existing + // link. If it is, it means we want to reset this link to a known state. + // We'll do that; note that that involves ditching any packets outstanding + // on the link. + // + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + Link->NextSend = 0; + Link->LastAckReceived = 0; + Link->NextReceive = 0; // expect next packet to be sequence 0 + Link->NextReceive = 0; + Link->LastAckSent = 0; + Link->NextReceive = 0; + Link->LastAckSent = 0; + + while (!IsListEmpty (&Link->WackQ)) { + p = RemoveHeadList (&Link->WackQ); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + packet = CONTAINING_RECORD (p, TP_PACKET, Linkage); + NbfDereferencePacket (packet); + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + } + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + } else { + if (PollFinal) { + StartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); + } + RELEASE_DPC_SPIN_LOCK(&Link->SpinLock); +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfSendSabme: packet not sent.\n"); + } +#endif + } +} /* NbfSendSabme */ + + +VOID +NbfSendDisc( + IN PTP_LINK Link, + IN BOOLEAN PollFinal + ) + +/*++ + +Routine Description: + + This routine sends a DISC-c/x DLC frame on the specified link. + +Arguments: + + Link - Pointer to a transport link object. + + PollFinal - TRUE if poll/final bit should be set. + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PTP_PACKET RawFrame; + PDLC_U_FRAME DlcHeader; // S-format frame alias. + KIRQL oldirql; + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + + Status = NbfCreatePacket (Link->Provider, Link, &RawFrame); + if (NT_SUCCESS (Status)) { + + RawFrame->Owner = NULL; + RawFrame->Action = PACKET_ACTION_NULL; + + // + // set the packet length correctly (Note that the NDIS_BUFFER + // gets returned to the proper length in NbfDestroyPacket) + // + + MacModifyHeader(&Link->Provider->MacInfo, RawFrame->Header, sizeof(DLC_FRAME)); + NbfSetNdisPacketLength (RawFrame->NdisPacket, Link->HeaderLength + sizeof(DLC_FRAME)); + + // + // Format LLC DISC-c/x header. + // + + DlcHeader = (PDLC_U_FRAME)&(RawFrame->Header[Link->HeaderLength]); + DlcHeader->Dsap = DSAP_NETBIOS_OVER_LLC; + DlcHeader->Ssap = DSAP_NETBIOS_OVER_LLC; + DlcHeader->Command = (UCHAR)(DLC_CMD_DISC | (PollFinal ? DLC_U_PF : 0)); + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + // + // This releases the spin lock. + // + + SendControlPacket (Link, RawFrame); + + } else { +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfSendDisc: packet not sent.\n"); + } +#endif + } + + KeLowerIrql (oldirql); + +} /* NbfSendDisc */ + + +VOID +NbfSendRr( + IN PTP_LINK Link, + IN BOOLEAN Command, + IN BOOLEAN PollFinal + ) + +/*++ + +Routine Description: + + This routine sends a RR-x/x DLC frame on the specified link. + + NOTE: This routine is called with the link spinlock held, + and returns with it released. THIS ROUTINE MUST BE CALLED + AT DPC LEVEL. + +Arguments: + + Link - Pointer to a transport link object. + + Command - TRUE if command bit should be set. + + PollFinal - TRUE if poll/final bit should be set. + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PTP_PACKET RawFrame; + PDLC_S_FRAME DlcHeader; // S-format frame alias. + + Status = NbfCreateRrPacket (Link->Provider, Link, &RawFrame); + if (NT_SUCCESS (Status)) { + + RawFrame->Owner = NULL; + + // + // RawFrame->Action will be set to PACKET_ACTION_RR if + // NbfCreateRrPacket got a packet from the RrPacketPool + // and PACKET_ACTION_NULL if it got one from the regular + // pool. + // + + // + // set the packet length correctly (Note that the NDIS_BUFFER + // gets returned to the proper length in NbfDestroyPacket) + // + + MacModifyHeader(&Link->Provider->MacInfo, RawFrame->Header, sizeof(DLC_S_FRAME)); + NbfSetNdisPacketLength (RawFrame->NdisPacket, Link->HeaderLength + sizeof(DLC_S_FRAME)); + + // + // Format LLC RR-x/x header. + // + + DlcHeader = (PDLC_S_FRAME)&(RawFrame->Header[Link->HeaderLength]); + DlcHeader->Dsap = DSAP_NETBIOS_OVER_LLC; + DlcHeader->Ssap = (UCHAR)(DSAP_NETBIOS_OVER_LLC | (Command ? 0 : DLC_SSAP_RESPONSE)); + DlcHeader->Command = DLC_CMD_RR; + DlcHeader->RcvSeq = (UCHAR)(PollFinal ? DLC_S_PF : 0); + + // + // If this is a command frame (which will always be a + // poll with the current code) set up so that T1 will + // be started when the send completes. + // + + if (Command) { + if (Link->Provider->MacInfo.MediumAsync) { + RawFrame->NdisIFrameLength = Link->HeaderLength + sizeof(DLC_S_FRAME); + RawFrame->Link = Link; + NbfReferenceLink ("Rr/p", Link, LREF_START_T1); + } else { + StartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); + } + } + + // + // This puts Link->NextReceive into DlcHeader->RcvSeq + // and releases the spinlock. + // + + SendControlPacket (Link, RawFrame); + + } else { + if (Command) { + StartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); + } + RELEASE_DPC_SPIN_LOCK(&Link->SpinLock); +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfSendRr: packet not sent.\n"); + } +#endif + } +} /* NbfSendRr */ + +#if 0 + +// +// These functions are not currently called, so they are commented +// out. +// + + +VOID +NbfSendRnr( + IN PTP_LINK Link, + IN BOOLEAN Command, + IN BOOLEAN PollFinal + ) + +/*++ + +Routine Description: + + This routine sends a RNR-x/x DLC frame on the specified link. + +Arguments: + + Link - Pointer to a transport link object. + + Command - TRUE if command bit should be set. + + PollFinal - TRUE if poll/final bit should be set. + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PTP_PACKET RawFrame; + PDLC_S_FRAME DlcHeader; // S-format frame alias. + KIRQL oldirql; + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + + Status = NbfCreatePacket (Link->Provider, Link, &RawFrame); + if (NT_SUCCESS (Status)) { + + RawFrame->Owner = NULL; + RawFrame->Action = PACKET_ACTION_NULL; + + // + // set the packet length correctly (Note that the NDIS_BUFFER + // gets returned to the proper length in NbfDestroyPacket) + // + + MacModifyHeader(&Link->Provider->MacInfo, RawFrame->Header, sizeof(DLC_S_FRAME)); + NbfSetNdisPacketLength (RawFrame->NdisPacket, Link->HeaderLength + sizeof(DLC_S_FRAME)); + + // + // Format LLC RR-x/x header. + // + + DlcHeader = (PDLC_S_FRAME)&(RawFrame->Header[Link->HeaderLength]); + DlcHeader->Dsap = DSAP_NETBIOS_OVER_LLC; + DlcHeader->Ssap = (UCHAR)(DSAP_NETBIOS_OVER_LLC | (Command ? 0 : DLC_SSAP_RESPONSE)); + DlcHeader->Command = DLC_CMD_RNR; + DlcHeader->RcvSeq = (UCHAR)(PollFinal ? DLC_S_PF : 0); + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + // + // This puts Link->NextReceive into DlcHeader->RcvSeq + // and releases the spin lock. + // + + SendControlPacket (Link, RawFrame); + + } else { +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfSendRnr: packet not sent.\n"); + } +#endif + } + KeLowerIrql (oldirql); +} /* NbfSendRnr */ + + +VOID +NbfSendTest( + IN PTP_LINK Link, + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PMDL Psdu + ) + +/*++ + +Routine Description: + + This routine sends a TEST-x/x DLC frame on the specified link. + +Arguments: + + Link - Pointer to a transport link object. + + Command - TRUE if command bit should be set. + + PollFinal - TRUE if poll/final bit should be set. + + Psdu - Pointer to an MDL chain describing received TEST-c frame's storage. + +Return Value: + + none. + +--*/ + +{ + Link, Command, PollFinal, Psdu; // prevent compiler warnings + + PANIC ("NbfSendTest: Entered (BUGBUG).\n"); +} /* NbfSendTest */ + + +VOID +NbfSendFrmr( + IN PTP_LINK Link, + IN BOOLEAN PollFinal + ) + +/*++ + +Routine Description: + + This routine sends a FRMR-r/x DLC frame on the specified link. + +Arguments: + + Link - Pointer to a transport link object. + + PollFinal - TRUE if poll/final bit should be set. + +Return Value: + + none. + +--*/ + +{ + Link, PollFinal; // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_FRAMESND) { + NbfPrint0 ("NbfSendFrmr: Entered (BUGBUG).\n"); + } +} /* NbfSendFrmr */ + +#endif + + +VOID +NbfSendXid( + IN PTP_LINK Link, + IN BOOLEAN Command, + IN BOOLEAN PollFinal + ) + +/*++ + +Routine Description: + + This routine sends an XID-x/x DLC frame on the specified link. + + NOTE: This routine is called with the link spinlock held, + and returns with it released. + +Arguments: + + Link - Pointer to a transport link object. + + Command - TRUE if command bit should be set. + + PollFinal - TRUE if poll/final bit should be set. + +Return Value: + + none. + +--*/ + +{ + Link, Command, PollFinal; // prevent compiler warnings + + RELEASE_DPC_SPIN_LOCK(&Link->SpinLock); + PANIC ("NbfSendXid: Entered (BUGBUG).\n"); +} /* NbfSendXid */ + + +VOID +NbfSendRej( + IN PTP_LINK Link, + IN BOOLEAN Command, + IN BOOLEAN PollFinal + ) + +/*++ + +Routine Description: + + This routine sends a REJ-x/x DLC frame on the specified link. + + NOTE: This function is called with Link->SpinLock held and + returns with it released. THIS MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Link - Pointer to a transport link object. + + Command - TRUE if command bit should be set. + + PollFinal - TRUE if poll/final bit should be set. + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PTP_PACKET RawFrame; + PDLC_S_FRAME DlcHeader; // S-format frame alias. + + IF_NBFDBG (NBF_DEBUG_FRAMESND) { + NbfPrint0 ("NbfSendRej: Entered.\n"); + } + + Status = NbfCreatePacket (Link->Provider, Link, &RawFrame); + if (NT_SUCCESS (Status)) { + + RawFrame->Owner = NULL; + RawFrame->Action = PACKET_ACTION_NULL; + + // + // set the packet length correctly (Note that the NDIS_BUFFER + // gets returned to the proper length in NbfDestroyPacket) + // + + MacModifyHeader(&Link->Provider->MacInfo, RawFrame->Header, sizeof(DLC_S_FRAME)); + NbfSetNdisPacketLength (RawFrame->NdisPacket, Link->HeaderLength + sizeof(DLC_S_FRAME)); + + // + // Format LLC REJ-x/x header. + // + + DlcHeader = (PDLC_S_FRAME)&(RawFrame->Header[Link->HeaderLength]); + DlcHeader->Dsap = DSAP_NETBIOS_OVER_LLC; + DlcHeader->Ssap = (UCHAR)(DSAP_NETBIOS_OVER_LLC | (Command ? 0 : DLC_SSAP_RESPONSE)); + DlcHeader->Command = DLC_CMD_REJ; + DlcHeader->RcvSeq = (UCHAR)(PollFinal ? DLC_S_PF : 0); + + // + // This puts Link->NextReceive into DlcHeader->RcvSeq + // and releases the spin lock. + // + + SendControlPacket (Link, RawFrame); + + } else { + RELEASE_DPC_SPIN_LOCK(&Link->SpinLock); +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfSendRej: packet not sent.\n"); + } +#endif + } +} /* NbfSendRej */ + + +NTSTATUS +NbfCreateConnectionlessFrame( + PDEVICE_CONTEXT DeviceContext, + PTP_UI_FRAME *RawFrame + ) + +/*++ + +Routine Description: + + This routine allocates a connectionless frame (either from the local + device context pool or out of non-paged pool). + +Arguments: + + DeviceContext - Pointer to our device context to charge the frame to. + + RawFrame - Pointer to a place where we will return a pointer to the + allocated frame. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PTP_UI_FRAME UIFrame; + PLIST_ENTRY p; + + IF_NBFDBG (NBF_DEBUG_FRAMESND) { + NbfPrint0 ("NbfCreateConnectionlessFrame: Entered.\n"); + } + + // + // Make sure that structure padding hasn't happened. + // + + ASSERT (sizeof(NBF_HDR_CONNECTIONLESS) == 44); + + p = ExInterlockedRemoveHeadList ( + &DeviceContext->UIFramePool, + &DeviceContext->Interlock); + + if (p == NULL) { +#if DBG + if (NbfPacketPanic) { + PANIC ("NbfCreateConnectionlessFrame: PANIC! no more UI frames in pool!\n"); + } +#endif + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + ++DeviceContext->UIFrameExhausted; + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + return STATUS_INSUFFICIENT_RESOURCES; + } + + UIFrame = (PTP_UI_FRAME) CONTAINING_RECORD (p, TP_UI_FRAME, Linkage); + + *RawFrame = UIFrame; + + return STATUS_SUCCESS; +} /* NbfCreateConnectionlessFrame */ + + +VOID +NbfDestroyConnectionlessFrame( + PDEVICE_CONTEXT DeviceContext, + PTP_UI_FRAME RawFrame + ) + +/*++ + +Routine Description: + + This routine destroys a connectionless frame by either returning it + to the device context's pool or to the system's non-paged pool. + +Arguments: + + DeviceContext - Pointer to our device context to return the frame to. + + RawFrame - Pointer to a frame to be returned. + +Return Value: + + none. + +--*/ + +{ + PNDIS_BUFFER HeaderBuffer; + PNDIS_BUFFER NdisBuffer; + + IF_NBFDBG (NBF_DEBUG_FRAMESND) { + NbfPrint0 ("NbfDestroyConnectionlessFrame: Entered.\n"); + } + + // + // Strip off and unmap the buffers describing data and header. + // + + NdisUnchainBufferAtFront (RawFrame->NdisPacket, &HeaderBuffer); + + // data buffers get thrown away + + NdisUnchainBufferAtFront (RawFrame->NdisPacket, &NdisBuffer); + while (NdisBuffer != NULL) { + NdisFreeBuffer (NdisBuffer); + NdisUnchainBufferAtFront (RawFrame->NdisPacket, &NdisBuffer); + } + + NDIS_BUFFER_LINKAGE(HeaderBuffer) = (PNDIS_BUFFER)NULL; + + // + // If this UI frame has some transport-created data, + // free the buffer now. + // + + if (RawFrame->DataBuffer) { + ExFreePool (RawFrame->DataBuffer); + RawFrame->DataBuffer = NULL; + } + + NdisChainBufferAtFront (RawFrame->NdisPacket, HeaderBuffer); + + ExInterlockedInsertTailList ( + &DeviceContext->UIFramePool, + &RawFrame->Linkage, + &DeviceContext->Interlock); + +} /* NbfDestroyConnectionlessFrame */ + + +VOID +NbfSendUIFrame( + PDEVICE_CONTEXT DeviceContext, + PTP_UI_FRAME RawFrame, + IN BOOLEAN Loopback + ) + +/*++ + +Routine Description: + + This routine sends a connectionless frame by calling the physical + provider's Send service. When the request completes, or if the service + does not return successfully, then the frame is deallocated. + +Arguments: + + DeviceContext - Pointer to our device context. + + RawFrame - Pointer to a connectionless frame to be sent. + + Loopback - A boolean flag set to TRUE if the source hardware address + of the packet should be set to zeros. + + SourceRoutingInformation - Pointer to optional source routing information. + +Return Value: + + None. + +--*/ + +{ + NDIS_STATUS NdisStatus; + PUCHAR DestinationAddress; + + UNREFERENCED_PARAMETER(Loopback); + +#if DBG + IF_NBFDBG (NBF_DEBUG_FRAMESND) { + NbfPrint2 ("NbfSendUIFrame: Entered, RawFrame: %lx NdisPacket %lx\n", + RawFrame, RawFrame->NdisPacket); + DbgPrint ("NbfSendUIFrame: MacHeader: %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x \n", + RawFrame->Header[0], + RawFrame->Header[1], + RawFrame->Header[2], + RawFrame->Header[3], + RawFrame->Header[4], + RawFrame->Header[5], + RawFrame->Header[6], + RawFrame->Header[7], + RawFrame->Header[8], + RawFrame->Header[9], + RawFrame->Header[10], + RawFrame->Header[11], + RawFrame->Header[12], + RawFrame->Header[13]); + } +#endif + + // + // Send the packet. + // + +#if DBG + NbfSendsIssued++; +#endif + + // + // Loopback will be FALSE for multicast frames or other + // frames that we know are not directly addressed to + // our hardware address. + // + + if (Loopback) { + + // + // See if this frame should be looped back. + // + + MacReturnDestinationAddress( + &DeviceContext->MacInfo, + RawFrame->Header, + &DestinationAddress); + + if (RtlEqualMemory( + DestinationAddress, + DeviceContext->LocalAddress.Address, + DeviceContext->MacInfo.AddressLength)) { + + NbfInsertInLoopbackQueue( + DeviceContext, + RawFrame->NdisPacket, + LOOPBACK_UI_FRAME + ); + + NdisStatus = NDIS_STATUS_PENDING; + + goto NoNdisSend; + + } + + } + + INCREMENT_COUNTER (DeviceContext, PacketsSent); + + NdisSend ( + &NdisStatus, + (NDIS_HANDLE)DeviceContext->NdisBindingHandle, + RawFrame->NdisPacket); + +NoNdisSend: + + if (NdisStatus != NDIS_STATUS_PENDING) { + +#if DBG + if (NdisStatus == NDIS_STATUS_SUCCESS) { + NbfSendsCompletedOk++; + } else { + NbfSendsCompletedFail++; + IF_NBFDBG (NBF_DEBUG_FRAMESND) { + NbfPrint1 ("NbfSendUIFrame: NdisSend failed, status other Pending or Complete: %s.\n", + NbfGetNdisStatus(NdisStatus)); + } + } +#endif + + NbfDestroyConnectionlessFrame (DeviceContext, RawFrame); + + } else { + +#if DBG + NbfSendsPended++; +#endif + } + +} /* NbfSendUIFrame */ + + +VOID +NbfSendUIMdlFrame( + PTP_ADDRESS Address + ) + +/*++ + +Routine Description: + + This routine sends a connectionless frame by calling the NbfSendUIFrame. + It is intended that this routine be used for sending datagrams and + braodcast datagrams. + + The datagram to be sent is described in the NDIS packet contained + in the Address. When the send completes, the send completion handler + returns the NDIS buffer describing the datagram to the buffer pool and + marks the address ndis packet as usable again. Thus, all datagram and + UI frames are sequenced through the address they are sent on. + +Arguments: + + Address - pointer to the address from which to send this datagram. + + SourceRoutingInformation - Pointer to optional source routing information. + +Return Value: + + None. + +--*/ + +{ +// NTSTATUS Status; + NDIS_STATUS NdisStatus; + PDEVICE_CONTEXT DeviceContext; + PUCHAR DestinationAddress; + + IF_NBFDBG (NBF_DEBUG_FRAMESND) { + NbfPrint0 ("NbfSendUIMdlFrame: Entered.\n"); + } + + + // + // Send the packet. + // + + DeviceContext = Address->Provider; + + INCREMENT_COUNTER (DeviceContext, PacketsSent); + + MacReturnDestinationAddress( + &DeviceContext->MacInfo, + Address->UIFrame->Header, + &DestinationAddress); + + if (RtlEqualMemory( + DestinationAddress, + DeviceContext->LocalAddress.Address, + DeviceContext->MacInfo.AddressLength)) { + + // + // This packet is sent to ourselves; we should loop it + // back. + // + + NbfInsertInLoopbackQueue( + DeviceContext, + Address->UIFrame->NdisPacket, + LOOPBACK_UI_FRAME + ); + + NdisStatus = NDIS_STATUS_PENDING; + + } else { + + NdisSend ( + &NdisStatus, + (NDIS_HANDLE)Address->Provider->NdisBindingHandle, + Address->UIFrame->NdisPacket); + + } + + if (NdisStatus != NDIS_STATUS_PENDING) { + + NbfSendDatagramCompletion (Address, Address->UIFrame->NdisPacket, NdisStatus); + +#if DBG + if (NdisStatus != NDIS_STATUS_SUCCESS) { // This is an error, trickle it up + IF_NBFDBG (NBF_DEBUG_FRAMESND) { + NbfPrint1 ("NbfSendUIMdlFrame: NdisSend failed, status other Pending or Complete: %s.\n", + NbfGetNdisStatus(NdisStatus)); + } + } +#endif + } + +} /* NbfSendUIMdlFrame */ + + +VOID +NbfSendDatagramCompletion( + IN PTP_ADDRESS Address, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called as an I/O completion handler at the time a + NbfSendUIMdlFrame send request is completed. Because this handler is only + associated with NbfSendUIMdlFrame, and because NbfSendUIMdlFrame is only + used with datagrams and broadcast datagrams, we know that the I/O being + completed is a datagram. Here we complete the in-progress datagram, and + start-up the next one if there is one. + +Arguments: + + Address - Pointer to a transport address on which the datagram + is queued. + + NdisPacket - pointer to the NDIS packet describing this request. + +Return Value: + + none. + +--*/ + +{ + PIRP Irp; + PLIST_ENTRY p; + KIRQL oldirql; + PNDIS_BUFFER HeaderBuffer; + + NdisPacket; // prevent compiler warnings. + + IF_NBFDBG (NBF_DEBUG_FRAMESND) { + NbfPrint0 ("NbfSendDatagramCompletion: Entered.\n"); + } + + + // + // Dequeue the current request and return it to the client. Release + // our hold on the send datagram queue. + // + // *** There may be no current request, if the one that was queued + // was aborted or timed out. If this is the case, we added a + // special reference to the address, so we still want to deref + // when we are done (I don't think this is true - adb 3/22/93). + // + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + p = RemoveHeadList (&Address->SendDatagramQueue); + + if (p != &Address->SendDatagramQueue) { + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + + IF_NBFDBG (NBF_DEBUG_FRAMESND) { + NbfPrint0 ("NbfDestroyConnectionlessFrame: Entered.\n"); + } + + // + // Strip off and unmap the buffers describing data and header. + // + + NdisUnchainBufferAtFront (Address->UIFrame->NdisPacket, &HeaderBuffer); + + // drop the rest of the packet + + NdisReinitializePacket (Address->UIFrame->NdisPacket); + + NDIS_BUFFER_LINKAGE(HeaderBuffer) = (PNDIS_BUFFER)NULL; + NdisChainBufferAtFront (Address->UIFrame->NdisPacket, HeaderBuffer); + + // + // Ignore NdisStatus; datagrams always "succeed". The Information + // field was filled in when we queued the datagram. + // + + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + Address->Flags &= ~ADDRESS_FLAGS_SEND_IN_PROGRESS; + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + // + // Send more datagrams on the Address if possible. + // + + NbfSendDatagramsOnAddress (Address); // do more datagrams. + + } else { + + ASSERT (FALSE); + + Address->Flags &= ~ADDRESS_FLAGS_SEND_IN_PROGRESS; + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + } + + NbfDereferenceAddress ("Complete datagram", Address, AREF_REQUEST); + +} /* NbfSendDatagramCompletion */ diff --git a/private/ntos/tdi/nbf/iframes.c b/private/ntos/tdi/nbf/iframes.c new file mode 100644 index 000000000..d14eeb505 --- /dev/null +++ b/private/ntos/tdi/nbf/iframes.c @@ -0,0 +1,3339 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + iframes.c + +Abstract: + + This module contains routines called to handle i-frames received + from the data link provider. Most of these routines are called at receive + indication time. + + Also included here are routines that process data at receive completion + time. These are limited to handling DFM/DOL frames. + + The following frame types are cracked by routines in this module: + + o NBF_CMD_DATA_ACK + o NBF_CMD_DATA_FIRST_MIDDLE + o NBF_CMD_DATA_ONLY_LAST + o NBF_CMD_SESSION_CONFIRM + o NBF_CMD_SESSION_END + o NBF_CMD_SESSION_INITIALIZE + o NBF_CMD_NO_RECEIVE + o NBF_CMD_RECEIVE_OUTSTANDING + o NBF_CMD_RECEIVE_CONTINUE + o NBF_CMD_SESSION_ALIVE + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode, DISPATCH_LEVEL. + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +extern ULONG StartTimerDelayedAck; + +#define NbfUsePiggybackAcks 1 +#if DBG +extern ULONG NbfDebugPiggybackAcks; +#endif + + +VOID +NbfAcknowledgeDataOnlyLast( + IN PTP_CONNECTION Connection, + IN ULONG MessageLength + ) + +/*++ + +Routine Description: + + This routine takes care of acknowledging a DOL which has + been received. It either sends a DATA_ACK right away, or + queues a request for a piggyback ack. + + NOTE: This routine is called with the connection spinlock + held, and it returns with it released. IT MUST BE CALLED + AT DPC LEVEL. + +Arguments: + + Connection - Pointer to a transport connection (TP_CONNECTION). + + MessageLength - the total length (including all DFMs and this + DOL) of the message. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + + + // + // Determine if we need to ack at all. + // + + if (Connection->CurrentReceiveNoAck) { + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + return; + } + + + // + // Determine if a piggyback ack is feasible. + // + if (NbfUsePiggybackAcks && + Connection->CurrentReceiveAckQueueable) { + + // + // The sender allows it, see if we want to. + // + +#if 0 + // + // First reset this variable, to be safe. + // + + Connection->CurrentReceiveAckQueueable = FALSE; +#endif + + // + // For long sends, don't bother since these + // often happen without back traffic. + // + + if (MessageLength >= 8192L) { +#if DBG + if (NbfDebugPiggybackAcks) { + NbfPrint0("M"); + } +#endif + goto NormalDataAck; + } + + // + // If there have been two receives in a row with + // no sends in between, don't wait for back traffic. + // + + if (Connection->ConsecutiveReceives >= 2) { +#if DBG + if (NbfDebugPiggybackAcks) { + NbfPrint0("R"); + } +#endif + goto NormalDataAck; + } + + // + // Do not put a stopping connection on the DataAckQueue + // + + if ((Connection->Flags & CONNECTION_FLAGS_READY) == 0) { +#if DBG + if (NbfDebugPiggybackAcks) { + NbfPrint0("S"); + } +#endif + goto NormalDataAck; + } + + // + // Queue the piggyback ack request. If the timer expires + // before a DFM or DOL is sent, a normal DATA ACK will + // be sent. + // + // Connection->Header.TransmitCorrelator has already been filled in. + // + + // + // BAD! We shouldn't already have an ack queued. + // + + ASSERT ((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_ACK) == 0); + + KeQueryTickCount (&Connection->ConnectStartTime); + Connection->DeferredFlags |= CONNECTION_FLAGS_DEFERRED_ACK; + +#if DBG + if (NbfDebugPiggybackAcks) { + NbfPrint0("Q"); + } +#endif + + DeviceContext = Connection->Link->Provider; + + if (!Connection->OnDataAckQueue) { + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + if (!Connection->OnDataAckQueue) { + + Connection->OnDataAckQueue = TRUE; + InsertTailList (&DeviceContext->DataAckQueue, &Connection->DataAckLinkage); + + if (!(DeviceContext->a.i.DataAckQueueActive)) { + + StartTimerDelayedAck++; + NbfStartShortTimer (DeviceContext); + DeviceContext->a.i.DataAckQueueActive = TRUE; + + } + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + } + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + INCREMENT_COUNTER (DeviceContext, PiggybackAckQueued); + + return; + + } + +NormalDataAck:; + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + NbfSendDataAck (Connection); + +} /* NbfAcknowledgeDataOnlyLast */ + + +NTSTATUS +ProcessSessionConfirm( + IN PTP_CONNECTION Connection, + IN PNBF_HDR_CONNECTION IFrame + ) + +/*++ + +Routine Description: + + This routine handles an incoming SESSION_CONFIRM NetBIOS frame. + +Arguments: + + Connection - Pointer to a transport connection (TP_CONNECTION). + + IFrame - Pointer to NetBIOS connection-oriented header. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL cancelirql; + PLIST_ENTRY p; + PTP_REQUEST request; + PTDI_CONNECTION_INFORMATION remoteInformation; + USHORT HisMaxDataSize; + NTSTATUS status; + PIO_STACK_LOCATION irpSp; + ULONG returnLength; + TA_NETBIOS_ADDRESS TempAddress; +// BOOLEAN TimerWasSet; + + IF_NBFDBG (NBF_DEBUG_IFRAMES) { + NbfPrint1 ("ProcessSessionConfirm: Entered, Flags: %lx\n", Connection->Flags); + } + + Connection->IndicationInProgress = FALSE; + + IoAcquireCancelSpinLock (&cancelirql); + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + if ((Connection->Flags & CONNECTION_FLAGS_WAIT_SC) != 0) { + Connection->Flags &= ~CONNECTION_FLAGS_WAIT_SC; + + // + // Get his capability bits and maximum frame size. + // + + if (IFrame->Data1 & SESSION_CONFIRM_OPTIONS_20) { + Connection->Flags |= CONNECTION_FLAGS_VERSION2; + } + + if (Connection->Link->Loopback) { + Connection->MaximumDataSize = 0x8000; + } else { + Connection->MaximumDataSize = (USHORT) + (Connection->Link->MaxFrameSize - sizeof(NBF_HDR_CONNECTION) - sizeof(DLC_I_FRAME)); + + HisMaxDataSize = (USHORT)(IFrame->Data2Low + IFrame->Data2High*256); + if (HisMaxDataSize < Connection->MaximumDataSize) { + Connection->MaximumDataSize = HisMaxDataSize; + } + } + + // + // Build a standard Netbios header for speed when sending + // data frames. + // + + ConstructDataOnlyLast( + &Connection->NetbiosHeader, + FALSE, + (USHORT)0, + Connection->Lsn, + Connection->Rsn); + + // + // Turn off the connection request timer if there is one, and set + // this connection's state to READY. + // + + Connection->Flags |= CONNECTION_FLAGS_READY; + + INCREMENT_COUNTER (Connection->Provider, OpenConnections); + + // + // Record that the connect request has been successfully + // completed by TpCompleteRequest. + // + + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + Connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED; + + if (Connection->Flags2 & CONNECTION_FLAGS2_STOPPING) { + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + Connection->IndicationInProgress = FALSE; + IoReleaseCancelSpinLock (cancelirql); + return STATUS_SUCCESS; + } + + // + // Complete the TdiConnect request. + // + + p = RemoveHeadList (&Connection->InProgressRequest); + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + // + // Now complete the request and get out. + // + + if (p == &Connection->InProgressRequest) { + + Connection->IndicationInProgress = FALSE; + IoReleaseCancelSpinLock (cancelirql); + return STATUS_SUCCESS; + + } + + + // + // We have a completed connection with a queued connect. Complete + // the connect. + // + + request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + IoSetCancelRoutine(request->IoRequestPacket, NULL); + IoReleaseCancelSpinLock(cancelirql); + + irpSp = IoGetCurrentIrpStackLocation (request->IoRequestPacket); + remoteInformation = + ((PTDI_REQUEST_KERNEL)(&irpSp->Parameters))->ReturnConnectionInformation; + if (remoteInformation != NULL) { + try { + if (remoteInformation->RemoteAddressLength != 0) { + + // + // Build a temporary TA_NETBIOS_ADDRESS, then + // copy over as many bytes as fit. + // + + TdiBuildNetbiosAddress( + Connection->CalledAddress.NetbiosName, + (BOOLEAN)(Connection->CalledAddress.NetbiosNameType == + TDI_ADDRESS_NETBIOS_TYPE_GROUP), + &TempAddress); + + if (remoteInformation->RemoteAddressLength >= + sizeof (TA_NETBIOS_ADDRESS)) { + + returnLength = sizeof(TA_NETBIOS_ADDRESS); + remoteInformation->RemoteAddressLength = returnLength; + + } else { + + returnLength = remoteInformation->RemoteAddressLength; + + } + + RtlCopyMemory( + (PTA_NETBIOS_ADDRESS)remoteInformation->RemoteAddress, + &TempAddress, + returnLength); + + } else { + + returnLength = 0; + } + + status = STATUS_SUCCESS; + + } except (EXCEPTION_EXECUTE_HANDLER) { + + returnLength = 0; + status = GetExceptionCode (); + + } + + } else { + + status = STATUS_SUCCESS; + returnLength = 0; + + } + + if (status == STATUS_SUCCESS) { + + if ((ULONG)Connection->Retries == Connection->Provider->NameQueryRetries) { + + INCREMENT_COUNTER (Connection->Provider, ConnectionsAfterNoRetry); + + } else { + + INCREMENT_COUNTER (Connection->Provider, ConnectionsAfterRetry); + + } + + } + + // + // Don't clear this until now, so that the connection is all + // set up before we allow more indications. + // + + Connection->IndicationInProgress = FALSE; + + NbfCompleteRequest (request, status, returnLength); + + } else { + + Connection->IndicationInProgress = FALSE; + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + IoReleaseCancelSpinLock(cancelirql); + + } + + return STATUS_SUCCESS; +} /* ProcessSessionConfirm */ + + +NTSTATUS +ProcessSessionEnd( + IN PTP_CONNECTION Connection, + IN PNBF_HDR_CONNECTION IFrame + ) + +/*++ + +Routine Description: + + This routine handles an incoming SESSION_END NetBIOS frame. + +Arguments: + + Connection - Pointer to a transport connection (TP_CONNECTION). + + IFrame - Pointer to NetBIOS connection-oriented header. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + USHORT data2; + NTSTATUS StopStatus; + + IF_NBFDBG (NBF_DEBUG_IFRAMES) { + NbfPrint0 ("ProcessSessionEnd: Entered.\n"); + } + + // + // Handle the error code in the Data2 field. Current protocol says + // if the field is 0, then this is a normal HANGUP.NCB operation. + // If the field is 1, then this is an abnormal session end, caused + // by something like a SEND.NCB timing out. Of course, new protocol + // may be added in the future, so we handle only these specific cases. + // + + data2 = (USHORT)(IFrame->Data2Low + IFrame->Data2High*256); + switch (data2) { + case 0: + case 1: + StopStatus = STATUS_REMOTE_DISCONNECT; + break; + + default: + PANIC ("ProcessSessionEnd: frame not expected.\n"); + StopStatus = STATUS_INVALID_NETWORK_RESPONSE; + } +#if DBG + if (NbfDisconnectDebug) { + STRING remoteName, localName; + remoteName.Length = NETBIOS_NAME_LENGTH - 1; + remoteName.Buffer = Connection->RemoteName; + localName.Length = NETBIOS_NAME_LENGTH - 1; + localName.Buffer = Connection->AddressFile->Address->NetworkName->NetbiosName; + NbfPrint3( "SessionEnd received for connection to %S from %S; reason %s\n", + &remoteName, &localName, + data2 == 0 ? "NORMAL" : data2 == 1 ? "ABORT" : "UNKNOWN" ); + } +#endif + + // + // Run-down this connection. + // + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint0 ("ProcessSessionEnd calling NbfStopConnection\n"); + } + NbfStopConnection (Connection, StopStatus); // disconnected by the other end + + Connection->IndicationInProgress = FALSE; + + return STATUS_SUCCESS; +} /* ProcessSessionEnd */ + + +NTSTATUS +ProcessSessionInitialize( + IN PTP_CONNECTION Connection, + IN PNBF_HDR_CONNECTION IFrame + ) + +/*++ + +Routine Description: + + This routine handles an incoming SESSION_INITIALIZE NetBIOS frame. + +Arguments: + + Connection - Pointer to a transport connection (TP_CONNECTION). + + IFrame - Pointer to NetBIOS connection-oriented header. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL cancelirql; + PLIST_ENTRY p; + PTP_REQUEST request; + PIO_STACK_LOCATION irpSp; + USHORT HisMaxDataSize; + ULONG returnLength; + PTDI_CONNECTION_INFORMATION remoteInformation; + NTSTATUS status; + TA_NETBIOS_ADDRESS TempAddress; + + IF_NBFDBG (NBF_DEBUG_IFRAMES) { + NbfPrint1 ("ProcessSessionInitialize: Entered, Flags: %lx\n", Connection->Flags); + } + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + if ((Connection->Flags & CONNECTION_FLAGS_WAIT_SI) != 0) { + Connection->Flags &= ~CONNECTION_FLAGS_WAIT_SI; + + // + // Get his capability bits and maximum frame size. + // + + if (IFrame->Data1 & SESSION_INIT_OPTIONS_20) { + Connection->Flags |= CONNECTION_FLAGS_VERSION2; + } + + if (Connection->Link->Loopback) { + Connection->MaximumDataSize = 0x8000; + } else { + Connection->MaximumDataSize = (USHORT) + (Connection->Link->MaxFrameSize - sizeof(NBF_HDR_CONNECTION) - sizeof(DLC_I_FRAME)); + + HisMaxDataSize = (USHORT)(IFrame->Data2Low + IFrame->Data2High*256); + if (HisMaxDataSize < Connection->MaximumDataSize) { + Connection->MaximumDataSize = HisMaxDataSize; + } + } + + // + // Build a standard Netbios header for speed when sending + // data frames. + // + + ConstructDataOnlyLast( + &Connection->NetbiosHeader, + FALSE, + (USHORT)0, + Connection->Lsn, + Connection->Rsn); + + // + // Save his session initialize correlator so we can send it + // in the session confirm frame. + // + + Connection->NetbiosHeader.TransmitCorrelator = RESPONSE_CORR(IFrame); + + // + // Turn off the connection request timer if there is one (we're done). + // Do this with the lock held in case the connection is about to + // be closed, to not interfere with the timer started when the + // connection started then. + // + + if (KeCancelTimer (&Connection->Timer)) { + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + NbfDereferenceConnection ("Timer canceled", Connection, CREF_TIMER); // remove timer reference. + + } else { + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + } + + // + // Now, complete the listen request on the connection (if there was + // one) and continue with as much of the protocol request as possible. + // if the user has "pre-accepted" the connection, we'll just continue + // onward here and complete the entire connection setup. If the user + // was indicated and has not yet accepted, we'll just put the + // connection into the proper state and fall out the bottom without + // completing anything. + // + + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + if (((Connection->Flags2 & CONNECTION_FLAGS2_ACCEPTED) != 0) || + ((Connection->Flags2 & CONNECTION_FLAGS2_PRE_ACCEPT) != 0)) { + + IF_NBFDBG (NBF_DEBUG_SETUP) { + NbfPrint1("SessionInitialize: Accepted connection %lx\n", Connection); + } + // + // we've already accepted the connection; allow it to proceed. + // this is the normal path for kernel mode indication clients, + // or for those who don't specify TDI_QUERY_ACCEPT on the listen. + // + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + Connection->Flags |= CONNECTION_FLAGS_READY; + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + INCREMENT_COUNTER (Connection->Provider, OpenConnections); + + // + // Record that the listen request has been successfully + // completed by NbfCompleteRequest. + // + + Connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED; + + status = STATUS_SUCCESS; + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + NbfSendSessionConfirm (Connection); + + } else { + + if ((Connection->Flags2 & CONNECTION_FLAGS2_DISCONNECT) != 0) { + + // + // we disconnected, destroy the connection + // + IF_NBFDBG (NBF_DEBUG_SETUP) { + NbfPrint1("SessionInitialize: Disconnected connection %lx\n", Connection); + } + + status = STATUS_LOCAL_DISCONNECT; + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + NbfStopConnection (Connection, STATUS_LOCAL_DISCONNECT); + + } else { + + // + // we've done nothing, wait for the user to accept on this + // connection. This is the "normal" path for non-indication + // clients. + // + + Connection->Flags2 |= CONNECTION_FLAGS2_WAITING_SC; + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + status = STATUS_SUCCESS; + } + } + + // + // Now, if there was no queued listen, we have done everything we can + // for this connection, so we simply exit and leave everything up to + // the user. If we've gotten an indication response that allows the + // connection to proceed, we will come out of here with a connection + // that's up and running. + // + + IoAcquireCancelSpinLock (&cancelirql); + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + p = RemoveHeadList (&Connection->InProgressRequest); + if (p == &Connection->InProgressRequest) { + + Connection->IndicationInProgress = FALSE; + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + IoReleaseCancelSpinLock (cancelirql); + return STATUS_SUCCESS; + + } + + // + // We have a completed connection with a queued listen. Complete + // the listen and let the user do an accept at some time down the + // road. + // + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + IoSetCancelRoutine(request->IoRequestPacket, NULL); + IoReleaseCancelSpinLock (cancelirql); + + irpSp = IoGetCurrentIrpStackLocation (request->IoRequestPacket); + remoteInformation = + ((PTDI_REQUEST_KERNEL)(&irpSp->Parameters))->ReturnConnectionInformation; + if (remoteInformation != NULL) { + try { + if (remoteInformation->RemoteAddressLength != 0) { + + // + // Build a temporary TA_NETBIOS_ADDRESS, then + // copy over as many bytes as fit. + // + + TdiBuildNetbiosAddress( + Connection->CalledAddress.NetbiosName, + (BOOLEAN)(Connection->CalledAddress.NetbiosNameType == + TDI_ADDRESS_NETBIOS_TYPE_GROUP), + &TempAddress); + + if (remoteInformation->RemoteAddressLength >= + sizeof (TA_NETBIOS_ADDRESS)) { + + returnLength = sizeof(TA_NETBIOS_ADDRESS); + remoteInformation->RemoteAddressLength = returnLength; + + } else { + + returnLength = remoteInformation->RemoteAddressLength; + + } + + RtlCopyMemory( + (PTA_NETBIOS_ADDRESS)remoteInformation->RemoteAddress, + &TempAddress, + returnLength); + + } else { + + returnLength = 0; + } + + status = STATUS_SUCCESS; + + } except (EXCEPTION_EXECUTE_HANDLER) { + + returnLength = 0; + status = GetExceptionCode (); + + } + + } else { + + status = STATUS_SUCCESS; + returnLength = 0; + + } + + // + // Don't clear this until now, so that the connection is all + // set up before we allow more indications. + // + + Connection->IndicationInProgress = FALSE; + + NbfCompleteRequest (request, status, 0); + + } else { + + Connection->IndicationInProgress = FALSE; +#if DBG + NbfPrint3 ("ProcessSessionInitialize: C %lx, Flags %lx, Flags2 %lx\n", + Connection, Connection->Flags, Connection->Flags2); +#endif + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + } + + return STATUS_SUCCESS; +} /* ProcessSessionInitialize */ + + +NTSTATUS +ProcessNoReceive( + IN PTP_CONNECTION Connection, + IN PNBF_HDR_CONNECTION IFrame + ) + +/*++ + +Routine Description: + + This routine handles an incoming NO_RECEIVE NetBIOS frame. + + NOTE: This routine is called with the connection spinlock + held and returns with it released. + +Arguments: + + Connection - Pointer to a transport connection (TP_CONNECTION). + + IFrame - Pointer to NetBIOS connection-oriented header. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + UNREFERENCED_PARAMETER (IFrame); // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_IFRAMES) { + NbfPrint0 ("ProcessNoReceive: Entered.\n"); + } + + switch (Connection->SendState) { + case CONNECTION_SENDSTATE_W_PACKET: // waiting for free packet. + case CONNECTION_SENDSTATE_PACKETIZE: // send being packetized. + case CONNECTION_SENDSTATE_W_LINK: // waiting for good link conditions. + case CONNECTION_SENDSTATE_W_EOR: // waiting for TdiSend(EOR). + case CONNECTION_SENDSTATE_W_ACK: // waiting for DATA_ACK. +// Connection->SendState = CONNECTION_SENDSTATE_W_RCVCONT; +// +// this used to be here, and is right for the other side of the connection. It's +// wrong here. +// Connection->Flags |= CONNECTION_FLAGS_W_RESYNCH; + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + ReframeSend (Connection, IFrame->Data2Low + IFrame->Data2High*256); + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + break; + + case CONNECTION_SENDSTATE_W_RCVCONT: // waiting for RECEIVE_CONTINUE. + case CONNECTION_SENDSTATE_IDLE: // no sends being processed. + PANIC ("ProcessNoReceive: Frame not expected.\n"); + break; + + default: + PANIC ("ProcessNoReceive: Invalid SendState.\n"); + } + + // + // Don't clear this until ReframeSend has been called + // + + Connection->IndicationInProgress = FALSE; + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + return STATUS_SUCCESS; +} /* ProcessNoReceive */ + + +NTSTATUS +ProcessReceiveOutstanding( + IN PTP_CONNECTION Connection, + IN PNBF_HDR_CONNECTION IFrame + ) + +/*++ + +Routine Description: + + This routine handles an incoming RECEIVE_OUTSTANDING NetBIOS frame. + + NOTE: This routine is called with the connection spinlock + held and returns with it released. + +Arguments: + + Connection - Pointer to a transport connection (TP_CONNECTION). + + IFrame - Pointer to NetBIOS connection-oriented header. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_IFRAMES) { + NbfPrint0 ("ProcessReceiveOutstanding: Entered.\n"); + } + + switch (Connection->SendState) { + case CONNECTION_SENDSTATE_W_PACKET: // waiting for free packet. + case CONNECTION_SENDSTATE_PACKETIZE: // send being packetized. + case CONNECTION_SENDSTATE_W_LINK: // waiting for good link conditions. + case CONNECTION_SENDSTATE_W_EOR: // waiting for TdiSend(EOR). + case CONNECTION_SENDSTATE_W_ACK: // waiting for DATA_ACK. + case CONNECTION_SENDSTATE_W_RCVCONT: // waiting for RECEIVE_CONTINUE. + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + ReframeSend (Connection, IFrame->Data2Low + IFrame->Data2High*256); + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + if ((Connection->Flags & CONNECTION_FLAGS_READY) != 0) { + Connection->Flags |= CONNECTION_FLAGS_RESYNCHING; + Connection->SendState = CONNECTION_SENDSTATE_PACKETIZE; + } + break; + + case CONNECTION_SENDSTATE_IDLE: // no sends being processed. + PANIC ("ProcessReceiveOutstanding: Frame not expected.\n"); + break; + + default: + PANIC ("ProcessReceiveOutstanding: Invalid SendState.\n"); + } + + // + // Don't clear this until ReframeSend has been called + // + + Connection->IndicationInProgress = FALSE; + + // + // Now start packetizing the connection again since we've reframed + // the current send. If we were idle or in a bad state, then the + // packetizing routine will detect that. + // + // *** StartPacketizingConnection releases the Connection spin lock. + // + + StartPacketizingConnection (Connection, FALSE); + return STATUS_SUCCESS; +} /* ProcessReceiveOutstanding */ + + +NTSTATUS +ProcessReceiveContinue( + IN PTP_CONNECTION Connection, + IN PNBF_HDR_CONNECTION IFrame + ) + +/*++ + +Routine Description: + + This routine handles an incoming RECEIVE_CONTINUE NetBIOS frame. + + NOTE: This routine is called with the connection spinlock + held and returns with it released. + +Arguments: + + Connection - Pointer to a transport connection (TP_CONNECTION). + + IFrame - Pointer to NetBIOS connection-oriented header. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_IFRAMES) { + NbfPrint0 ("ProcessReceiveContinue: Entered.\n"); + } + + switch (Connection->SendState) { + case CONNECTION_SENDSTATE_W_PACKET: // waiting for free packet. + case CONNECTION_SENDSTATE_PACKETIZE: // send being packetized. + case CONNECTION_SENDSTATE_W_LINK: // waiting for good link conditions. + case CONNECTION_SENDSTATE_W_EOR: // waiting for TdiSend(EOR). + case CONNECTION_SENDSTATE_W_ACK: // waiting for DATA_ACK. + case CONNECTION_SENDSTATE_W_RCVCONT: // waiting for RECEIVE_CONTINUE. + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + ReframeSend (Connection, Connection->sp.MessageBytesSent); + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + Connection->Flags |= CONNECTION_FLAGS_RESYNCHING; + Connection->SendState = CONNECTION_SENDSTATE_PACKETIZE; + break; + + case CONNECTION_SENDSTATE_IDLE: // no sends being processed. + PANIC ("ProcessReceiveContinue: Frame not expected.\n"); + break; + + default: + PANIC ("ProcessReceiveContinue: Invalid SendState.\n"); + } + + // + // Don't clear this until ReframeSend has been called + // + + Connection->IndicationInProgress = FALSE; + + // + // Now start packetizing the connection again since we've reframed + // the current send. If we were idle or in a bad state, then the + // packetizing routine will detect that. + // + // *** StartPacketizingConnection releases the Connection spin lock. + // + + StartPacketizingConnection (Connection, FALSE); + return STATUS_SUCCESS; +} /* ProcessReceiveContinue */ + + +NTSTATUS +ProcessSessionAlive( + IN PTP_CONNECTION Connection, + IN PNBF_HDR_CONNECTION IFrame + ) + +/*++ + +Routine Description: + + This routine handles an incoming SESSION_ALIVE NetBIOS frame. This + routine is by far the simplest in the transport because it does nothing. + The SESSION_ALIVE frame is simply a dummy frame that is sent on the + reliable data link layer to determine if the data link is still active; + no NetBIOS level protocol processing is performed. + +Arguments: + + Connection - Pointer to a transport connection (TP_CONNECTION). + + IFrame - Pointer to NetBIOS connection-oriented header. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + UNREFERENCED_PARAMETER (Connection); // prevent compiler warnings + UNREFERENCED_PARAMETER (IFrame); // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_IFRAMES) { + NbfPrint0 ("ProcessSessionAlive: Entered.\n"); + } + + Connection->IndicationInProgress = FALSE; + + return STATUS_SUCCESS; +} /* ProcessSessionAlive */ + + +VOID +NbfProcessIIndicate( + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PTP_LINK Link, + IN PUCHAR DlcHeader, + IN UINT DlcIndicatedLength, + IN UINT DlcTotalLength, + IN NDIS_HANDLE ReceiveContext, + IN BOOLEAN Loopback + ) + +/*++ + +Routine Description: + + This routine processes a received I frame at indication time. It will do + all necessary verification processing of the frame and pass those frames + that are valid on to the proper handling routines. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Command - Boolean set to TRUE if command, else FALSE if response. + + PollFinal - Boolean set to TRUE if Poll or Final. + + Link - Pointer to a transport link object. + + Header - Pointer to a DLC I-type frame. + + DlcHeader - A pointer to the start of the DLC header in the packet. + + DlcIndicatedLength - The length of the packet indicated, starting at + DlcHeader. + + DlcTotalLength - The total length of the packet, starting at DlcHeader. + + ReceiveContext - A magic value for NDIS that indicates which packet we're + talking about. + + Loopback - Is this a loopback indication; used to determine whether + to call NdisTransferData or NbfTransferLoopbackData. + +Return Value: + + None. + +--*/ + +{ +#if DBG + UCHAR *s; +#endif + PNBF_HDR_CONNECTION nbfHeader; + PDLC_I_FRAME header; + NTSTATUS Status; + UCHAR lsn, rsn; + PTP_CONNECTION connection; + PUCHAR DataHeader; + ULONG DataTotalLength; + PLIST_ENTRY p; + BOOLEAN ConnectionFound; + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessIIndicate: Entered.\n"); + } + + // + // Process any of: I-x/x. + // + + header = (PDLC_I_FRAME)DlcHeader; + nbfHeader = (PNBF_HDR_CONNECTION)((PUCHAR)header + 4); // skip DLC hdr. + + // + // Verify signatures. We test the signature as a 16-bit + // word as specified in the NetBIOS Formats and Protocols manual, + // with the assert guarding us against big-endian systems. + // + + ASSERT ((((PUCHAR)(&nbfHeader->Length))[0] + ((PUCHAR)(&nbfHeader->Length))[1]*256) == + HEADER_LENGTH(nbfHeader)); + + if (HEADER_LENGTH(nbfHeader) != sizeof(NBF_HDR_CONNECTION)) { + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 ("NbfProcessIIndicate: Dropped I frame, Too short or long.\n"); + } + return; // frame too small or too large. + } + + if (HEADER_SIGNATURE(nbfHeader) != NETBIOS_SIGNATURE) { + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 ("NbfProcessIIndicate: Dropped I frame, Signature bad.\n"); + } + return; // invalid signature in frame. + } + + DataHeader = (PUCHAR)DlcHeader + (4 + sizeof(NBF_HDR_CONNECTION)); + DataTotalLength = DlcTotalLength - (4 + sizeof(NBF_HDR_CONNECTION)); + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); // keep state stable + + switch (Link->State) { + + case LINK_STATE_READY: + + // + // Link is balanced. This code is extremely critical since + // it is the most-covered path in the system for small and + // large I/O. Be very careful in adding code here as it will + // seriously affect the overall performance of the LAN. It is + // first in the list of possible states for that reason. + // + +#if DBG + s = "READY"; +#endif + Link->LinkBusy = FALSE; + + // + // The I-frame's N(S) should match our V(R). If it + // doesn't, issue a reject. Otherwise, increment our V(R). + // + + if ((UCHAR)((header->SendSeq >> 1) & 0x7F) != Link->NextReceive) { + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessIIndicate: N(S) != V(R).\n"); + } + + if (Link->ReceiveState == RECEIVE_STATE_REJECTING) { + + + // + // We already sent a reject, only respond if + // he is polling. + // + + if (Command & PollFinal) { + NbfSendRr(Link, FALSE, TRUE); // releases lock + } else { + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + } + + } else { + + Link->ReceiveState = RECEIVE_STATE_REJECTING; + + // + // NbfSendRej releases the spinlock. + // + + if (Command) { + NbfSendRej (Link, FALSE, PollFinal); + } else { + NbfSendRej (Link, FALSE, FALSE); + } + } + + // + // Update our "bytes rejected" counters. + // + + ADD_TO_LARGE_INTEGER( + &Link->Provider->Statistics.DataFrameBytesRejected, + DataTotalLength); + ++Link->Provider->Statistics.DataFramesRejected; + + // + // Discard this packet. + // + + break; + + } + + + // + // Find the transport connection object associated with this frame. + // Because there may be several NetBIOS (transport) connections + // over the same data link connection, and the ConnectionContext + // value represents a data link connection to a specific address, + // we simply use the RSN field in the frame to index into the + // connection database for this link object. + // + // We do this before processing the rest of the LLC header, + // in case the connection is busy and we have to ignore + // the frame. + // + + ConnectionFound = FALSE; + + lsn = nbfHeader->DestinationSessionNumber; + rsn = nbfHeader->SourceSessionNumber; + + if ((lsn == 0) || (lsn > NETBIOS_SESSION_LIMIT)) { + + IF_NBFDBG (NBF_DEBUG_IFRAMES) { + NbfPrint0 ("NbfProcessIIndicate: Invalid LSN.\n"); + } + + } else { + + p = Link->ConnectionDatabase.Flink; + while (p != &Link->ConnectionDatabase) { + connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList); + if (connection->Lsn >= lsn) { // assumes ordered list + break; + } + p = p->Flink; + } + + // Don't use compound if 'cause Connection may be garbage + + if (p == &Link->ConnectionDatabase) { +#if DBG + NbfPrint2 ("NbfProcessIIndicate: Connection not found in database: \n Lsn %x Link %lx", + lsn, Link); + NbfPrint6 ("Remote: %x-%x-%x-%x-%x-%x\n", + Link->HardwareAddress.Address[0], Link->HardwareAddress.Address[1], + Link->HardwareAddress.Address[2], Link->HardwareAddress.Address[3], + Link->HardwareAddress.Address[4], Link->HardwareAddress.Address[5]); +#endif + } else if (connection->Lsn != lsn) { +#if DBG + NbfPrint0 ("NbfProcessIIndicate: Connection in database doesn't match.\n"); +#endif + } else if (connection->Rsn != rsn) { +#if DBG + NbfPrint3 ("NbfProcessIIndicate: Connection lsn %d had rsn %d, got frame for %d\n", + connection->Lsn, connection->Rsn, rsn); +#endif + } else { + + // + // The connection is good, proceed. + // + + ConnectionFound = TRUE; + + if (connection->IndicationInProgress) { + NbfPrint1("ProcessIIndicate: Indication in progress on %lx\n", connection); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + return; + } + + // + // Set this, it prevents other I-frames from being received + // on this connection. The various ProcessXXX routines + // that are called from the switch below will clear + // this flag when they determine it is OK to be reentered. + // + + connection->IndicationInProgress = TRUE; + + + // This reference is removed before this function returns or + // we are done with the LINK_STATE_READY part of the outer switch. + + NbfReferenceConnection ("Processing IFrame", connection, CREF_PROCESS_DATA); + + + } + + } + + + // + // As long as we don't have to drop this frame, adjust the link + // state correctly. If ConnectionFound is FALSE, then we exit + // right after doing this. + // + + + // + // The I-frame we expected arrived, clear rejecting state. + // + + if (Link->ReceiveState == RECEIVE_STATE_REJECTING) { + Link->ReceiveState = RECEIVE_STATE_READY; + } + + Link->NextReceive = (UCHAR)((Link->NextReceive+1) & 0x7f); + + // + // If he is checkpointing, we need to respond with RR-c/f. If + // we respond, then stop the delayed ack timer. Otherwise, we + // need to start it because this is an I-frame that will not be + // acked immediately. + // + + if (Command && PollFinal) { + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessI: he's checkpointing.\n"); + } + Link->RemoteNoPoll = FALSE; + StopT2 (Link); // we're acking, so no delay req'd. + NbfSendRr (Link, FALSE, TRUE); // releases lock + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + } else { + + if (Link->RemoteNoPoll) { + + if ((++Link->ConsecutiveIFrames) == Link->Provider->MaxConsecutiveIFrames) { + + // + // This appears to be one of those remotes which + // never polls, so we send an RR if there are two + // frames outstanding (StopT2 sets ConsecutiveIFrames + // to 0). + // + + StopT2 (Link); // we're acking, so no delay req'd. + NbfSendRr (Link, FALSE, FALSE); // releases lock + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + } else { + + StartT2 (Link); + + ACQUIRE_DPC_SPIN_LOCK (&Link->Provider->Interlock); + if (!Link->OnDeferredRrQueue) { + InsertTailList( + &Link->Provider->DeferredRrQueue, + &Link->DeferredRrLinkage); + Link->OnDeferredRrQueue = TRUE; + } + RELEASE_DPC_SPIN_LOCK (&Link->Provider->Interlock); + + } + + } else { + + StartT2 (Link); // start delayed ack sequence. + } + + // + // If he is responding to a checkpoint, we need to clear our + // send state. Any packets which are still waiting for acknowlegement + // at this point must now be resent. + // + + if ((!Command) && PollFinal) { + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint0 (" NbfProcessI: he's responding to our checkpoint.\n"); + } + if (Link->SendState != SEND_STATE_CHECKPOINTING) { + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 (" NbfProcessI: BUGBUG: Ckpt but SendState=%ld.\n", + Link->SendState); + } + } + StopT1 (Link); // checkpoint completed. + Link->SendState = SEND_STATE_READY; + StartTi (Link); + } + + } + + // + // Now, if we could not find the connection or the sequence + // numbers did not match, return. We don't call ResendLlcPackets + // in this case, but that is OK (eventually we will poll). + // + + if (!ConnectionFound) { + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + return; + } + + ASSERT (connection->LinkSpinLock == &Link->SpinLock); + + // + // The N(R) in this frame may acknowlege some WackQ packets. + // We delay checking this until after processing the I-frame, + // so that we can get IndicationInProgress set to FALSE + // before we start resending the WackQ. + // + + switch (nbfHeader->Command) { + + case NBF_CMD_DATA_FIRST_MIDDLE: + case NBF_CMD_DATA_ONLY_LAST: + + // + // First see if this packet has a piggyback ack -- we process + // this even if we throw the packet away below. + // + // This is a bit ugly since theoretically the piggyback + // ack bits in a DFM and a DOL could be different, but + // they aren't. + // + if (NbfUsePiggybackAcks) { + ASSERT (DFM_OPTIONS_ACK_INCLUDED == DOL_OPTIONS_ACK_INCLUDED); + + if ((nbfHeader->Data1 & DFM_OPTIONS_ACK_INCLUDED) != 0) { + + // + // This returns with the connection spinlock held + // but may release it and reacquire it. + // + + CompleteSend( + connection, + TRANSMIT_CORR(nbfHeader)); + + } + } + + // + // NOTE: The connection spinlock is held here. + // + + // + // If the connection is not ready, drop the frame. + // + + if ((connection->Flags & CONNECTION_FLAGS_READY) == 0) { + connection->IndicationInProgress = FALSE; + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + + Status = STATUS_SUCCESS; + goto SkipProcessIndicateData; + } + + // + // A quick check for the three flags that are + // rarely set. + // + + if ((connection->Flags & (CONNECTION_FLAGS_W_RESYNCH | + CONNECTION_FLAGS_RC_PENDING | + CONNECTION_FLAGS_RECEIVE_WAKEUP)) == 0) { + goto NoFlagsSet; + } + + // + // If we are waiting for a resynch bit to be set in an + // incoming frame, toss the frame if it isn't set. + // Otherwise, clear the wait condition. + // + + if (connection->Flags & CONNECTION_FLAGS_W_RESYNCH) { + if ((nbfHeader->Data2Low == 1) && (nbfHeader->Data2High == 0)) { + connection->Flags &= ~CONNECTION_FLAGS_W_RESYNCH; + } else { + connection->IndicationInProgress = FALSE; + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + IF_NBFDBG (NBF_DEBUG_IFRAMES) { + NbfPrint0 ("NbfProcessIIndicate: Discarded DFM/DOL, waiting for resynch.\n"); + } + + Status = STATUS_SUCCESS; + goto SkipProcessIndicateData; + } + } + + // + // If we have a previous receive that is pending + // completion, then we need to ignore this frame. + // This may be common on MP, so rather than drop + // it and wait for a poll, we send a NO_RECEIVE, + // then a RCV_OUTSTANDING when we have some + // resources. + // + + if (connection->Flags & CONNECTION_FLAGS_RC_PENDING) { + + // + // Hack the connection object so the NO_RECEIVE + // looks right. + // + + connection->MessageBytesReceived = 0; + connection->MessageBytesAcked = 0; + connection->MessageInitAccepted = 0; + + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + + NbfSendNoReceive (connection); + + ACQUIRE_DPC_SPIN_LOCK (connection->LinkSpinLock); + + // + // We now turn on the PEND_INDICATE flag to show + // that we need to send RCV_OUTSTANDING when the + // receive completes. If RC_PENDING is now off, + // it means the receive was just completed, so + // we ourselves need to send the RCV_OUTSTANDING. + // + + if ((connection->Flags & CONNECTION_FLAGS_RC_PENDING) == 0) { + + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + NbfSendReceiveOutstanding (connection); + ACQUIRE_DPC_SPIN_LOCK (connection->LinkSpinLock); + + } else { + + connection->Flags |= CONNECTION_FLAGS_PEND_INDICATE; + + } + + connection->IndicationInProgress = FALSE; + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + + IF_NBFDBG (NBF_DEBUG_IFRAMES) { + NbfPrint0 ("NbfProcessIIndicate: Discarded DFM/DOL, receive complete pending.\n"); + } + + Status = STATUS_SUCCESS; + goto SkipProcessIndicateData; + } + + // + // If we are discarding data received on this connection + // because we've sent a no receive, ditch it. + // + + if (connection->Flags & CONNECTION_FLAGS_RECEIVE_WAKEUP) { + connection->IndicationInProgress = FALSE; + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 ("NbfProcessIIndicate: In wakeup state, discarding frame.\n"); + } + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + + Status = STATUS_SUCCESS; + goto SkipProcessIndicateData; + } + +NoFlagsSet:; + + // + // The connection spinlock is held here. + // + + if (nbfHeader->Command == NBF_CMD_DATA_FIRST_MIDDLE) { + + // + // NOTE: This release connection->LinkSpinLock. + // + + Status = ProcessIndicateData ( + connection, + DlcHeader, + DlcIndicatedLength, + DataHeader, + DataTotalLength, + ReceiveContext, + FALSE, + Loopback); + + // + // If the receive-continue bit is set in this frame, then we must + // reply with a RECEIVE_CONTINUE frame saying that he can continue + // sending. This old protocol option allowed a sender to send a + // single frame over to see if there was a receive posted before + // sending the entire message and potentially dropping the entire + // message. Because the TDI is indication-based, we cannot know + // if there is NO receive available until we actually try perform + // the indication, so we simply say that there is one posted. + // (This will only happen on DFMs.) + // + + if (nbfHeader->Data1 & 0x01) { + + // + // Save this to use in RECEIVE_CONTINUE. + // + + connection->NetbiosHeader.TransmitCorrelator = + RESPONSE_CORR(nbfHeader); + + NbfSendReceiveContinue (connection); + } + } else { + + // + // Keep track of how many consecutive receives we have had. + // + + connection->ConsecutiveReceives++; + connection->ConsecutiveSends = 0; + + // + // Save this information now, it will be needed + // when the ACK for this DOL is sent. + // + + connection->CurrentReceiveAckQueueable = + (nbfHeader->Data1 & DOL_OPTIONS_ACK_W_DATA_ALLOWED); + + connection->CurrentReceiveNoAck = + (nbfHeader->Data1 & DOL_OPTIONS_NO_ACK); + + connection->NetbiosHeader.TransmitCorrelator = + RESPONSE_CORR(nbfHeader); + + // + // NOTE: This release connection->LinkSpinLock. + // + + Status = ProcessIndicateData ( + connection, + DlcHeader, + DlcIndicatedLength, + DataHeader, + DataTotalLength, + ReceiveContext, + TRUE, + Loopback); + } + + // + // Update our "bytes received" counters. + // + + Link->Provider->TempIFrameBytesReceived += DataTotalLength; + ++Link->Provider->TempIFramesReceived; + +SkipProcessIndicateData: + + break; + + case NBF_CMD_DATA_ACK: + + connection->IndicationInProgress = FALSE; + + // + // This returns with the lock held. + // + + CompleteSend( + connection, + TRANSMIT_CORR(nbfHeader)); + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + + Status = STATUS_SUCCESS; + break; + + case NBF_CMD_SESSION_CONFIRM: + + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + Status = ProcessSessionConfirm ( + connection, + nbfHeader); + break; + + case NBF_CMD_SESSION_END: + + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + Status = ProcessSessionEnd ( + connection, + nbfHeader); + break; + + case NBF_CMD_SESSION_INITIALIZE: + + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + Status = ProcessSessionInitialize ( + connection, + nbfHeader); + break; + + case NBF_CMD_NO_RECEIVE: + + // + // This releases the connection spinlock. + // + + Status = ProcessNoReceive ( + connection, + nbfHeader); + break; + + case NBF_CMD_RECEIVE_OUTSTANDING: + + // + // This releases the connection spinlock. + // + + Status = ProcessReceiveOutstanding ( + connection, + nbfHeader); + break; + + case NBF_CMD_RECEIVE_CONTINUE: + + // + // This releases the connection spinlock. + // + + Status = ProcessReceiveContinue ( + connection, + nbfHeader); + break; + + case NBF_CMD_SESSION_ALIVE: + + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + Status = ProcessSessionAlive ( + connection, + nbfHeader); + break; + + // + // An unrecognized command was found in a NetBIOS frame. Because + // this is a connection-oriented frame, we should probably shoot + // the sender, but for now we will simply discard the packet. + // + // BUGBUG: trash the session here-- protocol violation. + // + + default: + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + PANIC ("NbfProcessIIndicate: Unknown NBF command byte.\n"); + connection->IndicationInProgress = FALSE; + Status = STATUS_SUCCESS; + } /* switch */ + + // + // A status of STATUS_MORE_PROCESSING_REQUIRED means + // that the connection reference count was inherited + // by the routine we called, so we don't do the dereference + // here. + // + + if (Status != STATUS_MORE_PROCESSING_REQUIRED) { + NbfDereferenceConnectionMacro("ProcessIIndicate done", connection, CREF_PROCESS_DATA); + } + + + // + // The N(R) in this frame acknowleges some (or all) of our packets. + // This call must come after the checkpoint acknowlegement check + // so that an RR-r/f is always sent BEFORE any new I-frames. This + // allows us to always send I-frames as commands. + // If he responded to a checkpoint, then resend all left-over + // packets. + // + + // Link->NextSend = (UCHAR)(header->RcvSeq >> 1) < Link->NextSend ? + // Link->NextSend : (UCHAR)(header->RcvSeq >> 1); + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + if (Link->WackQ.Flink != &Link->WackQ) { + + UCHAR AckSequenceNumber = (UCHAR)(header->RcvSeq >> 1); + + // + // Verify that the sequence number is reasonable. + // + + if (Link->NextSend >= Link->LastAckReceived) { + + // + // There is no 127 -> 0 wrap between the two... + // + + if ((AckSequenceNumber < Link->LastAckReceived) || + (AckSequenceNumber > Link->NextSend)) { + goto NoResend; + } + + } else { + + // + // There is a 127 -> 0 wrap between the two... + // + + if ((AckSequenceNumber < Link->LastAckReceived) && + (AckSequenceNumber > Link->NextSend)) { + goto NoResend; + } + + } + + // + // NOTE: ResendLlcPackets may release and + // reacquire the link spinlock. + // + + (VOID)ResendLlcPackets( + Link, + AckSequenceNumber, + (BOOLEAN)((!Command) && PollFinal)); + +NoResend:; + + } + + + // + // Get link going again. + // + // NOTE: RestartLinkTraffic releases the link spinlock + // + + RestartLinkTraffic (Link); + break; + + case LINK_STATE_ADM: + + // + // used to be, we'd just blow off the other guy with a DM and go home. + // it seems that OS/2 likes to believe (under some conditions) that + // it has a link up and it is still potentially active (probably + // because we return the same connection number to him that he used + // to be using). This would all be ok, except for the fact that we + // may have a connection hanging on this link waiting for a listen + // to finish. If we're in that state, go ahead and accept the + // connect. + // Set our values for link packet serial numbers to what he wants. + // + + if (!IsListEmpty (&Link->ConnectionDatabase)) { + if (nbfHeader->Command == NBF_CMD_SESSION_INITIALIZE) { + + // + // OK, we're at the only legal case. We've gotten an SI + // and we have a connection on this link. If the connection + // is waiting SI, we will go ahead and make believe we did + // all the correct stuff before we got it. + // + + for ( + p = Link->ConnectionDatabase.Flink, connection = NULL; + p != &Link->ConnectionDatabase ; + p = p->Flink, connection = NULL + ) { + + connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList); + if ((connection->Flags & CONNECTION_FLAGS_WAIT_SI) != 0) { + // This reference is removed below + NbfReferenceConnection ("Found Listener at session init", connection, CREF_ADM_SESS); + break; + } + } + + // + // Well, we've looked through the connections, if we have one, + // make it the connection of the day. Note that it will + // complete when we call ProcessSessionInitialize. + // + + if (connection != NULL) { + + Link->NextReceive = (UCHAR)(header->SendSeq >> 1) & (UCHAR)0x7f; + Link->NextSend = (UCHAR)(header->RcvSeq >> 1) & (UCHAR)0x7F; + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + NbfCompleteLink (Link); // completes the listening connection + + Status = ProcessSessionInitialize ( + connection, + nbfHeader); + NbfDereferenceConnection ("Processed SessInit", connection, CREF_ADM_SESS); + +#if DBG + s = "ADM"; +#endif + + // Link is ready for use. + + break; + } + } + + // + // we've got a connection on a link that's in state admin. + // really bad, kill it and the link. + // + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + if (NbfDisconnectDebug) { + NbfPrint0( "NbfProcessIIndicate calling NbfStopLink\n" ); + } +#endif + NbfStopLink (Link); + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + } + + // + // We're disconnected. Tell him. + // + + NbfSendDm (Link, PollFinal); // releases lock +#if DBG + s = "ADM"; +#endif + break; + + case LINK_STATE_CONNECTING: + + // + // We've sent a SABME and are waiting for a UA. He's sent an + // I-frame too early, so just let the SABME time out. + // + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "CONNECTING"; +#endif + break; + + case LINK_STATE_W_POLL: + + // + // We're waiting for his initial poll on a RR-c/p. If he starts + // with an I-frame, then we'll let him squeak by. + // + + if (!Command) { + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "W_POLL"; +#endif + break; + } + + Link->State = LINK_STATE_READY; // we're up! + StopT1 (Link); // no longer waiting. + FakeUpdateBaseT1Timeout (Link); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + NbfCompleteLink (Link); // fire up the connections. + StartTi (Link); + NbfProcessIIndicate ( // recursive, but safe + Command, + PollFinal, + Link, + DlcHeader, + DlcIndicatedLength, + DlcTotalLength, + ReceiveContext, + Loopback); +#if DBG + s = "W_POLL"; +#endif + break; + + case LINK_STATE_W_FINAL: + + // + // We're waiting for a RR-r/f from the remote guy. I-r/f will do. + // + + if (Command || !PollFinal) { // don't allow this protocol. + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "W_FINAL"; +#endif + break; // we sent RR-c/p. + } + + Link->State = LINK_STATE_READY; // we're up. + StopT1 (Link); // no longer waiting. + StartT2 (Link); // we have an unacked I-frame. + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + NbfCompleteLink (Link); // fire up the connections. + StartTi (Link); + NbfProcessIIndicate ( // recursive, but safe + Command, + PollFinal, + Link, + DlcHeader, + DlcIndicatedLength, + DlcTotalLength, + ReceiveContext, + Loopback); +#if DBG + s = "W_FINAL"; +#endif + break; + + case LINK_STATE_W_DISC_RSP: + + // + // We're waiting for a response from our DISC-c/p but instead of + // a UA-r/f, we got this I-frame. Throw the packet away. + // + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + s = "W_DISC_RSP"; +#endif + break; + + + default: + + ASSERT (FALSE); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + +#if DBG + s = "Unknown link state"; +#endif + + } /* switch */ + +#if DBG + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 (" NbfProcessIIndicate: (%s) I-Frame processed.\n", s); + } +#endif + + return; +} /* NbfProcessIIndicate */ + + +NTSTATUS +ProcessIndicateData( + IN PTP_CONNECTION Connection, + IN PUCHAR DlcHeader, + IN UINT DlcIndicatedLength, + IN PUCHAR DataHeader, + IN UINT DataTotalLength, + IN NDIS_HANDLE ReceiveContext, + IN BOOLEAN Last, + IN BOOLEAN Loopback + ) + +/*++ + +Routine Description: + + This routine is called to process data received in a DATA_FIRST_MIDDLE + or DATA_ONLY_LAST frame. We attempt to satisfy as many TdiReceive + requests as possible with this data. + + If a receive is already active on this Connection, then we copy as much + data into the active receive's buffer as possible. If all the data is + copied and the receive request's buffer has not been filled, then the + Last flag is checked, and if it is TRUE, we go ahead and complete the + current receive with the TDI_END_OF_RECORD receive indicator. If Last + is FALSE, we simply return. + + If more (uncopied) data remains in the frame, or if there is no active + receive outstanding, then an indication is issued to the owning address's + receive event handler. The event handler can take one of three actions: + + 1. Return STATUS_SUCCESS, in which case the transport will assume that + all of the indicated data has been accepted by the client. + + 3. Return STATUS_DATA_NOT_ACCEPTED, in which case the transport will + discard the data and set the CONNECTION_FLAGS_RECEIVE_WAKEUP bitflag + in the Connection, indicating that remaining data is to be discarded + until a receive becomes available. + + NOTE: This routine is called with Connection->LinkSpinLock held, + and returns with it released. THIS ROUTINE MUST BE CALLED AT + DPC LEVEL. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + + DlcHeader - The pointer handed to us as the start of the NBF header by NDIS; + use this to compute the offset into the packet to start the transfer + of data to user buffers. + + DlcIndicatedLength - The amount of NBF data available at indicate. + + DataHeader - A pointer to the start of the data in the packet. + + DataTotalLength - The total length of the packet, starting at DataHeader. + + ReceiveContext - An NDIS handle that identifies the packet we are currently + processing. + + Last - Boolean value that indicates whether this is the last piece of data + in a message. The DATA_ONLY_LAST processor sets this flag to TRUE when + calling this routine, and the DATA_FIRST_MIDDLE processor resets this + flag to FALSE when calling this routine. + + Loopback - Is this a loopback indication; used to determine whether + to call NdisTransferData or NbfTransferLoopbackData. + + +Return Value: + + STATUS_SUCCESS if we've consumed the packet, + +--*/ + +{ + NTSTATUS status, tmpstatus; + PDEVICE_CONTEXT deviceContext; + NDIS_STATUS ndisStatus; + PNDIS_PACKET ndisPacket; + PSINGLE_LIST_ENTRY linkage; + PIRP irp; + PIO_STACK_LOCATION irpSp; + PNDIS_BUFFER ndisBuffer; + ULONG destBytes; + ULONG bufferChainLength; + ULONG indicateBytesTransferred; + ULONG ReceiveFlags; + ULONG ndisBytesTransferred; + UINT BytesToTransfer; + ULONG bytesIndicated; + ULONG DataOffset = (PUCHAR)DataHeader - (PUCHAR)DlcHeader; + PRECEIVE_PACKET_TAG receiveTag; + PTP_ADDRESS_FILE addressFile; + PMDL SavedCurrentMdl; + ULONG SavedCurrentByteOffset; + BOOLEAN ActivatedLongReceive = FALSE; + BOOLEAN CompleteReceiveBool, EndOfMessage; + ULONG DumpData[2]; + + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint4 (" ProcessIndicateData: Entered, PacketStart: %lx Offset: %lx \n TotalLength %ld DlcIndicatedLength: %ld\n", + DlcHeader, DataOffset, DataTotalLength, DlcIndicatedLength); + } + + + // + // copy this packet into our receive buffer. + // + + deviceContext = Connection->Provider; + + if ((Connection->Flags & CONNECTION_FLAGS_RCV_CANCELLED) != 0) { + + // + // A receive in progress was cancelled; we toss the data, + // but do send the DOL if it was the last piece of the + // send. + // + + if (Last) { + + Connection->Flags &= ~CONNECTION_FLAGS_RCV_CANCELLED; + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + NbfSendDataAck (Connection); + + } else { + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + } + + Connection->IndicationInProgress = FALSE; + + return STATUS_SUCCESS; + } + + // + // Initialize this to zero, in case we do not indicate or + // the client does not fill it in. + // + + indicateBytesTransferred = 0; + + if (!(Connection->Flags & CONNECTION_FLAGS_ACTIVE_RECEIVE)) { + + // + // check first to see if there is a receive available. If there is, + // use it before doing an indication. + // + + if (Connection->ReceiveQueue.Flink != &Connection->ReceiveQueue) { + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" ProcessIndicateData: Found receive. Prepping.\n"); + } + + // + // Found a receive, so make it the active one and + // cycle around again. + // + + Connection->Flags |= CONNECTION_FLAGS_ACTIVE_RECEIVE; + Connection->MessageBytesReceived = 0; + Connection->MessageBytesAcked = 0; + Connection->MessageInitAccepted = 0; + Connection->CurrentReceiveIrp = + CONTAINING_RECORD (Connection->ReceiveQueue.Flink, + IRP, Tail.Overlay.ListEntry); + Connection->CurrentReceiveSynchronous = + deviceContext->MacInfo.SingleReceive; + Connection->CurrentReceiveMdl = + Connection->CurrentReceiveIrp->MdlAddress; + Connection->ReceiveLength = + IRP_RECEIVE_LENGTH (IoGetCurrentIrpStackLocation (Connection->CurrentReceiveIrp)); + Connection->ReceiveByteOffset = 0; + status = STATUS_SUCCESS; + goto NormalReceive; + } + + // + // A receive is not active. Post a receive event. + // + + if ((Connection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) == 0) { + Connection->IndicationInProgress = FALSE; + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + return STATUS_SUCCESS; + } + + addressFile = Connection->AddressFile; + + if ((!addressFile->RegisteredReceiveHandler) || + (Connection->ReceiveBytesUnaccepted != 0)) { + + // + // There is no receive posted to the Connection, and + // no event handler. Set the RECEIVE_WAKEUP bit, so that when a + // receive does become available, it will restart the + // current send. Also send a NoReceive to tell the other + // guy he needs to resynch. + // + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" ProcessIndicateData: ReceiveQueue empty. Setting RECEIVE_WAKEUP.\n"); + } + Connection->Flags |= CONNECTION_FLAGS_RECEIVE_WAKEUP; + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + Connection->IndicationInProgress = FALSE; + + // NbfSendNoReceive (Connection); + return STATUS_SUCCESS; + } + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" ProcessIndicateData: Receive not active. Posting event.\n"); + } + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + LEAVE_NBF; + + // + // Indicate to the user. For BytesAvailable we + // always use DataTotalLength; for BytesIndicated we use + // MIN (DlcIndicatedLength - DataOffset, DataTotalLength). + // + // To clarify BytesIndicated, on an Ethernet packet + // which is padded DataTotalLength will be shorter; on an + // Ethernet packet which is not padded and which is + // completely indicated, the two will be equal; and + // on a long Ethernet packet DlcIndicatedLength - DataOffset + // will be shorter. + // + + bytesIndicated = DlcIndicatedLength - DataOffset; + if (DataTotalLength <= bytesIndicated) { + bytesIndicated = DataTotalLength; + } + + ReceiveFlags = TDI_RECEIVE_AT_DISPATCH_LEVEL; + if (Last) { + ReceiveFlags |= TDI_RECEIVE_ENTIRE_MESSAGE; + } + if (deviceContext->MacInfo.CopyLookahead) { + ReceiveFlags |= TDI_RECEIVE_COPY_LOOKAHEAD; + } + + status = (*addressFile->ReceiveHandler)( + addressFile->ReceiveHandlerContext, + Connection->Context, + ReceiveFlags, + bytesIndicated, + DataTotalLength, // BytesAvailable + &indicateBytesTransferred, + DataHeader, + &irp); + ENTER_NBF; + + if (status == STATUS_MORE_PROCESSING_REQUIRED) { + + ULONG SpecialIrpLength; + PTDI_REQUEST_KERNEL_RECEIVE Parameters; + + // + // The client's event handler has returned an IRP in the + // form of a TdiReceive that is to be associated with this + // data. The request will be installed at the front of the + // ReceiveQueue, and then made the active receive request. + // This request will be used to accept the incoming data, which + // will happen below. + // + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" ProcessIndicateData: Status=STATUS_MORE_PROCESSING_REQUIRED.\n"); + NbfPrint4 (" ProcessIndicateData: Irp=%lx, Mdl=%lx, UserBuffer=%lx, Count=%ld.\n", + irp, irp->MdlAddress, irp->UserBuffer, + MmGetMdlByteCount (irp->MdlAddress)); + } + + // + // Queueing a receive of any kind causes a Connection reference; + // that's what we've just done here, so make the Connection stick + // around. We create a request to keep a packets outstanding ref + // count for the current IRP; we queue this on the connection's + // receive queue so we can treat it like a normal receive. If + // we can't get a request to describe this irp, we can't keep it + // around hoping for better later; we simple fail it with + // insufficient resources. Note this is only likely to happen if + // we've completely run out of transport memory. + // + + irp->IoStatus.Information = 0; // byte transfer count. + irp->IoStatus.Status = STATUS_PENDING; + irpSp = IoGetCurrentIrpStackLocation (irp); + + ASSERT (irpSp->FileObject->FsContext == Connection); + + Parameters = (PTDI_REQUEST_KERNEL_RECEIVE)&irpSp->Parameters; + SpecialIrpLength = Parameters->ReceiveLength; + + // + // If the packet is a DOL, and it will fit entirely + // inside this posted IRP, then we don't bother + // creating a request, because we don't need any of + // that overhead. We also don't set ReceiveBytes + // Unaccepted, since this receive would clear it + // anyway. + // + + if (Last && + (SpecialIrpLength >= (DataTotalLength - indicateBytesTransferred))) { + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + Connection->SpecialReceiveIrp = irp; + + Connection->Flags |= CONNECTION_FLAGS_ACTIVE_RECEIVE; + Connection->ReceiveLength = SpecialIrpLength; + Connection->MessageBytesReceived = 0; + Connection->MessageInitAccepted = indicateBytesTransferred; + Connection->MessageBytesAcked = 0; + Connection->CurrentReceiveIrp = NULL; + Connection->CurrentReceiveSynchronous = TRUE; + Connection->CurrentReceiveMdl = irp->MdlAddress; + Connection->ReceiveByteOffset = 0; + if ((Parameters->ReceiveFlags & TDI_RECEIVE_NO_RESPONSE_EXP) != 0) { + Connection->CurrentReceiveAckQueueable = FALSE; + } + +#if DBG + // + // switch our reference from PROCESS_DATA to + // RECEIVE_IRP, this is OK because the RECEIVE_IRP + // reference won't be removed until Transfer + // DataComplete, which is the last thing + // we call. + // + + NbfReferenceConnection("Special IRP", Connection, CREF_RECEIVE_IRP); + NbfDereferenceConnection("ProcessIIndicate done", Connection, CREF_PROCESS_DATA); +#endif + + } else { + KIRQL cancelIrql; + + // + // The normal path, for longer receives. + // + + IoAcquireCancelSpinLock(&cancelIrql); + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + IRP_RECEIVE_IRP(irpSp) = irp; + if (deviceContext->MacInfo.SingleReceive) { + IRP_RECEIVE_REFCOUNT(irpSp) = 1; + } else { +#if DBG + IRP_RECEIVE_REFCOUNT(irpSp) = 1; + NbfReferenceReceiveIrpLocked ("Transfer Data", irpSp, RREF_RECEIVE); +#else + IRP_RECEIVE_REFCOUNT(irpSp) = 2; // include one for first xfer +#endif + } + + // + // If the Connection is stopping, abort this request. + // + + if ((Connection->Flags & CONNECTION_FLAGS_READY) == 0) { + Connection->IndicationInProgress = FALSE; + + NbfReferenceConnection("Special IRP stopping", Connection, CREF_RECEIVE_IRP); + NbfCompleteReceiveIrp ( + irp, + Connection->Status, + 0); + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + IoReleaseCancelSpinLock(cancelIrql); + + if (!deviceContext->MacInfo.SingleReceive) { + NbfDereferenceReceiveIrp ("Not ready", irpSp, RREF_RECEIVE); + } + return STATUS_SUCCESS; // we have consumed the packet + + } + + // + // If this IRP has been cancelled, complete it now. + // + + if (irp->Cancel) { + + Connection->Flags |= CONNECTION_FLAGS_RECEIVE_WAKEUP; + + Connection->IndicationInProgress = FALSE; + + NbfReferenceConnection("Special IRP cancelled", Connection, CREF_RECEIVE_IRP); + + // + // It is safe to call this with locks held. + // + NbfCompleteReceiveIrp (irp, STATUS_CANCELLED, 0); + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + IoReleaseCancelSpinLock(cancelIrql); + + if (!deviceContext->MacInfo.SingleReceive) { + NbfDereferenceReceiveIrp ("Cancelled", irpSp, RREF_RECEIVE); + } + + return STATUS_SUCCESS; + } + + // + // Insert the request on the head of the connection's + // receive queue, so it can be handled like a normal + // receive. + // + + InsertHeadList (&Connection->ReceiveQueue, &irp->Tail.Overlay.ListEntry); + + IoSetCancelRoutine(irp, NbfCancelReceive); + + // + // Release the cancel spinlock out of order. Since we were + // at DPC level when we acquired it, we don't have to fiddle + // with swapping irqls. + // + ASSERT(cancelIrql == DISPATCH_LEVEL); + IoReleaseCancelSpinLock(cancelIrql); + + Connection->Flags |= CONNECTION_FLAGS_ACTIVE_RECEIVE; + Connection->ReceiveLength = Parameters->ReceiveLength; + Connection->MessageBytesReceived = 0; + Connection->MessageInitAccepted = indicateBytesTransferred; + Connection->ReceiveBytesUnaccepted = DataTotalLength - indicateBytesTransferred; + Connection->MessageBytesAcked = 0; + Connection->CurrentReceiveIrp = irp; + Connection->CurrentReceiveSynchronous = + deviceContext->MacInfo.SingleReceive; + Connection->CurrentReceiveMdl = irp->MdlAddress; + Connection->ReceiveByteOffset = 0; + +#if DBG + // + // switch our reference from PROCESS_DATA to + // REQUEST, this is OK because the REQUEST + // reference won't be removed until Transfer + // DataComplete, which is the last thing + // we call. + // + + NbfReferenceConnection("Special IRP", Connection, CREF_RECEIVE_IRP); + NbfDereferenceConnection("ProcessIIndicate done", Connection, CREF_PROCESS_DATA); +#endif + // + // Make a note so we know what to do below. + // + + ActivatedLongReceive = TRUE; + +#if DBG + NbfReceives[NbfReceivesNext].Irp = irp; + NbfReceivesNext = (NbfReceivesNext++) % TRACK_TDI_LIMIT; +#endif + } + + } else if (status == STATUS_SUCCESS) { + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" ProcessIndicateData: Status=STATUS_SUCCESS.\n"); + } + + // + // The client has accepted some or all of the indicated data in + // the event handler. Update MessageBytesReceived variable in + // the Connection so that if we are called upon to ACK him + // at the byte level, then we can correctly report the + // number of bytes received thus far. If this is a DOL, + // then reset the number of bytes received, since this value + // always at zero for new messages. If the data indicated wasn't + // all the data in this packet, flow control to the sender that + // didn't get all of the data. + // + + if (Last && (indicateBytesTransferred >= DataTotalLength)) { + + ASSERT (indicateBytesTransferred == DataTotalLength); + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + // + // This will send a DATA ACK or queue a request for + // a piggyback ack. + // + // NOTE: It will also release the connection spinlock. + // + + Connection->MessageBytesReceived = 0; + Connection->MessageInitAccepted = indicateBytesTransferred; + + NbfAcknowledgeDataOnlyLast( + Connection, + Connection->MessageBytesReceived + ); + + Connection->IndicationInProgress = FALSE; + return STATUS_SUCCESS; + + } else { + + // + // This gets gory. + // If this packet wasn't a DOL, we have no way of knowing how + // much the client will take of the data in this send that is + // now arriving. Pathological clients will break this protocol + // if they do things like taking part of the receive at indicate + // immediate and then return an irp (this would make the byte + // count wrong for the irp). + // + // Since the client did not take all the data that we + // told him about, he will eventually post a receive. + // If this has not already happened then we set the + // RECEIVE_WAKEUP bit and send a NO_RECEIVE. + // + +#if DBG + NbfPrint0("NBF: Indicate returned SUCCESS but did not take all data\n"); +#endif + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + Connection->MessageBytesReceived = 0; + Connection->MessageInitAccepted = indicateBytesTransferred; + Connection->ReceiveBytesUnaccepted = DataTotalLength - indicateBytesTransferred; + Connection->MessageBytesAcked = 0; + + if (Connection->ReceiveQueue.Flink == &Connection->ReceiveQueue) { + + // + // There is no receive posted to the Connection. + // + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" ProcessIndicateData: ReceiveQueue empty. Setting RECEIVE_WAKEUP.\n"); + } + + if (indicateBytesTransferred == DataTotalLength) { + + // + // This means he took everything, but it was not + // a DOL; there is no need to do anything since + // the rest of the data will be right behind. + // + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + } else { + + Connection->Flags |= CONNECTION_FLAGS_RECEIVE_WAKEUP; + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + NbfSendNoReceive (Connection); + + } + + Connection->IndicationInProgress = FALSE; + + return STATUS_SUCCESS; + + } else { + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" ProcessIndicateData: Found receive. Prepping.\n"); + } + + // + // Found a receive, so make it the active one. This will cause + // an NdisTransferData below, so we don't dereference the + // Connection here. + // + + Connection->Flags |= CONNECTION_FLAGS_ACTIVE_RECEIVE; + Connection->CurrentReceiveIrp = + CONTAINING_RECORD (Connection->ReceiveQueue.Flink, + IRP, Tail.Overlay.ListEntry); + Connection->CurrentReceiveSynchronous = + deviceContext->MacInfo.SingleReceive; + Connection->CurrentReceiveMdl = + Connection->CurrentReceiveIrp->MdlAddress; + Connection->ReceiveLength = + IRP_RECEIVE_LENGTH (IoGetCurrentIrpStackLocation(Connection->CurrentReceiveIrp)); + Connection->ReceiveByteOffset = 0; + } + + } + + } else { // STATUS_DATA_NOT_ACCEPTED or other + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" ProcessIndicateData: Status=STATUS_DATA_NOT_ACCEPTED.\n"); + } + + // + // Either there is no event handler installed (the default + // handler returns this code) or the event handler is not + // able to process the received data at this time. If there + // is a TdiReceive request outstanding on this Connection's + // ReceiveQueue, then we may use it to receive this data. + // If there is no request outstanding, then we must initiate + // flow control at the transport level. + // + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + Connection->ReceiveBytesUnaccepted = DataTotalLength; + + if (Connection->ReceiveQueue.Flink == &Connection->ReceiveQueue) { + + // + // There is no receive posted to the Connection, and + // the event handler didn't want to accept the incoming + // data. Set the RECEIVE_WAKEUP bit, so that when a + // receive does become available, it will restart the + // current send. Also send a NoReceive to tell the other + // guy he needs to resynch. + // + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" ProcessIndicateData: ReceiveQueue empty. Setting RECEIVE_WAKEUP.\n"); + } + Connection->Flags |= CONNECTION_FLAGS_RECEIVE_WAKEUP; + Connection->IndicationInProgress = FALSE; + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + return STATUS_SUCCESS; + + } else { + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" ProcessIndicateData: Found receive. Prepping.\n"); + } + + // + // Found a receive, so make it the active one. This will cause + // an NdisTransferData below, so we don't dereference the + // Connection here. + // + + Connection->Flags |= CONNECTION_FLAGS_ACTIVE_RECEIVE; + Connection->MessageBytesReceived = 0; + Connection->MessageBytesAcked = 0; + Connection->MessageInitAccepted = 0; + Connection->CurrentReceiveIrp = + CONTAINING_RECORD (Connection->ReceiveQueue.Flink, + IRP, Tail.Overlay.ListEntry); + Connection->CurrentReceiveSynchronous = + deviceContext->MacInfo.SingleReceive; + Connection->CurrentReceiveMdl = + Connection->CurrentReceiveIrp->MdlAddress; + Connection->ReceiveLength = + IRP_RECEIVE_LENGTH (IoGetCurrentIrpStackLocation(Connection->CurrentReceiveIrp)); + Connection->ReceiveByteOffset = 0; + } + + } + + } else { + + // + // A receive is active, set the status to show + // that so far. + // + + status = STATUS_SUCCESS; + + } + + +NormalReceive:; + + // + // NOTE: The connection spinlock is held here. + // + // We should only get through here if a receive is active + // and we have not released the lock since checking or + // making one active. + // + + ASSERT(Connection->Flags & CONNECTION_FLAGS_ACTIVE_RECEIVE); + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint2 (" ProcessIndicateData: Receive is active. ReceiveLengthLength: %ld Offset: %ld.\n", + Connection->ReceiveLength, Connection->MessageBytesReceived); + } + + destBytes = Connection->ReceiveLength - Connection->MessageBytesReceived; + + // + // If we just activated a non-special receive IRP, we already + // added a refcount for this transfer. + // + + if (!Connection->CurrentReceiveSynchronous && !ActivatedLongReceive) { + NbfReferenceReceiveIrpLocked ("Transfer Data", IoGetCurrentIrpStackLocation(Connection->CurrentReceiveIrp), RREF_RECEIVE); + } + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + + // + // Determine how much data remains to be transferred. + // + + ASSERT (indicateBytesTransferred <= DataTotalLength); + BytesToTransfer = DataTotalLength - indicateBytesTransferred; + + if (destBytes < BytesToTransfer) { + + // + // If the data overflows the current receive, then make a + // note that we should complete the receive at the end of + // transfer data, but with EOR false. + // + + EndOfMessage = FALSE; + CompleteReceiveBool = TRUE; + BytesToTransfer = destBytes; + + } else if (destBytes == BytesToTransfer) { + + // + // If the data just fills the current receive, then complete + // the receive; EOR depends on whether this is a DOL or not. + // + + EndOfMessage = Last; + CompleteReceiveBool = TRUE; + + } else { + + // + // Complete the receive if this is a DOL. + // + + EndOfMessage = Last; + CompleteReceiveBool = Last; + + } + + + // + // If we can copy the data directly, then do so. + // + + if ((BytesToTransfer > 0) && + (DataOffset + indicateBytesTransferred + BytesToTransfer <= DlcIndicatedLength)) { + + // + // All the data that we need to transfer is available in + // the indication, so copy it directly. + // + + ULONG BytesNow, BytesLeft; + PUCHAR CurTarget, CurSource; + ULONG CurTargetLen; + PMDL CurMdl; + ULONG CurByteOffset; + + // + // First we advance the connection pointers by the appropriate + // number of bytes, so that we can reallow indications (only + // do this if needed). + // + + CurMdl = Connection->CurrentReceiveMdl; + CurByteOffset = Connection->ReceiveByteOffset; + + if (!deviceContext->MacInfo.ReceiveSerialized) { + + SavedCurrentMdl = CurMdl; + SavedCurrentByteOffset = CurByteOffset; + + BytesLeft = BytesToTransfer; + CurTargetLen = MmGetMdlByteCount (CurMdl) - CurByteOffset; + while (TRUE) { + if (BytesLeft >= CurTargetLen) { + BytesLeft -= CurTargetLen; + CurMdl = CurMdl->Next; + CurByteOffset = 0; + if (BytesLeft == 0) { + break; + } + CurTargetLen = MmGetMdlByteCount (CurMdl); + } else { + CurByteOffset += BytesLeft; + break; + } + } + + Connection->CurrentReceiveMdl = CurMdl; + Connection->ReceiveByteOffset = CurByteOffset; + Connection->MessageBytesReceived += BytesToTransfer; + + // + // Set this up, we know the transfer won't + // "fail" but another one at the same time + // might. + // + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + if (Connection->TransferBytesPending == 0) { + Connection->TransferBytesPending = BytesToTransfer; + Connection->TotalTransferBytesPending = BytesToTransfer; + Connection->SavedCurrentReceiveMdl = SavedCurrentMdl; + Connection->SavedReceiveByteOffset = SavedCurrentByteOffset; + } else { + Connection->TransferBytesPending += BytesToTransfer; + Connection->TotalTransferBytesPending += BytesToTransfer; + } + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + Connection->IndicationInProgress = FALSE; + + // + // Restore these for the next section of code. + // + + CurMdl = SavedCurrentMdl; + CurByteOffset = SavedCurrentByteOffset; + + } + + CurTarget = (PUCHAR)(MmGetSystemAddressForMdl(CurMdl)) + CurByteOffset; + CurTargetLen = MmGetMdlByteCount(CurMdl) - CurByteOffset; + CurSource = DataHeader + indicateBytesTransferred; + + BytesLeft = BytesToTransfer; + + while (TRUE) { + + if (CurTargetLen < BytesLeft) { + BytesNow = CurTargetLen; + } else { + BytesNow = BytesLeft; + } + TdiCopyLookaheadData( + CurTarget, + CurSource, + BytesNow, + deviceContext->MacInfo.CopyLookahead ? TDI_RECEIVE_COPY_LOOKAHEAD : 0); + + if (BytesNow == CurTargetLen) { + BytesLeft -= BytesNow; + CurMdl = CurMdl->Next; + CurByteOffset = 0; + if (BytesLeft > 0) { + CurTarget = MmGetSystemAddressForMdl(CurMdl); + CurTargetLen = MmGetMdlByteCount(CurMdl); + CurSource += BytesNow; + } else { + break; + } + } else { + CurByteOffset += BytesNow; + ASSERT (BytesLeft == BytesNow); + break; + } + + } + + if (deviceContext->MacInfo.ReceiveSerialized) { + + // + // If we delayed updating these, do it now. + // + + Connection->CurrentReceiveMdl = CurMdl; + Connection->ReceiveByteOffset = CurByteOffset; + Connection->MessageBytesReceived += BytesToTransfer; + Connection->IndicationInProgress = FALSE; + + } else { + + // + // Check if something else failed and we are the + // last to complete, if so then back up our + // receive pointers and send a receive + // outstanding to make him resend. + // + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + Connection->TransferBytesPending -= BytesToTransfer; + + if ((Connection->TransferBytesPending == 0) && + (Connection->Flags & CONNECTION_FLAGS_TRANSFER_FAIL)) { + + Connection->CurrentReceiveMdl = Connection->SavedCurrentReceiveMdl; + Connection->ReceiveByteOffset = Connection->SavedReceiveByteOffset; + Connection->MessageBytesReceived -= Connection->TotalTransferBytesPending; + Connection->Flags &= ~CONNECTION_FLAGS_TRANSFER_FAIL; + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + NbfSendReceiveOutstanding (Connection); + + if (!Connection->SpecialReceiveIrp && + !Connection->CurrentReceiveSynchronous) { + NbfDereferenceReceiveIrp ("TransferData complete", IoGetCurrentIrpStackLocation(Connection->CurrentReceiveIrp), RREF_RECEIVE); + } + + return status; + + } else { + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + } + + } + + // + // Now that the transfer is complete, simulate a call to + // TransferDataComplete. + // + + + if (!Connection->SpecialReceiveIrp) { + + Connection->CurrentReceiveIrp->IoStatus.Information += BytesToTransfer; + if (!Connection->CurrentReceiveSynchronous) { + NbfDereferenceReceiveIrp ("TransferData complete", IoGetCurrentIrpStackLocation(Connection->CurrentReceiveIrp), RREF_RECEIVE); + } + + } + + // + // see if we've completed the current receive. If so, move to the next one. + // + + if (CompleteReceiveBool) { + CompleteReceive (Connection, EndOfMessage, BytesToTransfer); + } + + return status; + + } + + + // + // Get a packet for the coming transfer + // + + linkage = ExInterlockedPopEntryList( + &deviceContext->ReceivePacketPool, + &deviceContext->Interlock); + + if (linkage != NULL) { + ndisPacket = CONTAINING_RECORD( linkage, NDIS_PACKET, ProtocolReserved[0] ); + } else { + deviceContext->ReceivePacketExhausted++; + if (!Connection->CurrentReceiveSynchronous) { + NbfDereferenceReceiveIrp ("No receive packet", IoGetCurrentIrpStackLocation(Connection->CurrentReceiveIrp), RREF_RECEIVE); + } + + // + // We could not get a receive packet. We do have an active + // receive, so we just send a receive outstanding to + // get him to resend. Hopefully we will have a receive + // packet available when the data is resent. + // + + if ((Connection->Flags & CONNECTION_FLAGS_VERSION2) == 0) { + NbfSendNoReceive (Connection); + } + NbfSendReceiveOutstanding (Connection); + +#if DBG + NbfPrint0 (" ProcessIndicateData: Discarding Packet, no receive packets\n"); +#endif + Connection->IndicationInProgress = FALSE; + + return status; + } + + // + // Initialize the receive packet. + // + + receiveTag = (PRECEIVE_PACKET_TAG)(ndisPacket->ProtocolReserved); + // receiveTag->PacketType = TYPE_AT_INDICATE; + receiveTag->Connection = Connection; + receiveTag->TransferDataPended = TRUE; + + receiveTag->EndOfMessage = EndOfMessage; + receiveTag->CompleteReceive = CompleteReceiveBool; + + + // + // if we've got zero bytes left, avoid the TransferData below and + // just deliver. + // + + if (BytesToTransfer <= 0) { + Connection->IndicationInProgress = FALSE; + receiveTag->TransferDataPended = FALSE; + receiveTag->AllocatedNdisBuffer = FALSE; + receiveTag->BytesToTransfer = 0; + NbfTransferDataComplete ( + deviceContext, + ndisPacket, + NDIS_STATUS_SUCCESS, + 0); + + return status; + } + + // + // describe the right part of the user buffer to NDIS. If we can't get + // the mdl for the packet, drop dead. Bump the request reference count + // so that we know we need to hold open receives until the NDIS transfer + // data requests complete. + // + + SavedCurrentMdl = Connection->CurrentReceiveMdl; + SavedCurrentByteOffset = Connection->ReceiveByteOffset; + + if ((Connection->ReceiveByteOffset == 0) && + (CompleteReceiveBool)) { + + // + // If we are transferring into the beginning of + // the current MDL, and we will be completing the + // receive after the transfer, then we don't need to + // copy it. + // + + ndisBuffer = (PNDIS_BUFFER)Connection->CurrentReceiveMdl; + bufferChainLength = BytesToTransfer; + Connection->CurrentReceiveMdl = NULL; + // Connection->ReceiveByteOffset = 0; + receiveTag->AllocatedNdisBuffer = FALSE; + tmpstatus = STATUS_SUCCESS; + + } else { + + tmpstatus = BuildBufferChainFromMdlChain ( + deviceContext, + Connection->CurrentReceiveMdl, + Connection->ReceiveByteOffset, + BytesToTransfer, + &ndisBuffer, + &Connection->CurrentReceiveMdl, + &Connection->ReceiveByteOffset, + &bufferChainLength); + + receiveTag->AllocatedNdisBuffer = TRUE; + + } + + + if ((!NT_SUCCESS (tmpstatus)) || (bufferChainLength != BytesToTransfer)) { + + DumpData[0] = bufferChainLength; + DumpData[1] = BytesToTransfer; + + NbfWriteGeneralErrorLog( + deviceContext, + EVENT_TRANSPORT_TRANSFER_DATA, + 604, + tmpstatus, + NULL, + 2, + DumpData); + + if (!Connection->CurrentReceiveSynchronous) { + NbfDereferenceReceiveIrp ("No MDL chain", IoGetCurrentIrpStackLocation(Connection->CurrentReceiveIrp), RREF_RECEIVE); + } + + // + // Restore our old state and make him resend. + // + + Connection->CurrentReceiveMdl = SavedCurrentMdl; + Connection->ReceiveByteOffset = SavedCurrentByteOffset; + + if ((Connection->Flags & CONNECTION_FLAGS_VERSION2) == 0) { + NbfSendNoReceive (Connection); + } + NbfSendReceiveOutstanding (Connection); + + Connection->IndicationInProgress = FALSE; + + ExInterlockedPushEntryList( + &deviceContext->ReceivePacketPool, + &receiveTag->Linkage, + &deviceContext->Interlock); + + return status; + } + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint3 (" ProcessIndicateData: Mdl: %lx user buffer: %lx user offset: %lx \n", + ndisBuffer, Connection->CurrentReceiveMdl, Connection->ReceiveByteOffset); + } + + NdisChainBufferAtFront (ndisPacket, ndisBuffer); + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 (" ProcessIndicateData: Transferring Complete Packet: %lx\n", + ndisPacket); + } + + // + // update the number of bytes received; OK to do this + // unprotected since IndicationInProgress is still FALSE. + // + // + + Connection->MessageBytesReceived += BytesToTransfer; + + // + // We have to do this for two reasons: for MACs that + // are not receive-serialized, to keep track of it, + // and for MACs where transfer data can pend, so + // we have stuff saved to handle failure later (if + // the MAC is synchronous on transfers and it fails, + // we fill these fields in before calling CompleteTransferData). + // + + if (!deviceContext->MacInfo.SingleReceive) { + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + receiveTag->BytesToTransfer = BytesToTransfer; + if (Connection->TransferBytesPending == 0) { + Connection->TransferBytesPending = BytesToTransfer; + Connection->TotalTransferBytesPending = BytesToTransfer; + Connection->SavedCurrentReceiveMdl = SavedCurrentMdl; + Connection->SavedReceiveByteOffset = SavedCurrentByteOffset; + } else { + Connection->TransferBytesPending += BytesToTransfer; + Connection->TotalTransferBytesPending += BytesToTransfer; + } + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + } + + // + // We have now updated all the connection counters (BUGBUG + // assuming the TransferData will succeed) and this + // packet's location in the request is secured, so we + // can be reentered. + // + + Connection->IndicationInProgress = FALSE; + + if (Loopback) { + + NbfTransferLoopbackData( + &ndisStatus, + deviceContext, + ReceiveContext, + deviceContext->MacInfo.TransferDataOffset + + DataOffset + indicateBytesTransferred, + BytesToTransfer, + ndisPacket, + (PUINT)&ndisBytesTransferred + ); + + } else { + + NdisTransferData ( + &ndisStatus, + deviceContext->NdisBindingHandle, + ReceiveContext, + deviceContext->MacInfo.TransferDataOffset + + DataOffset + indicateBytesTransferred, + BytesToTransfer, + ndisPacket, + (PUINT)&ndisBytesTransferred); + + } + + // + // handle the various completion codes + // + + if ((ndisStatus == NDIS_STATUS_SUCCESS) && + (ndisBytesTransferred == BytesToTransfer)) { + + // + // deallocate the buffers and such that we've used if at indicate + // + + receiveTag->TransferDataPended = FALSE; + + NbfTransferDataComplete ( + deviceContext, + ndisPacket, + ndisStatus, + BytesToTransfer); + + } else if (ndisStatus == NDIS_STATUS_PENDING) { + + // + // Because TransferDataPended stays TRUE, this reference will + // be removed in TransferDataComplete. It is OK to do this + // now, even though TransferDataComplete may already have been + // called, because we also hold the ProcessIIndicate reference + // so there will be no "bounce". + // + + NbfReferenceConnection ("TransferData pended", Connection, CREF_TRANSFER_DATA); + + } else { + + // + // something broke; certainly we'll never get NdisTransferData + // asynch completion with this error status. We set things up + // to that NbfTransferDataComplete will do the right thing. + // + + if (deviceContext->MacInfo.SingleReceive) { + Connection->TransferBytesPending = BytesToTransfer; + Connection->TotalTransferBytesPending = BytesToTransfer; + Connection->SavedCurrentReceiveMdl = SavedCurrentMdl; + Connection->SavedReceiveByteOffset = SavedCurrentByteOffset; + receiveTag->BytesToTransfer = BytesToTransfer; + } + + receiveTag->TransferDataPended = FALSE; + + NbfTransferDataComplete ( + deviceContext, + ndisPacket, + ndisStatus, + BytesToTransfer); + + } + + return status; // which only means we've dealt with the packet + +} /* ProcessIndicateData */ diff --git a/private/ntos/tdi/nbf/info.c b/private/ntos/tdi/nbf/info.c new file mode 100644 index 000000000..f1cf8890b --- /dev/null +++ b/private/ntos/tdi/nbf/info.c @@ -0,0 +1,3487 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + info.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiQueryInformation + o TdiSetInformation + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +// +// Only the following routine is active in this module. All is is commented +// out waiting for the definition of Get/Set info in TDI version 2. +// + +// +// Useful macro to obtain the total length of an MDL chain. +// + +#define NbfGetMdlChainLength(Mdl, Length) { \ + PMDL _Mdl = (Mdl); \ + *(Length) = 0; \ + while (_Mdl) { \ + *(Length) += MmGetMdlByteCount(_Mdl); \ + _Mdl = _Mdl->Next; \ + } \ +} + + +// +// Local functions used to satisfy various requests. +// + +VOID +NbfStoreProviderStatistics( + IN PDEVICE_CONTEXT DeviceContext, + IN PTDI_PROVIDER_STATISTICS ProviderStatistics + ); + +VOID +NbfStoreAdapterStatus( + IN PDEVICE_CONTEXT DeviceContext, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + IN PVOID StatusBuffer + ); + +VOID +NbfStoreNameBuffers( + IN PDEVICE_CONTEXT DeviceContext, + IN PVOID Buffer, + IN ULONG BufferLength, + IN ULONG NamesToSkip, + OUT PULONG NamesWritten, + OUT PULONG TotalNameCount OPTIONAL, + OUT PBOOLEAN Truncated + ); + + +NTSTATUS +NbfTdiQueryInformation( + IN PDEVICE_CONTEXT DeviceContext, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiQueryInformation request for the transport + provider. + +Arguments: + + Irp - the Irp for the requested operation. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PIO_STACK_LOCATION irpSp; + PVOID adapterStatus; + PTDI_REQUEST_KERNEL_QUERY_INFORMATION query; + PTA_NETBIOS_ADDRESS broadcastAddress; + PTDI_PROVIDER_STATISTICS ProviderStatistics; + PTDI_CONNECTION_INFO ConnectionInfo; + ULONG TargetBufferLength; + PFIND_NAME_HEADER FindNameHeader; + LARGE_INTEGER timeout = {0,0}; + PTP_REQUEST tpRequest; + PTP_CONNECTION Connection; + PTP_ADDRESS_FILE AddressFile; + PTP_ADDRESS Address; + ULONG NamesWritten, TotalNameCount, BytesWritten; + BOOLEAN Truncated; + BOOLEAN RemoteAdapterStatus; + TDI_ADDRESS_NETBIOS UNALIGNED * RemoteAddress; + struct { + ULONG ActivityCount; + TA_NETBIOS_ADDRESS TaAddressBuffer; + } AddressInfo; + PTRANSPORT_ADDRESS TaAddress; + TDI_DATAGRAM_INFO DatagramInfo; + BOOLEAN UsedConnection; + PLIST_ENTRY p; + KIRQL oldirql; + + // + // what type of status do we want? + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + query = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION)&irpSp->Parameters; + + switch (query->QueryType) { + +#if 0 + case 0x12345678: + + { + typedef struct _NBF_CONNECTION_STATUS { + UCHAR LocalName[16]; + UCHAR RemoteName[16]; + BOOLEAN SendActive; + BOOLEAN ReceiveQueued; + BOOLEAN ReceiveActive; + BOOLEAN ReceiveWakeUp; + ULONG Flags; + ULONG Flags2; + } NBF_CONNECTION_STATUS, *PNBF_CONNECTION_STATUS; + + PNBF_CONNECTION_STATUS CurStatus; + ULONG TotalStatus; + ULONG AllowedStatus; + PLIST_ENRY q; + + CurStatus = MmGetSystemAddressForMdl (Irp->MdlAddress); + TotalStatus = 0; + AllowedStatus = MmGetMdlByteCount (Irp->MdlAddress) / sizeof(NBF_CONNECTION_STATUS); + + for (p = DeviceContext->AddressDatabase.Flink; + p != &DeviceContext->AddressDatabase; + p = p->Flink) { + + Address = CONTAINING_RECORD (p, TP_ADDRESS, Linkage); + + if ((Address->Flags & ADDRESS_FLAGS_STOPPING) != 0) { + continue; + } + + for (q = Address->ConnectionDatabase.Flink; + q != &Address->ConnectionDatabase; + q = q->Flink) { + + Connection = CONTAINING_RECORD (q, TP_CONNECTION, AddressList); + + if ((Connection->Flags & CONNECTION_FLAGS_READY) == 0) { + continue; + } + + if (TotalStatus >= AllowedStatus) { + continue; + } + + RtlMoveMemory (CurStatus->LocalName, Address->NetworkName->NetbiosName, 16); + RtlMoveMemory (CurStatus->RemoteName, Connection->RemoteName, 16); + + CurStatus->Flags = Connection->Flags; + CurStatus->Flags2 = Connection->Flags2; + CurStatus->SendActive = (BOOLEAN)(!IsListEmpty(&Connection->SendQueue)); + CurStatus->ReceiveQueued = (BOOLEAN)(!IsListEmpty(&Connection->ReceiveQueue)); + CurStatus->ReceiveActive = (BOOLEAN)((Connection->Flags & CONNECTION_FLAGS_ACTIVE_RECEIVE) != 0); + CurStatus->ReceiveWakeUp = (BOOLEAN)((Connection->Flags & CONNECTION_FLAGS_RECEIVE_WAKEUP) != 0); + + ++CurStatus; + ++TotalStatus; + + } + } + + Irp->IoStatus.Information = TotalStatus * sizeof(NBF_CONNECTION_STATUS); + status = STATUS_SUCCESS; + + } + + break; +#endif + + case TDI_QUERY_CONNECTION_INFO: + + // + // Connection info is queried on a connection, + // verify this. + // + + Connection = irpSp->FileObject->FsContext; + + status = NbfVerifyConnectionObject (Connection); + + if (!NT_SUCCESS (status)) { +#if DBG + NbfPrint2 ("TdiQueryInfo: Invalid Connection %lx Irp %lx\n", Connection, Irp); +#endif + return status; + } + + ConnectionInfo = ExAllocatePoolWithTag ( + NonPagedPool, + sizeof (TDI_CONNECTION_INFO), + ' FBN'); + + if (ConnectionInfo == NULL) { + + PANIC ("NbfQueryInfo: Cannot allocate connection info!\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 6, + sizeof(TDI_CONNECTION_INFO), + 0); + status = STATUS_INSUFFICIENT_RESOURCES; + + } else if ((Connection->Flags & CONNECTION_FLAGS_READY) == 0) { + + status = STATUS_INVALID_CONNECTION; + ExFreePool (ConnectionInfo); + + } else { + + PTP_LINK Link = Connection->Link; + + RtlZeroMemory ((PVOID)ConnectionInfo, sizeof(TDI_CONNECTION_INFO)); + + + // + // Get link delay and throughput. + // + + if (Link->Delay == 0xffffffff) { + + // + // If delay is not known, assume 0. + // + + ConnectionInfo->Delay.HighPart = 0; + ConnectionInfo->Delay.LowPart = 0; + + } else { + + // + // Copy the delay as an NT relative time. + // + + ConnectionInfo->Delay.HighPart = -1L; + ConnectionInfo->Delay.LowPart = (ULONG)-((LONG)(Link->Delay)); + + } + + if (DeviceContext->MacInfo.MediumAsync) { + + ULONG PacketsSent; + ULONG PacketsResent; + ULONG MultiplyFactor; + + // + // Calculate the packets sent and resent since the + // last time the throughput was queried. + // + + PacketsSent = Link->PacketsSent - Connection->LastPacketsSent; + PacketsResent = Link->PacketsResent - Connection->LastPacketsResent; + + // + // Save these for next time. + // + + Connection->LastPacketsSent = Link->PacketsSent; + Connection->LastPacketsResent = Link->PacketsResent; + + // + // To convert exactly from 100 bits-per-second to + // bytes-per-second, we need to multiply by 12.5. + // Using lower numbers will give worse throughput. + // If there have been no errors we use 12, if there + // have been 20% or more errors we use 1, and in + // between we subtract 11 * (error%/20%) from 12 + // and use that. + // + + if (PacketsResent == 0 || PacketsSent <= 10) { + + MultiplyFactor = 12; + + } else if ((PacketsSent / PacketsResent) <= 5) { + + MultiplyFactor = 1; + + } else { + + // + // error%/20% is error%/(1/5), which is 5*error%, + // which is 5 * (resent/send). + // + + ASSERT (((11 * 5 * PacketsResent) / PacketsSent) <= 11); + MultiplyFactor = 12 - ((11 * 5 * PacketsResent) / PacketsSent); + + } + + ConnectionInfo->Throughput.QuadPart = + UInt32x32To64(DeviceContext->MediumSpeed, MultiplyFactor); + + } else if (!Link->ThroughputAccurate) { + + // + // If throughput is not known, then guess. We + // have MediumSpeed in units of 100 bps; we + // return four times that number as the throughput, + // which corresponds to about 1/3 of the + // maximum bandwidth expressed in bytes/sec. + // + + ConnectionInfo->Throughput.QuadPart = + UInt32x32To64(DeviceContext->MediumSpeed, 4); + + } else { + + // + // Throughput is accurate, return it. + // + + ConnectionInfo->Throughput = Link->Throughput; + + } + + + // + // Calculate reliability using the sent/resent ratio, + // if there has been enough activity to make it + // worthwhile. >10% resent is unreliable. + // + + if ((Link->PacketsResent > 0) && + (Link->PacketsSent > 20)) { + + ConnectionInfo->Unreliable = + ((Link->PacketsSent / Link->PacketsResent) < 10); + + } else { + + ConnectionInfo->Unreliable = FALSE; + + } + + ConnectionInfo->TransmittedTsdus = Connection->TransmittedTsdus; + ConnectionInfo->ReceivedTsdus = Connection->ReceivedTsdus; + ConnectionInfo->TransmissionErrors = Connection->TransmissionErrors; + ConnectionInfo->ReceiveErrors = Connection->ReceiveErrors; + + status = TdiCopyBufferToMdl ( + (PVOID)ConnectionInfo, + 0L, + sizeof(TDI_CONNECTION_INFO), + Irp->MdlAddress, + 0, + &(Irp->IoStatus.Information)); + + ExFreePool (ConnectionInfo); + } + + NbfDereferenceConnection ("query connection info", Connection, CREF_BY_ID); + + break; + + case TDI_QUERY_ADDRESS_INFO: + + if (irpSp->FileObject->FsContext2 == (PVOID)TDI_TRANSPORT_ADDRESS_FILE) { + + AddressFile = irpSp->FileObject->FsContext; + + status = NbfVerifyAddressObject(AddressFile); + + if (!NT_SUCCESS (status)) { +#if DBG + NbfPrint2 ("TdiQueryInfo: Invalid AddressFile %lx Irp %lx\n", AddressFile, Irp); +#endif + return status; + } + + UsedConnection = FALSE; + + } else if (irpSp->FileObject->FsContext2 == (PVOID)TDI_CONNECTION_FILE) { + + Connection = irpSp->FileObject->FsContext; + + status = NbfVerifyConnectionObject (Connection); + + if (!NT_SUCCESS (status)) { +#if DBG + NbfPrint2 ("TdiQueryInfo: Invalid Connection %lx Irp %lx\n", Connection, Irp); +#endif + return status; + } + + AddressFile = Connection->AddressFile; + + UsedConnection = TRUE; + + } else { + + return STATUS_INVALID_ADDRESS; + + } + + Address = AddressFile->Address; + + TdiBuildNetbiosAddress( + Address->NetworkName->NetbiosName, + (BOOLEAN)(Address->Flags & ADDRESS_FLAGS_GROUP ? TRUE : FALSE), + &AddressInfo.TaAddressBuffer); + + // + // Count the active addresses. + // + + AddressInfo.ActivityCount = 0; + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + for (p = Address->AddressFileDatabase.Flink; + p != &Address->AddressFileDatabase; + p = p->Flink) { + ++AddressInfo.ActivityCount; + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + status = TdiCopyBufferToMdl ( + &AddressInfo, + 0, + sizeof(ULONG) + sizeof(TA_NETBIOS_ADDRESS), + Irp->MdlAddress, + 0, + &Irp->IoStatus.Information); + + if (UsedConnection) { + + NbfDereferenceConnection ("query address info", Connection, CREF_BY_ID); + + } else { + + NbfDereferenceAddress ("query address info", Address, AREF_VERIFY); + + } + + break; + + case TDI_QUERY_BROADCAST_ADDRESS: + + // + // for this provider, the broadcast address is a zero byte name, + // contained in a Transport address structure. + // + + broadcastAddress = ExAllocatePoolWithTag ( + NonPagedPool, + sizeof (TA_NETBIOS_ADDRESS), + ' FBN'); + if (broadcastAddress == NULL) { + PANIC ("NbfQueryInfo: Cannot allocate broadcast address!\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 2, + sizeof(TA_NETBIOS_ADDRESS), + 0); + status = STATUS_INSUFFICIENT_RESOURCES; + } else { + + broadcastAddress->TAAddressCount = 1; + broadcastAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; + broadcastAddress->Address[0].AddressLength = 0; + + Irp->IoStatus.Information = + sizeof (broadcastAddress->TAAddressCount) + + sizeof (broadcastAddress->Address[0].AddressType) + + sizeof (broadcastAddress->Address[0].AddressLength); + + status = TdiCopyBufferToMdl ( + (PVOID)broadcastAddress, + 0L, + Irp->IoStatus.Information, + Irp->MdlAddress, + 0, + &(Irp->IoStatus.Information)); + + ExFreePool (broadcastAddress); + } + + break; + + case TDI_QUERY_PROVIDER_INFO: + + status = TdiCopyBufferToMdl ( + &(DeviceContext->Information), + 0, + sizeof (TDI_PROVIDER_INFO), + Irp->MdlAddress, + 0, + &Irp->IoStatus.Information); + break; + + case TDI_QUERY_PROVIDER_STATISTICS: + + // + // BUGBUG: This information is probablt available somewhere else. + // + + NbfGetMdlChainLength (Irp->MdlAddress, &TargetBufferLength); + + if (TargetBufferLength < sizeof(TDI_PROVIDER_STATISTICS) + ((NBF_TDI_RESOURCES-1) * sizeof(TDI_PROVIDER_RESOURCE_STATS))) { + + Irp->IoStatus.Information = 0; + status = STATUS_BUFFER_OVERFLOW; + + } else { + + ProviderStatistics = ExAllocatePoolWithTag( + NonPagedPool, + sizeof(TDI_PROVIDER_STATISTICS) + + ((NBF_TDI_RESOURCES-1) * sizeof(TDI_PROVIDER_RESOURCE_STATS)), + ' FBN'); + + if (ProviderStatistics == NULL) { + + PANIC ("NbfQueryInfo: Cannot allocate provider statistics!\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 7, + sizeof(TDI_PROVIDER_STATISTICS), + 0); + status = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + NbfStoreProviderStatistics (DeviceContext, ProviderStatistics); + + status = TdiCopyBufferToMdl ( + (PVOID)ProviderStatistics, + 0L, + sizeof(TDI_PROVIDER_STATISTICS) + + ((NBF_TDI_RESOURCES-1) * sizeof(TDI_PROVIDER_RESOURCE_STATS)), + Irp->MdlAddress, + 0, + &(Irp->IoStatus.Information)); + + ExFreePool (ProviderStatistics); + } + + } + + break; + + case TDI_QUERY_SESSION_STATUS: + + status = STATUS_NOT_IMPLEMENTED; + break; + + case TDI_QUERY_ADAPTER_STATUS: + + NbfGetMdlChainLength (Irp->MdlAddress, &TargetBufferLength); + + // + // Determine if this is a local or remote query. It is + // local if there is no remote address specific at all, + // or if it is equal to our reserved address. + // + + RemoteAdapterStatus = FALSE; + + if (query->RequestConnectionInformation != NULL) { + + if (!NbfValidateTdiAddress( + query->RequestConnectionInformation->RemoteAddress, + query->RequestConnectionInformation->RemoteAddressLength)) { + return STATUS_BAD_NETWORK_PATH; + } + + RemoteAddress = NbfParseTdiAddress(query->RequestConnectionInformation->RemoteAddress, FALSE); + + if (!RemoteAddress) { + return STATUS_BAD_NETWORK_PATH; + } + if (!RtlEqualMemory( + RemoteAddress->NetbiosName, + DeviceContext->ReservedNetBIOSAddress, + NETBIOS_NAME_LENGTH)) { + + RemoteAdapterStatus = TRUE; + + } + } + + if (RemoteAdapterStatus) { + + // + // We need a request object to keep track of this TDI request. + // Attach this request to the device context. + // + + status = NbfCreateRequest ( + Irp, // IRP for this request. + DeviceContext, // context. + REQUEST_FLAGS_DC, // partial flags. + Irp->MdlAddress, // the data to be received. + TargetBufferLength, // length of the data. + timeout, // do this ourselves here. + &tpRequest); + + if (NT_SUCCESS (status)) { + + NbfReferenceDeviceContext ("Remote status", DeviceContext, DCREF_REQUEST); + tpRequest->Owner = DeviceContextType; + + // + // Allocate a temp buffer to hold our results. + // + + tpRequest->ResponseBuffer = ExAllocatePoolWithTag( + NonPagedPool, + TargetBufferLength, + ' FBN'); + + if (tpRequest->ResponseBuffer == NULL) { + + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 12, + TargetBufferLength, + 0); + NbfCompleteRequest (tpRequest, STATUS_INSUFFICIENT_RESOURCES, 0); + + } else { + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock,&oldirql); + + if (DeviceContext->State != DEVICECONTEXT_STATE_OPEN) { + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock,oldirql); + NbfCompleteRequest (tpRequest, STATUS_DEVICE_NOT_READY, 0); + + } else { + + PUCHAR SingleSR; + UINT SingleSRLength; + + InsertTailList ( + &DeviceContext->StatusQueryQueue, + &tpRequest->Linkage); + + tpRequest->FrameContext = DeviceContext->UniqueIdentifier | 0x8000; + ++DeviceContext->UniqueIdentifier; + if (DeviceContext->UniqueIdentifier == 0x8000) { + DeviceContext->UniqueIdentifier = 1; + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + // + // The request is queued. Now send out the first packet and + // start the timer. + // + + tpRequest->Retries = DeviceContext->GeneralRetries; + tpRequest->BytesWritten = 0; + + // + // STATUS_QUERY frames go out as + // single-route source routing. + // + + MacReturnSingleRouteSR( + &DeviceContext->MacInfo, + &SingleSR, + &SingleSRLength); + + NbfSendStatusQuery( + DeviceContext, + tpRequest, + &DeviceContext->NetBIOSAddress, + SingleSR, + SingleSRLength); + + } + + } + + // + // As long as the request is created, pend here. + // The IRP will complete when the request completes. + // + + status = STATUS_PENDING; + + } + + } else { + + // + // Local. + // + + adapterStatus = ExAllocatePoolWithTag ( + NonPagedPool, + TargetBufferLength, + ' FBN'); + + if (adapterStatus == NULL) { + PANIC("NbfQueryInfo: PANIC! Could not allocate adapter status buffer\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 3, + TargetBufferLength, + 0); + return STATUS_INSUFFICIENT_RESOURCES; + } + + NbfStoreAdapterStatus ( + DeviceContext, + NULL, + 0, + adapterStatus); + + NbfStoreNameBuffers ( + DeviceContext, + (PUCHAR)adapterStatus + sizeof(ADAPTER_STATUS), + TargetBufferLength - sizeof(ADAPTER_STATUS), + 0, + &NamesWritten, + &TotalNameCount, + &Truncated); + + ((PADAPTER_STATUS)adapterStatus)->name_count = (WORD)TotalNameCount; + + BytesWritten = sizeof(ADAPTER_STATUS) + (NamesWritten * sizeof(NAME_BUFFER)); + + status = TdiCopyBufferToMdl ( + adapterStatus, + 0, + BytesWritten, + Irp->MdlAddress, + 0, + &Irp->IoStatus.Information); + + if (Truncated) { + status = STATUS_BUFFER_OVERFLOW; + } + + ExFreePool (adapterStatus); + + } + + break; + + case TDI_QUERY_FIND_NAME: + + NbfGetMdlChainLength (Irp->MdlAddress, &TargetBufferLength); + + // + // Check that there is a valid Netbios remote address. + // + + if (!NbfValidateTdiAddress( + query->RequestConnectionInformation->RemoteAddress, + query->RequestConnectionInformation->RemoteAddressLength)) { + return STATUS_BAD_NETWORK_PATH; + } + + RemoteAddress = NbfParseTdiAddress(query->RequestConnectionInformation->RemoteAddress, FALSE); + + if (!RemoteAddress) { + return STATUS_BAD_NETWORK_PATH; + } + + // + // We need a request object to keep track of this TDI request. + // Attach this request to the device context. + // + + status = NbfCreateRequest ( + Irp, // IRP for this request. + DeviceContext, // context. + REQUEST_FLAGS_DC, // partial flags. + Irp->MdlAddress, // the data to be received. + TargetBufferLength, // length of the data. + timeout, // do this ourselves here. + &tpRequest); + + if (NT_SUCCESS (status)) { + + NbfReferenceDeviceContext ("Find name", DeviceContext, DCREF_REQUEST); + tpRequest->Owner = DeviceContextType; + + // + // Allocate a temp buffer to hold our results. + // + + tpRequest->ResponseBuffer = ExAllocatePoolWithTag( + NonPagedPool, + TargetBufferLength, + ' FBN'); + + if (tpRequest->ResponseBuffer == NULL) { + + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 4, + TargetBufferLength, + 0); + NbfCompleteRequest (tpRequest, STATUS_INSUFFICIENT_RESOURCES, 0); + + } else { + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock,&oldirql); + + if (DeviceContext->State != DEVICECONTEXT_STATE_OPEN) { + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock,oldirql); + NbfCompleteRequest (tpRequest, STATUS_DEVICE_NOT_READY, 0); + + } else { + + InsertTailList ( + &DeviceContext->FindNameQueue, + &tpRequest->Linkage); + + tpRequest->FrameContext = DeviceContext->UniqueIdentifier | 0x8000; + ++DeviceContext->UniqueIdentifier; + if (DeviceContext->UniqueIdentifier == 0x8000) { + DeviceContext->UniqueIdentifier = 1; + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + // + // The request is queued. Now send out the first packet and + // start the timer. + // + // We fill in the FIND_NAME_HEADER in the buffer, but + // set BytesWritten to 0; we don't include the header + // in BytesWritten until we get a response, so that + // a BytesWritten of 0 means "no response". + // + + tpRequest->Retries = DeviceContext->GeneralRetries; + tpRequest->BytesWritten = 0; + FindNameHeader = (PFIND_NAME_HEADER)tpRequest->ResponseBuffer; + FindNameHeader->node_count = 0; + FindNameHeader->unique_group = NETBIOS_NAME_TYPE_UNIQUE; + + NbfSendQueryFindName (DeviceContext, tpRequest); + + } + + } + + // + // As long as the request is created, pend here. + // The IRP will complete when the request completes. + // + + status = STATUS_PENDING; + } + + break; + + case TDI_QUERY_DATA_LINK_ADDRESS: + case TDI_QUERY_NETWORK_ADDRESS: + + TaAddress = (PTRANSPORT_ADDRESS)&AddressInfo.TaAddressBuffer; + TaAddress->TAAddressCount = 1; + TaAddress->Address[0].AddressLength = 6; + if (query->QueryType == TDI_QUERY_DATA_LINK_ADDRESS) { + TaAddress->Address[0].AddressType = + DeviceContext->MacInfo.MediumAsync ? + NdisMediumWan : DeviceContext->MacInfo.MediumType; + } else { + TaAddress->Address[0].AddressType = TDI_ADDRESS_TYPE_UNSPEC; + } + RtlCopyMemory (TaAddress->Address[0].Address, DeviceContext->LocalAddress.Address, 6); + + status = TdiCopyBufferToMdl ( + &AddressInfo.TaAddressBuffer, + 0, + sizeof(TRANSPORT_ADDRESS)+5, + Irp->MdlAddress, + 0, + &Irp->IoStatus.Information); + break; + + case TDI_QUERY_DATAGRAM_INFO: + + DatagramInfo.MaximumDatagramBytes = 0; + DatagramInfo.MaximumDatagramCount = 0; + + status = TdiCopyBufferToMdl ( + &DatagramInfo, + 0, + sizeof(DatagramInfo), + Irp->MdlAddress, + 0, + &Irp->IoStatus.Information); + break; + + default: + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + return status; + +} /* NbfTdiQueryInformation */ + +// +// Quick macros, assumes DeviceContext and ProviderStatistics exist. +// + +#define STORE_RESOURCE_STATS_1(_ResourceNum,_ResourceId,_ResourceName) \ +{ \ + PTDI_PROVIDER_RESOURCE_STATS RStats = &ProviderStatistics->ResourceStats[_ResourceNum]; \ + RStats->ResourceId = (_ResourceId); \ + RStats->MaximumResourceUsed = DeviceContext->_ResourceName ## MaxInUse; \ + if (DeviceContext->_ResourceName ## Samples > 0) { \ + RStats->AverageResourceUsed = DeviceContext->_ResourceName ## Total / DeviceContext->_ResourceName ## Samples; \ + } else { \ + RStats->AverageResourceUsed = 0; \ + } \ + RStats->ResourceExhausted = DeviceContext->_ResourceName ## Exhausted; \ +} + +#define STORE_RESOURCE_STATS_2(_ResourceNum,_ResourceId,_ResourceName) \ +{ \ + PTDI_PROVIDER_RESOURCE_STATS RStats = &ProviderStatistics->ResourceStats[_ResourceNum]; \ + RStats->ResourceId = (_ResourceId); \ + RStats->MaximumResourceUsed = DeviceContext->_ResourceName ## Allocated; \ + RStats->AverageResourceUsed = DeviceContext->_ResourceName ## Allocated; \ + RStats->ResourceExhausted = DeviceContext->_ResourceName ## Exhausted; \ +} + + +VOID +NbfStoreProviderStatistics( + IN PDEVICE_CONTEXT DeviceContext, + IN PTDI_PROVIDER_STATISTICS ProviderStatistics + ) + +/*++ + +Routine Description: + + This routine writes the TDI_PROVIDER_STATISTICS structure + from the device context into ProviderStatistics. + +Arguments: + + DeviceContext - a pointer to the device context. + + ProviderStatistics - The buffer that holds the result. It is assumed + that it is long enough. + +Return Value: + + None. + +--*/ + +{ + + // + // Copy all the statistics up to NumberOfResources + // in one move. + // + + RtlCopyMemory( + ProviderStatistics, + &DeviceContext->Statistics, + FIELD_OFFSET (TDI_PROVIDER_STATISTICS, NumberOfResources)); + + // + // Calculate AverageSendWindow. + // + + if (DeviceContext->SendWindowSamples > 0) { + ProviderStatistics->AverageSendWindow = + DeviceContext->SendWindowTotal / DeviceContext->SendWindowSamples; + } else { + ProviderStatistics->AverageSendWindow = 1; + } + + // + // Copy the resource statistics. + // + + ProviderStatistics->NumberOfResources = NBF_TDI_RESOURCES; + + STORE_RESOURCE_STATS_1 (0, LINK_RESOURCE_ID, Link); + STORE_RESOURCE_STATS_1 (1, ADDRESS_RESOURCE_ID, Address); + STORE_RESOURCE_STATS_1 (2, ADDRESS_FILE_RESOURCE_ID, AddressFile); + STORE_RESOURCE_STATS_1 (3, CONNECTION_RESOURCE_ID, Connection); + STORE_RESOURCE_STATS_1 (4, REQUEST_RESOURCE_ID, Request); + + STORE_RESOURCE_STATS_2 (5, UI_FRAME_RESOURCE_ID, UIFrame); + STORE_RESOURCE_STATS_2 (6, PACKET_RESOURCE_ID, Packet); + STORE_RESOURCE_STATS_2 (7, RECEIVE_PACKET_RESOURCE_ID, ReceivePacket); + STORE_RESOURCE_STATS_2 (8, RECEIVE_BUFFER_RESOURCE_ID, ReceiveBuffer); + +} /* NbfStoreProviderStatistics */ + + +VOID +NbfStoreAdapterStatus( + IN PDEVICE_CONTEXT DeviceContext, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + IN PVOID StatusBuffer + ) + +/*++ + +Routine Description: + + This routine writes the ADAPTER_STATUS structure for the + device context into StatusBuffer. The name_count field is + initialized to zero; NbfStoreNameBuffers is used to write + name buffers. + +Arguments: + + DeviceContext - a pointer to the device context. + + SourceRouting - If this is a remote request, the source + routing information from the frame. + + SourceRoutingLength - The length of SourceRouting. + + StatusBuffer - The buffer that holds the result. It is assumed + that it is at least sizeof(ADAPTER_STATUS) bytes long. + +Return Value: + + None. + +--*/ + +{ + + PADAPTER_STATUS AdapterStatus = (PADAPTER_STATUS)StatusBuffer; + UINT MaxUserData; + + RtlZeroMemory ((PVOID)AdapterStatus, sizeof(ADAPTER_STATUS)); + + RtlCopyMemory (AdapterStatus->adapter_address, DeviceContext->LocalAddress.Address, 6); + AdapterStatus->rev_major = 0x03; + + switch (DeviceContext->MacInfo.MediumType) { + case NdisMedium802_5: AdapterStatus->adapter_type = 0xff; break; + default: AdapterStatus->adapter_type = 0xfe; break; + } + + AdapterStatus->frmr_recv = (WORD)DeviceContext->FrmrReceived; + AdapterStatus->frmr_xmit = (WORD)DeviceContext->FrmrTransmitted; + + AdapterStatus->recv_buff_unavail = (WORD)(DeviceContext->ReceivePacketExhausted + DeviceContext->ReceiveBufferExhausted); + AdapterStatus->xmit_buf_unavail = (WORD)DeviceContext->PacketExhausted; + + AdapterStatus->xmit_success = (WORD)(DeviceContext->Statistics.DataFramesSent - DeviceContext->Statistics.DataFramesResent); + AdapterStatus->recv_success = (WORD)DeviceContext->Statistics.DataFramesReceived; + AdapterStatus->iframe_recv_err = (WORD)DeviceContext->Statistics.DataFramesRejected; + AdapterStatus->iframe_xmit_err = (WORD)DeviceContext->Statistics.DataFramesResent; + + AdapterStatus->t1_timeouts = (WORD)DeviceContext->Statistics.ResponseTimerExpirations; + AdapterStatus->ti_timeouts = (WORD)DeviceContext->TiExpirations; + AdapterStatus->xmit_aborts = (WORD)0; + + + AdapterStatus->free_ncbs = (WORD)0xffff; + AdapterStatus->max_cfg_ncbs = (WORD)0xffff; + AdapterStatus->max_ncbs = (WORD)0xffff; + AdapterStatus->pending_sess = (WORD)DeviceContext->Statistics.OpenConnections; + AdapterStatus->max_cfg_sess = (WORD)0xffff; + AdapterStatus->max_sess = (WORD)0xffff; + + + MacReturnMaxDataSize( + &DeviceContext->MacInfo, + NULL, + 0, + DeviceContext->MaxSendPacketSize, + TRUE, + &MaxUserData); + AdapterStatus->max_dgram_size = (WORD)(MaxUserData - (sizeof(DLC_FRAME) + sizeof(NBF_HDR_CONNECTIONLESS))); + + MacReturnMaxDataSize( + &DeviceContext->MacInfo, + SourceRouting, + SourceRoutingLength, + DeviceContext->MaxSendPacketSize, + FALSE, + &MaxUserData); + AdapterStatus->max_sess_pkt_size = (WORD)(MaxUserData - (sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION))); + + return; + +} /* NbfStoreAdapterStatus */ + + +VOID +NbfStoreNameBuffers( + IN PDEVICE_CONTEXT DeviceContext, + IN PVOID Buffer, + IN ULONG BufferLength, + IN ULONG NamesToSkip, + OUT PULONG NamesWritten, + OUT PULONG TotalNameCount OPTIONAL, + OUT PBOOLEAN Truncated + ) + +/*++ + +Routine Description: + + This routine writes NAME_BUFFER structures for the + device context into NameBuffer. It can skip a specified + number of names at the beginning, and returns the number + of names written into NameBuffer. If a name will only + partially fit, it is not written. + +Arguments: + + DeviceContext - a pointer to the device context. + + NameBuffer - The buffer to write the names into. + + NameBufferLength - The length of NameBuffer. + + NamesToSkip - The number of names to skip. + + NamesWritten - Returns the number of names written. + + TotalNameCount - Returns the total number of names available, + if specified. + + Truncated - More names are available than were written. + +Return Value: + + None. + +--*/ + +{ + + ULONG NameCount = 0; + ULONG BytesWritten = 0; + KIRQL oldirql; + PLIST_ENTRY p; + PNAME_BUFFER NameBuffer = (PNAME_BUFFER)Buffer; + PTP_ADDRESS address; + + + // + // Spin through the address list for this device context. + // + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + p = DeviceContext->AddressDatabase.Flink; + + for (p = DeviceContext->AddressDatabase.Flink; + p != &DeviceContext->AddressDatabase; + p = p->Flink) { + + address = CONTAINING_RECORD (p, TP_ADDRESS, Linkage); + + // + // Ignore addresses that are shutting down. + // + + if ((address->Flags & ADDRESS_FLAGS_STOPPING) != 0) { + continue; + } + + // + // Ignore the broadcast address. + // + + if (address->NetworkName == NULL) { + continue; + } + + // + // Ignore the reserved address. + // + + if ((address->NetworkName->NetbiosName[0] == 0) && + (RtlEqualMemory( + address->NetworkName->NetbiosName, + DeviceContext->ReservedNetBIOSAddress, + NETBIOS_NAME_LENGTH))) { + + continue; + } + + // + // Check if we are still skipping. + // + + if (NameCount < NamesToSkip) { + ++NameCount; + continue; + } + + // + // Make sure we still have room. + // + + if (BytesWritten + sizeof(NAME_BUFFER) > BufferLength) { + break; + } + + RtlCopyMemory( + NameBuffer->name, + address->NetworkName->NetbiosName, + NETBIOS_NAME_LENGTH); + + ++NameCount; + NameBuffer->name_num = (UCHAR)NameCount; + + NameBuffer->name_flags = REGISTERED; + if (address->Flags & ADDRESS_FLAGS_GROUP) { + NameBuffer->name_flags |= GROUP_NAME; + } + + // BUGBUG: name_flags should be done more accurately. + + BytesWritten += sizeof(NAME_BUFFER); + ++NameBuffer; + + } + + *NamesWritten = NameBuffer - (PNAME_BUFFER)Buffer; + + if (p == &DeviceContext->AddressDatabase) { + + *Truncated = FALSE; + if (ARGUMENT_PRESENT(TotalNameCount)) { + *TotalNameCount = NameCount; + } + + } else { + + *Truncated = TRUE; + + // + // If requested, continue through the list and count + // all the addresses. + // + + if (ARGUMENT_PRESENT(TotalNameCount)) { + + for ( ; + p != &DeviceContext->AddressDatabase; + p = p->Flink) { + + address = CONTAINING_RECORD (p, TP_ADDRESS, Linkage); + + // + // Ignore addresses that are shutting down. + // + + if ((address->Flags & ADDRESS_FLAGS_STOPPING) != 0) { + continue; + } + + // + // Ignore the broadcast address. + // + + if (address->NetworkName == NULL) { + continue; + } + + // + // Ignore the reserved address, since we count it no matter what. + // + + if ((address->NetworkName->NetbiosName[0] == 0) && + (RtlEqualMemory( + address->NetworkName->NetbiosName, + DeviceContext->ReservedNetBIOSAddress, + NETBIOS_NAME_LENGTH))) { + + continue; + } + + ++NameCount; + + } + + *TotalNameCount = NameCount; + + } + + } + + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + return; + +} /* NbfStoreNameBuffers */ + + +NTSTATUS +NbfProcessStatusQuery( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS Address OPTIONAL, + IN PNBF_HDR_CONNECTIONLESS UiFrame, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ) + +/*++ + +Routine Description: + + This routine processes a STATUS.QUERY packet. + +Arguments: + + DeviceContext - a pointer to the device context the frame was received on. + + Address - The address we are responding from, or NULL if the STATUS.QUERY + was sent to the reserved address. + + UiFrame - The packet in question, starting at the Netbios header. + + SourceAddress - The source hardware address of the packet. + + SourceRouting - Source routing data in the query. + + SourceRoutingLength - The length of SourceRouting. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + NTSTATUS Status; + NDIS_STATUS NdisStatus; + PTP_UI_FRAME RawFrame; + PVOID ResponseBuffer; + UINT ResponseBufferLength; + ULONG NamesWritten, TotalNameCount; + ULONG BytesWritten; + UCHAR RequestType; + BOOLEAN Truncated, UsersBufferTooShort; + USHORT UsersBufferLength; + UINT HeaderLength; + UCHAR TempSR[MAX_SOURCE_ROUTING]; + PUCHAR ResponseSR; + PNDIS_BUFFER NdisBuffer; + + // + // Allocate a buffer to hold the status. + // + + MacReturnMaxDataSize( + &DeviceContext->MacInfo, + SourceRouting, + SourceRoutingLength, + DeviceContext->CurSendPacketSize, + FALSE, + &ResponseBufferLength); + + ResponseBufferLength -= (sizeof(DLC_FRAME) + sizeof(NBF_HDR_CONNECTIONLESS)); + + UsersBufferLength = (UiFrame->Data2High * 256) + UiFrame->Data2Low; + + // + // See how big to make our buffer; if the amount remaining in the user's + // buffer is less than our max size, chop it down. + // + + if (UiFrame->Data1 <= 1) { + + // + // This is the initial request. + // + + if (ResponseBufferLength > (UINT)UsersBufferLength) { + ResponseBufferLength = UsersBufferLength; + } + + } else { + + // + // Subsequent request; compensate for already-sent data. + // + + UsersBufferLength -= (sizeof(ADAPTER_STATUS) + (UiFrame->Data1 * sizeof(NAME_BUFFER))); + + if (ResponseBufferLength > (UINT)UsersBufferLength) { + ResponseBufferLength = UsersBufferLength; + } + + } + + // + // If the remote station is asking for no data, ignore this request. + // This prevents us from trying to allocate 0 bytes of pool. + // + + if ( (LONG)ResponseBufferLength <= 0 ) { + return STATUS_ABANDONED; + } + + ResponseBuffer = ExAllocatePoolWithTag( + NonPagedPool, + ResponseBufferLength, + ' FBN'); + + if (ResponseBuffer == NULL) { + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 5, + ResponseBufferLength, + 0); + return STATUS_ABANDONED; + } + + + // + // Fill in the response buffer. + // + + if (UiFrame->Data1 <= 1) { + + // + // First request. + // + + NbfStoreAdapterStatus ( + DeviceContext, + SourceRouting, + SourceRoutingLength, + ResponseBuffer); + + NbfStoreNameBuffers ( + DeviceContext, + (PUCHAR)ResponseBuffer + sizeof(ADAPTER_STATUS), + ResponseBufferLength - sizeof(ADAPTER_STATUS), + 0, + &NamesWritten, + &TotalNameCount, + &Truncated); + + BytesWritten = sizeof(ADAPTER_STATUS) + (NamesWritten * sizeof(NAME_BUFFER)); + + // + // If the data was truncated, but we are returning the maximum + // that the user requested, report that as "user's buffer + // too short" instead of "truncated". + // + + if (Truncated && (ResponseBufferLength >= (UINT)UsersBufferLength)) { + Truncated = FALSE; + UsersBufferTooShort = TRUE; + } else { + UsersBufferTooShort = FALSE; + } + + ((PADAPTER_STATUS)ResponseBuffer)->name_count = (WORD)TotalNameCount; + + } else { + + NbfStoreNameBuffers ( + DeviceContext, + ResponseBuffer, + ResponseBufferLength, + UiFrame->Data1, + &NamesWritten, + NULL, + &Truncated); + + BytesWritten = NamesWritten * sizeof(NAME_BUFFER); + + if (Truncated && (ResponseBufferLength >= (UINT)UsersBufferLength)) { + Truncated = FALSE; + UsersBufferTooShort = TRUE; + } else { + UsersBufferTooShort = FALSE; + } + + } + + // + // Allocate a UI frame from the pool. + // + + Status = NbfCreateConnectionlessFrame (DeviceContext, &RawFrame); + if (!NT_SUCCESS (Status)) { // couldn't make frame. + ExFreePool (ResponseBuffer); + return STATUS_ABANDONED; + } + + IF_NBFDBG (NBF_DEBUG_DEVCTX) { + NbfPrint2 ("NbfProcessStatusQuery: Sending Frame: %lx, NdisPacket: %lx\n", + RawFrame, RawFrame->NdisPacket); + } + + + // + // Build the MAC header. STATUS_RESPONSE frames go out as + // non-broadcast source routing. + // + + if (SourceRouting != NULL) { + + RtlCopyMemory( + TempSR, + SourceRouting, + SourceRoutingLength); + + MacCreateNonBroadcastReplySR( + &DeviceContext->MacInfo, + TempSR, + SourceRoutingLength, + &ResponseSR); + + } else { + + ResponseSR = NULL; + + } + + MacConstructHeader ( + &DeviceContext->MacInfo, + RawFrame->Header, + SourceAddress->Address, + DeviceContext->LocalAddress.Address, + sizeof (DLC_FRAME) + sizeof (NBF_HDR_CONNECTIONLESS) + BytesWritten, + ResponseSR, + SourceRoutingLength, + &HeaderLength); + + + // + // Build the DLC UI frame header. + // + + NbfBuildUIFrameHeader(&RawFrame->Header[HeaderLength]); + HeaderLength += sizeof(DLC_FRAME); + + + // + // Build the Netbios header. + // + + switch (UiFrame->Data1) { + case 0: // pre 2.1 request + RequestType = (UCHAR)0; + break; + case 1: // 2.1, first request + RequestType = (UCHAR)NamesWritten; + break; + default: // 2.1, subsequent request + RequestType = (UCHAR)(UiFrame->Data1 + NamesWritten); + break; + } + + ConstructStatusResponse ( + (PNBF_HDR_CONNECTIONLESS)&(RawFrame->Header[HeaderLength]), + RequestType, // request type. + Truncated, // more data. + UsersBufferTooShort, // user's buffer too small + (USHORT)BytesWritten, // bytes in response + RESPONSE_CORR(UiFrame), // correlator + UiFrame->SourceName, // receiver permanent name + (ARGUMENT_PRESENT(Address)) ? + Address->NetworkName->NetbiosName : + DeviceContext->ReservedNetBIOSAddress); // source name + + HeaderLength += sizeof(NBF_HDR_CONNECTIONLESS); + + + // + // Munge the packet length (now, before we append the second + // buffer). + // + + NbfSetNdisPacketLength(RawFrame->NdisPacket, HeaderLength); + + + // + // Now, if we have any name data, attach our buffer onto the frame. + // Note that it's possible at the end of the user's buffer for us + // to not have room for any names, and thus we'll have no data to + // send. + // + + if ( BytesWritten != 0 ) { + + RawFrame->DataBuffer = ResponseBuffer; + + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + DeviceContext->NdisBufferPool, + ResponseBuffer, + BytesWritten); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + PANIC ("ConstructStatusResponse: NdisAllocateBuffer failed.\n"); + NbfDestroyConnectionlessFrame (DeviceContext, RawFrame); + return STATUS_ABANDONED; + } + + NdisChainBufferAtBack (RawFrame->NdisPacket, NdisBuffer); + + } else { + + RawFrame->DataBuffer = NULL; + + } + + + NbfSendUIFrame ( + DeviceContext, + RawFrame, + FALSE); // no loopback (MC frame) + + return STATUS_ABANDONED; + +} /* NbfProcessStatusQuery */ + + +VOID +NbfSendQueryFindName( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_REQUEST Request + ) + +/*++ + +Routine Description: + + This routine will send a FIND.NAME packet for the specified + find name request, and start the request timer. + +Arguments: + + DeviceContext - a pointer to the device context to send the find name on. + + Request - The find name request. + +Return Value: + + None. + +--*/ + +{ + TDI_ADDRESS_NETBIOS UNALIGNED * remoteAddress; + PIO_STACK_LOCATION irpSp; + NTSTATUS Status; + PTP_UI_FRAME RawFrame; + PUCHAR SingleSR; + UINT SingleSRLength; + UINT HeaderLength; + LARGE_INTEGER Timeout; + + irpSp = IoGetCurrentIrpStackLocation (Request->IoRequestPacket); + + remoteAddress = NbfParseTdiAddress( + ((PTDI_REQUEST_KERNEL_QUERY_INFORMATION)(&irpSp->Parameters))-> + RequestConnectionInformation->RemoteAddress, FALSE); + + // + // Start the timer for this request. + // + + Request->Flags |= REQUEST_FLAGS_TIMER; // there is a timeout on this request. + KeInitializeTimer (&Request->Timer); // set to not-signaled state. + NbfReferenceRequest ("Find Name: timer", Request, RREF_TIMER); // one for the timer + Timeout.LowPart = (ULONG)(-(LONG)DeviceContext->GeneralTimeout); + Timeout.HighPart = -1; + KeSetTimer (&Request->Timer, Timeout, &Request->Dpc); + + // + // Allocate a UI frame from the pool. + // + + Status = NbfCreateConnectionlessFrame (DeviceContext, &RawFrame); + if (!NT_SUCCESS (Status)) { // couldn't make frame. + return; + } + + IF_NBFDBG (NBF_DEBUG_DEVCTX) { + NbfPrint2 ("NbfSendFindNames: Sending Frame: %lx, NdisPacket: %lx\n", + RawFrame, RawFrame->NdisPacket); + } + + + // + // Build the MAC header. NAME_QUERY frames go out as + // single-route source routing. + // + + MacReturnSingleRouteSR( + &DeviceContext->MacInfo, + &SingleSR, + &SingleSRLength); + + MacConstructHeader ( + &DeviceContext->MacInfo, + RawFrame->Header, + DeviceContext->NetBIOSAddress.Address, + DeviceContext->LocalAddress.Address, + sizeof (DLC_FRAME) + sizeof (NBF_HDR_CONNECTIONLESS), + SingleSR, + SingleSRLength, + &HeaderLength); + + + // + // Build the DLC UI frame header. + // + + NbfBuildUIFrameHeader(&RawFrame->Header[HeaderLength]); + HeaderLength += sizeof(DLC_FRAME); + + + // + // Build the Netbios header. + // + + ConstructNameQuery ( + (PNBF_HDR_CONNECTIONLESS)&(RawFrame->Header[HeaderLength]), + NETBIOS_NAME_TYPE_UNIQUE, // call from a unique name. + NAME_QUERY_LSN_FIND_NAME, // LSN + Request->FrameContext, // corr. in 1st NAME_RECOGNIZED. + DeviceContext->ReservedNetBIOSAddress, + (PNAME)remoteAddress->NetbiosName); + + HeaderLength += sizeof(NBF_HDR_CONNECTIONLESS); + + + // + // Munge the packet length. + // + + NbfSetNdisPacketLength(RawFrame->NdisPacket, HeaderLength); + + NbfSendUIFrame ( + DeviceContext, + RawFrame, + FALSE); // no loopback (MC frame) + +} /* NbfSendQueryFindName */ + + +NTSTATUS +NbfProcessQueryNameRecognized( + IN PDEVICE_CONTEXT DeviceContext, + IN PUCHAR Packet, + PNBF_HDR_CONNECTIONLESS UiFrame + ) + +/*++ + +Routine Description: + + This routine processes a NAME.RECOGNIZED request with a + correlator of 0, indicating it was a response to a previous + FIND.NAME packet. + +Arguments: + + DeviceContext - a pointer to the device context the frame was received on. + + Packet - The packet in question, starting at the MAC header. + + UiFrame - The packet, starting at the Netbios header. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + KIRQL oldirql; + PTP_REQUEST Request; + PFIND_NAME_BUFFER FindNameBuffer; + PFIND_NAME_HEADER FindNameHeader; + PUCHAR DestinationAddress; + HARDWARE_ADDRESS SourceAddressBuffer; + PHARDWARE_ADDRESS SourceAddress; + PUCHAR SourceRouting; + UINT SourceRoutingLength; + PUCHAR TargetBuffer; + USHORT FrameContext; + PLIST_ENTRY p; + + + MacReturnDestinationAddress( + &DeviceContext->MacInfo, + Packet, + &DestinationAddress); + + MacReturnSourceAddress( + &DeviceContext->MacInfo, + Packet, + &SourceAddressBuffer, + &SourceAddress, + NULL); + + MacReturnSourceRouting( + &DeviceContext->MacInfo, + Packet, + &SourceRouting, + &SourceRoutingLength); + + // + // Find the request that this is for, using the frame context. + // + + FrameContext = TRANSMIT_CORR(UiFrame); + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + for (p=DeviceContext->FindNameQueue.Flink; + p != &DeviceContext->FindNameQueue; + p=p->Flink) { + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + + if (Request->FrameContext == FrameContext) { + + break; + + } + + } + + if (p == &DeviceContext->FindNameQueue) { + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + return STATUS_SUCCESS; + + } + + NbfReferenceRequest ("Name Recognized", Request, RREF_FIND_NAME); + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + // + // Make sure that this physical address has not + // responded yet. + // + + ACQUIRE_SPIN_LOCK (&Request->SpinLock, &oldirql); + + // + // Make sure this request is not stopping. + // + + if ((Request->Flags & REQUEST_FLAGS_STOPPING) != 0) { + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + NbfDereferenceRequest ("Stopping", Request, RREF_STATUS); + return STATUS_SUCCESS; + } + + // + // If this is the first response, update BytesWritten to include + // the header that is already written in ResponseBuffer. + // + + if (Request->BytesWritten == 0) { + Request->BytesWritten = sizeof(FIND_NAME_HEADER); + } + + TargetBuffer = Request->ResponseBuffer; + FindNameBuffer = (PFIND_NAME_BUFFER)(TargetBuffer + sizeof(FIND_NAME_HEADER)); + + for ( ; FindNameBuffer < (PFIND_NAME_BUFFER)(TargetBuffer + Request->BytesWritten); FindNameBuffer++) { + + if (RtlEqualMemory (FindNameBuffer->source_addr, SourceAddress->Address, 6)) { + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + NbfDereferenceRequest ("Duplicate NR", Request, RREF_FIND_NAME); + return STATUS_SUCCESS; + + } + + } + + // + // This is a new address, update if there is room. + // + + if ((Request->BytesWritten + sizeof(FIND_NAME_BUFFER)) > + Request->Buffer2Length) { + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock,&oldirql); + RemoveEntryList (&Request->Linkage); + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + NbfCompleteRequest (Request, STATUS_SUCCESS, Request->BytesWritten); + NbfDereferenceRequest ("No Buffer", Request, RREF_FIND_NAME); + return STATUS_SUCCESS; + + } + + FindNameHeader = (PFIND_NAME_HEADER)TargetBuffer; + FindNameHeader->unique_group = UiFrame->Data2High; + + Request->BytesWritten += sizeof(FIND_NAME_BUFFER); + ++FindNameHeader->node_count; + + RtlCopyMemory(FindNameBuffer->source_addr, SourceAddress->Address, 6); + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + + RtlCopyMemory(FindNameBuffer->destination_addr, DestinationAddress, 6); + FindNameBuffer->length = 14; + + if (DeviceContext->MacInfo.MediumType == NdisMedium802_5) { + + // + // token-ring, copy the correct fields. + // + + FindNameBuffer->access_control = Packet[0]; + FindNameBuffer->frame_control = Packet[1]; + + if (SourceRouting != NULL) { + RtlCopyMemory (FindNameBuffer->routing_info, SourceRouting, SourceRoutingLength); + FindNameBuffer->length += SourceRoutingLength; + } + + } else { + + // + // non-token-ring, nothing else is significant. + // + + FindNameBuffer->access_control = 0x0; + FindNameBuffer->frame_control = 0x0; + + } + + + // + // If this is a unique name, complete the request now. + // + + if (UiFrame->Data2High == NETBIOS_NAME_TYPE_UNIQUE) { + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock,&oldirql); + RemoveEntryList (&Request->Linkage); + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + NbfCompleteRequest(Request, STATUS_SUCCESS, Request->BytesWritten); + + } + + NbfDereferenceRequest ("NR processed", Request, RREF_FIND_NAME); + return STATUS_SUCCESS; + +} /* NbfProcessQueryNameRecognized */ + + +VOID +NbfSendStatusQuery( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_REQUEST Request, + IN PHARDWARE_ADDRESS DestinationAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ) + +/*++ + +Routine Description: + + This routine will send a STATUS.NAME packet for the specified + find name request, and start the request timer. + +Arguments: + + DeviceContext - a pointer to the device context to send the status query on. + + Request - The find name request. + + DestinationAddress - The hardware destination address of the frame. + + SourceRouting - Optional source routing information in the frame. + + SourceRoutingLength - The length of SourceRouting. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + TDI_ADDRESS_NETBIOS UNALIGNED * remoteAddress; + PIO_STACK_LOCATION irpSp; + NTSTATUS Status; + PTP_UI_FRAME RawFrame; + PUCHAR SingleSR; + UINT SingleSRLength; + UINT HeaderLength; + LARGE_INTEGER Timeout; + UCHAR RequestType; + + irpSp = IoGetCurrentIrpStackLocation (Request->IoRequestPacket); + + remoteAddress = NbfParseTdiAddress( + ((PTDI_REQUEST_KERNEL_QUERY_INFORMATION)(&irpSp->Parameters))-> + RequestConnectionInformation->RemoteAddress, FALSE); + + // + // Start the timer for this request. + // + + Request->Flags |= REQUEST_FLAGS_TIMER; // there is a timeout on this request. + KeInitializeTimer (&Request->Timer); // set to not-signaled state. + NbfReferenceRequest ("Find Name: timer", Request, RREF_TIMER); // one for the timer + Timeout.LowPart = (ULONG)(-(LONG)DeviceContext->GeneralTimeout); + Timeout.HighPart = -1; + KeSetTimer (&Request->Timer, Timeout, &Request->Dpc); + + // + // Allocate a UI frame from the pool. + // + + Status = NbfCreateConnectionlessFrame (DeviceContext, &RawFrame); + if (!NT_SUCCESS (Status)) { // couldn't make frame. + return; + } + + IF_NBFDBG (NBF_DEBUG_DEVCTX) { + NbfPrint2 ("NbfSendFindNames: Sending Frame: %lx, NdisPacket: %lx\n", + RawFrame, RawFrame->NdisPacket); + } + + + // + // Build the MAC header. STATUS_QUERY frames go out as + // single-route source routing. + // + + MacReturnSingleRouteSR( + &DeviceContext->MacInfo, + &SingleSR, + &SingleSRLength); + + MacConstructHeader ( + &DeviceContext->MacInfo, + RawFrame->Header, + DeviceContext->NetBIOSAddress.Address, + DeviceContext->LocalAddress.Address, + sizeof (DLC_FRAME) + sizeof (NBF_HDR_CONNECTIONLESS), + SingleSR, + SingleSRLength, + &HeaderLength); + + + // + // Build the DLC UI frame header. + // + + NbfBuildUIFrameHeader(&RawFrame->Header[HeaderLength]); + HeaderLength += sizeof(DLC_FRAME); + + + // + // Build the Netbios header. + // + + // + // Determine what RequestType should be. + // + + if (Request->BytesWritten == 0) { + + // + // No way to know if he is 2.1 or not, so we put a 1 here + // instead of 0. + // + + RequestType = 1; + + } else { + + RequestType = (UCHAR)((Request->BytesWritten - sizeof(ADAPTER_STATUS)) / sizeof(NAME_BUFFER)); + + } + + ConstructStatusQuery ( + (PNBF_HDR_CONNECTIONLESS)&(RawFrame->Header[HeaderLength]), + RequestType, // request status type. + (USHORT)Request->Buffer2Length, // user's buffer length + Request->FrameContext, // corr. in 1st NAME_RECOGNIZED. + (PNAME)remoteAddress->NetbiosName, + DeviceContext->ReservedNetBIOSAddress); + + HeaderLength += sizeof(NBF_HDR_CONNECTIONLESS); + + + // + // Munge the packet length. + // + + NbfSetNdisPacketLength(RawFrame->NdisPacket, HeaderLength); + + NbfSendUIFrame ( + DeviceContext, + RawFrame, + FALSE); // no loopback (MC frame) + +} /* NbfSendStatusQuery */ + + +NTSTATUS +NbfProcessStatusResponse( + IN PDEVICE_CONTEXT DeviceContext, + IN NDIS_HANDLE ReceiveContext, + IN PNBF_HDR_CONNECTIONLESS UiFrame, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ) + +/*++ + +Routine Description: + + This routine processes a STATUS.RESPONSE packet. + +Arguments: + + DeviceContext - a pointer to the device context the frame was received on. + + ReceiveContext - The context for calling NdisTransferData. + + UiFrame - The packet in question, starting at the Netbios header. + + SourceAddress - The source hardware address of the packet. + + SourceRouting - Source routing data in the query. + + SourceRoutingLength - The length of SourceRouting. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + + KIRQL oldirql; + PTP_REQUEST Request; + PUCHAR TargetBuffer; + USHORT FrameContext; + USHORT NamesReceived; + USHORT ResponseLength, ResponseBytesToCopy; + PLIST_ENTRY p; + PSINGLE_LIST_ENTRY linkage; + NDIS_STATUS ndisStatus; + PNDIS_BUFFER NdisBuffer; + PNDIS_PACKET ndisPacket; + ULONG ndisBytesTransferred; + PRECEIVE_PACKET_TAG receiveTag; + NDIS_STATUS NdisStatus; + + + // + // Find the request that this is for, using the frame context. + // + + FrameContext = TRANSMIT_CORR(UiFrame); + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + for (p=DeviceContext->StatusQueryQueue.Flink; + p != &DeviceContext->StatusQueryQueue; + p=p->Flink) { + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + + if (Request->FrameContext == FrameContext) { + + break; + + } + + } + + if (p == &DeviceContext->StatusQueryQueue) { + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + return STATUS_SUCCESS; + + } + + NbfReferenceRequest ("Status Response", Request, RREF_STATUS); + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + ACQUIRE_SPIN_LOCK (&Request->SpinLock, &oldirql); + + // + // Make sure this request is not stopping. + // + + if ((Request->Flags & REQUEST_FLAGS_STOPPING) != 0) { + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + NbfDereferenceRequest ("Stopping", Request, RREF_STATUS); + return STATUS_SUCCESS; + } + + // + // See if this is packet has new data. + // + + if (Request->BytesWritten == 0) { + + NamesReceived = 0; + + } else { + + NamesReceived = (USHORT)(Request->BytesWritten - sizeof(ADAPTER_STATUS)) / sizeof(NAME_BUFFER); + + } + + if ((UiFrame->Data1 > 0) && (UiFrame->Data1 <= NamesReceived)) { + + // + // If it is a post-2.1 response, but we already got + // this data, ignore it. + // + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + NbfDereferenceRequest ("Duplicate SR", Request, RREF_STATUS); + return STATUS_SUCCESS; + + } + + + // + // This is new data, append if there is room. + // + + ResponseLength = ((UiFrame->Data2High & 0x3f) * 256) + UiFrame->Data2Low; + + if ((ULONG)(Request->BytesWritten + ResponseLength) > + Request->Buffer2Length) { + + ResponseBytesToCopy = (USHORT)(Request->Buffer2Length - Request->BytesWritten); + + } else { + + ResponseBytesToCopy = ResponseLength; + + } + + // + // Allocate a receive packer for this operation. + // + + linkage = ExInterlockedPopEntryList( + &DeviceContext->ReceivePacketPool, + &DeviceContext->Interlock); + + if (linkage != NULL) { + ndisPacket = CONTAINING_RECORD( linkage, NDIS_PACKET, ProtocolReserved[0] ); + } else { + + // + // Could not get a packet, oh well, it is connectionless. + // + + DeviceContext->ReceivePacketExhausted++; + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + return STATUS_SUCCESS; + } + + receiveTag = (PRECEIVE_PACKET_TAG)(ndisPacket->ProtocolReserved); + receiveTag->PacketType = TYPE_STATUS_RESPONSE; + receiveTag->Connection = (PTP_CONNECTION)Request; + + TargetBuffer = (PUCHAR)Request->ResponseBuffer + Request->BytesWritten; + + // + // Allocate an MDL to describe the part of the buffer we + // want transferred. + // + + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + DeviceContext->NdisBufferPool, + TargetBuffer, + ResponseBytesToCopy); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + + ExInterlockedPushEntryList( + &DeviceContext->ReceivePacketPool, + linkage, + &DeviceContext->Interlock); + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + return STATUS_SUCCESS; + } + + // + // Assume success, if not we fail the request. + // + + Request->BytesWritten += ResponseBytesToCopy; + + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + + NdisChainBufferAtFront(ndisPacket, NdisBuffer); + + // + // See if the response was too big (we can complete the + // request here since we still reference it). + // + + if ((ResponseLength > ResponseBytesToCopy) || + (UiFrame->Data2High & 0x40)) { + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock,&oldirql); + RemoveEntryList (&Request->Linkage); + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + receiveTag->CompleteReceive = TRUE; + receiveTag->EndOfMessage = FALSE; + + } else { + + // + // If we are done, complete the packet, otherwise send off + // the next request (unless it is a pre-2.1 response). + // + + if ((UiFrame->Data1 > 0) && (UiFrame->Data2High & 0x80)) { + + UCHAR TempSR[MAX_SOURCE_ROUTING]; + PUCHAR ResponseSR; + + receiveTag->CompleteReceive = FALSE; + + // + // Try to cancel the timer, no harm if we fail. + // + + ACQUIRE_SPIN_LOCK (&Request->SpinLock, &oldirql); + if ((Request->Flags & REQUEST_FLAGS_TIMER) != 0) { + + Request->Flags &= ~REQUEST_FLAGS_TIMER; + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + if (KeCancelTimer (&Request->Timer)) { + NbfDereferenceRequest ("Status Response: stop timer", Request, RREF_TIMER); + } + + } else { + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + } + + Request->Retries = DeviceContext->GeneralRetries; + + // + // Send a STATUS_QUERY directed. + // + + if (SourceRouting != NULL) { + + RtlCopyMemory( + TempSR, + SourceRouting, + SourceRoutingLength); + + MacCreateNonBroadcastReplySR( + &DeviceContext->MacInfo, + TempSR, + SourceRoutingLength, + &ResponseSR); + + } else { + + ResponseSR = NULL; + + } + + NbfSendStatusQuery( + DeviceContext, + Request, + SourceAddress, + ResponseSR, + SourceRoutingLength); + + } else { + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock,&oldirql); + RemoveEntryList (&Request->Linkage); + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + receiveTag->CompleteReceive = TRUE; + receiveTag->EndOfMessage = TRUE; + + } + + } + + // + // Now do the actual data transfer. + // + + NdisTransferData ( + &ndisStatus, + DeviceContext->NdisBindingHandle, + ReceiveContext, + DeviceContext->MacInfo.TransferDataOffset + + 3 + sizeof(NBF_HDR_CONNECTIONLESS), + ResponseBytesToCopy, + ndisPacket, + (PUINT)&ndisBytesTransferred); + + if (ndisStatus != NDIS_STATUS_PENDING) { + + NbfTransferDataComplete( + (NDIS_HANDLE)DeviceContext, + ndisPacket, + ndisStatus, + ndisBytesTransferred); + + } + + return STATUS_SUCCESS; + +} /* NbfProcessStatusResponse */ + + +NTSTATUS +NbfTdiSetInformation( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiSetInformation request for the transport + provider. + +Arguments: + + Irp - the Irp for the requested operation. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + UNREFERENCED_PARAMETER (Irp); // prevent compiler warnings + + return STATUS_NOT_IMPLEMENTED; + +} /* NbfTdiQueryInformation */ + +#if 0 + +NTSTATUS +NbfQueryInfoEndpoint( + IN PTP_ENDPOINT Endpoint, + IN PTDI_REQ_QUERY_INFORMATION TdiRequest, + IN ULONG TdiRequestLength, + OUT PTDI_ENDPOINT_INFO InfoBuffer, + IN ULONG InfoBufferLength, + OUT PULONG InformationSize + ) + +/*++ + +Routine Description: + + This routine returns information for the specified endpoint. + +Arguments: + + Endpoint - Pointer to transport endpoint context. + + TdiRequest - Pointer to request buffer. + + TdiRequestLength - Length of request buffer. + + InfoBuffer - Pointer to output buffer to return information into. + + InfoBufferLength - Length of output buffer. + + InformationSize - Pointer to ulong where actual size of returned + information is to be stored. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + + TdiRequest, TdiRequestLength; // prevent compiler warnings + + if (InfoBufferLength < sizeof (TDI_ENDPOINT_INFO)) { + return STATUS_BUFFER_TOO_SMALL; + } + + ACQUIRE_SPIN_LOCK (&Endpoint->SpinLock, &oldirql); + + *InfoBuffer = Endpoint->Information; // structure copy. + + RELEASE_SPIN_LOCK (&Endpoint->SpinLock, oldirql); + + *InformationSize = sizeof (Endpoint->Information); + + return STATUS_SUCCESS; +} /* NbfQueryInfoEndpoint */ + + +NTSTATUS +NbfQueryInfoConnection( + IN PTP_CONNECTION Connection, + IN PTDI_REQUEST_KERNEL TdiRequest, + IN ULONG TdiRequestLength, + OUT PTDI_CONNECTION_INFO InfoBuffer, + IN ULONG InfoBufferLength, + OUT PULONG InformationSize + ) + +/*++ + +Routine Description: + + This routine returns information for the specified connection. + +Arguments: + + Connection - Pointer to transport connection object. + + TdiRequest - Pointer to request buffer. + + TdiRequestLength - Length of request buffer. + + InfoBuffer - Pointer to output buffer to return information into. + + InfoBufferLength - Length of output buffer. + + InformationSize - Pointer to ulong where actual size of returned + information is to be stored. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + + TdiRequest, TdiRequestLength; // prevent compiler warnings + + if (InfoBufferLength < sizeof (TDI_CONNECTION_INFO)) { + return STATUS_BUFFER_TOO_SMALL; + } + + ACQUIRE_C_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + *InfoBuffer = Connection->Information; // structure copy. + + RELEASE_C_SPIN_LOCK (&Connection->SpinLock, oldirql); + + *InformationSize = sizeof (Connection->Information); + + return STATUS_SUCCESS; +} /* NbfQueryInfoConnection */ + + +NTSTATUS +NbfQueryInfoAddress( + IN PTP_ADDRESS Address, + IN PTDI_REQUEST_KERNEL TdiRequest, + IN ULONG TdiRequestLength, + OUT PTDI_ADDRESS_INFO InfoBuffer, + IN ULONG InfoBufferLength, + OUT PULONG InformationSize + ) + +/*++ + +Routine Description: + + This routine returns information for the specified address. We + don't acquire a spinlock in this routine because there are no statistics + which must be read atomically. + +Arguments: + + Address - Pointer to transport address object. + + TdiRequest - Pointer to request buffer. + + TdiRequestLength - Length of request buffer. + + InfoBuffer - Pointer to output buffer to return information into. + + InfoBufferLength - Length of output buffer. + + InformationSize - Pointer to ulong where actual size of returned + information is to be stored. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + SHORT i; + PSZ p, q; + + TdiRequest, TdiRequestLength; // prevent compiler warnings + + // + // Calculate whether his buffer is big enough to return the entire + // information. The total size of the address information is the + // size of the fixed part, plus the size of the variable-length flat + // string in the NETWORK_NAME component of the TRANSPORT_ADDRESS + // component. + // + + if (InfoBufferLength < + sizeof (TDI_ADDRESS_INFO) + + Address->NetworkName.Length) + { + return STATUS_BUFFER_TOO_SMALL; + } + + // + // Copy both the fixed part of the address information, and the variable + // part. The variable part comes from the NETWORK_NAME component of the + // TRANSPORT_ADDRESS structure. This component contains a FLAT_STRING, + // which is of variable length. + // + + InfoBuffer->Address.AddressComponents = Address->AddressComponents; + InfoBuffer->Address.Tsap = Address->Tsap; + + InfoBuffer->Address.NetworkName.Name.Length = + Address->NetworkName.Length; + + p = Address->NetworkName.Buffer; // p = ptr, source string. + q = InfoBuffer->Address.NetworkName.Name.Buffer; // q = ptr, dest string. + for (i=0; i<InfoBuffer->Address.NetworkName.Name.Length; i++) { + *(q++) = *(p++); + } + + *InformationSize = sizeof (TDI_ADDRESS_INFO) + + Address->NetworkName.Length; + + return STATUS_SUCCESS; +} /* NbfQueryInfoAddress */ + + +NTSTATUS +NbfQueryInfoProvider( + IN PDEVICE_CONTEXT Provider, + IN PTDI_REQUEST_KERNEL TdiRequest, + IN ULONG TdiRequestLength, + OUT PTDI_PROVIDER_INFO InfoBuffer, + IN ULONG InfoBufferLength, + OUT PULONG InformationSize + ) + +/*++ + +Routine Description: + + This routine returns information for the transport provider. + +Arguments: + + Provider - Pointer to device context for provider. + + TdiRequest - Pointer to request buffer. + + TdiRequestLength - Length of request buffer. + + InfoBuffer - Pointer to output buffer to return information into. + + InfoBufferLength - Length of output buffer. + + InformationSize - Pointer to ulong where actual size of returned + information is to be stored. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + + TdiRequest, TdiRequestLength; // prevent compiler warnings + + if (InfoBufferLength < sizeof (TDI_PROVIDER_INFO)) { + return STATUS_BUFFER_TOO_SMALL; + } + + ACQUIRE_SPIN_LOCK (&Provider->SpinLock, &oldirql); + + *InfoBuffer = Provider->Information; // structure copy. + + RELEASE_SPIN_LOCK (&Provider->SpinLock, oldirql); + + *InformationSize = sizeof (Provider->Information); + + return STATUS_SUCCESS; +} /* NbfQueryInfoProvider */ + + +NTSTATUS +NbfQueryInfoNetman( + IN PDEVICE_CONTEXT Provider, + IN PTDI_REQUEST_KERNEL TdiRequest, + IN ULONG TdiRequestLength, + OUT PTDI_NETMAN_INFO InfoBuffer, + IN ULONG InfoBufferLength, + OUT PULONG InformationSize + ) + +/*++ + +Routine Description: + + This routine returns information for the specified network-managable + variable managed by the transport provider. + +Arguments: + + Provider - Pointer to device context for provider. + + TdiRequest - Pointer to request buffer. + + TdiRequestLength - Length of request buffer. + + InfoBuffer - Pointer to output buffer to return information into. + + InfoBufferLength - Length of output buffer. + + InformationSize - Pointer to ulong where actual size of returned + information is to be stored. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PFLAT_STRING p; + PTP_VARIABLE v; + PTDI_NETMAN_VARIABLE n; + USHORT i; + ULONG NameOffset, ValueOffset; + + TdiRequest, TdiRequestLength; // prevent compiler warnings + InfoBufferLength, InformationSize; + + // + // check param lengths here. + // + + ACQUIRE_SPIN_LOCK (&Provider->SpinLock, &oldirql); + NbfReferenceDeviceContext ("Query InfoNetMan", Provider, DCREF_QUERY_INFO); + for (v=Provider->NetmanVariables; v != NULL; v=v->Fwdlink) { + if (TdiRequest->Identification == v->VariableSerialNumber) { + + // + // Return the variable information here. + // + + NameOffset = sizeof (TDI_NETMAN_INFO); + ValueOffset = NameOffset + (sizeof (FLAT_STRING)-1) + + v->VariableName.Length; + + InfoBuffer->VariableName = NameOffset; + InfoBuffer->VariableValue = ValueOffset; + + // + // Copy the variable name to the user's buffer. + // + + p = (PFLAT_STRING)((PUCHAR)InfoBuffer + NameOffset); + p->MaximumLength = v->VariableName.Length; + p->Length = v->VariableName.Length; + for (i=0; i<v->VariableName.Length; i++) { + p->Buffer [i] = v->VariableName.Buffer [i]; + } + + // + // Now copy the variable's contents to the user's buffer. + // + + n = (PTDI_NETMAN_VARIABLE)((PUCHAR)InfoBuffer + ValueOffset); + n->VariableType = v->VariableType; + + switch (v->VariableType) { + + case NETMAN_VARTYPE_ULONG: + n->Value.LongValue = v->Value.LongValue; + break; + + case NETMAN_VARTYPE_HARDWARE_ADDRESS: + n->Value.HardwareAddressValue = + v->Value.HardwareAddressValue; + break; + + case NETMAN_VARTYPE_STRING: + p = &n->Value.StringValue; + p->MaximumLength = v->Value.StringValue.Length; + p->Length = v->Value.StringValue.Length; + for (i=0; i<v->Value.StringValue.Length; i++) { + p->Buffer [i] = v->Value.StringValue.Buffer [i]; + } + + } /* switch */ + + RELEASE_SPIN_LOCK (&Provider->SpinLock, oldirql); + NbfDereferenceDeviceContext ("Query InfoNetMan success", Provider, DCREF_QUERY_INFO); + return STATUS_SUCCESS; + } /* if */ + } /* for */ + + RELEASE_SPIN_LOCK (&Provider->SpinLock, oldirql); + + NbfDereferenceDeviceContext ("Query InfoNetMan no exist", Provider, DCREF_QUERY_INFO); + + return STATUS_INVALID_INFO_CLASS; // variable does not exist. +} /* NbfQueryInfoNetman */ + + +NTSTATUS +NbfSetInfoEndpoint( + IN PTP_ENDPOINT Endpoint, + IN PTDI_REQUEST_KERNEL TdiRequest, + IN ULONG TdiRequestLength + ) + +/*++ + +Routine Description: + + This routine sets information for the specified endpoint. + +Arguments: + + Endpoint - Pointer to transport endpoint context. + + TdiRequest - Pointer to request buffer. + + TdiRequestLength - Length of request buffer. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PTDI_ENDPOINT_INFO InfoBuffer; + + if (TdiRequestLength != + sizeof (TDI_ENDPOINT_INFO) + sizeof (TDI_REQ_SET_INFORMATION) - + sizeof (TDI_INFO_BUFFER)) { + return STATUS_BUFFER_TOO_SMALL; // buffer sizes must match. + } + + InfoBuffer = (PTDI_ENDPOINT_INFO)&TdiRequest->InfoBuffer; + + if ((InfoBuffer->MinimumLookaheadData <= NBF_MAX_LOOKAHEAD_DATA) || + (InfoBuffer->MaximumLookaheadData <= NBF_MAX_LOOKAHEAD_DATA) || + (InfoBuffer->MinimumLookaheadData > InfoBuffer->MaximumLookaheadData)) { + return STATUS_INVALID_PARAMETER; + } + + ACQUIRE_SPIN_LOCK (&Endpoint->SpinLock, &oldirql); + + // + // Set minimum lookahead data size. This is the number of bytes of + // contiguous data that will be supplied to TDI_IND_RECEIVE and + // TDI_IND_RECEIVE_DATAGRAM event handlers at indication time. + // + + Endpoint->Information.MinimumLookaheadData = InfoBuffer->MinimumLookaheadData; + + // + // Set maximum lookahead data size. This is the number of bytes of + // contiguous data that will be supplied to TDI_IND_RECEIVE and + // TDI_IND_RECEIVE_DATAGRAM event handlers at indication time. + // + + Endpoint->Information.MaximumLookaheadData = InfoBuffer->MaximumLookaheadData; + + // + // Reset all the statistics to his new values. + // + + Endpoint->Information.TransmittedTsdus = InfoBuffer->TransmittedTsdus; + Endpoint->Information.ReceivedTsdus = InfoBuffer->ReceivedTsdus; + Endpoint->Information.TransmissionErrors = InfoBuffer->TransmissionErrors; + Endpoint->Information.ReceiveErrors = InfoBuffer->ReceiveErrors; + Endpoint->Information.PriorityLevel = InfoBuffer->PriorityLevel; + Endpoint->Information.SecurityLevel = InfoBuffer->SecurityLevel; + Endpoint->Information.SecurityCompartment = InfoBuffer->SecurityCompartment; + + // + // The State and Event fields are read-only, so we DON'T set them here. + // + + RELEASE_SPIN_LOCK (&Endpoint->SpinLock, oldirql); + + return STATUS_SUCCESS; +} /* NbfSetInfoEndpoint */ + + +NTSTATUS +NbfSetInfoAddress( + IN PTP_ADDRESS Address, + IN PTDI_REQUEST_KERNEL TdiRequest, + IN ULONG TdiRequestLength + ) + +/*++ + +Routine Description: + + This routine sets information for the specified address. Currently, + all the user-visible fields in the transport address object are read-only. + +Arguments: + + Address - Pointer to transport address object. + + TdiRequest - Pointer to request buffer. + + TdiRequestLength - Length of request buffer. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + Address, TdiRequest, TdiRequestLength; // prevent compiler warnings + + return STATUS_SUCCESS; +} /* NbfSetInfoAddress */ + + +NTSTATUS +NbfSetInfoConnection( + IN PTP_CONNECTION Connection, + IN PTDI_REQUEST_KERNEL TdiRequest, + IN ULONG TdiRequestLength + ) + +/*++ + +Routine Description: + + This routine sets information for the specified connection. + +Arguments: + + Connection - Pointer to transport connection object. + + TdiRequest - Pointer to request buffer. + + TdiRequestLength - Length of request buffer. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PTDI_CONNECTION_INFO InfoBuffer; + + if (TdiRequestLength != + sizeof (TDI_CONNECTION_INFO) + sizeof (TDI_REQ_SET_INFORMATION) - + sizeof (TDI_INFO_BUFFER)) { + return STATUS_BUFFER_TOO_SMALL; // buffer sizes must match. + } + + InfoBuffer = (PTDI_CONNECTION_INFO)&TdiRequest->InfoBuffer; + + ACQUIRE_C_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + // + // Reset all the statistics to his new values. + // + + Connection->Information.TransmittedTsdus = InfoBuffer->TransmittedTsdus; + Connection->Information.ReceivedTsdus = InfoBuffer->ReceivedTsdus; + Connection->Information.TransmissionErrors = InfoBuffer->TransmissionErrors; + Connection->Information.ReceiveErrors = InfoBuffer->ReceiveErrors; + + // + // The State and Event fields are read-only, so we DON'T set them here. + // + + RELEASE_C_SPIN_LOCK (&Connection->SpinLock, oldirql); + + return STATUS_SUCCESS; +} /* NbfSetInfoConnection */ + + +NTSTATUS +NbfSetInfoProvider( + IN PDEVICE_CONTEXT Provider, + IN PTDI_REQUEST_KERNEL TdiRequest, + IN ULONG TdiRequestLength + ) + +/*++ + +Routine Description: + + This routine sets information for the specified transport provider. + +Arguments: + + Provider - Pointer to device context. + + TdiRequest - Pointer to request buffer. + + TdiRequestLength - Length of request buffer. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PTDI_PROVIDER_INFO InfoBuffer; + + if (TdiRequestLength != + sizeof (TDI_PROVIDER_INFO) + sizeof (TDI_REQ_SET_INFORMATION) - + sizeof (TDI_INFO_BUFFER)) { + return STATUS_BUFFER_TOO_SMALL; // buffer sizes must match. + } + + InfoBuffer = (PTDI_PROVIDER_INFO)&TdiRequest->InfoBuffer; + + // + // By changing the service flags the caller can request additional + // or fewer services on the fly. Make sure that he is requesting + // services we can provide, or else fail the request. + // + + if (InfoBuffer->ServiceFlags & ~NBF_SERVICE_FLAGS) { + return STATUS_NOT_SUPPORTED; + } + + ACQUIRE_SPIN_LOCK (&Provider->SpinLock, &oldirql); + + // + // Reset all the statistics to his new values. + // + + Provider->Information.TransmittedTsdus = InfoBuffer->TransmittedTsdus; + Provider->Information.ReceivedTsdus = InfoBuffer->ReceivedTsdus; + Provider->Information.TransmissionErrors = InfoBuffer->TransmissionErrors; + Provider->Information.ReceiveErrors = InfoBuffer->ReceiveErrors; + Provider->Information.DiscardedFrames = InfoBuffer->DiscardedFrames; + Provider->Information.ReceiveErrors = InfoBuffer->ReceiveErrors; + Provider->Information.OversizeTsdusReceived = InfoBuffer->OversizeTsdusReceived; + Provider->Information.UndersizeTsdusReceived = InfoBuffer->UndersizeTsdusReceived; + Provider->Information.MulticastTsdusReceived = InfoBuffer->MulticastTsdusReceived; + Provider->Information.BroadcastTsdusReceived = InfoBuffer->BroadcastTsdusReceived; + Provider->Information.MulticastTsdusTransmitted = InfoBuffer->MulticastTsdusTransmitted; + Provider->Information.BroadcastTsdusTransmitted = InfoBuffer->BroadcastTsdusTransmitted; + Provider->Information.SendTimeouts = InfoBuffer->SendTimeouts; + Provider->Information.ReceiveTimeouts = InfoBuffer->ReceiveTimeouts; + Provider->Information.ConnectionIndicationsReceived = InfoBuffer->ConnectionIndicationsReceived; + Provider->Information.ConnectionIndicationsAccepted = InfoBuffer->ConnectionIndicationsAccepted; + Provider->Information.ConnectionsInitiated = InfoBuffer->ConnectionsInitiated; + Provider->Information.ConnectionsAccepted = InfoBuffer->ConnectionsAccepted; + + // + // The following fields are read-only, so we DON'T set them here: + // Version, MaxTsduSize, MaxConnectionUserData, MinimumLookaheadData, + // MaximumLookaheadData. + // + + RELEASE_SPIN_LOCK (&Provider->SpinLock, oldirql); + + return STATUS_SUCCESS; +} /* NbfSetInfoProvider */ + + +NTSTATUS +NbfSetInfoNetman( + IN PDEVICE_CONTEXT Provider, + IN PTDI_REQ_SET_INFORMATION TdiRequest, + IN ULONG TdiRequestLength + ) + +/*++ + +Routine Description: + + This routine sets information for the specified transport provider's + network-managable variable. + +Arguments: + + Provider - Pointer to device context. + + TdiRequest - Pointer to request buffer. + + TdiRequestLength - Length of request buffer. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTDI_NETMAN_INFO InfoBuffer; + + Provider; // prevent compiler warnings + + if (TdiRequestLength != + sizeof (TDI_NETMAN_INFO) + sizeof (TDI_REQ_SET_INFORMATION) - + sizeof (TDI_INFO_BUFFER)) { + return STATUS_BUFFER_TOO_SMALL; // buffer sizes must match. + } + + InfoBuffer = (PTDI_NETMAN_INFO)&TdiRequest->InfoBuffer; + + // + // BUGBUG: set the network-managable variable here. + // + + return STATUS_SUCCESS; +} /* NbfSetInfoNetman */ + + +NTSTATUS +NbfTdiQueryInformation( + IN PTP_ENDPOINT Endpoint, + IN PTDI_REQ_QUERY_INFORMATION TdiRequest, + IN ULONG TdiRequestLength, + OUT PTDI_INFO_BUFFER InfoBuffer, + IN ULONG InfoBufferLength, + OUT PULONG InformationSize + ) + +/*++ + +Routine Description: + + This routine performs the TdiQueryInformation request for the transport + provider. + +Arguments: + + Endpoint - Pointer to transport endpoint context. + + TdiRequest - Pointer to request buffer. + + TdiRequestLength - Length of request buffer. + + InfoBuffer - Pointer to output buffer to return information into. + + InfoBufferLength - Length of output buffer. + + InformationSize - Pointer to ulong where actual size of returned + information is to be stored. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + PTP_CONNECTION Connection; + + switch (TdiRequest->InformationClass) { + + // + // ENDPOINT information: return information about the endpoint + // to which this request was submitted. + // + + case TDI_INFO_CLASS_ENDPOINT: + Status = NbfQueryInfoEndpoint ( + Endpoint, + TdiRequest, + TdiRequestLength, + (PTDI_ENDPOINT_INFO)InfoBuffer, + InfoBufferLength, + InformationSize); + break; + + // + // CONNECTION information: return information about a connection + // that is associated with the endpoint on which this request was + // submitted. + // + + case TDI_INFO_CLASS_CONNECTION: + // This causes a connection reference which is removed below. + Connection = NbfLookupConnectionById ( + Endpoint, + TdiRequest->Identification); + if (Connection == NULL) { + Status = STATUS_INVALID_HANDLE; + break; + } + + Status = NbfQueryInfoConnection ( + Connection, + TdiRequest, + TdiRequestLength, + (PTDI_CONNECTION_INFO)InfoBuffer, + InfoBufferLength, + InformationSize); + + NbfDereferenceConnection("Query Connection Info", Connection, CREF_BY_ID); + break; + + // + // ADDRESS information: return information about the address object + // that is associated with the endpoint on which this request was + // submitted. + // + + case TDI_INFO_CLASS_ADDRESS: + Status = NbfQueryInfoAddress ( + Endpoint->BoundAddress, + TdiRequest, + TdiRequestLength, + (PTDI_ADDRESS_INFO)InfoBuffer, + InfoBufferLength, + InformationSize); + break; + + // + // PROVIDER information: return information about the transport + // provider itself. + // + + case TDI_INFO_CLASS_PROVIDER: + Status = NbfQueryInfoProvider ( + Endpoint->BoundAddress->Provider, + TdiRequest, + TdiRequestLength, + (PTDI_PROVIDER_INFO)InfoBuffer, + InfoBufferLength, + InformationSize); + break; + + // + // NETMAN information: return information about the network-managable + // variables managed by the provider itself. + // + + case TDI_INFO_CLASS_NETMAN: + Status = NbfQueryInfoNetman ( + Endpoint->BoundAddress->Provider, + TdiRequest, + TdiRequestLength, + (PTDI_NETMAN_INFO)InfoBuffer, + InfoBufferLength, + InformationSize); + break; + + default: + Status = STATUS_INVALID_INFO_CLASS; + + } /* switch */ + + return Status; +} /* TdiQueryInformation */ + + +NTSTATUS +TdiSetInformation( + IN PTP_ENDPOINT Endpoint, + IN PTDI_REQ_SET_INFORMATION TdiRequest, + IN ULONG TdiRequestLength + ) + +/*++ + +Routine Description: + + This routine performs the TdiSetInformation request for the transport + provider. + +Arguments: + + Endpoint - Pointer to transport endpoint context. + + TdiRequest - Pointer to request buffer. + + TdiRequestLength - Length of request buffer. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS Status; + PTP_CONNECTION Connection; + + switch (TdiRequest->InformationClass) { + + // + // ENDPOINT information: set information on the endpoint + // to which this request was submitted. + // + + case TDI_INFO_CLASS_ENDPOINT: + Status = NbfSetInfoEndpoint ( + Endpoint, + TdiRequest, + TdiRequestLength); + break; + + // + // CONNECTION information: set information for a connection + // that is associated with the endpoint on which this request + // was submitted. + // + + case TDI_INFO_CLASS_CONNECTION: + // This causes a connection reference which is removed below. + Connection = NbfLookupConnectionById ( + Endpoint, + TdiRequest->Identification); + if (Connection == NULL) { + Status = STATUS_INVALID_HANDLE; + break; + } + + Status = NbfSetInfoConnection ( + Connection, + TdiRequest, + TdiRequestLength); + + NbfDereferenceConnection("Set Connection Info", Connection, CREF_BY_ID); + break; + + // + // ADDRESS information: set information for the address object + // that is associated with the endpoint on which this request + // was submitted. + // + + case TDI_INFO_CLASS_ADDRESS: + Status = NbfSetInfoAddress ( + Endpoint->BoundAddress, + TdiRequest, + TdiRequestLength); + break; + + // + // PROVIDER information: set information for the transport + // provider itself. + // + + case TDI_INFO_CLASS_PROVIDER: + Status = NbfSetInfoProvider ( + Endpoint->BoundAddress->Provider, + TdiRequest, + TdiRequestLength); + break; + + // + // NETMAN information: set information for the network-managable + // variables managed by the provider itself. + // + + case TDI_INFO_CLASS_NETMAN: + Status = NbfSetInfoNetman ( + Endpoint->BoundAddress->Provider, + TdiRequest, + TdiRequestLength); + break; + + default: + Status = STATUS_INVALID_INFO_CLASS; + + } /* switch */ + + return Status; +} /* TdiSetInformation */ + +#endif diff --git a/private/ntos/tdi/nbf/link.c b/private/ntos/tdi/nbf/link.c new file mode 100644 index 000000000..e99bbb8f4 --- /dev/null +++ b/private/ntos/tdi/nbf/link.c @@ -0,0 +1,2315 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + link.c + +Abstract: + + This module contains code which implements the TP_LINK object. + Routines are provided to create, destroy, reference, and dereference, + transport link objects. + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +extern ULONG StartTimerLinkDeferredAdd; +extern ULONG StartTimerLinkDeferredDelete; + +#if DBG +// The following is here for debugging purposes to make it easy to change +// the maximum packet size. + +ULONG MaxUserPacketData = 18000; +#endif + +#if 0 + +VOID +DisconnectCompletionHandler( + IN PTP_LINK TransportLink + ) + +/*++ + +Routine Description: + + This routine is called as an I/O completion handler at the time a + TdiDisconnect request is completed. Here we dereference the link + object, and optionally reference it again and start up the link if + some transport connection started up on the link during the time we + were trying to shut it down. + +Arguments: + + TransportLink - Pointer to a transport link object. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint1 ("DisconnectCompletionHandler: Entered for link %lx.\n", + TransportLink); + } + + // + // The following call will dereference this link for the last time, + // unless another transport connection has been assigned to the link + // during the time the data link layer was bringing the link down and + // when we got here. If this condition exists, then now is the time + // to bring the link back up, else destroy it. + // + + // BUGBUG: don't forget to check for bringing it back up again. + + // BUGBUG: Is this right??? - ADB 6/26 + + NbfDereferenceLink ("Disconnecting", TransportLink, LREF_CONNECTION); // this makes it go away. +#if DBG + NbfPrint0("Disconnecting Completion Handler\n"); +#endif + +} /* DisconnectCompletionHandler */ +#endif + + +VOID +NbfCompleteLink( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine is called by the UA-r/x handler, NbfWaitLink, and + NbfActivateLink to startup the NBF connections associated with + a link because they were waiting for the link to become established. + + When we get here, the link has been established, so we need to + start the next set of connection-establishment protocols: + + SESSION_INIT -----------------> + <----------------- SESSION_CONFIRM + + (TdiConnect completes) (TdiListen completes) + + NOTE: THIS ROUTINE MUST BE CALLED FROM DPC LEVEL. + +Arguments: + + Link - Pointer to a transport link object. + +Return Value: + + none. + +--*/ + +{ + PTP_CONNECTION Connection; + BOOLEAN TimerWasCleared; + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint1 ("NbfCompleteLink: Entered for link %lx.\n", Link); + } + + ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); + + // + // Officially declare that this link is ready for I-frame business. + // + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + // + // We can now send and receive I-frames on this link. We are in ABME. + // + + // + // This probably isn't necessary, but to be safe for now.. (adb 6/28) + // + if (Link->State == LINK_STATE_ADM) { + // Moving out of ADM, add special reference + NbfReferenceLinkSpecial("To READY in NbfCompleteLink", Link, LREF_NOT_ADM); + } + + Link->State = LINK_STATE_READY; + Link->SendState = SEND_STATE_READY; + Link->ReceiveState = RECEIVE_STATE_READY; + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + // + // Complete all of the listens first, so they will be expecting + // incoming SESSION_INITIALIZEs. Then do the connects. + // + + // This creates a connection reference which is removed below. + while ((Connection=NbfLookupPendingListenOnLink (Link)) != NULL) { + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + // + // This loop looks unnecessary, let's make sure... - adb 9/11/91 + // + ASSERT(Connection->Flags & CONNECTION_FLAGS_WAIT_SI); + + Connection->Flags |= CONNECTION_FLAGS_WAIT_SI; // wait session initialize. + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + NbfDereferenceConnection ("Pending listen", Connection, CREF_P_LINK); + } /* while */ + + // + // And do the connects. If there are connections in progress, they'll + // also have timers associated with them. Cancel those timers. + // + + while ((Connection=NbfLookupPendingConnectOnLink (Link)) != NULL) { + TimerWasCleared = KeCancelTimer (&Connection->Timer); + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint2 ("NbfCompleteLink: Timer for connection %lx %s canceled.\n", + Connection, TimerWasCleared ? "was" : "was NOT" ); + } + if (TimerWasCleared) { + NbfDereferenceConnection("Cancel timer", Connection, CREF_TIMER); + } + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + Connection->Flags |= CONNECTION_FLAGS_WAIT_SC; // wait session confirm. + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + // + // No timeout for this frame is required since the link is responsible + // for reliable delivery. If we can't send this frame, however, the + // data link connection will happily keep quiet without timeouts. + // + + NbfSendSessionInitialize (Connection); + NbfDereferenceConnection ("NbfCompleteLink", Connection, CREF_P_CONNECT); + } /* while */ + +} /* NbfCompleteLink */ + + +VOID +NbfAllocateLink( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_LINK *TransportLink + ) + +/*++ + +Routine Description: + + This routine allocates storage for a data link connection. It + performs minimal initialization of the object. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + link. + + TransportLink - Pointer to a place where this routine will return a + pointer to an allocated transport link structure. Returns + NULL if no storage can be allocated. + +Return Value: + + None. + +--*/ + +{ + PTP_LINK Link; + + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + sizeof(TP_LINK)) > + DeviceContext->MemoryLimit)) { + PANIC("NBF: Could not allocate link: limit\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_LIMIT, + 105, + sizeof(TP_LINK), + LINK_RESOURCE_ID); + *TransportLink = NULL; + return; + } + Link = (PTP_LINK)ExAllocatePoolWithTag ( + NonPagedPool, + sizeof (TP_LINK), + 'lFBN'); + if (Link == NULL) { + PANIC("NBF: Could not allocate link: no pool\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 205, + sizeof(TP_LINK), + LINK_RESOURCE_ID); + *TransportLink = NULL; + return; + } + RtlZeroMemory (Link, sizeof(TP_LINK)); + + ++DeviceContext->LinkAllocated; + DeviceContext->MemoryUsage += sizeof(TP_LINK); + + Link->Type = NBF_LINK_SIGNATURE; + Link->Size = sizeof (TP_LINK); + + KeInitializeSpinLock (&Link->SpinLock); + Link->Provider = DeviceContext; + Link->ProviderInterlock = &DeviceContext->Interlock; + + InitializeListHead (&Link->Linkage); + InitializeListHead (&Link->ConnectionDatabase); + InitializeListHead (&Link->WackQ); + InitializeListHead (&Link->NdisSendQueue); + InitializeListHead (&Link->ShortList); + Link->OnShortList = FALSE; + InitializeListHead (&Link->LongList); + Link->OnLongList = FALSE; + InitializeListHead (&Link->PurgeList); + + Link->T1 = 0; // 0 indicates they are not in the list + Link->T2 = 0; + Link->Ti = 0; + + NbfAddSendPacket (DeviceContext); + NbfAddReceivePacket (DeviceContext); + + *TransportLink = Link; + +} /* NbfAllocateLink */ + + +VOID +NbfDeallocateLink( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_LINK TransportLink + ) + +/*++ + +Routine Description: + + This routine frees storage for a data link connection. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + link. + + TransportLink - Pointer to the transport link structure. + +Return Value: + + None. + +--*/ + +{ + + ExFreePool (TransportLink); + --DeviceContext->LinkAllocated; + DeviceContext->MemoryUsage -= sizeof(TP_LINK); + + NbfRemoveSendPacket (DeviceContext); + NbfRemoveReceivePacket (DeviceContext); + +} /* NbfDeallocateLink */ + + +NTSTATUS +NbfCreateLink( + IN PDEVICE_CONTEXT DeviceContext, + IN PHARDWARE_ADDRESS HardwareAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + IN USHORT LoopbackLinkIndex, + OUT PTP_LINK *TransportLink + ) + +/*++ + +Routine Description: + + This routine creates a data link connection between the local + data link station and the specified remote data link address. + As an option (Passive=TRUE), the caller may specify that instead + of a Connect activity, a Listen is to be performed instead. + + Normally, if a link to the remote address is not already active, + then a link object is allocated, the reference count in the link + is set to 1, and the reference count of the device context is + incremented. + + If a link is already active to the remote address, then the existing + link object is referenced with NbfReferenceLink() so that it can be + shared between the transport connections. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + link. + + HardwareAddress - Pointer to a HARDWARE_ADDRESS type containing the + hardware address of the REMOTE link station to connect to/listen for. + + LoopbackLinkIndex - In the case that this turns out to be created + as one of the LoopbackLinks, this will indicate which one to + use. + + TransportLink - Pointer to a place where this routine will return a + pointer to an allocated transport link structure. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_LINK Link; + PLIST_ENTRY p; + UCHAR TempSR[MAX_SOURCE_ROUTING]; + PUCHAR ResponseSR; + USHORT i; + + ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); + + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint1 ("NbfCreateLink: Entered, DeviceContext: %lx\n", DeviceContext); + } + + // + // Walk the list of addresses to see if we already have a link to this + // remote address. + // + + // This adds a reference if the link is found. + + Link = NbfFindLink (DeviceContext, HardwareAddress->Address); + + + if (Link == (PTP_LINK)NULL) { + + // + // If necessary, check whether we are looking for one of + // the loopback links (NbfFindLink won't find those). + // + + if (RtlEqualMemory( + HardwareAddress->Address, + DeviceContext->LocalAddress.Address, + DeviceContext->MacInfo.AddressLength)) { + + Link = DeviceContext->LoopbackLinks[LoopbackLinkIndex]; + + if (Link != (PTP_LINK)NULL) { + + // + // Add a reference to simulate the one from NbfFindLink + // + // BUGBUG: This needs to be atomically done with + // the assignment above. + // + + NbfReferenceLink ("Found loopback link", Link, LREF_TREE); + + } else { + + // + // May have the first loopback link; need to make sure the + // buffer for indications is allocated. + // + + if (DeviceContext->LookaheadContiguous == NULL) { + + DeviceContext->LookaheadContiguous = + ExAllocatePoolWithTag ( + NonPagedPool, + NBF_MAX_LOOPBACK_LOOKAHEAD, + ' FBN'); + if (DeviceContext->LookaheadContiguous == NULL) { + PANIC ("NbfCreateLink: Could not allocate loopback buffer!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + } + + } + + } + + } + + + if (Link != (PTP_LINK)NULL) { + + // + // Found the link structure here, so use the existing link. + // + +#if DBG + // + // These two operations have no net effect, so if not in debug + // mode we can remove them. + // + + // This reference is removed by NbfDisconnectFromLink + // (this assumes that NbfConnectToLink is always called + // if this function returns success). + + NbfReferenceLink ("New Ref, Found existing link", Link, LREF_CONNECTION); // extra reference. + + // Now we can remove the NbfFindLinkInTree reference. + + NbfDereferenceLink ("Found link in tree", Link, LREF_TREE); +#endif + + *TransportLink = Link; // return pointer to the link. + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint0 ("NbfCreateLink: returning ptr to existing link object.\n"); + } + return STATUS_SUCCESS; // all done. + + } /* if LINK != NULL */ + + + // + // We don't have an existing link, so we have to create one. + // + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint0 ("NbfCreateLink: using new link object.\n"); + } + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + p = RemoveHeadList (&DeviceContext->LinkPool); + if (p == &DeviceContext->LinkPool) { + + if ((DeviceContext->LinkMaxAllocated == 0) || + (DeviceContext->LinkAllocated < DeviceContext->LinkMaxAllocated)) { + + NbfAllocateLink (DeviceContext, &Link); + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint1 ("NBF: Allocated link at %lx\n", Link); + } + + } else { + + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_SPECIFIC, + 405, + sizeof(TP_LINK), + LINK_RESOURCE_ID); + Link = NULL; + + } + + if (Link == NULL) { + ++DeviceContext->LinkExhausted; + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + PANIC ("NbfCreateConnection: Could not allocate link object!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + } else { + + Link = CONTAINING_RECORD (p, TP_LINK, Linkage); + + } + + ++DeviceContext->LinkInUse; + ASSERT(DeviceContext->LinkInUse > 0); + + if (DeviceContext->LinkInUse > DeviceContext->LinkMaxInUse) { + ++DeviceContext->LinkMaxInUse; + } + + DeviceContext->LinkTotal += DeviceContext->LinkInUse; + ++DeviceContext->LinkSamples; + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint1 ("NbfCreateLink: Link at %lx.\n", Link); + } + + // + // Initialize all of the static data for this link. + // + + Link->SpecialRefCount = 1; + Link->ReferenceCount = 0; +#if DBG + { + UINT Counter; + for (Counter = 0; Counter < NUMBER_OF_LREFS; Counter++) { + Link->RefTypes[Counter] = 0; + } + + // This reference is removed by NbfDisconnectFromLink + // (this assumes that NbfConnectToLink is always called + // if this function returns success). + // + + Link->RefTypes[LREF_CONNECTION] = 1; + Link->RefTypes[LREF_SPECIAL_TEMP] = 1; + } + Link->Destroyed = FALSE; + Link->TotalReferences = 0; + Link->TotalDereferences = 0; + Link->NextRefLoc = 0; + ExInterlockedInsertHeadList (&NbfGlobalLinkList, &Link->GlobalLinkage, &NbfGlobalInterlock); + StoreLinkHistory (Link, TRUE); +#endif + Link->Flags = 0; // in the beginning, the link is closed. + Link->DeferredFlags = 0; + Link->State = LINK_STATE_ADM; // async disconnected mode. + + Link->NdisSendsInProgress = 0; + Link->ResendingPackets = FALSE; + + // + // Initialize the counters + // + + Link->FrmrsReceived = 0; + Link->FrmrsTransmitted = 0; + Link->ErrorIFramesReceived = 0; + Link->ErrorIFramesTransmitted = 0; + Link->AbortedTransmissions = 0; + Link->BuffersNotAvailable = 0; + Link->SuccessfulTransmits = 0; + Link->SuccessfulReceives = 0; + Link->T1Expirations = 0; + Link->TiExpirations = 0; + +#if DBG + Link->CreatePacketFailures = 0; +#endif + + + // + // At first, the delay and throughput are unknown. + // + + Link->Delay = 0xffffffff; + Link->Throughput.HighPart = 0xffffffff; + Link->Throughput.LowPart = 0xffffffff; + Link->ThroughputAccurate = FALSE; + Link->CurrentT1Backoff = FALSE; + + Link->OnDeferredRrQueue = FALSE; + InitializeListHead (&Link->DeferredRrLinkage); + + + // + // Determine the maximum sized data frame that can be sent + // on this link, based on the source routing information and + // the size of the MAC header ("data frame" means the frame + // without the MAC header). We don't assume the worst case + // about source routing since we create a link in response + // to a received frame, so if there is no source routing it + // is because we are not going over a bridge. The exception + // is if we are creating a link to a group name, in which + // case we come back later and hack the MaxFrameSize in. + // + + MacReturnMaxDataSize( + &DeviceContext->MacInfo, + SourceRouting, + SourceRoutingLength, + DeviceContext->CurSendPacketSize, + FALSE, + (PUINT)&(Link->MaxFrameSize)); + + +#if DBG + if (Link->MaxFrameSize > MaxUserPacketData) { + Link->MaxFrameSize = MaxUserPacketData; + } +#endif + + // Link->Provider = DeviceContext; + + // + // Build the default MAC header. I-frames go out as + // non-broadcast source routing. + // + + if (SourceRouting != NULL) { + + RtlCopyMemory( + TempSR, + SourceRouting, + SourceRoutingLength); + + MacCreateNonBroadcastReplySR( + &DeviceContext->MacInfo, + TempSR, + SourceRoutingLength, + &ResponseSR); + + } else { + + ResponseSR = NULL; + + } + + MacConstructHeader ( + &DeviceContext->MacInfo, + Link->Header, + HardwareAddress->Address, + DeviceContext->LocalAddress.Address, + 0, // PacketLength, filled in later + ResponseSR, + SourceRoutingLength, + (PUINT)&(Link->HeaderLength)); + + // + // We optimize for fourteen-byte headers by putting + // the correct Dsap/Ssap at the end, so we can fill + // in new packets as one 16-byte move. + // + + if (Link->HeaderLength <= 14) { + Link->Header[Link->HeaderLength] = DSAP_NETBIOS_OVER_LLC; + Link->Header[Link->HeaderLength+1] = DSAP_NETBIOS_OVER_LLC; + } + + Link->RespondToPoll = FALSE; + Link->NumberOfConnectors = 0; + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + NbfResetLink (Link); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + Link->ActiveConnectionCount = 0; + if (!IsListEmpty(&Link->ConnectionDatabase)) { + + // + // Not good; we've got something left over... + // +#if DBG + NbfPrint1 ("NbfCreateLink: Link 0x%lx has connections at startup, disconnecting...\n", Link); + DbgBreakPoint(); +#endif + // + // BUGBUG: This won't work, the link ref count will be bad. + // + NbfStopLink (Link); + } + + for (i=0; i<(USHORT)DeviceContext->MacInfo.AddressLength; i++) { + Link->HardwareAddress.Address[i] = HardwareAddress->Address[i]; + } + MacReturnMagicAddress (&DeviceContext->MacInfo, HardwareAddress, &Link->MagicAddress); + + // + // Determine if this is a loopback link. + // + + if (RtlEqualMemory( + HardwareAddress->Address, + DeviceContext->LocalAddress.Address, + DeviceContext->MacInfo.AddressLength)) { + + // + // Yes, just fill it in, no need to do deferred processing + // since this link does not go in the tree. + // + + if (LoopbackLinkIndex == LISTENER_LINK) { + Link->LoopbackDestinationIndex = LOOPBACK_TO_CONNECTOR; + } else { + Link->LoopbackDestinationIndex = LOOPBACK_TO_LISTENER; + } + + Link->Loopback = TRUE; + DeviceContext->LoopbackLinks[LoopbackLinkIndex] = Link; + + } else { + + Link->Loopback = FALSE; + + // + // Now put the link in the deferred operations queue and go away. We'll + // insert this link in the tree at some future time (soon). + // + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint6 ("NbfCreateLink: link to deferred queue %lx %lx %lx %lx %lx Flags: %lx \n", + Link, Link->DeferredList.Flink, Link->DeferredList.Blink, + DeviceContext->LinkDeferred.Flink, DeviceContext->LinkDeferred.Blink, + Link->Flags); + } + + // + // We should not have any deferred flags yet! + // + + ASSERT ((Link->DeferredFlags & LINK_FLAGS_DEFERRED_MASK) == 0); + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + if ((Link->DeferredFlags & LINK_FLAGS_DEFERRED_DELETE) == 0) { + Link->DeferredFlags |= LINK_FLAGS_DEFERRED_ADD; + InsertTailList (&DeviceContext->LinkDeferred, &Link->DeferredList); + + if (!(DeviceContext->a.i.LinkDeferredActive)) { + StartTimerLinkDeferredAdd++; + NbfStartShortTimer (DeviceContext); + DeviceContext->a.i.LinkDeferredActive = TRUE; + } + } + else { + Link->DeferredFlags = LINK_FLAGS_DEFERRED_ADD; + if (!(DeviceContext->a.i.LinkDeferredActive)) { + StartTimerLinkDeferredAdd++; + NbfStartShortTimer (DeviceContext); + DeviceContext->a.i.LinkDeferredActive = TRUE; + } + } + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint6 ("NbfCreateLink: link on deferred queue %lx %lx %lx %lx %lx Flags: %lx \n", + Link, Link->DeferredList.Flink, Link->DeferredList.Blink, + DeviceContext->LinkDeferred.Flink, DeviceContext->LinkDeferred.Blink, + Link->DeferredFlags); + } + + } + + NbfReferenceDeviceContext ("Create Link", DeviceContext, DCREF_LINK); // count refs to the device context. + *TransportLink = Link; // return a pointer to the link object. + return STATUS_SUCCESS; +} /* NbfCreateLink */ + + +NTSTATUS +NbfDestroyLink( + IN PTP_LINK TransportLink + ) + +/*++ + +Routine Description: + + This routine destroys a transport link and removes all references + made to it by other objects in the transport. The link is expected + to still be on the splay tree of links. This routine merely marks the + link as needing to be deleted and pushes it onto the deferred operations + queue. The deferred operations processor actually removes the link from + tree and returns the link to pool. + +Arguments: + + TransportLink - Pointer to a transport link structure to be destroyed. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PTP_PACKET packet; + PLIST_ENTRY pkt; + PDEVICE_CONTEXT DeviceContext; + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint1 ("NbfDestroyLink: Entered for link %lx.\n", TransportLink); + } + +#if DBG + if (TransportLink->Destroyed) { + NbfPrint1 ("attempt to destroy already-destroyed link 0x%lx\n", TransportLink); + DbgBreakPoint (); + } + TransportLink->Destroyed = TRUE; +#if 1 + ACQUIRE_SPIN_LOCK (&NbfGlobalInterlock, &oldirql); + RemoveEntryList (&TransportLink->GlobalLinkage); + RELEASE_SPIN_LOCK (&NbfGlobalInterlock, oldirql); +#else + ExInterlockedRemoveHeadList (TransportLink->GlobalLinkage.Blink, &NbfGlobalInterlock); +#endif +#endif + + DeviceContext = TransportLink->Provider; + + // + // In case there's a holdover from the DISC link shutdown protocol + // + + // + // We had better be in ADM, otherwise the reference count should + // be non-zero and what are we doing in NbfDestroyLink? + // + + ASSERT(TransportLink->State == LINK_STATE_ADM); + // TransportLink->State = LINK_STATE_ADM; + + StopT1 (TransportLink); + StopT2 (TransportLink); + StopTi (TransportLink); + + + // + // Make sure we are not in the deferred timer queue. + // + + ACQUIRE_SPIN_LOCK (&DeviceContext->TimerSpinLock, &oldirql); + + if (TransportLink->OnShortList) { + TransportLink->OnShortList = FALSE; + RemoveEntryList (&TransportLink->ShortList); + } + + if (TransportLink->OnLongList) { + TransportLink->OnLongList = FALSE; + RemoveEntryList (&TransportLink->LongList); + } + + RELEASE_SPIN_LOCK (&DeviceContext->TimerSpinLock, oldirql); + + ASSERT (!TransportLink->OnDeferredRrQueue); + + // + // Now free this link object's resources. + // BUGBUG: later, we'll spin through the WackQ and verify that sequencing + // is correct and we've gotten an implicit ack for these packets. This + // maybe should be handled in ResendLlcPackets for non-final, non-command + // packets. + // + + while (!IsListEmpty (&TransportLink->WackQ)) { + pkt = RemoveHeadList (&TransportLink->WackQ); + packet = CONTAINING_RECORD (pkt, TP_PACKET, Linkage); +#if DBG + // IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint1 ("NbfDereferenceLink: Destroying packets on Link WackQ! %lx\n", packet); + // } +#endif + NbfDereferencePacket (packet); + + } + + // + // The NDIS send queue should be empty!! + // + + ASSERT (IsListEmpty (&TransportLink->NdisSendQueue)); + +#if DBG + if (!IsListEmpty (&TransportLink->ConnectionDatabase)) { + NbfPrint1 ("NbfDestroyLink: link 0x%lx still has connections\n", TransportLink); + DbgBreakPoint (); + } +#endif + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + DeviceContext->LinkTotal += DeviceContext->LinkInUse; + ++DeviceContext->LinkSamples; + ASSERT(DeviceContext->LinkInUse > 0); + --DeviceContext->LinkInUse; + + ASSERT(DeviceContext->LinkAllocated > DeviceContext->LinkInUse); + + if ((DeviceContext->LinkAllocated - DeviceContext->LinkInUse) > + DeviceContext->LinkInitAllocated) { + NbfDeallocateLink (DeviceContext, TransportLink); + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint1 ("NBF: Deallocated link at %lx\n", TransportLink); + } + } else { + InsertTailList (&DeviceContext->LinkPool, &TransportLink->Linkage); + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + NbfDereferenceDeviceContext ("Destroy Link", DeviceContext, DCREF_LINK); // just housekeeping. + + return STATUS_SUCCESS; + +} /* NbfDestroyLink */ + + +VOID +NbfDisconnectLink( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine calls the data link provider to disconnect a data link + connection associated with a TP_LINK object. + +Arguments: + + Link - Pointer to a transport link object. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql; + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint1 ("NbfDisconnectLink: Entered for link %lx.\n", Link); + } + + ACQUIRE_SPIN_LOCK (&Link->SpinLock, &oldirql); + + if ((Link->Flags & LINK_FLAGS_LOCAL_DISC) != 0) { + + Link->Flags &= ~LINK_FLAGS_LOCAL_DISC; + + if (Link->State == LINK_STATE_ADM) { + + RELEASE_SPIN_LOCK (&Link->SpinLock, oldirql); + + } else { + + PLIST_ENTRY p; + PTP_PACKET packet; + + Link->State = LINK_STATE_W_DISC_RSP; // we are awaiting a DISC/f. + Link->SendState = SEND_STATE_DOWN; + Link->ReceiveState = RECEIVE_STATE_DOWN; + StopT1 (Link); + StopT2 (Link); + StopTi (Link); + + // + // check for left over packets on the link WackQ; we'll never get + // acked for these if the link is in W_DISC_RSP. + // + + while (!IsListEmpty (&Link->WackQ)) { + p = RemoveHeadList (&Link->WackQ); + RELEASE_SPIN_LOCK (&Link->SpinLock, oldirql); + packet = CONTAINING_RECORD (p, TP_PACKET, Linkage); + NbfDereferencePacket (packet); + ACQUIRE_SPIN_LOCK (&Link->SpinLock, &oldirql); + } + + Link->SendRetries = (UCHAR)Link->LlcRetries; + StartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); // retransmit timer. + RELEASE_SPIN_LOCK (&Link->SpinLock, oldirql); + NbfSendDisc (Link, TRUE); // send DISC-c/p. + + } + + } else { + + RELEASE_SPIN_LOCK (&Link->SpinLock, oldirql); + + } + +} /* NbfDisconnectLink */ + +#if DBG + +VOID +NbfRefLink( + IN PTP_LINK TransportLink + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport link. If we are + currently in the state waiting for disconnect response, we do not + reference; this avoids the link "bouncing" during disconnect (trying to + disconnect multiple times). + +Arguments: + + TransportLink - Pointer to a transport link object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint2 ("NbfReferenceLink: Entered for link %lx, current level=%ld.\n", + TransportLink, TransportLink->ReferenceCount); + } + +#if DBG + StoreLinkHistory( TransportLink, TRUE ); +#endif + + result = InterlockedIncrement (&TransportLink->ReferenceCount); + + if (result == 0) { + + // + // The first increment causes us to increment the + // "ref count is not zero" special ref. + // + + NbfReferenceLinkSpecial ("first ref", TransportLink, LREF_SPECIAL_TEMP); + + } + + ASSERT (result >= 0); + +} /* NbfRefLink */ +#endif + + +VOID +NbfDerefLink( + IN PTP_LINK TransportLink + ) + +/*++ + +Routine Description: + + This routine dereferences a transport link by decrementing the + reference count contained in the structure. + + There are two special reference counts, 1 and 0. If, after dereferencing, + the reference count is one (1), then we initiate a disconnect protocol + sequence (DISC/UA) to terminate the connection. When this request + completes, the completion routine will dereference the link object again. + While this protocol is in progress, we will not allow the link to be + incremented again. + + If the reference count becomes 0 after dereferencing, then we are in + the disconnection request completion handler, and we should actually + destroy the link object. We place the link on the deferred operations + queue and let the link get deleted later at a safe time. + + Warning: Watch out for cases where a link is going down, and it is + suddenly needed again. Keep a bitflag for that in the link object. + +Arguments: + + TransportLink - Pointer to a transport link object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint2 ("NbfDereferenceLink: Entered for link %lx, current level=%ld.\n", + TransportLink, TransportLink->ReferenceCount); + } + +#if DBG + StoreLinkHistory( TransportLink, FALSE ); +#endif + + result = InterlockedDecrement(&TransportLink->ReferenceCount); + + // + // If all the normal references to this link are gone, then + // we can remove the special reference that stood for + // "the regular ref count is non-zero". + // + + + if (result < 0) { + + // + // If the refcount is -1 we want to call DisconnectLink, + // we do this before removing the special ref so that + // the link does not go away during the call. + // + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint0 ("NbfDereferenceLink: refcnt=1, disconnecting Link object.\n"); + } + + NbfDisconnectLink (TransportLink); + + // + // Now it is OK to let the link go away. + // + + NbfDereferenceLinkSpecial ("Regular ref 0", TransportLink, LREF_SPECIAL_TEMP); + + } + +} /* NbfDerefLink */ + + +VOID +NbfRefLinkSpecial( + IN PTP_LINK TransportLink + ) + +/*++ + +Routine Description: + + This routine increments the special reference count on a transport link. + +Arguments: + + TransportLink - Pointer to a transport link object. + +Return Value: + + none. + +--*/ + +{ + ULONG result; + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint3 ("NbfRefLinkSpecial: Entered for link %lx, current level=%ld (%ld).\n", + TransportLink, TransportLink->ReferenceCount, TransportLink->SpecialRefCount); + } + +#if DBG + StoreLinkHistory( TransportLink, TRUE ); +#endif + + result = ExInterlockedAddUlong ( + (PULONG)&TransportLink->SpecialRefCount, + 1, + TransportLink->ProviderInterlock); + +} /* NbfRefLinkSpecial */ + + +VOID +NbfDerefLinkSpecial( + IN PTP_LINK TransportLink + ) + +/*++ + +Routine Description: + + This routine dereferences a transport link by decrementing the + special reference count contained in the structure. + + The special reference may be decremented at any time, however + the effect of those dereferences only happen when the normal + reference count is 0, to prevent the link from going away + while the operations due to the ->0 transition of the + normal reference count are done. + + If the special reference count becomes 0 after dereferencing, then we + are in the disconnection request completion handler, and we should actually + destroy the link object. We place the link on the deferred operations + queue and let the link get deleted later at a safe time. + + Warning: Watch out for cases where a link is going down, and it is + suddenly needed again. Keep a bitflag for that in the link object. + +Arguments: + + TransportLink - Pointer to a transport link object. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql, oldirql1; + ULONG OldRefCount; + PDEVICE_CONTEXT DeviceContext = TransportLink->Provider; + + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint3 ("NbfDerefLinkSpecial: Entered for link %lx, current level=%ld (%ld).\n", + TransportLink, TransportLink->ReferenceCount, TransportLink->SpecialRefCount); + } + +#if DBG + StoreLinkHistory( TransportLink, FALSE ); +#endif + + // + // Links stay in the device context tree with a ref count + // of 0. Routines that scan this queue check the DEFERRED_DELETE + // flag, so we need to synchronize the decrementing of the + // ref count with setting that flag. DeviceContext->LinkSpinLock + // is used to synchronize this. + // + + ACQUIRE_SPIN_LOCK (&DeviceContext->LinkSpinLock, &oldirql1); + + OldRefCount = ExInterlockedAddUlong ( + (PULONG)&TransportLink->SpecialRefCount, + (ULONG)-1, + TransportLink->ProviderInterlock); + + ASSERT (OldRefCount > 0); + + if ((OldRefCount == 1) && + (TransportLink->ReferenceCount == -1)) { + + if (TransportLink->Loopback) { + + // + // It is a loopback link, hence not in the link + // tree so we don't need to queue a deferred removal. + // + + if (TransportLink == DeviceContext->LoopbackLinks[0]) { + DeviceContext->LoopbackLinks[0] = NULL; + } else if (TransportLink == DeviceContext->LoopbackLinks[1]) { + DeviceContext->LoopbackLinks[1] = NULL; + } else { +#if DBG + NbfPrint0("Destroying unknown loopback link!!\n"); +#endif + ASSERT(FALSE); + } + + RELEASE_SPIN_LOCK (&DeviceContext->LinkSpinLock, oldirql1); + + NbfDestroyLink (TransportLink); + + } else { + + // + // Not only are all transport connections gone, but the data link + // provider does not have a reference to this object, so we can + // safely delete it from the system. Make sure we haven't already + // been here before we try to insert this link. + // + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint6 ("NbfDerefLink: link to deferred queue %lx %lx %lx %lx %lx Flags: %lx \n", + TransportLink, TransportLink->DeferredList.Flink, + TransportLink->DeferredList.Blink, DeviceContext->LinkDeferred.Flink, + DeviceContext->LinkDeferred.Blink, TransportLink->Flags); + } + + if ((TransportLink->DeferredFlags & LINK_FLAGS_DEFERRED_MASK) == 0) { + + TransportLink->DeferredFlags |= LINK_FLAGS_DEFERRED_DELETE; + + ACQUIRE_SPIN_LOCK (&DeviceContext->TimerSpinLock, &oldirql); + InsertTailList (&DeviceContext->LinkDeferred, &TransportLink->DeferredList); + if (!(DeviceContext->a.i.LinkDeferredActive)) { + StartTimerLinkDeferredDelete++; + NbfStartShortTimer (DeviceContext); + DeviceContext->a.i.LinkDeferredActive = TRUE; + } + RELEASE_SPIN_LOCK (&DeviceContext->TimerSpinLock, oldirql); + + } else { + + TransportLink->DeferredFlags |= LINK_FLAGS_DEFERRED_DELETE; + + } + + RELEASE_SPIN_LOCK (&DeviceContext->LinkSpinLock, oldirql1); + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint0 ("NbfDereferenceLink: refcnt=0, link placed on deferred operations queue.\n"); + } + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint6 ("NbfDerefLink: link on deferred queue %lx %lx %lx %lx %lx Flags: %lx \n", + TransportLink, TransportLink->DeferredList.Flink, + TransportLink->DeferredList.Blink, DeviceContext->LinkDeferred.Flink, + DeviceContext->LinkDeferred.Blink, TransportLink->DeferredFlags); + } + + } + + } else { + + RELEASE_SPIN_LOCK (&DeviceContext->LinkSpinLock, oldirql1); + + } + +} /* NbfDerefLinkSpecial */ + + +NTSTATUS +NbfAssignGroupLsn( + IN PTP_CONNECTION TransportConnection + ) + +/*++ + +Routine Description: + + This routine is called to assign a global LSN to the connection + in question. If successful, it fills in the connection's LSN + appropriately. + +Arguments: + + TransportConnection - Pointer to a transport connection object. + +Return Value: + + STATUS_SUCCESS if we got an LSN for the connection; + STATUS_INSUFFICIENT_RESOURCES if we didn't. + +--*/ + +{ + KIRQL oldirql; + UCHAR Lsn; + PDEVICE_CONTEXT DeviceContext; + BOOLEAN FoundLsn = FALSE; + + DeviceContext = TransportConnection->Provider; + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + // + // Scan through the device context tables to find an LSN that + // is not in use, starting with NextLsnStart+128. + // + + Lsn = (UCHAR)DeviceContext->NextLsnStart; + + do { + + if (DeviceContext->LsnTable[Lsn] == 0) { + DeviceContext->LsnTable[Lsn] = LSN_TABLE_MAX; + FoundLsn = TRUE; + break; + } + + Lsn = (Lsn % NETBIOS_SESSION_LIMIT) + 1; + + } while (Lsn != DeviceContext->NextLsnStart); + + DeviceContext->NextLsnStart = (DeviceContext->NextLsnStart % 64) + 1; + + if (!FoundLsn) { + + // + // Could not find an empty LSN; have to fail. + // + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + return STATUS_INSUFFICIENT_RESOURCES; + + } + + TransportConnection->Lsn = Lsn; + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + return STATUS_SUCCESS; + +} + + +NTSTATUS +NbfConnectToLink( + IN PTP_LINK Link, + IN PTP_CONNECTION TransportConnection + ) + +/*++ + +Routine Description: + + This routine is called to establish a linkage between a transport + connection and a transport link. We find a session number in one + of two ways. If the last connection on the link's list has a number less + than the maximum session number, we simply increment it's number and + assign it to this session. If that doesn't work, we scan through the + sessions associated with this link until we find a hole in the LSNs; + we then use the first number in that hole. If that fails, we've used + the number of sessions we can create on this link and we fail. + + It is assumed that the caller holds at least temporary references + on both the connection and link objects, or they could go away during + the call sequence or during this routine's execution. + +Arguments: + + Link - Pointer to a transport link object. + + TransportConnection - Pointer to a transport connection object. + +Return Value: + + STATUS_SUCCESS if we got an LSN for the connection; + STATUS_INSUFFICIENT_RESOURCES if we didn't. + +--*/ + +{ + KIRQL oldirql; + UCHAR lastSession=0; + PTP_CONNECTION connection; + PLIST_ENTRY p; + PDEVICE_CONTEXT DeviceContext; + UCHAR Lsn; + BOOLEAN FoundLsn; + + // + // Assign an LSN for a new connection. If this connection makes for more + // connections than the maximum, blow off the creation. + // + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint2 ("NbfConnectToLink: Entered for connection %lx, link %lx.\n", + TransportConnection, Link); + } + + DeviceContext = Link->Provider; + + ACQUIRE_SPIN_LOCK (&Link->SpinLock, &oldirql); +#if DBG + if (!(IsListEmpty(&TransportConnection->LinkList)) || + (TransportConnection->Link != NULL)) { + DbgPrint ("Connecting C %lx to L %lx, appears to be in use\n", TransportConnection, Link); + DbgBreakPoint(); + } +#endif + + if ((TransportConnection->Flags2 & CONNECTION_FLAGS2_GROUP_LSN) == 0) { + + // + // This connection is to a remote unique name, which means + // we need to assign the LSN here based on the link. We + // scan through our LSN table starting with NextLsnStart + // (which cycles from 1 to 64) to find an LSN which is not + // used by any connections on this link. + // + + ASSERT (TransportConnection->Lsn == 0); + + FoundLsn = FALSE; + Lsn = (UCHAR)DeviceContext->NextLsnStart; + + // + // First scan through the database until we reach + // Lsn (or hit the end of the database). + // + + for (p = Link->ConnectionDatabase.Flink; + p != &Link->ConnectionDatabase; + p = p->Flink) { + + connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList); + if (connection->Lsn >= Lsn) { + break; + } + } + + // + // p now points to the first element after Lsn's spot. + // We now scan forwards until we hit NETBIOS_SESSION_LIMIT, + // looking for an Lsn that is available. + // + + for ( ; Lsn <= NETBIOS_SESSION_LIMIT; ++Lsn) { + + // + // At some point (perhaps right away) we may + // pass the end of the database without finding + // an LSN. If we have not yet done this, see + // if we need to skip this lsn because it is + // in use by a connection on this link. + // + + if (p != &Link->ConnectionDatabase) { + if (connection->Lsn == Lsn) { + p = p->Flink; + if (p != &Link->ConnectionDatabase) { + connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList); + } + continue; + } + } + + // + // This lsn is not in use on this link, see if + // there is room for it to be used. + // + + if (DeviceContext->LsnTable[Lsn] < LSN_TABLE_MAX) { + ++(DeviceContext->LsnTable[Lsn]); + TransportConnection->Lsn = Lsn; + InsertTailList (p, &TransportConnection->LinkList); + FoundLsn = TRUE; + break; + } + + } + + DeviceContext->NextLsnStart = (DeviceContext->NextLsnStart % 64) + 1; + + } else { + + // + // This connection is to a group name; we already assigned + // the LSN on a global basis. + // + + FoundLsn = TRUE; + + // + // Find the spot for this LSN in the database. + // + + p = Link->ConnectionDatabase.Flink; + while (p != &Link->ConnectionDatabase) { + + connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList); + if (TransportConnection->Lsn < connection->Lsn) { + InsertTailList (p, &TransportConnection->LinkList); + break; + } + p = p->Flink; + + } + + if (p == &Link->ConnectionDatabase) { + InsertTailList (&Link->ConnectionDatabase, &TransportConnection->LinkList); + } + + } + + if (!FoundLsn) { + + ULONG DumpData = NETBIOS_SESSION_LIMIT; + + ASSERT (Link->ActiveConnectionCount == NETBIOS_SESSION_LIMIT); + + RELEASE_SPIN_LOCK (&Link->SpinLock, oldirql); + + PANIC ("NbfConnectToLink: PANIC! too many active connections!\n"); + + NbfWriteGeneralErrorLog( + DeviceContext, + EVENT_TRANSPORT_TOO_MANY_LINKS, + 602, + STATUS_INSUFFICIENT_RESOURCES, + NULL, + 1, + &DumpData); + + return STATUS_INSUFFICIENT_RESOURCES; + + } + + TransportConnection->Link = Link; + TransportConnection->LinkSpinLock = &Link->SpinLock; + TransportConnection->Flags |= CONNECTION_FLAGS_WAIT_LINK_UP; + + TransportConnection->LastPacketsSent = Link->PacketsSent; + TransportConnection->LastPacketsResent = Link->PacketsResent; + + Link->ActiveConnectionCount++; + + // + // Note that the connection is already inserted in the + // link's ConnectionDatabase. + // + + // This reference is removed in NbfDisconnectFromLink + NbfReferenceConnection("Adding link", TransportConnection, CREF_LINK); + + RELEASE_SPIN_LOCK (&Link->SpinLock, oldirql); + + return STATUS_SUCCESS; // we did it! + +} /* NbfConnectToLink */ + + +BOOLEAN +NbfDisconnectFromLink( + IN PTP_CONNECTION TransportConnection, + IN BOOLEAN VerifyReferenceCount + ) + +/*++ + +Routine Description: + + This routine is called to terminate a linkage between a transport + connection and its associated transport link. If it turns out that + this is the last connection to be removed from this link, then the + link's disconnection protocol is engaged. + +Arguments: + + TransportConnection - Pointer to a transport connection object. + + VerifyReferenceCount - TRUE if we should check that the refcount + is still -1 before removing the connection from the link. + If it is not, it means someone just referenced us and we + exit. + +Return Value: + + FALSE if VerifyReferenceCount was TRUE but the refcount was + not -1; TRUE otherwise. + + +--*/ + +{ + KIRQL oldirql, oldirql1; + PTP_LINK Link; + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint2 ("NbfDisconnectFromLink: Entered for connection %lx, link %lx.\n", + TransportConnection, TransportConnection->Link); + } + + ACQUIRE_C_SPIN_LOCK (&TransportConnection->SpinLock, &oldirql); + Link = TransportConnection->Link; + if (Link != NULL) { + + ACQUIRE_SPIN_LOCK (&Link->SpinLock, &oldirql1); + + if ((VerifyReferenceCount) && + (TransportConnection->ReferenceCount != -1)) { + + RELEASE_SPIN_LOCK (&Link->SpinLock, oldirql1); + RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql); + return FALSE; + + } + + TransportConnection->Link = NULL; + TransportConnection->LinkSpinLock = NULL; + RemoveEntryList (&TransportConnection->LinkList); +#if DBG + InitializeListHead (&TransportConnection->LinkList); +#endif + + // + // If this was the last connection being serviced by this link, + // then we can shut the link down. It still has a reference + // from the device context, which will go away in the DM/UA + // DLC frame handler. + // + + if (--Link->ActiveConnectionCount == 0) { + + // + // only want to send DISC if the remote was NOT the originator + // of the disconnect. + // + + if ((TransportConnection->Status == STATUS_LOCAL_DISCONNECT) || + (TransportConnection->Status == STATUS_CANCELLED)) { + + // + // This is a local disconnect of the last connection + // on the link, let's get the disconnect ball rolling. + // + + Link->Flags |= LINK_FLAGS_LOCAL_DISC; + + // + // When the link reference count drops down to 1, + // that will cause the DISC to get sent. + // + + } + + } + + RELEASE_SPIN_LOCK (&Link->SpinLock, oldirql1); + + // + // Clear these now that we are off the link's database. + // + + NbfClearConnectionLsn (TransportConnection); + TransportConnection->Rsn = 0; + + RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql); + + if ((TransportConnection->Flags2 & CONNECTION_FLAGS2_CONNECTOR) != 0) { + + (VOID)InterlockedDecrement(&Link->NumberOfConnectors); + } + + // + // All done with this connection's reference to link. + // + + NbfDereferenceLink ("Disconnecting connection",Link, LREF_CONNECTION); + + } else { + + // + // A group LSN may have been assigned even though Link is NULL. + // + + if ((TransportConnection->Flags2 & CONNECTION_FLAGS2_GROUP_LSN) != 0) { + NbfClearConnectionLsn (TransportConnection); + } + + RELEASE_C_SPIN_LOCK (&TransportConnection->SpinLock, oldirql); + + } + + return TRUE; + +} /* NbfDisconnectFromLink */ + + +PTP_CONNECTION +NbfLookupPendingListenOnLink( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine scans the LSN database on a transport link object to find + a TP_CONNECTION object which has the CONNECTION_FLAGS_WAIT_LINK_UP and + CONNECTION_FLAGS2_LISTENER flags set. It returns a pointer to the found + connection object (and simultaneously resets the LINK_UP flag) or NULL + if it could not be found. The reference count is also incremented + atomically on the connection. + + NOTE: THIS ROUTINE MUST BE CALLED FROM DPC LEVEL. + +Arguments: + + Link - Pointer to a transport link object. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_CONNECTION connection; + PLIST_ENTRY p; + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + for (p = Link->ConnectionDatabase.Flink; + p != &Link->ConnectionDatabase; + p = p->Flink) { + connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList); + if ((connection->Flags & CONNECTION_FLAGS_WAIT_LINK_UP) && + (connection->Flags2 & CONNECTION_FLAGS2_LISTENER) && + ((connection->Flags2 & CONNECTION_FLAGS2_STOPPING) == 0)) { + // This reference is removed by the calling function + NbfReferenceConnection ("Found Pending Listen", connection, CREF_P_LINK); + connection->Flags &= ~CONNECTION_FLAGS_WAIT_LINK_UP; + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + return connection; + } + } + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + return NULL; + +} /* NbfLookupPendingListenOnLink */ + + +PTP_CONNECTION +NbfLookupPendingConnectOnLink( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine scans the LSN database on a transport link object to find + a TP_CONNECTION object which has the CONNECTION_FLAGS_WAIT_LINK_UP and + CONNECTION_FLAGS2_CONNECTOR flags set. It returns a pointer to the found + connection object (and simultaneously resets the LINK_UP flag) or NULL + if it could not be found. The reference count is also incremented + atomically on the connection. + + NOTE: THIS ROUTINE MUST BE CALLED FROM DPC LEVEL. + +Arguments: + + Link - Pointer to a transport link object. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_CONNECTION connection; + PLIST_ENTRY p; + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + for (p = Link->ConnectionDatabase.Flink; + p != &Link->ConnectionDatabase; + p = p->Flink) { + connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList); + if ((connection->Flags & CONNECTION_FLAGS_WAIT_LINK_UP) && + (connection->Flags2 & CONNECTION_FLAGS2_CONNECTOR) && + ((connection->Flags2 & CONNECTION_FLAGS2_STOPPING) == 0)) { + // This reference is removed by the calling function + NbfReferenceConnection ("Found pending Connect", connection, CREF_P_CONNECT); + connection->Flags &= ~CONNECTION_FLAGS_WAIT_LINK_UP; + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + return connection; + } + } + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + return NULL; + +} /* NbfLookupPendingConnectOnLink */ + + +VOID +NbfActivateLink( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine activates a link if it is not already active. The other + related routines, NbfCreateLink and NbfConnectToLink, simply set up data + structures which represent active links so that we can reuse links + wherever possible. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Link - Pointer to a transport link object. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint1 ("NbfActivateLink: Entered for link %lx.\n", Link); + } + + ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); + + switch (Link->State) { + case LINK_STATE_READY: + NbfCompleteLink (Link); + break; + + case LINK_STATE_ADM: + + // Moving out of ADM, add reference + + NbfReferenceLinkSpecial("Wait on ADM", Link, LREF_NOT_ADM); + + // + // Intentionally fall through to the next case. + // + + case LINK_STATE_W_DISC_RSP: + case LINK_STATE_CONNECTING: + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + Link->State = LINK_STATE_CONNECTING; + Link->SendState = SEND_STATE_DOWN; + Link->ReceiveState = RECEIVE_STATE_DOWN; + Link->SendRetries = (UCHAR)Link->LlcRetries; + NbfSendSabme (Link, TRUE); // send SABME/p, StartT1, release lock + break; + + } +} /* NbfActivateLink */ + + +VOID +NbfWaitLink( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine waits for a remote link activation if it is not already + active. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Link - Pointer to a transport link object. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint1 ("NbfWaitLink: Entered for link %lx.\n", Link); + } + + ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); + + switch (Link->State) { + case LINK_STATE_READY: + NbfCompleteLink (Link); + break; + + case LINK_STATE_W_DISC_RSP: + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + Link->State = LINK_STATE_CONNECTING; + Link->SendState = SEND_STATE_DOWN; + Link->ReceiveState = RECEIVE_STATE_DOWN; + NbfSendSabme (Link, TRUE); // send SABME/p, StartT1, release lock + break; + + } +} /* NbfWaitLink */ + + +VOID +NbfStopLink( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine terminates a link and all outstanding connections attached + to the link. It is called from routines such as ExpireT2Timer, because + the remote connection partner seems dead or inoperative. As a consequence + of this routine being called, every outstanding connection will have its + disconnect handler called (in NbfStopConnection). + + NOTE: THIS ROUTINE MUST BE CALLED FROM DPC LEVEL. + +Arguments: + + Link - Pointer to a transport link object. + +Return Value: + + none. + +--*/ + +{ + PLIST_ENTRY p; + PTP_PACKET packet; + PTP_CONNECTION connection; + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint1 ("NbfStopLink: Entered for link %lx.\n", Link); + } + + ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); + + // Take a reference so the link won't go away inside this function + + NbfReferenceLink("Temp in NbfStopLink", Link, LREF_STOPPING); + + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + StopT1 (Link); + StopT2 (Link); + StopTi (Link); + + p = RemoveHeadList (&Link->ConnectionDatabase); + + while (p != &Link->ConnectionDatabase) { + + // + // This will allow this connection to be "removed" + // from its link's list in NbfDisconnectFromLink, even if + // its not on a list. + // + InitializeListHead (p); + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList); + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint1 ("NbfStopLink stopping connection, refcnt=%ld", + connection->ReferenceCount); + } +#if DBG + if (NbfDisconnectDebug) { + STRING remoteName, localName; + remoteName.Length = NETBIOS_NAME_LENGTH - 1; + remoteName.Buffer = connection->RemoteName; + localName.Length = NETBIOS_NAME_LENGTH - 1; + localName.Buffer = connection->AddressFile->Address->NetworkName->NetbiosName; + NbfPrint2( "TpStopLink stopping connection to %S from %S\n", + &remoteName, &localName ); + } +#endif + NbfStopConnection (connection, STATUS_LINK_FAILED); + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + p = RemoveHeadList (&Link->ConnectionDatabase); + } + + // + // We hold the link spinlock here. + // + + // + // check for left over packets on the link WackQ; we'll never get + // acked for these if the link is in ADM mode. + // + + while (!IsListEmpty (&Link->WackQ)) { + p = RemoveHeadList (&Link->WackQ); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + packet = CONTAINING_RECORD (p, TP_PACKET, Linkage); + NbfDereferencePacket (packet); + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + } + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + StopT1 (Link); + StopT2 (Link); + StopTi (Link); + + + // + // Make sure we are not waiting for a deferred RR to be sent. + // + + if (Link->OnDeferredRrQueue) { + + ACQUIRE_DPC_SPIN_LOCK (Link->ProviderInterlock); + if (Link->OnDeferredRrQueue) { + RemoveEntryList (&Link->DeferredRrLinkage); + Link->OnDeferredRrQueue = FALSE; + } + RELEASE_DPC_SPIN_LOCK (Link->ProviderInterlock); + + } + + // Remove the temporary reference. + + NbfDereferenceLink ("Temp in NbfStopLink", Link, LREF_STOPPING); + + +} /* NbfStopLink */ + + +VOID +NbfResetLink( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine is called by DLC.C routines only to reset this link + object and restart in-progress transport data transfers. + + NOTE: This routine is called with the link spinlock acquired + at *OldIrqlP, and will return with it held, although it may + release it in the interim. + +Arguments: + + Link - Pointer to a transport link object. + + OldIrqlP - Pointer to where the IRQL at which Link->SpinLock + was acquired is stored. + +Return Value: + + none. + +--*/ + +{ + PTP_PACKET packet; + PLIST_ENTRY p; + + IF_NBFDBG (NBF_DEBUG_LINK) { + NbfPrint1 ("NbfResetLink: Entered for link %lx.\n", Link); + } + + // + // Reset the link state to waiting for connection to start. + // Note that this is NOT the same as initiating a new link, as some things + // don't change, such as provider (devicecontext binding stays the same), + // Max Packet Length (can't change if provider doesn't), and other things + // that would bind this link structure to a different provider or provider + // type. Note also that we acquire the spinlock because, in the case of a + // link that's dropped (remotely) and is restarting, activities on this + // link could be occurring while we're in this routine. + // + + StopT1 (Link); + StopT2 (Link); + // StopTi (Link); + Link->Flags = 0; // clear this, keep DeferredFlags + + Link->SendState = SEND_STATE_DOWN; // send side is down. + Link->NextSend = 0; + Link->LastAckReceived = 0; + if (Link->Provider->MacInfo.MediumAsync) { + Link->SendWindowSize = (UCHAR)Link->Provider->RecommendedSendWindow; + Link->PrevWindowSize = (UCHAR)Link->Provider->RecommendedSendWindow; + } else { + Link->SendWindowSize = (UCHAR)1; + Link->PrevWindowSize = (UCHAR)1; + } + Link->WindowsUntilIncrease = 1; + Link->LinkBusy = FALSE; + Link->ConsecutiveLastPacketLost = 0; + + // + // check for left over packets on the link WackQ; we'll never get + // acked for these if the link is resetting. + // + + while (!IsListEmpty (&Link->WackQ)) { + p = RemoveHeadList (&Link->WackQ); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + packet = CONTAINING_RECORD (p, TP_PACKET, Linkage); + NbfDereferencePacket (packet); + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + } + + Link->ReceiveState = RECEIVE_STATE_DOWN; // receive side down. + Link->NextReceive = 0; + Link->LastAckSent = 0; + Link->ReceiveWindowSize = 1; + + Link->WindowErrors = 0; + Link->BestWindowSize = 1; + Link->WorstWindowSize = (UCHAR)Link->MaxWindowSize; + Link->Flags |= LINK_FLAGS_JUMP_START; + + // + // This must be accurate before we set up timeouts. + // + + Link->CurrentT1Timeout = Link->Provider->DefaultT1Timeout; + Link->BaseT1Timeout = Link->Provider->DefaultT1Timeout << DLC_TIMER_ACCURACY; + Link->MinimumBaseT1Timeout = Link->Provider->MinimumT1Timeout << DLC_TIMER_ACCURACY; + Link->BaseT1RecalcThreshhold = Link->MaxFrameSize / 2; + Link->CurrentPollRetransmits = 0; + Link->CurrentT1Backoff = FALSE; + Link->CurrentPollOutstanding = FALSE; + Link->RemoteNoPoll = TRUE; + Link->ConsecutiveIFrames = 0; + Link->T2Timeout = Link->Provider->DefaultT2Timeout; + Link->TiTimeout = Link->Provider->DefaultTiTimeout; + Link->LlcRetries = Link->Provider->LlcRetries; + Link->MaxWindowSize = Link->Provider->LlcMaxWindowSize; + + Link->SendRetries = (UCHAR)Link->LlcRetries; + +} /* NbfResetLink */ + + +VOID +NbfDumpLinkInfo ( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine is called when any of the link timers fire and the + link send state is not ready. This gives us a way to track the + link state when strange things are happening. + +Arguments: + + Link - Pointer to a transport link object. + +Return Value: + + none. + +--*/ +{ + Link; // avoid compiler warnings in non-debug versions + +#if DBG + NbfPrint4 ("NbfDumpLinkInfo: Link %lx : State: %x SendState: %x ReceiveState: %x\n", + Link, Link->State, Link->SendState, Link->ReceiveState); + NbfPrint1 (" Flags: %lx\n",Link->Flags); + NbfPrint4 (" NextReceive: %d LastAckRcvd: %d NextSend: %d LastAckSent: %d\n", + Link->NextReceive, Link->LastAckReceived, Link->NextSend, Link->LastAckSent); +#endif + +} diff --git a/private/ntos/tdi/nbf/linktree.c b/private/ntos/tdi/nbf/linktree.c new file mode 100644 index 000000000..5c62fd8b8 --- /dev/null +++ b/private/ntos/tdi/nbf/linktree.c @@ -0,0 +1,537 @@ +/*++ + +Copyright (c) 1990, 1991 Microsoft Corporation + +Module Name: + + linktree.c + +Abstract: + + This module contains code which implements the management of the link + splay tree. This splay tree is maintained to minimize the lookup time + needed with each individual packet that comes in. To this end, we create a + ULARGE_INTEGER that contains the transport address of the remote and + do a ULARGE_INTEGER comaprison of the addresses (rather than comparing + the bytes 1 by 1). Assuming that the ULARGE_INTEGER comparison routines are + optimized for the hardware on the machine, this should be as fast as or + faster than comparing bytes. + + DEBUG: there is currently code in the comparison routines that will let + me fine-tune the search and ordering algorithm as we gain more + experience with it. + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +NTSTATUS +NbfAddLinkToTree( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine adds a link to the tree of links maintained for this device. + Note that since this routine needs to modify the link tree, it is called + in the context of a deferred processing routine, and must have exclusive + access to the tree. The spinlock is taken by the routine that calls this + one, as this operation must be atomic in the eyes of the rest of NBF. + Note further that this routine insists that there not be a link with this + address in the tree. + + As the final operation of this insertion, the splay tree is balanced. + +Arguments: + + Link - Pointer to a transport link object. + +Return Value: + + STATUS_SUCCESS if the link is successfully added, + STATUS_DRIVER_INTERNAL_ERROR if there was a problem adding + the link (implying the tree was in a bad state). + +--*/ +{ + PTP_LINK treeLink; + PRTL_SPLAY_LINKS linkLink; + + // + // initialize the link and check for the trivial case. + // + + RtlInitializeSplayLinks (Link); + linkLink = DeviceContext->LinkTreeRoot; + if (linkLink == NULL) { // null tree, make this the parent + DeviceContext->LinkTreeRoot = (PRTL_SPLAY_LINKS)Link; + DeviceContext->LinkTreeElements++; + DeviceContext->LastLink = Link; + return STATUS_SUCCESS; + } + + // + // Wasn't a null tree, so set up for the addition + // + + treeLink = (PTP_LINK) linkLink; + + IF_NBFDBG(NBF_DEBUG_LINKTREE) { + NbfPrint1 ("NbfAddLinkToTree: starting insert, Elements: %ld \n",DeviceContext->LinkTreeElements); + } + + // + // find the proper spot to put this link. + // + + do { + IF_NBFDBG(NBF_DEBUG_LINKTREE) { + NbfPrint3 ("NbfAddLinkToTree: searching, Link: %lx LC: %lx RC: %lx\n", + linkLink, RtlLeftChild (linkLink), RtlRightChild (linkLink)); + } + + // + // Bad news == means we already have this link, someone is messed up. + // it's possible to be adding and deleting things at the same time; + // that's + // + + if ((treeLink->MagicAddress).QuadPart == (Link->MagicAddress).QuadPart) { + + // + // First make sure we don't have the splay tree in a loop. + // + + ASSERT (treeLink != Link); + + // + // This link is already in the tree. This is OK if it is + // due to be deleted; we can just do the delete right now, + // since AddLinkToTree is only called from the deferred + // timer routine. + // + + if (treeLink->DeferredFlags & LINK_FLAGS_DEFERRED_DELETE) { + + // + // It will be in the deferred list. We remove it, + // we don't worry about LinkDeferredActive since + // the timeout routine that is calling us handles + // that. + // + + RemoveEntryList (&treeLink->DeferredList); + + treeLink->DeferredFlags &= ~LINK_FLAGS_DEFERRED_DELETE; + NbfRemoveLinkFromTree (DeviceContext, treeLink); + NbfDestroyLink (treeLink); + +#if DBG + NbfPrint2 ("NbfAddLinkToTree: Link %lx removed for %lx\n", + treeLink, Link); +#endif + + // + // Now that that link is out of the tree, call + // ourselves recursively to do the insert. + // + + return NbfAddLinkToTree (DeviceContext, Link); + + } else { + + ASSERTMSG ("NbfAddLinkToTree: Found identical Link in tree!\n", FALSE); + return STATUS_DRIVER_INTERNAL_ERROR; + + } + + } + + // + // traverse the tree for the correct spot + // + + if ((Link->MagicAddress).QuadPart < (treeLink->MagicAddress).QuadPart) { + if ((linkLink = RtlLeftChild (linkLink)) == NULL) { + IF_NBFDBG(NBF_DEBUG_LINKTREE) { + NbfPrint0 ("NbfAddLinkToTree: Adding link as LC.\n"); + } + RtlInsertAsLeftChild ((PRTL_SPLAY_LINKS)treeLink, + (PRTL_SPLAY_LINKS)Link); + // DeviceContext->LinkTreeRoot = RtlSplay (DeviceContext->LinkTreeRoot); + DeviceContext->LinkTreeElements++; + return STATUS_SUCCESS; + + } else { + treeLink = (PTP_LINK) linkLink; + continue; + } // Left Child + + } else { // is greater + if ((linkLink = RtlRightChild (linkLink)) == NULL) { + IF_NBFDBG(NBF_DEBUG_LINKTREE) { + NbfPrint0 ("NbfAddLinkToTree: Adding link as RC.\n"); + } + RtlInsertAsRightChild ((PRTL_SPLAY_LINKS)treeLink, + (PRTL_SPLAY_LINKS)Link); + // DeviceContext->LinkTreeRoot = RtlSplay (DeviceContext->LinkTreeRoot); + DeviceContext->LinkTreeElements++; + return STATUS_SUCCESS; + + } else { + treeLink = (PTP_LINK) linkLink; + continue; + } // Right Child + + } // end else addresses comparison + + } while (TRUE); + + +} // NbfAddLinkToTree + + +NTSTATUS +NbfRemoveLinkFromTree( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine removes a link from the tree of links. + Note that since this routine needs to modify the link tree, it is called + in the context of a deferred processing routine, and must have exclusive + access to the tree. The spinlock is taken by the routine that calls this + one, as this operation must be atomic in the eyes of the rest of NBF. + Note further that this routine insists that there not be a link with this + address in the tree. + +Arguments: + + Link - Pointer to a transport link object. + DeviceContext - pointer to the device context on which this + +Return Value: + + STATUS_SUCCESS if the link was removed, + STATUS_DRIVER_INTERNAL_ERROR if there was a problem removing + the link (implying the tree was in a bad state). + +--*/ +{ + DeviceContext->LinkTreeRoot = RtlDelete ((PRTL_SPLAY_LINKS)Link); + DeviceContext->LinkTreeElements--; + if (DeviceContext->LastLink == Link) { + DeviceContext->LastLink = (PTP_LINK)DeviceContext->LinkTreeRoot; + } + return STATUS_SUCCESS; + +} //NbfRemoveLinkFromTree + + + +PTP_LINK +NbfFindLinkInTree( + IN PDEVICE_CONTEXT DeviceContext, + IN PUCHAR Remote + ) + +/*++ + +Routine Description: + + This routine traverses the link tree looking for the given remote address. + The link tree spinlock is held while looking for the link. After the link + is found, it's reference count is incremented. + + NOTE: This function is called with the device context LinkSpinLock + held. + +Arguments: + + DeviceContext - pointer to the device this address is associated with. + + Remote - pointer to the hardware address of the remote node. + +Return Value: + + Pointer to the link in the tree that matches this remote address. If + no link is found, NULL is returned. + +--*/ +{ + PTP_LINK link; + PRTL_SPLAY_LINKS linkLink; + ULARGE_INTEGER Magic = {0,0}; + + + // + // Are there even any links in the tree? + // + + if (DeviceContext->LinkTreeElements <= 0) { + return NULL; + } + + linkLink = DeviceContext->LinkTreeRoot; + + // + // Make a magic number for this link + // + + MacReturnMagicAddress (&DeviceContext->MacInfo, Remote, &Magic); + + IF_NBFDBG(NBF_DEBUG_LINKTREE) { + NbfPrint1 ("NbfFindLinkInTree: starting search, Elements: %ld \n", + DeviceContext->LinkTreeElements); + } + + // + // Do a quick check if the last link found is this one. + // + + ASSERT (DeviceContext->LastLink != NULL); + + if ((DeviceContext->LastLink->MagicAddress).QuadPart == Magic.QuadPart) { + + link = DeviceContext->LastLink; + + } else { + + // + // find the link. + // + + link = (PTP_LINK) linkLink; // depends upon splay links being first + // subfield in link! + IF_NBFDBG(NBF_DEBUG_LINKTREE) { + NbfPrint3 ("NbfFindLinkInTree: searching, Link: %lx LC: %lx RC: %lx \n", + linkLink, RtlLeftChild (linkLink), RtlRightChild (linkLink)); + } + + do { + + IF_NBFDBG(NBF_DEBUG_LINKTREE) { + NbfPrint4 ("NbfFindLinkInTree: Comparing: %lx%lx to %lx%lx\n", + link->MagicAddress.HighPart,link->MagicAddress.LowPart, + Magic.HighPart, Magic.LowPart); + } + + if ((link->MagicAddress).QuadPart == Magic.QuadPart) { + IF_NBFDBG(NBF_DEBUG_LINKTREE) { + NbfPrint0 ("NbfFindLinkInTree: equal, going to end.\n"); + } + break; + + } else { + if ((link->MagicAddress).QuadPart < Magic.QuadPart) { + if ((linkLink = RtlRightChild (linkLink)) == NULL) { + + IF_NBFDBG(NBF_DEBUG_LINKTREE) { + NbfPrint0 ("NbfFindLinkInTree: Link Not Found.\n"); + } + return NULL; + + } else { + link = (PTP_LINK) linkLink; + IF_NBFDBG(NBF_DEBUG_LINKTREE) { + NbfPrint3 ("NbfFindLinkInTree: less, took right child, Link: %lx LC: %lx RC: %lx \n", + linkLink, RtlLeftChild (linkLink), RtlRightChild (linkLink)); + } + continue; + } + + } else { // is greater + if ((linkLink = RtlLeftChild (linkLink)) == NULL) { + IF_NBFDBG(NBF_DEBUG_LINKTREE) { + NbfPrint0 ("NbfFindLinkInTree: Link Not Found.\n"); + } + return NULL; + + } else { + link = (PTP_LINK) linkLink; + IF_NBFDBG(NBF_DEBUG_LINKTREE) { + NbfPrint3 ("NbfFindLinkInTree: greater, took left child, Link: %lx LC: %lx RC: %lx \n", + linkLink, RtlLeftChild (linkLink), RtlRightChild (linkLink)); + } + continue; + } // got left child branch + } // greater branch + } // equal to branch + } while (TRUE); + + DeviceContext->LastLink = link; + + } + + // + // Only break out when we've actually found a match.. + // + + if ((link->DeferredFlags & LINK_FLAGS_DEFERRED_DELETE) != 0) { + IF_NBFDBG(NBF_DEBUG_LINKTREE) { + NbfPrint0 ("NbfFindLinkInTree: Link Found but delete pending.\n"); + } + return NULL; + } + + // + // Mark the link as in use and say we don't need the tree stable any more. + // + + NbfReferenceLink ("Found in tree", link, LREF_TREE); + + IF_NBFDBG(NBF_DEBUG_LINKTREE) { + NbfPrint0 ("NbfFindLinkInTree: Link Found.\n"); + } + + return link; + +} // NbfFindLinkInTree + + +PTP_LINK +NbfFindLink( + IN PDEVICE_CONTEXT DeviceContext, + IN PUCHAR Remote + ) + +/*++ + +Routine Description: + + This routine looks for a link in the link tree, and if + not found there in the deferred queue. + +Arguments: + + DeviceContext - pointer to the device this address is associated with. + + Remote - pointer to the hardware address of the remote node. + +Return Value: + + Pointer to the link in the tree that matches this remote address. If + no link is found, NULL is returned. + +--*/ + +{ + PTP_LINK Link; + BOOLEAN MatchedLink; + PLIST_ENTRY p; + UINT i; + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + Link = NbfFindLinkInTree (DeviceContext, Remote); + + if (Link == NULL) { + + // + // Not found there, try in deferred queue. + // + + MatchedLink = FALSE; // Assume failure + + // + // Hold the spinlock while we walk the deferred list. We need + // TimerSpinLock to stop the list from changing, and we need + // LinkSpinLock to synchronize checking DEFERRED_DELETE and + // referencing the link. + // + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + for (p = DeviceContext->LinkDeferred.Flink; + p != &DeviceContext->LinkDeferred; + p = p->Flink) { + + // + // BUGBUG: What about taking a lock while we walk + // this list? It won't be removed from at the front, + // but it may be added to at the back. + // + + // + // We're probably still getting this link to the splay tree. + // find it and process normally. + // + + Link = CONTAINING_RECORD (p, TP_LINK, DeferredList); + + // + // NOTE: We know that the link is not going to be destroyed + // now, because we have increased the semaphore. We + // reference the link if DEFERRED_DELETE is not on; the + // setting of this flag is synchronized (also using + // DeviceContext->LinkSpinLock) with the refcount going + // to 0). + // + + if ((Link->DeferredFlags & LINK_FLAGS_DEFERRED_DELETE) != 0) { + continue; // we're deleting link, can't handle + } + + for (i=0; i<(UINT)DeviceContext->MacInfo.AddressLength; i++) { + if (Remote[i] != Link->HardwareAddress.Address[i]){ + break; + } + } + + if (i == (UINT)DeviceContext->MacInfo.AddressLength) { // addresses match. Deliver packet. + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 ("NbfFindLink: Found link on deferred queue, Link: %lx\n", + Link); + } + NbfReferenceLink ("Got Frame on Deferred", Link, LREF_TREE); + MatchedLink = TRUE; + break; + } + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + // + // If this didn't find the link, make note of that. + // + + if (MatchedLink == FALSE) { + + Link = (PTP_LINK)NULL; + + } + + } else { + + IF_NBFDBG (NBF_DEBUG_DLC) { + NbfPrint1 ("NbfFindLink: Found link in tree, Link: %lx\n", Link); + } + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + return Link; + +} diff --git a/private/ntos/tdi/nbf/makefile b/private/ntos/tdi/nbf/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/nbf/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/tdi/nbf/nbf.h b/private/ntos/tdi/nbf/nbf.h new file mode 100644 index 000000000..2efb3f50d --- /dev/null +++ b/private/ntos/tdi/nbf/nbf.h @@ -0,0 +1,166 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + nbf.h + +Abstract: + + Private include file for the NBF (NetBIOS Frames Protocol) transport + provider subcomponent of the NTOS project. + +Author: + + Stephen E. Jones (stevej) 25-Oct-1989 + +Revision History: + + David Beaver (dbeaver) 24-Sep-1990 + Remove PDI and PC586-specific support; add NDIS support + +--*/ + +#ifndef _NBF_ +#define _NBF_ + +#include <ntddk.h> + +typedef struct _RTL_SPLAY_LINKS { + struct _RTL_SPLAY_LINKS *Parent; + struct _RTL_SPLAY_LINKS *LeftChild; + struct _RTL_SPLAY_LINKS *RightChild; +} RTL_SPLAY_LINKS; +typedef RTL_SPLAY_LINKS *PRTL_SPLAY_LINKS; + +#define RtlInitializeSplayLinks(Links) { \ + PRTL_SPLAY_LINKS _SplayLinks; \ + _SplayLinks = (PRTL_SPLAY_LINKS)(Links); \ + _SplayLinks->Parent = _SplayLinks; \ + _SplayLinks->LeftChild = NULL; \ + _SplayLinks->RightChild = NULL; \ + } + +#define RtlLeftChild(Links) ( \ + (PRTL_SPLAY_LINKS)(Links)->LeftChild \ + ) + +#define RtlRightChild(Links) ( \ + (PRTL_SPLAY_LINKS)(Links)->RightChild \ + ) + +#define RtlInsertAsLeftChild(ParentLinks,ChildLinks) { \ + PRTL_SPLAY_LINKS _SplayParent; \ + PRTL_SPLAY_LINKS _SplayChild; \ + _SplayParent = (PRTL_SPLAY_LINKS)(ParentLinks); \ + _SplayChild = (PRTL_SPLAY_LINKS)(ChildLinks); \ + _SplayParent->LeftChild = _SplayChild; \ + _SplayChild->Parent = _SplayParent; \ + } + +#define RtlInsertAsRightChild(ParentLinks,ChildLinks) { \ + PRTL_SPLAY_LINKS _SplayParent; \ + PRTL_SPLAY_LINKS _SplayChild; \ + _SplayParent = (PRTL_SPLAY_LINKS)(ParentLinks); \ + _SplayChild = (PRTL_SPLAY_LINKS)(ChildLinks); \ + _SplayParent->RightChild = _SplayChild; \ + _SplayChild->Parent = _SplayParent; \ + } + + +PRTL_SPLAY_LINKS +NTAPI +RtlDelete ( + PRTL_SPLAY_LINKS Links + ); + + +VOID +NTAPI +RtlGetCallersAddress( + OUT PVOID *CallersAddress, + OUT PVOID *CallersCaller + ); + +#include <tdikrnl.h> // Transport Driver Interface. + +#include <ndis.h> // Physical Driver Interface. + +#if DEVL +#define STATIC +#else +#define STATIC static +#endif + +#include "nbfconst.h" // private NETBEUI constants. +#include "nbfmac.h" // mac-specific definitions +#include "nbfhdrs.h" // private NETBEUI protocol headers. +#include "nbftypes.h" // private NETBEUI types. +#include "nbfcnfg.h" // configuration information. +#include "nbfprocs.h" // private NETBEUI function prototypes. +#ifdef MEMPRINT +#include "memprint.h" // drt's memory debug print +#endif + + +#ifndef NBF_LOCKS + +#define ACQUIRE_SPIN_LOCK(lock,irql) KeAcquireSpinLock(lock,irql) +#define RELEASE_SPIN_LOCK(lock,irql) KeReleaseSpinLock(lock,irql) + +#if 0 +#define ACQUIRE_DPC_SPIN_LOCK(lock) \ + { KIRQL OldIrql; ASSERT ((lock != NULL) && (KeGetCurrentIrql() == DISPATCH_LEVEL)); KeAcquireSpinLock(lock,&OldIrql); } +#define RELEASE_DPC_SPIN_LOCK(lock) \ + { ASSERT(lock != NULL); KeReleaseSpinLock(lock,DISPATCH_LEVEL); } +#else +#define ACQUIRE_DPC_SPIN_LOCK(lock) KeAcquireSpinLockAtDpcLevel(lock) +#define RELEASE_DPC_SPIN_LOCK(lock) KeReleaseSpinLockFromDpcLevel(lock) +#endif + +#define ENTER_NBF +#define LEAVE_NBF + +#else + +VOID +NbfAcquireSpinLock( + IN PKSPIN_LOCK Lock, + OUT PKIRQL OldIrql, + IN PSZ LockName, + IN PSZ FileName, + IN ULONG LineNumber + ); + +VOID +NbfReleaseSpinLock( + IN PKSPIN_LOCK Lock, + IN KIRQL OldIrql, + IN PSZ LockName, + IN PSZ FileName, + IN ULONG LineNumber + ); + +#define ACQUIRE_SPIN_LOCK(lock,irql) \ + NbfAcquireSpinLock( lock, irql, #lock, __FILE__, __LINE__ ) +#define RELEASE_SPIN_LOCK(lock,irql) \ + NbfReleaseSpinLock( lock, irql, #lock, __FILE__, __LINE__ ) + +#define ACQUIRE_DPC_SPIN_LOCK(lock) \ + { \ + KIRQL OldIrql; \ + NbfAcquireSpinLock( lock, &OldIrql, #lock, __FILE__, __LINE__ ); \ + } +#define RELEASE_DPC_SPIN_LOCK(lock) \ + NbfReleaseSpinLock( lock, DISPATCH_LEVEL, #lock, __FILE__, __LINE__ ) + +#define ENTER_NBF \ + NbfAcquireSpinLock( (PKSPIN_LOCK)NULL, (PKIRQL)NULL, "(Global)", __FILE__, __LINE__ ) +#define LEAVE_NBF \ + NbfReleaseSpinLock( (PKSPIN_LOCK)NULL, (KIRQL)-1, "(Global)", __FILE__, __LINE__ ) + +#endif + + +#endif // def _NBF_ diff --git a/private/ntos/tdi/nbf/nbf.rc b/private/ntos/tdi/nbf/nbf.rc new file mode 100644 index 000000000..be3b9d409 --- /dev/null +++ b/private/ntos/tdi/nbf/nbf.rc @@ -0,0 +1,12 @@ +#include <windows.h> + +#include <ntverp.h> + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_NETWORK +#define VER_FILEDESCRIPTION_STR "NetBEUI Frames Protocol Driver" +#define VER_INTERNALNAME_STR "nbf.sys" +#define VER_ORIGINALFILENAME_STR "nbf.sys" + +#include "common.ver" + diff --git a/private/ntos/tdi/nbf/nbfcnfg.c b/private/ntos/tdi/nbf/nbfcnfg.c new file mode 100644 index 000000000..30d0a02f3 --- /dev/null +++ b/private/ntos/tdi/nbf/nbfcnfg.c @@ -0,0 +1,1155 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + nbfconfig.c + +Abstract: + + This contains all routines necessary for the support of the dynamic + configuration of NBF. Note that the parts of this file that are + called at initialization time will be replaced by calls to the configuration manager over time. + +Author: + + David Beaver (dbeaver) 13-Feb-1991 + +Revision History: + + David Beaver (dbeaver) 1-July-1991 + modified to use new tdi interface + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// +// Local functions used to access the registry. +// + +VOID +NbfFreeConfigurationInfo ( + IN PCONFIG_DATA ConfigurationInfo + ); + +NTSTATUS +NbfOpenParametersKey( + IN HANDLE NbfConfigHandle, + OUT PHANDLE ParametersHandle + ); + +VOID +NbfCloseParametersKey( + IN HANDLE ParametersHandle + ); + +NTSTATUS +NbfCountEntries( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +NbfAddBind( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +NbfAddExport( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +VOID +NbfReadLinkageInformation( + IN PWSTR RegistryPathBuffer, + IN PCONFIG_DATA * ConfigurationInfo + ); + +ULONG +NbfReadSingleParameter( + IN HANDLE ParametersHandle, + IN PWCHAR ValueName, + IN ULONG DefaultValue + ); + +VOID +NbfWriteSingleParameter( + IN HANDLE ParametersHandle, + IN PWCHAR ValueName, + IN ULONG ValueData + ); + +UINT +NbfWstrLength( + IN PWSTR Wstr + ); + +#ifdef ALLOC_PRAGMA +#ifndef _PNP_POWER +#pragma alloc_text(INIT,NbfWstrLength) +#pragma alloc_text(INIT,NbfConfigureTransport) +#pragma alloc_text(INIT,NbfFreeConfigurationInfo) +#pragma alloc_text(INIT,NbfOpenParametersKey) +#pragma alloc_text(INIT,NbfCloseParametersKey) +#pragma alloc_text(INIT,NbfCountEntries) +#pragma alloc_text(INIT,NbfAddBind) +#pragma alloc_text(INIT,NbfAddExport) +#pragma alloc_text(INIT,NbfReadLinkageInformation) +#pragma alloc_text(INIT,NbfReadSingleParameter) +#pragma alloc_text(INIT,NbfWriteSingleParameter) +#else +#pragma alloc_text(PAGE,NbfWstrLength) +#pragma alloc_text(PAGE,NbfConfigureTransport) +#pragma alloc_text(PAGE,NbfFreeConfigurationInfo) +#pragma alloc_text(PAGE,NbfOpenParametersKey) +#pragma alloc_text(PAGE,NbfCloseParametersKey) +#pragma alloc_text(PAGE,NbfCountEntries) +#pragma alloc_text(PAGE,NbfAddBind) +#pragma alloc_text(PAGE,NbfAddExport) +#pragma alloc_text(PAGE,NbfReadLinkageInformation) +#pragma alloc_text(PAGE,NbfReadSingleParameter) +#pragma alloc_text(PAGE,NbfWriteSingleParameter) +#endif +#endif + +UINT +NbfWstrLength( + IN PWSTR Wstr + ) +{ + UINT Length = 0; + while (*Wstr++) { + Length += sizeof(WCHAR); + } + return Length; +} + +#define InsertAdapter(ConfigurationInfo, Subscript, Name) \ +{ \ + PWSTR _S; \ + PWSTR _N = (Name); \ + UINT _L = NbfWstrLength(_N)+sizeof(WCHAR); \ + _S = (PWSTR)ExAllocatePoolWithTag(NonPagedPool, _L, ' FBN'); \ + if (_S != NULL) { \ + RtlCopyMemory(_S, _N, _L); \ + RtlInitUnicodeString (&(ConfigurationInfo)->Names[Subscript], _S); \ + } \ +} + +#define InsertDevice(ConfigurationInfo, Subscript, Name) \ +{ \ + PWSTR _S; \ + PWSTR _N = (Name); \ + UINT _L = NbfWstrLength(_N)+sizeof(WCHAR); \ + _S = (PWSTR)ExAllocatePoolWithTag(NonPagedPool, _L, ' FBN'); \ + if (_S != NULL) { \ + RtlCopyMemory(_S, _N, _L); \ + RtlInitUnicodeString (&(ConfigurationInfo)->Names[(ConfigurationInfo)->DevicesOffset+Subscript], _S); \ + } \ +} + + +#define RemoveAdapter(ConfigurationInfo, Subscript) \ + ExFreePool ((ConfigurationInfo)->Names[Subscript].Buffer) + +#define RemoveDevice(ConfigurationInfo, Subscript) \ + ExFreePool ((ConfigurationInfo)->Names[(ConfigurationInfo)->DevicesOffset+Subscript].Buffer) + + + +// +// These strings are used in various places by the registry. +// + +#define DECLARE_STRING(_str_) WCHAR Str ## _str_[] = L#_str_ + + +#define READ_HIDDEN_CONFIG(_Field) \ +{ \ + ConfigurationInfo->_Field = \ + NbfReadSingleParameter( \ + ParametersHandle, \ + Str ## _Field, \ + ConfigurationInfo->_Field); \ +} + +#define WRITE_HIDDEN_CONFIG(_Field) \ +{ \ + NbfWriteSingleParameter( \ + ParametersHandle, \ + Str ## _Field, \ + ConfigurationInfo->_Field); \ +} + + + +NTSTATUS +NbfConfigureTransport ( + IN PUNICODE_STRING RegistryPath, + IN PCONFIG_DATA * ConfigurationInfoPtr + ) +/*++ + +Routine Description: + + This routine is called by NBF to get information from the configuration + management routines. We read the registry, starting at RegistryPath, + to get the parameters. If they don't exist, we use the defaults + set in nbfcnfg.h file. + +Arguments: + + RegistryPath - The name of NBF's node in the registry. + + ConfigurationInfoPtr - A pointer to the configuration information structure. + +Return Value: + + Status - STATUS_SUCCESS if everything OK, STATUS_INSUFFICIENT_RESOURCES + otherwise. + +--*/ +{ + + NTSTATUS OpenStatus; + HANDLE ParametersHandle; + HANDLE NbfConfigHandle; + NTSTATUS Status; + ULONG Disposition; + PWSTR RegistryPathBuffer; + OBJECT_ATTRIBUTES TmpObjectAttributes; + PCONFIG_DATA ConfigurationInfo; + + DECLARE_STRING(InitRequests); + DECLARE_STRING(InitLinks); + DECLARE_STRING(InitConnections); + DECLARE_STRING(InitAddressFiles); + DECLARE_STRING(InitAddresses); + + DECLARE_STRING(MaxRequests); + DECLARE_STRING(MaxLinks); + DECLARE_STRING(MaxConnections); + DECLARE_STRING(MaxAddressFiles); + DECLARE_STRING(MaxAddresses); + + DECLARE_STRING(InitPackets); + DECLARE_STRING(InitReceivePackets); + DECLARE_STRING(InitReceiveBuffers); + DECLARE_STRING(InitUIFrames); + + DECLARE_STRING(SendPacketPoolSize); + DECLARE_STRING(ReceivePacketPoolSize); + DECLARE_STRING(MaxMemoryUsage); + + DECLARE_STRING(DefaultT1Timeout); + DECLARE_STRING(DefaultT2Timeout); + DECLARE_STRING(DefaultTiTimeout); + DECLARE_STRING(LlcRetries); + DECLARE_STRING(LlcMaxWindowSize); + DECLARE_STRING(MaximumIncomingFrames); + + DECLARE_STRING(NameQueryRetries); + DECLARE_STRING(NameQueryTimeout); + DECLARE_STRING(AddNameQueryRetries); + DECLARE_STRING(AddNameQueryTimeout); + DECLARE_STRING(GeneralRetries); + DECLARE_STRING(GeneralTimeout); + DECLARE_STRING(WanNameQueryRetries); + + DECLARE_STRING(UseDixOverEthernet); + DECLARE_STRING(QueryWithoutSourceRouting); + DECLARE_STRING(AllRoutesNameRecognized); + DECLARE_STRING(MinimumSendWindowLimit); + + // + // Open the registry. + // + + InitializeObjectAttributes( + &TmpObjectAttributes, + RegistryPath, // name + OBJ_CASE_INSENSITIVE, // attributes + NULL, // root + NULL // security descriptor + ); + + Status = ZwCreateKey( + &NbfConfigHandle, + KEY_WRITE, + &TmpObjectAttributes, + 0, // title index + NULL, // class + 0, // create options + &Disposition); // disposition + + if (!NT_SUCCESS(Status)) { + NbfPrint1("NBF: Could not open/create NBF key: %lx\n", Status); + return Status; + } + + IF_NBFDBG (NBF_DEBUG_REGISTRY) { + NbfPrint2("%s NBF key: %lx\n", + (Disposition == REG_CREATED_NEW_KEY) ? "created" : "opened", + NbfConfigHandle); + } + + + OpenStatus = NbfOpenParametersKey (NbfConfigHandle, &ParametersHandle); + + if (OpenStatus != STATUS_SUCCESS) { + return OpenStatus; + } + + // + // Read in the NDIS binding information (if none is present + // the array will be filled with all known drivers). + // + // NbfReadLinkageInformation expects a null-terminated path, + // so we have to create one from the UNICODE_STRING. + // + + RegistryPathBuffer = (PWSTR)ExAllocatePoolWithTag( + NonPagedPool, + RegistryPath->Length + sizeof(WCHAR), + ' FBN'); + if (RegistryPathBuffer == NULL) { + NbfCloseParametersKey (ParametersHandle); + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlCopyMemory (RegistryPathBuffer, RegistryPath->Buffer, RegistryPath->Length); + *(PWCHAR)(((PUCHAR)RegistryPathBuffer)+RegistryPath->Length) = (WCHAR)'\0'; + + NbfReadLinkageInformation (RegistryPathBuffer, ConfigurationInfoPtr); + + if (*ConfigurationInfoPtr == NULL) { + ExFreePool (RegistryPathBuffer); + NbfCloseParametersKey (ParametersHandle); + return STATUS_INSUFFICIENT_RESOURCES; + } + ConfigurationInfo = *ConfigurationInfoPtr; + + + // + // Configure the initial values for some NBF resources. + // + + ConfigurationInfo->InitRequests = 1; + ConfigurationInfo->InitLinks = 2; + ConfigurationInfo->InitConnections = 2; + ConfigurationInfo->InitAddressFiles = 0; + ConfigurationInfo->InitAddresses = 0; + + // + // These are the initial values; remember that the + // resources above also allocate some of these each + // time they are allocated (shown in the comment). + // + + ConfigurationInfo->InitPackets = 30; // + link + 2*conn + ConfigurationInfo->InitReceivePackets = 10; // + link + addr + ConfigurationInfo->InitReceiveBuffers = 5; // + addr + ConfigurationInfo->InitUIFrames = 5; // + addr + conn + + // + // Set the size of the packet pools and the total + // allocateable by NBF. + // + + ConfigurationInfo->SendPacketPoolSize = 100; + ConfigurationInfo->ReceivePacketPoolSize = 30; + ConfigurationInfo->MaxMemoryUsage = 0; // no limit + + + // + // Now initialize the timeout etc. values. + // + + ConfigurationInfo->DefaultT1Timeout = DLC_DEFAULT_T1; + ConfigurationInfo->DefaultT2Timeout = DLC_DEFAULT_T2; + ConfigurationInfo->DefaultTiTimeout = DLC_DEFAULT_TI; + ConfigurationInfo->LlcRetries = DLC_RETRIES; + ConfigurationInfo->LlcMaxWindowSize = DLC_WINDOW_LIMIT; + ConfigurationInfo->MaximumIncomingFrames = 4; + ConfigurationInfo->NameQueryRetries = NAME_QUERY_RETRIES; + ConfigurationInfo->NameQueryTimeout = NAME_QUERY_TIMEOUT; + ConfigurationInfo->AddNameQueryRetries = ADD_NAME_QUERY_RETRIES; + ConfigurationInfo->AddNameQueryTimeout = ADD_NAME_QUERY_TIMEOUT; + ConfigurationInfo->GeneralRetries = NAME_QUERY_RETRIES; + ConfigurationInfo->GeneralTimeout = NAME_QUERY_TIMEOUT; + ConfigurationInfo->WanNameQueryRetries = WAN_NAME_QUERY_RETRIES; + + ConfigurationInfo->UseDixOverEthernet = 0; + ConfigurationInfo->QueryWithoutSourceRouting = 0; + ConfigurationInfo->AllRoutesNameRecognized = 0; + ConfigurationInfo->MinimumSendWindowLimit = 2; + + + // + // Now read the optional "hidden" parameters; if these do + // not exist then the current values are used. Note that + // the current values will be 0 unless they have been + // explicitly initialized above. + // + // NOTE: These macros expect "ConfigurationInfo" and + // "ParametersHandle" to exist when they are expanded. + // + + READ_HIDDEN_CONFIG (InitRequests); + READ_HIDDEN_CONFIG (InitLinks); + READ_HIDDEN_CONFIG (InitConnections); + READ_HIDDEN_CONFIG (InitAddressFiles); + READ_HIDDEN_CONFIG (InitAddresses); + + READ_HIDDEN_CONFIG (MaxRequests); + READ_HIDDEN_CONFIG (MaxLinks); + READ_HIDDEN_CONFIG (MaxConnections); + READ_HIDDEN_CONFIG (MaxAddressFiles); + READ_HIDDEN_CONFIG (MaxAddresses); + + READ_HIDDEN_CONFIG (InitPackets); + READ_HIDDEN_CONFIG (InitReceivePackets); + READ_HIDDEN_CONFIG (InitReceiveBuffers); + READ_HIDDEN_CONFIG (InitUIFrames); + + READ_HIDDEN_CONFIG (SendPacketPoolSize); + READ_HIDDEN_CONFIG (ReceivePacketPoolSize); + READ_HIDDEN_CONFIG (MaxMemoryUsage); + + READ_HIDDEN_CONFIG (DefaultT1Timeout); + READ_HIDDEN_CONFIG (DefaultT2Timeout); + READ_HIDDEN_CONFIG (DefaultTiTimeout); + READ_HIDDEN_CONFIG (LlcRetries); + READ_HIDDEN_CONFIG (LlcMaxWindowSize); + READ_HIDDEN_CONFIG (MaximumIncomingFrames); + + READ_HIDDEN_CONFIG (NameQueryRetries); + READ_HIDDEN_CONFIG (NameQueryTimeout); + READ_HIDDEN_CONFIG (AddNameQueryRetries); + READ_HIDDEN_CONFIG (AddNameQueryTimeout); + READ_HIDDEN_CONFIG (GeneralRetries); + READ_HIDDEN_CONFIG (GeneralTimeout); + READ_HIDDEN_CONFIG (WanNameQueryRetries); + + READ_HIDDEN_CONFIG (UseDixOverEthernet); + READ_HIDDEN_CONFIG (QueryWithoutSourceRouting); + READ_HIDDEN_CONFIG (AllRoutesNameRecognized); + READ_HIDDEN_CONFIG (MinimumSendWindowLimit); + + + // + // Print out some config info, to make sure it is read right. + // + + IF_NBFDBG (NBF_DEBUG_REGISTRY) { + NbfPrint2("Links: init %d, max %d\n", + ConfigurationInfo->InitLinks, + ConfigurationInfo->MaxLinks); + NbfPrint3("Timeouts (NBF ticks): T1 %d, T2 %d, Ti %d\n", + ConfigurationInfo->DefaultT1Timeout / SHORT_TIMER_DELTA, + ConfigurationInfo->DefaultT2Timeout / SHORT_TIMER_DELTA, + ConfigurationInfo->DefaultTiTimeout / LONG_TIMER_DELTA); + NbfPrint2("Pools: send %d, receive %d\n", + ConfigurationInfo->SendPacketPoolSize, + ConfigurationInfo->ReceivePacketPoolSize); + NbfPrint1("Max mem %d\n", + ConfigurationInfo->MaxMemoryUsage); + NbfPrint2("NQRetries %d, NQTimeout %d\n", + ConfigurationInfo->NameQueryRetries, + ConfigurationInfo->NameQueryTimeout / SHORT_TIMER_DELTA); + } + + // + // Save the "hidden" parameters, these may not exist in + // the registry. + // + // NOTE: These macros expect "ConfigurationInfo" and + // "ParametersHandle" to exist when they are expanded. + // + + // + // 5/22/92 - don't write the parameters that are set + // based on Size, since otherwise these will overwrite + // those values since hidden parameters are set up + // after the Size-based configuration is done. + // + + WRITE_HIDDEN_CONFIG (MaxRequests); + WRITE_HIDDEN_CONFIG (MaxLinks); + WRITE_HIDDEN_CONFIG (MaxConnections); + WRITE_HIDDEN_CONFIG (MaxAddressFiles); + WRITE_HIDDEN_CONFIG (MaxAddresses); + + WRITE_HIDDEN_CONFIG (DefaultT1Timeout); + WRITE_HIDDEN_CONFIG (DefaultT2Timeout); + WRITE_HIDDEN_CONFIG (DefaultTiTimeout); + WRITE_HIDDEN_CONFIG (LlcRetries); + WRITE_HIDDEN_CONFIG (LlcMaxWindowSize); + WRITE_HIDDEN_CONFIG (MaximumIncomingFrames); + + WRITE_HIDDEN_CONFIG (NameQueryRetries); + WRITE_HIDDEN_CONFIG (NameQueryTimeout); + WRITE_HIDDEN_CONFIG (AddNameQueryRetries); + WRITE_HIDDEN_CONFIG (AddNameQueryTimeout); + WRITE_HIDDEN_CONFIG (GeneralRetries); + WRITE_HIDDEN_CONFIG (GeneralTimeout); + WRITE_HIDDEN_CONFIG (WanNameQueryRetries); + + WRITE_HIDDEN_CONFIG (UseDixOverEthernet); + WRITE_HIDDEN_CONFIG (QueryWithoutSourceRouting); + WRITE_HIDDEN_CONFIG (AllRoutesNameRecognized); + + // ZwFlushKey (ParametersHandle); + + ExFreePool (RegistryPathBuffer); + NbfCloseParametersKey (ParametersHandle); + ZwClose (NbfConfigHandle); + + return STATUS_SUCCESS; + +} /* NbfConfigureTransport */ + + +VOID +NbfFreeConfigurationInfo ( + IN PCONFIG_DATA ConfigurationInfo + ) + +/*++ + +Routine Description: + + This routine is called by NBF to get free any storage that was allocated + by NbfConfigureTransport in producing the specified CONFIG_DATA structure. + +Arguments: + + ConfigurationInfo - A pointer to the configuration information structure. + +Return Value: + + None. + +--*/ +{ + UINT i; + + for (i=0; i<ConfigurationInfo->NumAdapters; i++) { + RemoveAdapter (ConfigurationInfo, i); + RemoveDevice (ConfigurationInfo, i); + } + ExFreePool (ConfigurationInfo); + +} /* NbfFreeConfigurationInfo */ + + +NTSTATUS +NbfOpenParametersKey( + IN HANDLE NbfConfigHandle, + OUT PHANDLE ParametersHandle + ) + +/*++ + +Routine Description: + + This routine is called by NBF to open the NBF "Parameters" key. + +Arguments: + + ParametersHandle - Returns the handle used to read parameters. + +Return Value: + + The status of the request. + +--*/ +{ + + NTSTATUS Status; + HANDLE ParamHandle; + PWSTR ParametersString = L"Parameters"; + UNICODE_STRING ParametersKeyName; + OBJECT_ATTRIBUTES TmpObjectAttributes; + + // + // Open the NBF parameters key. + // + + RtlInitUnicodeString (&ParametersKeyName, ParametersString); + + InitializeObjectAttributes( + &TmpObjectAttributes, + &ParametersKeyName, // name + OBJ_CASE_INSENSITIVE, // attributes + NbfConfigHandle, // root + NULL // security descriptor + ); + + + Status = ZwOpenKey( + &ParamHandle, + KEY_READ, + &TmpObjectAttributes); + + if (!NT_SUCCESS(Status)) { + + NbfPrint1("Could not open parameters key: %lx\n", Status); + return Status; + + } + + IF_NBFDBG (NBF_DEBUG_REGISTRY) { + NbfPrint1("Opened parameters key: %lx\n", ParamHandle); + } + + + *ParametersHandle = ParamHandle; + + + // + // All keys successfully opened or created. + // + + return STATUS_SUCCESS; + +} /* NbfOpenParametersKey */ + +VOID +NbfCloseParametersKey( + IN HANDLE ParametersHandle + ) + +/*++ + +Routine Description: + + This routine is called by NBF to close the "Parameters" key. + It closes the handles passed in and does any other work needed. + +Arguments: + + ParametersHandle - The handle used to read other parameters. + +Return Value: + + None. + +--*/ + +{ + + ZwClose (ParametersHandle); + +} /* NbfCloseParametersKey */ + + +NTSTATUS +NbfCountEntries( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called with the "Bind" and "Export" multi-strings. + It counts the number of name entries required in the + CONFIGURATION_DATA structure and then allocates it. + +Arguments: + + ValueName - The name of the value ("Bind" or "Export" -- ignored). + + ValueType - The type of the value (REG_MULTI_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData (ignored). + + Context - A pointer to a pointer to the ConfigurationInfo structure. + When the "Export" callback is made this is filled in + with the allocate structure. + + EntryContext - A pointer to a counter holding the total number + of name entries required. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + ULONG StringCount; + PWCHAR ValuePointer = (PWCHAR)ValueData; + PCONFIG_DATA * ConfigurationInfo = (PCONFIG_DATA *)Context; + PULONG TotalCount = ((PULONG)EntryContext); + ULONG OldTotalCount = *TotalCount; + +#if DBG + ASSERT (ValueType == REG_MULTI_SZ); +#else + UNREFERENCED_PARAMETER(ValueType); +#endif + + // + // Count the number of strings in the multi-string; first + // check that it is NULL-terminated to make the rest + // easier. + // + + if ((ValueLength < 2) || + (ValuePointer[(ValueLength/2)-1] != (WCHAR)'\0')) { + return STATUS_INVALID_PARAMETER; + } + + StringCount = 0; + while (*ValuePointer != (WCHAR)'\0') { + while (*ValuePointer != (WCHAR)'\0') { + ++ValuePointer; + } + ++StringCount; + ++ValuePointer; + if ((ULONG)((PUCHAR)ValuePointer - (PUCHAR)ValueData) >= ValueLength) { + break; + } + } + + (*TotalCount) += StringCount; + + if (*ValueName == (WCHAR)'E') { + + // + // This is "Export", allocate the config data structure. + // + + *ConfigurationInfo = ExAllocatePoolWithTag( + NonPagedPool, + sizeof (CONFIG_DATA) + + ((*TotalCount-1) * sizeof(NDIS_STRING)), + ' FBN'); + + if (*ConfigurationInfo == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory( + *ConfigurationInfo, + sizeof(CONFIG_DATA) + ((*TotalCount-1) * sizeof(NDIS_STRING))); + + (*ConfigurationInfo)->DevicesOffset = OldTotalCount; + + } + + return STATUS_SUCCESS; + +} /* NbfCountEntries */ + + +NTSTATUS +NbfAddBind( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each piece of the "Bind" multi-string and + saves the information in a ConfigurationInfo structure. + +Arguments: + + ValueName - The name of the value ("Bind" -- ignored). + + ValueType - The type of the value (REG_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData (ignored). + + Context - A pointer to the ConfigurationInfo structure. + + EntryContext - A pointer to a count of binds that is incremented. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG_DATA ConfigurationInfo = *(PCONFIG_DATA *)Context; + PULONG CurBindNum = ((PULONG)EntryContext); + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + UNREFERENCED_PARAMETER(ValueLength); + + InsertAdapter( + ConfigurationInfo, + *CurBindNum, + (PWSTR)(ValueData)); + + ++(*CurBindNum); + + return STATUS_SUCCESS; + +} /* NbfAddBind */ + + +NTSTATUS +NbfAddExport( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ) + +/*++ + +Routine Description: + + This routine is a callback routine for RtlQueryRegistryValues + It is called for each piece of the "Export" multi-string and + saves the information in a ConfigurationInfo structure. + +Arguments: + + ValueName - The name of the value ("Export" -- ignored). + + ValueType - The type of the value (REG_SZ -- ignored). + + ValueData - The null-terminated data for the value. + + ValueLength - The length of ValueData (ignored). + + Context - A pointer to the ConfigurationInfo structure. + + EntryContext - A pointer to a count of exports that is incremented. + +Return Value: + + STATUS_SUCCESS + +--*/ + +{ + PCONFIG_DATA ConfigurationInfo = *(PCONFIG_DATA *)Context; + PULONG CurExportNum = ((PULONG)EntryContext); + + UNREFERENCED_PARAMETER(ValueName); + UNREFERENCED_PARAMETER(ValueType); + UNREFERENCED_PARAMETER(ValueLength); + + InsertDevice( + ConfigurationInfo, + *CurExportNum, + (PWSTR)(ValueData)); + + ++(*CurExportNum); + + return STATUS_SUCCESS; + +} /* NbfAddExport */ + + +VOID +NbfReadLinkageInformation( + IN PWSTR RegistryPathBuffer, + IN PCONFIG_DATA * ConfigurationInfo + ) + +/*++ + +Routine Description: + + This routine is called by NBF to read its linkage information + from the registry. If there is none present, then ConfigData + is filled with a list of all the adapters that are known + to NBF. + +Arguments: + + RegistryPathBuffer - The null-terminated root of the NBF registry tree. + + ConfigurationInfo - Returns NBF's current configuration. + +Return Value: + + None. + +--*/ + +{ + + UINT ConfigBindings; + UINT NameCount = 0; + NTSTATUS Status; + RTL_QUERY_REGISTRY_TABLE QueryTable[6]; + PWSTR Subkey = L"Linkage"; + PWSTR Bind = L"Bind"; + PWSTR Export = L"Export"; + ULONG BindCount, ExportCount; + UINT i; + + + // + // Set up QueryTable to do the following: + // + + // + // 1) Switch to the Linkage key below NBF + // + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Subkey; + + // + // 2) Call NbfCountEntries for the "Bind" multi-string + // + + QueryTable[1].QueryRoutine = NbfCountEntries; + QueryTable[1].Flags = RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_NOEXPAND; + QueryTable[1].Name = Bind; + QueryTable[1].EntryContext = (PVOID)&NameCount; + QueryTable[1].DefaultType = REG_NONE; + + // + // 3) Call NbfCountEntries for the "Export" multi-string + // + + QueryTable[2].QueryRoutine = NbfCountEntries; + QueryTable[2].Flags = RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_NOEXPAND; + QueryTable[2].Name = Export; + QueryTable[2].EntryContext = (PVOID)&NameCount; + QueryTable[2].DefaultType = REG_NONE; + + // + // 4) Call NbfAddBind for each string in "Bind" + // + + QueryTable[3].QueryRoutine = NbfAddBind; + QueryTable[3].Flags = 0; + QueryTable[3].Name = Bind; + QueryTable[3].EntryContext = (PVOID)&BindCount; + QueryTable[3].DefaultType = REG_NONE; + + // + // 5) Call NbfAddExport for each string in "Export" + // + + QueryTable[4].QueryRoutine = NbfAddExport; + QueryTable[4].Flags = 0; + QueryTable[4].Name = Export; + QueryTable[4].EntryContext = (PVOID)&ExportCount; + QueryTable[4].DefaultType = REG_NONE; + + // + // 6) Stop + // + + QueryTable[5].QueryRoutine = NULL; + QueryTable[5].Flags = 0; + QueryTable[5].Name = NULL; + + + BindCount = 0; + ExportCount = 0; + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + RegistryPathBuffer, + QueryTable, + (PVOID)ConfigurationInfo, + NULL); + + if (Status != STATUS_SUCCESS) { + return; + } + + // + // Make sure that BindCount and ExportCount match, if not + // remove the extras. + // + + if (BindCount < ExportCount) { + + for (i=BindCount; i<ExportCount; i++) { + RemoveDevice (*ConfigurationInfo, i); + } + ConfigBindings = BindCount; + + } else if (ExportCount < BindCount) { + + for (i=ExportCount; i<BindCount; i++) { + RemoveAdapter (*ConfigurationInfo, i); + } + ConfigBindings = ExportCount; + + } else { + + ConfigBindings = BindCount; // which is equal to ExportCount + + } + + (*ConfigurationInfo)->NumAdapters = ConfigBindings; + +} /* NbfReadLinkageInformation */ + + +ULONG +NbfReadSingleParameter( + IN HANDLE ParametersHandle, + IN PWCHAR ValueName, + IN ULONG DefaultValue + ) + +/*++ + +Routine Description: + + This routine is called by NBF to read a single parameter + from the registry. If the parameter is found it is stored + in Data. + +Arguments: + + ParametersHandle - A pointer to the open registry. + + ValueName - The name of the value to search for. + + DefaultValue - The default value. + +Return Value: + + The value to use; will be the default if the value is not + found or is not in the correct range. + +--*/ + +{ + ULONG InformationBuffer[32]; // declare ULONG to get it aligned + PKEY_VALUE_FULL_INFORMATION Information = + (PKEY_VALUE_FULL_INFORMATION)InformationBuffer; + UNICODE_STRING ValueKeyName; + ULONG InformationLength; + ULONG ReturnValue; + NTSTATUS Status; + + RtlInitUnicodeString (&ValueKeyName, ValueName); + + Status = ZwQueryValueKey( + ParametersHandle, + &ValueKeyName, + KeyValueFullInformation, + (PVOID)Information, + sizeof (InformationBuffer), + &InformationLength); + + if ((Status == STATUS_SUCCESS) && + (Information->DataLength == sizeof(ULONG))) { + + RtlCopyMemory( + (PVOID)&ReturnValue, + ((PUCHAR)Information) + Information->DataOffset, + sizeof(ULONG)); + + if (ReturnValue < 0) { + + ReturnValue = DefaultValue; + + } + + } else { + + ReturnValue = DefaultValue; + + } + + return ReturnValue; + +} /* NbfReadSingleParameter */ + + +VOID +NbfWriteSingleParameter( + IN HANDLE ParametersHandle, + IN PWCHAR ValueName, + IN ULONG ValueData + ) + +/*++ + +Routine Description: + + This routine is called by NBF to write a single parameter + from the registry. + +Arguments: + + ParametersHandle - A pointer to the open registry. + + ValueName - The name of the value to store. + + ValueData - The data to store at the value. + +Return Value: + + None. + +--*/ + +{ + UNICODE_STRING ValueKeyName; + NTSTATUS Status; + ULONG TmpValueData = ValueData; + + RtlInitUnicodeString (&ValueKeyName, ValueName); + + Status = ZwSetValueKey( + ParametersHandle, + &ValueKeyName, + 0, + REG_DWORD, + (PVOID)&TmpValueData, + sizeof(ULONG)); + + if (!NT_SUCCESS(Status)) { + NbfPrint1("NBF: Could not write dword key: %lx\n", Status); + } + +} /* NbfWriteSingleParameter */ + diff --git a/private/ntos/tdi/nbf/nbfcnfg.h b/private/ntos/tdi/nbf/nbfcnfg.h new file mode 100644 index 000000000..ca8c382f3 --- /dev/null +++ b/private/ntos/tdi/nbf/nbfcnfg.h @@ -0,0 +1,102 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + nbfcnfg.h + +Abstract: + + Private include file for the NBF (NetBIOS Frames Protocol) transport. This + file defines all constants and structures necessary for support of + the dynamic configuration of NBF. Note that this file will be replaced + by calls to the configuration manager over time. + +Author: + + David Beaver (dbeaver) 13-Feb-1991 + +Revision History: + +--*/ + +#ifndef _NBFCONFIG_ +#define _NBFCONFIG_ + +// +// Define the devices we support; this is in leiu of a real configuration +// manager. +// + +#define NBF_SUPPORTED_ADAPTERS 10 + +#define NE3200_ADAPTER_NAME L"\\Device\\NE320001" +#define ELNKII_ADAPTER_NAME L"\\Device\\Elnkii" // adapter we will talk to +#define ELNKMC_ADAPTER_NAME L"\\Device\\Elnkmc01" +#define ELNK16_ADAPTER_NAME L"\\Device\\Elnk1601" +#define SONIC_ADAPTER_NAME L"\\Device\\Sonic01" +#define LANCE_ADAPTER_NAME L"\\Device\\Lance01" +#define PC586_ADAPTER_NAME L"\\Device\\Pc586" +#define IBMTOK_ADAPTER_NAME L"\\Device\\Ibmtok01" +#define PROTEON_ADAPTER_NAME L"\\Device\\Proteon01" +#define WDLAN_ADAPTER_NAME L"\\Device\\Wdlan01" + + +// +// configuration structure. +// + +typedef struct { + + ULONG InitRequests; + ULONG InitLinks; + ULONG InitConnections; + ULONG InitAddressFiles; + ULONG InitAddresses; + ULONG MaxRequests; + ULONG MaxLinks; + ULONG MaxConnections; + ULONG MaxAddressFiles; + ULONG MaxAddresses; + ULONG InitPackets; + ULONG InitReceivePackets; + ULONG InitReceiveBuffers; + ULONG InitUIFrames; + ULONG SendPacketPoolSize; + ULONG ReceivePacketPoolSize; + ULONG MaxMemoryUsage; + ULONG DefaultT1Timeout; + ULONG DefaultT2Timeout; + ULONG DefaultTiTimeout; + ULONG LlcRetries; + ULONG LlcMaxWindowSize; + ULONG MaximumIncomingFrames; + ULONG NameQueryRetries; + ULONG NameQueryTimeout; + ULONG AddNameQueryRetries; + ULONG AddNameQueryTimeout; + ULONG GeneralRetries; + ULONG GeneralTimeout; + ULONG WanNameQueryRetries; + + ULONG UseDixOverEthernet; + ULONG QueryWithoutSourceRouting; + ULONG AllRoutesNameRecognized; + ULONG MinimumSendWindowLimit; + + // + // Names contains NumAdapters pairs of NDIS adapter names (which + // nbf binds to) and device names (which nbf exports). The nth + // adapter name is in location n and the device name is in + // DevicesOffset+n (DevicesOffset may be different from NumAdapters + // if the registry Bind and Export strings are different sizes). + // + + ULONG NumAdapters; + ULONG DevicesOffset; + NDIS_STRING Names[1]; + +} CONFIG_DATA, *PCONFIG_DATA; + +#endif diff --git a/private/ntos/tdi/nbf/nbfconst.h b/private/ntos/tdi/nbf/nbfconst.h new file mode 100644 index 000000000..9ca5f4072 --- /dev/null +++ b/private/ntos/tdi/nbf/nbfconst.h @@ -0,0 +1,436 @@ + +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + nbfconst.h + +Abstract: + + This header file defines manifest constants for the NT NBF transport + provider. It is included by nbf.h. + +Author: + + Stephen E. Jones (stevej) 25-Oct-1989 + +Revision History: + + David Beaver (dbeaver) 24-Sep-1990 + Remove pc586- and PDI-specific support. Add NDIS support. Note + changes to be made here if MAC dependence of NDIS changes. (search + for (PDI) + +--*/ + +#ifndef _NBFCONST_ +#define _NBFCONST_ + + +// +// DEBUGGING SUPPORT. DBG is a macro that is turned on at compile time +// to enable debugging code in the system. If this is turned on, then +// you can use the IF_NBFDBG(flags) macro in the NBF code to selectively +// enable a piece of debugging code in the transport. This macro tests +// NbfDebug, a global ULONG defined in NBFDRVR.C. +// + +#if DBG + +#define NBF_DEBUG_SENDENG 0x00000001 // sendeng.c debugging. +#define NBF_DEBUG_RCVENG 0x00000002 // rcveng.c debugging. +#define NBF_DEBUG_IFRAMES 0x00000004 // displays sent/rec'd iframes. +#define NBF_DEBUG_UFRAMES 0x00000008 // displays sent/rec'd uframes. +#define NBF_DEBUG_DLCFRAMES 0x00000010 // displays sent/rec'd dlc frames. +#define NBF_DEBUG_ADDRESS 0x00000020 // address.c debugging. +#define NBF_DEBUG_CONNECT 0x00000040 // connect.c debugging. +#define NBF_DEBUG_CONNOBJ 0x00000080 // connobj.c debugging. +#define NBF_DEBUG_DEVCTX 0x00000100 // devctx.c debugging. +#define NBF_DEBUG_DLC 0x00000200 // dlc.c data link engine debugging. +#define NBF_DEBUG_INFO 0x00000400 // info.c debugging +#define NBF_DEBUG_EVENT 0x00000800 // event.c debugging. +#define NBF_DEBUG_FRAMECON 0x00001000 // framecon.c debugging. +#define NBF_DEBUG_FRAMESND 0x00002000 // framesnd.c debugging. +#define NBF_DEBUG_DYNAMIC 0x00004000 // dynamic allocation debugging. +#define NBF_DEBUG_LINK 0x00008000 // link.c debugging. +#define NBF_DEBUG_RESOURCE 0x00010000 // resource allocation debugging. +#define NBF_DEBUG_DISPATCH 0x00020000 // IRP request dispatching. +#define NBF_DEBUG_PACKET 0x00040000 // packet.c debugging. +#define NBF_DEBUG_REQUEST 0x00080000 // request.c debugging. +#define NBF_DEBUG_TIMER 0x00100000 // timer.c debugging. +#define NBF_DEBUG_DLCRETRANSMIT 0x00200000 // DLC REJ debugging. +#define NBF_DEBUG_REGISTRY 0x00400000 // registry access. +#define NBF_DEBUG_NDIS 0x00800000 // NDIS related information +#define NBF_DEBUG_LINKTREE 0x01000000 // Link splay tree debugging +#define NBF_DEBUG_TEARDOWN 0x02000000 // link/connection teardown info +#define NBF_DEBUG_REFCOUNTS 0x04000000 // link/connection ref/deref information +#define NBF_DEBUG_IRP 0x08000000 // irp completion debugging +#define NBF_DEBUG_DATAGRAMS 0x10000000 // datagram send/receive +#define NBF_DEBUG_SETUP 0x20000000 // debug session setup +#define NBF_DEBUG_CONFIG 0x40000000 // debug configuration + +// +// past here are debug things that are really frequent; don't use them +// unless you want LOTS of output +// +#define NBF_DEBUG_TIMERDPC 0x10000000 // the timer DPC +#define NBF_DEBUG_PKTCONTENTS 0x20000000 // dump packet contents in dbg +#define NBF_DEBUG_TIMERCODE 0x40000000 // enable check code in timer +#define NBF_DEBUG_TRACKTDI 0x80000000 // store tdi info when set + + +extern ULONG NbfDebug; // in NBFDRVR.C. +extern BOOLEAN NbfDisconnectDebug; // in NBFDRVR.C. + +#define TRACK_TDI_LIMIT 25 +#define TRACK_TDI_CAPTURE 36 // chosen to make debug line up nice +typedef struct { + PVOID Request; + PIRP Irp; + PVOID Connection; + UCHAR Contents[TRACK_TDI_CAPTURE]; + } NBF_SEND; + +typedef struct { + PVOID Request; + PIRP Irp; + NTSTATUS Status; + PVOID NothingYet; + } NBF_SEND_COMPLETE; + +typedef struct { + PVOID Request; + PIRP Irp; + PVOID Connection; + PVOID NothingYet; + } NBF_RECEIVE; + +typedef struct { + PVOID Request; + PIRP Irp; + NTSTATUS Status; + UCHAR Contents[TRACK_TDI_CAPTURE]; + } NBF_RECEIVE_COMPLETE; + +extern NBF_SEND NbfSends[TRACK_TDI_LIMIT+1]; +extern LONG NbfSendsNext; + +extern NBF_SEND_COMPLETE NbfCompletedSends[TRACK_TDI_LIMIT+1]; +extern LONG NbfCompletedSendsNext; + +extern NBF_RECEIVE NbfReceives[TRACK_TDI_LIMIT+1]; +extern LONG NbfReceivesNext; + +extern NBF_RECEIVE_COMPLETE NbfCompletedReceives[TRACK_TDI_LIMIT+1]; +extern LONG NbfCompletedReceivesNext; + +#endif + +// +// some convenient constants used for timing. All values are in clock ticks. +// + +#define MICROSECONDS 10 +#define MILLISECONDS 10000 // MICROSECONDS*1000 +#define SECONDS 10000000 // MILLISECONDS*1000 + + +// +// BUGBUG: temporary things used by nbf that are caused by the change-over from +// (never implimented) PDI support to NDIS support. They may be removed pending +// resolution of NDIS issues about MAC support. +// + +#define PDI_SOURCE_ROUTE 0x00000002 // source routing field is specified. +#define PDI_HARDWARE_ADDRESS 0x00000004 // hardware address field is specified. +#define PDI_TRUNCATED 0x00000001 // PSDU was truncated. +#define PDI_FRAGMENT 0x00000002 // PSDU was fragmented. +#define PDI_BROADCAST 0x00000004 // PSDU was broadcast. +#define PDI_MULTICAST 0x00000008 // PSDU was multicast/functional. +#define PDI_SOURCE_ROUTING 0x00000010 // PSDU contained source routing information. + + + +// +// MAJOR PROTOCOL IDENTIFIERS THAT CHARACTERIZE THIS DRIVER. +// + +#define NBF_DEVICE_NAME L"\\Device\\Nbf"// name of our driver. +#ifdef _PNP_POWER +#define NBF_NAME L"Nbf" // name for protocol chars. +#endif +#define DSAP_NETBIOS_OVER_LLC 0xf0 // NETBEUI always has DSAP 0xf0. +#define PSAP_LLC 0 // LLC always runs over PSAP 0. +#define MAX_SOURCE_ROUTE_LENGTH 32 // max. bytes of SR. info. +#define MAX_NETWORK_NAME_LENGTH 128 // # bytes in netname in TP_ADDRESS. +#define MAX_USER_PACKET_DATA 1500 // max. user bytes per DFM/DOL. + +#define NBF_FILE_TYPE_CONTROL (ULONG)0x4701 // file is type control + + +// +// MAJOR CONFIGURATION PARAMETERS THAT WILL BE MOVED TO THE INIT-LARGE_INTEGER +// CONFIGURATION MANAGER. +// + +#define MAX_REQUESTS 30 +#define MAX_UI_FRAMES 25 +#define MAX_SEND_PACKETS 40 +#define MAX_RECEIVE_PACKETS 30 +#define MAX_RECEIVE_BUFFERS 15 +#define MAX_LINKS 10 +#define MAX_CONNECTIONS 10 +#define MAX_ADDRESSFILES 10 +#define MAX_ADDRESSES 10 + +#define MIN_UI_FRAMES 5 // + one per address + one per connection +#define MIN_SEND_PACKETS 20 // + one per link + one per connection +#define MIN_RECEIVE_PACKETS 10 // + one per link + one per address +#define MIN_RECEIVE_BUFFERS 5 // + one per address + +#define SEND_PACKET_RESERVED_LENGTH (sizeof (SEND_PACKET_TAG)) +#define RECEIVE_PACKET_RESERVED_LENGTH (sizeof (RECEIVE_PACKET_TAG)) + + +#define ETHERNET_HEADER_SIZE 14 // BUGBUG: used for current NDIS compliance +#define ETHERNET_PACKET_SIZE 1514 + +#define MAX_DEFERRED_TRAVERSES 6 // number of times we can go through + // the deferred operations queue and + // not do anything without causing an + // error indication + + +// +// NETBIOS PROTOCOL CONSTANTS. +// + +#define NETBIOS_NAME_LENGTH 16 +#define NETBIOS_SESSION_LIMIT 254 // max # of sessions/link. (abs limit is 254) + +#define NAME_QUERY_RETRIES 3 // 2 retrie(s), plus the first one. +#define ADD_NAME_QUERY_RETRIES 3 // 1 retrie(s) plus the first one. +#define WAN_NAME_QUERY_RETRIES 5 // for NdisMediumWan only. + +#define NAME_QUERY_TIMEOUT (500*MILLISECONDS) +#define ADD_NAME_QUERY_TIMEOUT (500*MILLISECONDS) + +// +// DATA LINK PROTOCOL CONSTANTS. +// +// There are two timers, short and long. T1, T2, and the purge +// timer are run off of the short timer, Ti and the adaptive timer +// is run off of the long one. +// + +#define SHORT_TIMER_DELTA (50*MILLISECONDS) +#define LONG_TIMER_DELTA (1*SECONDS) + +#define DLC_DEFAULT_T1 (600 * MILLISECONDS) +#define DLC_DEFAULT_T2 (150 * MILLISECONDS) +#define DLC_DEFAULT_TI (30 * SECONDS) +#define DLC_RETRIES (8) // number of poll retries at LLC level. +#define DLC_RETRANSMIT_THRESHOLD (10) // up to n retransmissions acceptable. +#define DLC_WINDOW_LIMIT (10) // incr. to 127 when packet pool expanded. + +#define DLC_TIMER_ACCURACY 8 // << between BaseT1Timeout and CurrentT1Timeout + + +#define TIMER_ADAPTIVE_TICKS ((DLC_DEFAULT_T1*60)/LONG_TIMER_DELTA) // time between adaptive runs. +#define TIMER_PURGE_TICKS ((DLC_DEFAULT_T1*10)/SHORT_TIMER_DELTA) // time between adaptive purges. + + +// +// TDI defined timeouts +// + +#define TDI_TIMEOUT_SEND 60L // sends go 120 seconds +#define TDI_TIMEOUT_RECEIVE 0L // receives +#define TDI_TIMEOUT_CONNECT 60L +#define TDI_TIMEOUT_LISTEN 0L // listens default to never. +#define TDI_TIMEOUT_DISCONNECT 60L // should be 30 +#define TDI_TIMEOUT_NAME_REGISTRATION 60L + + + +// +// GENERAL CAPABILITIES STATEMENTS THAT CANNOT CHANGE. +// + +#define NBF_MAX_TSDU_SIZE 65535 // maximum TSDU size supported by NetBIOS. +#define NBF_MAX_DATAGRAM_SIZE 512 // maximum Datagram size supported by NetBIOS. +#define NBF_MAX_CONNECTION_USER_DATA 0 // no user data supported on connect. +#define NBF_SERVICE_FLAGS ( \ + TDI_SERVICE_CONNECTION_MODE | \ + TDI_SERVICE_CONNECTIONLESS_MODE | \ + TDI_SERVICE_MESSAGE_MODE | \ + TDI_SERVICE_ERROR_FREE_DELIVERY | \ + TDI_SERVICE_BROADCAST_SUPPORTED | \ + TDI_SERVICE_MULTICAST_SUPPORTED | \ + TDI_SERVICE_DELAYED_ACCEPTANCE ) + +#define NBF_MIN_LOOKAHEAD_DATA 256 // minimum guaranteed lookahead data. +#define NBF_MAX_LOOKAHEAD_DATA 256 // maximum guaranteed lookahead data. + +#define NBF_MAX_LOOPBACK_LOOKAHEAD 192 // how much is copied over for loopback + +// +// Number of TDI resources that we report. +// + +#define NBF_TDI_RESOURCES 9 + + +// +// NetBIOS name types used in the NetBIOS Frames Protocol Connectionless PDUs. +// + +#define NETBIOS_NAME_TYPE_UNIQUE 0x00 // name is unique on the network. +#define NETBIOS_NAME_TYPE_GROUP 0x01 // name is a group name. +#define NETBIOS_NAME_TYPE_EITHER 0x02 // used in NbfMatchNetbiosAddress + +// +// STATUS_QUERY request types. If the sender is following pre-2.1 protocol, +// then a simple request-response exchange is performed. Later versions +// store the "total number of names received so far" in the request type +// field, except for the first request, which must contain a 1 in this field. +// + +#define STATUS_QUERY_REQUEST_TYPE_PRE21 0x00 // request is 1.x or 2.0. +#define STATUS_QUERY_REQUEST_TYPE_FIRST 0x01 // first request, 2.1 or above. + +// +// If the LocalSessionNumber field contains a 0, then the request is really +// a FIND.NAME. If the field is non-zero, then it is the local session +// number that will be provided in all connection-oriented headers thereafter. +// + +#define NAME_QUERY_LSN_FIND_NAME 0x00 // LSN for FIND.NAME request. + +// +// NAME_RECOGNIZED LocalSessionNumber status values. If the connection +// request was rejected, then one of the following values is placed in +// the LocalSessionNumber field. NAME_RECOGNIZED can also be used as a +// FIND.NAME response, in which case the NO_LISTENS status is overloaded +// to also mean a FIND.NAME. +// + +#define NAME_RECOGNIZED_LSN_NO_LISTENS 0x00 // no listens available. +#define NAME_RECOGNIZED_LSN_FIND_NAME 0x00 // this is a find name response. +#define NAME_RECOGNIZED_LSN_NO_RESOURCE 0xff // listen available, but no resources. + +// +// STATUS_RESPONSE response types. If the sender is following pre-2.1 +// protocol, then a simple request-response exchange is performed. Later +// versions store the "total number of names sent so far" in the request +// type field. This value is cumulative, and includes the count of names +// sent with the current response, as well as from previous responses. +// + +#define STATUS_RESPONSE_PRE21 0x00 // request is 1.x or 2.0. +#define STATUS_RESPONSE_FIRST 0x01 // first request, 2.1 or above. + +// +// DATA_FIRST_MIDDLE option bitflags. +// + +#define DFM_OPTIONS_RECEIVE_CONTINUE 0x01 // RECEIVE_CONTINUE requested. +#define DFM_OPTIONS_NO_ACK 0x02 // no DATA_ACK frame expected. +#define DFM_OPTIONS_RESYNCH 0x04 // set resynch indicator/this frame. +#define DFM_OPTIONS_ACK_INCLUDED 0x08 // piggyback ack included. + +// +// DATA_ONLY_LAST option bitflags. +// + +#define DOL_OPTIONS_RESYNCH 0x01 // set resynch indicator/this frame. +#define DOL_OPTIONS_NO_ACK 0x02 // no DATA_ACK frame expected. +#define DOL_OPTIONS_ACK_W_DATA_ALLOWED 0x04 // piggyback ack allowed. +#define DOL_OPTIONS_ACK_INCLUDED 0x08 // piggyback ack included. + +// +// SESSION_CONFIRM option bitflags. +// + +#define SESSION_CONFIRM_OPTIONS_20 0x01 // set if NETBIOS 2.0 or above. +#define SESSION_CONFIRM_NO_ACK 0x80 // set if NO.ACK protocol supported. + +// +// SESSION_END reason codes. +// + +#define SESSION_END_REASON_HANGUP 0x0000 // normal termination via HANGUP. +#define SESSION_END_REASON_ABEND 0x0001 // abnormal session termination. + +// +// SESSION_INITIALIZE option bitflags. +// + +#define SESSION_INIT_OPTIONS_20 0x01 // set if NETBIOS 2.0 or above. +#define SESSION_INIT_OPTIONS_LF 0x0E // Maximum largest frame value +#define SESSION_INIT_NO_ACK 0x80 // set if NO.ACK protocol supported. + +// +// NO_RECEIVE option bitflags. +// + +#define NO_RECEIVE_PARTIAL_NO_ACK 0x02 // NO.ACK data partially received. + +// +// Resource IDs for query and error logging. +// + +#define LINK_RESOURCE_ID 11 +#define ADDRESS_RESOURCE_ID 12 +#define ADDRESS_FILE_RESOURCE_ID 13 +#define CONNECTION_RESOURCE_ID 14 +#define REQUEST_RESOURCE_ID 15 + +#define UI_FRAME_RESOURCE_ID 21 +#define PACKET_RESOURCE_ID 22 +#define RECEIVE_PACKET_RESOURCE_ID 23 +#define RECEIVE_BUFFER_RESOURCE_ID 24 + + +// +// memory management additions +// + +// +// Fake IOCTLs used for kernel mode testing. +// + +#define IOCTL_NBF_BASE FILE_DEVICE_TRANSPORT + +#define _NBF_CONTROL_CODE(request,method) \ + ((IOCTL_NBF_BASE)<<16 | (request<<2) | method) + +#define IOCTL_TDI_SEND_TEST _NBF_CONTROL_CODE(26,0) +#define IOCTL_TDI_RECEIVE_TEST _NBF_CONTROL_CODE(27,0) +#define IOCTL_TDI_SERVER_TEST _NBF_CONTROL_CODE(28,0) + +// +// More debugging stuff +// + +#define NBF_REQUEST_SIGNATURE ((CSHORT)0x4702) +#define NBF_LINK_SIGNATURE ((CSHORT)0x4703) +#define NBF_CONNECTION_SIGNATURE ((CSHORT)0x4704) +#define NBF_ADDRESSFILE_SIGNATURE ((CSHORT)0x4705) +#define NBF_ADDRESS_SIGNATURE ((CSHORT)0x4706) +#define NBF_DEVICE_CONTEXT_SIGNATURE ((CSHORT)0x4707) +#define NBF_PACKET_SIGNATURE ((CSHORT)0x4708) + +#if DBG +extern PVOID * NbfConnectionTable; +extern PVOID * NbfRequestTable; +extern PVOID * NbfUiFrameTable; +extern PVOID * NbfSendPacketTable; +extern PVOID * NbfLinkTable; +extern PVOID * NbfAddressFileTable; +extern PVOID * NbfAddressTable; +#endif + +#endif // _NBFCONST_ diff --git a/private/ntos/tdi/nbf/nbfdebug.c b/private/ntos/tdi/nbf/nbfdebug.c new file mode 100644 index 000000000..2f5aadbd7 --- /dev/null +++ b/private/ntos/tdi/nbf/nbfdebug.c @@ -0,0 +1,646 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + nbfdebug.c + +Abstract: + + This module contains code that implements debug things for NBF. It is + compiled only if debug is on in the compile phase. + +Author: + + David Beaver (dbeaver) 18-Apr-1991 + +Environment: + + Kernel mode + +Revision History: + + David Beaver (dbeaver) 1-July-1991 + modified to use new TDI interface + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#if DBG + +VOID +DisplayOneFrame( + PTP_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine is a temporary debugging aid that displays an I-frame + before it is sent. This ensures that we have formatted all our packets + correctly. + +Arguments: + + Packet - Pointer to a TP_PACKET representing an I-frame to be displayed. + +Return Value: + + none. + +--*/ + +{ + PCH s, e; + ULONG ns, nr; // I-frame (NetBIOS) cracking. + PNBF_HDR_CONNECTION NbfHeader; + PDLC_I_FRAME DlcHeader; + BOOLEAN Command, PollFinal; + BOOLEAN IsUFrame=FALSE; + UCHAR CmdByte; + + PDLC_S_FRAME SFrame; // DLC frame cracking. + PDLC_U_FRAME UFrame; + + DlcHeader = (PDLC_I_FRAME)&(Packet->Header[14]); + NbfHeader = (PNBF_HDR_CONNECTION)&(Packet->Header[18]); + ns = DlcHeader->SendSeq >> 1; + nr = DlcHeader->RcvSeq >> 1; + PollFinal = (BOOLEAN)(DlcHeader->RcvSeq & DLC_I_PF); + Command = (BOOLEAN)!(DlcHeader->Ssap & DLC_SSAP_RESPONSE); + + if (DlcHeader->SendSeq & DLC_I_INDICATOR) { + IF_NBFDBG (NBF_DEBUG_DLCFRAMES) { + } else { + return; // if DLCFRAMES not set, don't print. + } + + SFrame = (PDLC_S_FRAME)DlcHeader; // alias. + UFrame = (PDLC_U_FRAME)DlcHeader; // alias. + CmdByte = SFrame->Command; + IsUFrame = (BOOLEAN)((UFrame->Command & DLC_U_INDICATOR) == DLC_U_INDICATOR); + if (IsUFrame) { + CmdByte = (UCHAR)(UFrame->Command & ~DLC_U_PF); + } + + switch (CmdByte) { + case DLC_CMD_RR: + s = "RR"; + PollFinal = (BOOLEAN)(SFrame->RcvSeq & DLC_S_PF); + DbgPrint ("DLC: %s-%s/%s(%ld) ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0", + (ULONG)(SFrame->RcvSeq >> 1)); + break; + + case DLC_CMD_RNR: + s = "RNR"; + PollFinal = (BOOLEAN)(SFrame->RcvSeq & DLC_S_PF); + DbgPrint ("DLC: %s-%s/%s(%ld) ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0", + (ULONG)(SFrame->RcvSeq >> 1)); + break; + + case DLC_CMD_REJ: + s = "REJ"; + PollFinal = (BOOLEAN)(SFrame->RcvSeq & DLC_S_PF); + DbgPrint ("DLC: %s-%s/%s(%ld) ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0", + (ULONG)(SFrame->RcvSeq >> 1)); + break; + + case DLC_CMD_SABME: + s = "SABME"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + break; + + case DLC_CMD_DISC: + s = "DISC"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + break; + + case DLC_CMD_UA: + s = "UA"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + break; + + case DLC_CMD_DM: + s = "DM"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + break; + + case DLC_CMD_FRMR: + s = "FRMR"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + break; + + case DLC_CMD_XID: + s = "XID"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + break; + + case DLC_CMD_TEST: + s = "TEST"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + break; + + default: + s = "(UNKNOWN)"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + } + return; + } + + IF_NBFDBG (NBF_DEBUG_IFRAMES) { + } else { + return; // if IFRAMES not set, don't print. + } + + switch (NbfHeader->Command) { + case NBF_CMD_ADD_GROUP_NAME_QUERY: + s = "ADD_GROUP_NAME_QUERY"; break; + + case NBF_CMD_ADD_NAME_QUERY: + s = "ADD_NAME_QUERY"; break; + + case NBF_CMD_NAME_IN_CONFLICT: + s = "NAME_IN_CONFLICT"; break; + + case NBF_CMD_STATUS_QUERY: + s = "STATUS_QUERY"; break; + + case NBF_CMD_TERMINATE_TRACE: + s = "TERMINATE_TRACE"; break; + + case NBF_CMD_DATAGRAM: + s = "DATAGRAM"; break; + + case NBF_CMD_DATAGRAM_BROADCAST: + s = "BROADCAST_DATAGRAM"; break; + + case NBF_CMD_NAME_QUERY: + s = "NAME_QUERY"; break; + + case NBF_CMD_ADD_NAME_RESPONSE: + s = "ADD_NAME_RESPONSE"; break; + + case NBF_CMD_NAME_RECOGNIZED: + s = "NAME_RECOGNIZED"; break; + + case NBF_CMD_STATUS_RESPONSE: + s = "STATUS_RESPONSE"; break; + + case NBF_CMD_TERMINATE_TRACE2: + s = "TERMINATE_TRACE2"; break; + + case NBF_CMD_DATA_ACK: + s = "DATA_ACK"; break; + + case NBF_CMD_DATA_FIRST_MIDDLE: + s = "DATA_FIRST_MIDDLE"; break; + + case NBF_CMD_DATA_ONLY_LAST: + s = "DATA_ONLY_LAST"; break; + + case NBF_CMD_SESSION_CONFIRM: + s = "SESSION_CONFIRM"; break; + + case NBF_CMD_SESSION_END: + s = "SESSION_END"; break; + + case NBF_CMD_SESSION_INITIALIZE: + s = "SESSION_INITIALIZE"; break; + + case NBF_CMD_NO_RECEIVE: + s = "NO_RECEIVE"; break; + + case NBF_CMD_RECEIVE_OUTSTANDING: + s = "RECEIVE_OUTSTANDING"; break; + + case NBF_CMD_RECEIVE_CONTINUE: + s = "RECEIVE_CONTINUE"; break; + + case NBF_CMD_SESSION_ALIVE: + s = "SESSION_ALIVE"; break; + + default: + s = "<<<<UNKNOWN I PACKET TYPE>>>>"; + } /* switch */ + + if (HEADER_LENGTH(NbfHeader) != 14) { + e = "(LENGTH IN ERROR) "; + } else if (HEADER_SIGNATURE(NbfHeader) != NETBIOS_SIGNATURE) { + e = "(SIGNATURE IN ERROR) "; + } else { + e = ""; + } + + DbgPrint ("DLC: I-%s/%s, N(S)=%ld, N(R)=%ld %s", + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0", + ns, nr, e); + DbgPrint (s); + DbgPrint (" ( D1=%ld, D2=%ld, XC=%ld, RC=%ld )\n", + (ULONG)NbfHeader->Data1, + (ULONG)(NbfHeader->Data2Low+NbfHeader->Data2High*256), + TRANSMIT_CORR(NbfHeader), + RESPONSE_CORR(NbfHeader)); +} /* DisplayOneFrame */ + + +VOID +NbfDisplayUIFrame( + PTP_UI_FRAME OuterFrame + ) + +/*++ + +Routine Description: + + This routine is a temporary debugging aid that displays a UI frame + before it is sent by NbfSendUIFrame. This ensures that we have formatted + all our UI frames correctly. + +Arguments: + + RawFrame - Pointer to a connectionless frame to be sent. + +Return Value: + + none. + +--*/ + +{ + PCH s, e; + UCHAR ReceiverName [17]; + UCHAR SenderName [17]; + BOOLEAN PollFinal, Command; + PDLC_S_FRAME SFrame; + PDLC_U_FRAME UFrame; + USHORT i; + PDLC_FRAME DlcHeader; + PNBF_HDR_CONNECTIONLESS NbfHeader; + + // + + DlcHeader = (PDLC_FRAME)&(OuterFrame->Header[14]); + NbfHeader = (PNBF_HDR_CONNECTIONLESS)&(OuterFrame->Header[17]); + + if (DlcHeader->Byte1 != DLC_CMD_UI) { + + IF_NBFDBG (NBF_DEBUG_DLCFRAMES) { + } else { + return; // don't print this if DLCFRAMES is off. + } + + Command = (BOOLEAN)!(DlcHeader->Ssap & DLC_SSAP_RESPONSE); + SFrame = (PDLC_S_FRAME)DlcHeader; // alias. + UFrame = (PDLC_U_FRAME)DlcHeader; // alias. + switch (DlcHeader->Byte1) { + case DLC_CMD_RR: + s = "RR"; + PollFinal = (BOOLEAN)(SFrame->RcvSeq & DLC_S_PF); + DbgPrint ("DLC: %s-%s/%s(%ld) ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0", + (ULONG)(SFrame->RcvSeq >> 1)); + break; + + case DLC_CMD_RNR: + s = "RNR"; + PollFinal = (BOOLEAN)(SFrame->RcvSeq & DLC_S_PF); + DbgPrint ("DLC: %s-%s/%s(%ld) ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0", + (ULONG)(SFrame->RcvSeq >> 1)); + break; + + case DLC_CMD_REJ: + s = "REJ"; + PollFinal = (BOOLEAN)(SFrame->RcvSeq & DLC_S_PF); + DbgPrint ("DLC: %s-%s/%s(%ld) ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0", + (ULONG)(SFrame->RcvSeq >> 1)); + break; + + case DLC_CMD_SABME: + s = "SABME"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + break; + + case DLC_CMD_DISC: + s = "DISC"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + break; + + case DLC_CMD_UA: + s = "UA"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + break; + + case DLC_CMD_DM: + s = "DM"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + break; + + case DLC_CMD_FRMR: + s = "FRMR"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + break; + + case DLC_CMD_XID: + s = "XID"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + break; + + case DLC_CMD_TEST: + s = "TEST"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + break; + + default: + s = "(UNKNOWN)"; + PollFinal = (BOOLEAN)(UFrame->Command & DLC_U_PF); + DbgPrint ("DLC: %s-%s/%s ---->\n", + s, + Command ? "c" : "r", + PollFinal ? (Command ? "p" : "f") : "0"); + } + return; + } + + // + // We know that this is an I-frame, because the bottom bit of the + // first byte in the DLC header is cleared. Go ahead and print it + // as though it were a NetBIOS packet, which it should be. + // + + IF_NBFDBG (NBF_DEBUG_IFRAMES) { + } else { + return; // don't print this if IFRAMES is off. + } + + switch (NbfHeader->Command) { + case NBF_CMD_ADD_GROUP_NAME_QUERY: + s = "ADD_GROUP_NAME_QUERY"; break; + + case NBF_CMD_ADD_NAME_QUERY: + s = "ADD_NAME_QUERY"; break; + + case NBF_CMD_NAME_IN_CONFLICT: + s = "NAME_IN_CONFLICT"; break; + + case NBF_CMD_STATUS_QUERY: + s = "STATUS_QUERY"; break; + + case NBF_CMD_TERMINATE_TRACE: + s = "TERMINATE_TRACE"; break; + + case NBF_CMD_DATAGRAM: + s = "DATAGRAM"; break; + + case NBF_CMD_DATAGRAM_BROADCAST: + s = "BROADCAST_DATAGRAM"; break; + + case NBF_CMD_NAME_QUERY: + s = "NAME_QUERY"; break; + + case NBF_CMD_ADD_NAME_RESPONSE: + s = "ADD_NAME_RESPONSE"; break; + + case NBF_CMD_NAME_RECOGNIZED: + s = "NAME_RECOGNIZED"; break; + + case NBF_CMD_STATUS_RESPONSE: + s = "STATUS_RESPONSE"; break; + + case NBF_CMD_TERMINATE_TRACE2: + s = "TERMINATE_TRACE2"; break; + + case NBF_CMD_DATA_ACK: + s = "DATA_ACK"; break; + + case NBF_CMD_DATA_FIRST_MIDDLE: + s = "DATA_FIRST_MIDDLE"; break; + + case NBF_CMD_DATA_ONLY_LAST: + s = "DATA_ONLY_LAST"; break; + + case NBF_CMD_SESSION_CONFIRM: + s = "SESSION_CONFIRM"; break; + + case NBF_CMD_SESSION_END: + s = "SESSION_END"; break; + + case NBF_CMD_SESSION_INITIALIZE: + s = "SESSION_INITIALIZE"; break; + + case NBF_CMD_NO_RECEIVE: + s = "NO_RECEIVE"; break; + + case NBF_CMD_RECEIVE_OUTSTANDING: + s = "RECEIVE_OUTSTANDING"; break; + + case NBF_CMD_RECEIVE_CONTINUE: + s = "RECEIVE_CONTINUE"; break; + + case NBF_CMD_SESSION_ALIVE: + s = "SESSION_ALIVE"; break; + + default: + s = "<<<<UNKNOWN UI PACKET TYPE>>>>"; + } /* switch */ + + for (i=0; i<16; i++) { // copy NetBIOS names. + SenderName [i] = NbfHeader->SourceName [i]; + ReceiverName [i] = NbfHeader->DestinationName [i]; + } + SenderName [16] = 0; // install zero bytes. + ReceiverName [16] = 0; + + if (HEADER_LENGTH(NbfHeader) != 44) { + e = "(LENGTH IN ERROR) "; + } else if (HEADER_SIGNATURE(NbfHeader) != NETBIOS_SIGNATURE) { + e = "(SIGNATURE IN ERROR) "; + } else { + e = ""; + } + + DbgPrint ("[UI] %s", e); + DbgPrint (s); + DbgPrint (" ( D1=%ld, D2=%ld, XC=%ld, RC=%ld, ", + (ULONG)NbfHeader->Data1, + (ULONG)(NbfHeader->Data2Low+NbfHeader->Data2High*256), + TRANSMIT_CORR(NbfHeader), + RESPONSE_CORR(NbfHeader)); + DbgPrint ("'%s'->'%s' ) ---->\n", SenderName, ReceiverName); +} /* NbfDisplayUIFrame */ + + +VOID +NbfHexDumpLine( + PCHAR pch, + ULONG len, + PCHAR s, + PCHAR t + ) +/*++ + +Routine Description: + + This routine builds a line of text containing hex and printable characters. + +Arguments: + + IN pch - Supplies buffer to be displayed. + IN len - Supplies the length of the buffer in bytes. + IN s - Supplies the start of the buffer to be loaded with the string + of hex characters. + IN t - Supplies the start of the buffer to be loaded with the string + of printable ascii characters. + + +Return Value: + + none. + +--*/ +{ + static UCHAR rghex[] = "0123456789ABCDEF"; + + UCHAR c; + UCHAR *hex, *asc; + + + hex = s; + asc = t; + + *(asc++) = '*'; + while (len--) { + c = *(pch++); + *(hex++) = rghex [c >> 4] ; + *(hex++) = rghex [c & 0x0F]; + *(hex++) = ' '; + *(asc++) = (c < ' ' || c > '~') ? (CHAR )'.' : c; + } + *(asc++) = '*'; + *asc = 0; + *hex = 0; + +} + + +VOID +NbfFormattedDump( + PCHAR far_p, + ULONG len + ) +/*++ + +Routine Description: + + This routine outputs a buffer in lines of text containing hex and + printable characters. + +Arguments: + + IN far_p - Supplies buffer to be displayed. + IN len - Supplies the length of the buffer in bytes. + +Return Value: + + none. + +--*/ +{ + ULONG l; + char s[80], t[80]; + + while (len) { + l = len < 16 ? len : 16; + + DbgPrint ("\n%lx ", far_p); + NbfHexDumpLine (far_p, l, s, t); + DbgPrint ("%s%.*s%s", s, 1 + ((16 - l) * 3), "", t); + + len -= l; + far_p += l; + } + DbgPrint ("\n"); +} + +#endif diff --git a/private/ntos/tdi/nbf/nbfdrvr.c b/private/ntos/tdi/nbf/nbfdrvr.c new file mode 100644 index 000000000..2f42766ff --- /dev/null +++ b/private/ntos/tdi/nbf/nbfdrvr.c @@ -0,0 +1,2493 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + nbfdrvr.c + +Abstract: + + This module contains code which defines the NetBIOS Frames Protocol + transport provider's device object. + +Author: + + David Beaver (dbeaver) 2-July-1991 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop +//#pragma warning(error:4101) // Unreferenced local variable + +// +// This is a list of all the device contexts that NBF owns, +// used while unloading. +// + +LIST_ENTRY NbfDeviceList = {0,0}; // initialized for real at runtime. + +#ifdef _PNP_POWER + +// +// Global variables this is a copy of the path in the registry for +// configuration data. +// + +UNICODE_STRING NbfRegistryPath; + +// +// We need the driver object to create device context structures. +// + +PDRIVER_OBJECT NbfDriverObject; + +#endif + + +#ifdef NBF_LOCKS // see spnlckdb.c + +extern KSPIN_LOCK NbfGlobalLock; + +#endif // def NBF_LOCKS + +// +// The debugging longword, containing a bitmask as defined in NBFCONST.H. +// If a bit is set, then debugging is turned on for that component. +// + +#if DBG + +ULONG NbfDebug = 0; +BOOLEAN NbfDisconnectDebug; + +NBF_SEND NbfSends[TRACK_TDI_LIMIT+1]; +LONG NbfSendsNext; + +NBF_SEND_COMPLETE NbfCompletedSends[TRACK_TDI_LIMIT+1]; +LONG NbfCompletedSendsNext; + +NBF_RECEIVE NbfReceives[TRACK_TDI_LIMIT+1]; +LONG NbfReceivesNext; + +NBF_RECEIVE_COMPLETE NbfCompletedReceives[TRACK_TDI_LIMIT+1]; +LONG NbfCompletedReceivesNext=0; + +PVOID * NbfConnectionTable; +PVOID * NbfRequestTable; +PVOID * NbfUiFrameTable; +PVOID * NbfSendPacketTable; +PVOID * NbfLinkTable; +PVOID * NbfAddressFileTable; +PVOID * NbfAddressTable; + + +LIST_ENTRY NbfGlobalRequestList; +LIST_ENTRY NbfGlobalLinkList; +LIST_ENTRY NbfGlobalConnectionList; +KSPIN_LOCK NbfGlobalInterlock; +KSPIN_LOCK NbfGlobalHistoryLock; + +PVOID +TtdiSend (); + +PVOID +TtdiReceive (); + +PVOID +TtdiServer (); + +KEVENT TdiSendEvent; +KEVENT TdiReceiveEvent; +KEVENT TdiServerEvent; + +#endif + +#if MAGIC + +BOOLEAN NbfEnableMagic = FALSE; // Controls sending of magic bullets. + +#endif // MAGIC + +// +// This prevents us from having a bss section +// + +ULONG _setjmpexused = 0; + +// +// Forward declaration of various routines used in this module. +// + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +VOID +NbfUnload( + IN PDRIVER_OBJECT DriverObject + ); + +VOID +NbfFreeConfigurationInfo ( + IN PCONFIG_DATA ConfigurationInfo + ); + +NTSTATUS +NbfDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NbfDispatchInternal( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NbfDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +NbfDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +VOID +NbfDeallocateResources( + IN PDEVICE_CONTEXT DeviceContext + ); + +#ifdef RASAUTODIAL +VOID +NbfAcdBind(); + +VOID +NbfAcdUnbind(); +#endif // RASAUTODIAL + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,DriverEntry) +#ifdef _PNP_POWER +#pragma alloc_text(PAGE,NbfInitializeOneDeviceContext) +#endif +#endif + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine performs initialization of the NetBIOS Frames Protocol + transport driver. It creates the device objects for the transport + provider and performs other driver initialization. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + + RegistryPath - The name of NBF's node in the registry. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + ULONG j; + UNICODE_STRING nameString; + NTSTATUS status; + UINT SuccessfulOpens; + + PCONFIG_DATA NbfConfig = NULL; + + // + + ASSERT (sizeof (SHORT) == 2); + +#ifdef MEMPRINT + MemPrintInitialize (); +#endif + +#ifdef NBF_LOCKS + KeInitializeSpinLock( &NbfGlobalLock ); +#endif + +#if DBG + InitializeListHead (&NbfGlobalRequestList); + InitializeListHead (&NbfGlobalLinkList); + InitializeListHead (&NbfGlobalConnectionList); + KeInitializeSpinLock (&NbfGlobalInterlock); + KeInitializeSpinLock (&NbfGlobalHistoryLock); +#endif + +#ifdef _PNP_POWER + + NbfRegistryPath = *RegistryPath; + NbfRegistryPath.Buffer = ExAllocatePoolWithTag(PagedPool, + RegistryPath->MaximumLength, + ' FBN'); + + if (NbfRegistryPath.Buffer == NULL) { + PANIC(" Failed to allocate Registry Path!\n"); + return(STATUS_INSUFFICIENT_RESOURCES); + } + + RtlCopyMemory(NbfRegistryPath.Buffer, RegistryPath->Buffer, + RegistryPath->MaximumLength); + NbfDriverObject = DriverObject; + RtlInitUnicodeString( &nameString, NBF_NAME); + TdiInitialize(); + +#else // NOT _PNP_POWER + + // + // This allocates the CONFIG_DATA structure and returns + // it in NbfConfig. + // + + status = NbfConfigureTransport(RegistryPath, &NbfConfig); + + if (!NT_SUCCESS (status)) { + PANIC (" Failed to initialize transport, Nbf initialization failed.\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // make ourselves known to the NDIS wrapper. + // + + RtlInitUnicodeString( &nameString, NBF_DEVICE_NAME ); +#endif + + status = NbfRegisterProtocol (&nameString); + + if (!NT_SUCCESS (status)) { + +#ifdef _PNP_POWER + + // + // No configuration info read at startup when using PNP + // + + ExFreePool(NbfRegistryPath.Buffer); +#else + // + // Free up config info for non PNP mode operation. + // + + NbfFreeConfigurationInfo(NbfConfig); +#endif + PANIC ("NbfInitialize: RegisterProtocol failed!\n"); + + NbfWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_TRANSPORT_REGISTER_FAILED, + 607, + status, + NULL, + 0, + NULL); + + return STATUS_INSUFFICIENT_RESOURCES; + + } + + + // + // Initialize the driver object with this driver's entry points. + // + + DriverObject->MajorFunction [IRP_MJ_CREATE] = NbfDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLOSE] = NbfDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLEANUP] = NbfDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_INTERNAL_DEVICE_CONTROL] = NbfDispatchInternal; + DriverObject->MajorFunction [IRP_MJ_DEVICE_CONTROL] = NbfDispatch; + + DriverObject->DriverUnload = NbfUnload; + + // + // Initialize the global list of devices. + // + + InitializeListHead (&NbfDeviceList); + +#ifndef _PNP_POWER + +#if DBG + + // + // Allocate the debugging tables. In the PNP case we don't need these + // until we are activated by ProtocolBindAdapter + // + + NbfConnectionTable = (PVOID *)ExAllocatePoolWithTag(NonPagedPool, + sizeof(PVOID) * + (NbfConfig->InitConnections + 2 + + NbfConfig->InitRequests + 2 + + NbfConfig->InitUIFrames + 2 + + NbfConfig->InitPackets + 2 + + NbfConfig->InitLinks + 2 + + NbfConfig->InitAddressFiles + 2 + + NbfConfig->InitAddresses + 2), + ' FBN'); + + ASSERT (NbfConnectionTable); + + NbfRequestTable = NbfConnectionTable + (NbfConfig->InitConnections + 2); + NbfUiFrameTable = NbfRequestTable + (NbfConfig->InitRequests + 2); + NbfSendPacketTable = NbfUiFrameTable + (NbfConfig->InitUIFrames + 2); + NbfLinkTable = NbfSendPacketTable + (NbfConfig->InitPackets + 2); + NbfAddressFileTable = NbfLinkTable + (NbfConfig->InitLinks + 2); + NbfAddressTable = NbfAddressFileTable + (NbfConfig->InitAddressFiles + 2); +#endif + + + SuccessfulOpens = 0; + + for (j=0;j<NbfConfig->NumAdapters;j++ ) { + + // + // Loop through all the adapters that are in the configuration + // information structure. Allocate a device object for each + // one that we find. + // + SuccessfulOpens += NbfInitializeOneDeviceContext(&status, + DriverObject, + NbfConfig, j + ); + + } + + NbfFreeConfigurationInfo(NbfConfig); + +#ifdef RASAUTODIAL + // + // Get the automatic connection + // driver entry points. + // + if (SuccessfulOpens > 0) + NbfAcdBind(); +#endif // RASAUTODIAL + + return ((SuccessfulOpens>0) ? STATUS_SUCCESS :STATUS_DEVICE_DOES_NOT_EXIST); +#else // _PNP_POWER + return(status); +#endif + +} + +VOID +NbfUnload( + IN PDRIVER_OBJECT DriverObject + ) + +/*++ + +Routine Description: + + This routine unloads the NetBIOS Frames Protocol transport driver. + It unbinds from any NDIS drivers that are open and frees all resources + associated with the transport. The I/O system will not call us until + nobody above has NBF open. + +Arguments: + + DriverObject - Pointer to driver object created by the system. + +Return Value: + + None. When the function returns, the driver is unloaded. + +--*/ + +{ + + PDEVICE_CONTEXT DeviceContext; + PLIST_ENTRY p; + + + UNREFERENCED_PARAMETER (DriverObject); + + +#ifdef RASAUTODIAL + // + // Unbind from the + // automatic connection driver. + // + NbfAcdUnbind(); +#endif // RASAUTODIAL + + // + // Walk the list of device contexts. + // + + while (!IsListEmpty (&NbfDeviceList)) { + + p = RemoveHeadList (&NbfDeviceList); + DeviceContext = CONTAINING_RECORD (p, DEVICE_CONTEXT, Linkage); + + DeviceContext->State = DEVICECONTEXT_STATE_STOPPING; + + // + // Remove all the storage associated with the device. + // + + NbfFreeResources (DeviceContext); + + // + // Free the packet pools, etc. and close the + // adapter. + // + + NbfCloseNdis (DeviceContext); + + // + // And remove the creation reference from the device + // context. + // + + NbfDereferenceDeviceContext ("Unload", DeviceContext, DCREF_CREATION); + + } + + // + // Finally, remove ourselves as an NDIS protocol. + // + + NbfDeregisterProtocol(); + + return; + +} + + +VOID +NbfFreeResources ( + IN PDEVICE_CONTEXT DeviceContext + ) +/*++ + +Routine Description: + + This routine is called by NBF to clean up the data structures associated + with a given DeviceContext. When this routine exits, the DeviceContext + should be deleted as it no longer has any assocaited resources. + +Arguments: + + DeviceContext - Pointer to the DeviceContext we wish to clean up. + +Return Value: + + None. + +--*/ +{ + PLIST_ENTRY p; + PSINGLE_LIST_ENTRY s; + PTP_PACKET packet; + PTP_UI_FRAME uiFrame; + PTP_ADDRESS address; + PTP_CONNECTION connection; + PTP_REQUEST request; + PTP_LINK link; + PTP_ADDRESS_FILE addressFile; + PNDIS_PACKET ndisPacket; + PBUFFER_TAG BufferTag; + + + // + // Stop the timers. + // + + NbfStopTimerSystem (DeviceContext); + + + // + // Clean up packet pool. + // + + while ( DeviceContext->PacketPool.Next != NULL ) { + s = PopEntryList( &DeviceContext->PacketPool ); + packet = CONTAINING_RECORD( s, TP_PACKET, Linkage ); + + NbfDeallocateSendPacket (DeviceContext, packet); + } + + // + // Clean up UI frame pool. + // + + while ( !IsListEmpty( &DeviceContext->UIFramePool ) ) { + p = RemoveHeadList( &DeviceContext->UIFramePool ); + uiFrame = CONTAINING_RECORD (p, TP_UI_FRAME, Linkage ); + + NbfDeallocateUIFrame (DeviceContext, uiFrame); + } + + // + // Clean up address pool. + // + + while ( !IsListEmpty (&DeviceContext->AddressPool) ) { + p = RemoveHeadList (&DeviceContext->AddressPool); + address = CONTAINING_RECORD (p, TP_ADDRESS, Linkage); + + NbfDeallocateAddress (DeviceContext, address); + } + + // + // Clean up address file pool. + // + + while ( !IsListEmpty (&DeviceContext->AddressFilePool) ) { + p = RemoveHeadList (&DeviceContext->AddressFilePool); + addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); + + NbfDeallocateAddressFile (DeviceContext, addressFile); + } + + // + // Clean up connection pool. + // + + while ( !IsListEmpty (&DeviceContext->ConnectionPool) ) { + p = RemoveHeadList (&DeviceContext->ConnectionPool); + connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList); + + NbfDeallocateConnection (DeviceContext, connection); + } + + // + // Clean up link pool. + // + + while ( !IsListEmpty (&DeviceContext->LinkPool) ) { + p = RemoveHeadList (&DeviceContext->LinkPool); + link = CONTAINING_RECORD (p, TP_LINK, Linkage); + + NbfDeallocateLink (DeviceContext, link); + } + + // + // Clean up request pool. + // + + while ( !IsListEmpty( &DeviceContext->RequestPool ) ) { + p = RemoveHeadList( &DeviceContext->RequestPool ); + request = CONTAINING_RECORD (p, TP_REQUEST, Linkage ); + + NbfDeallocateRequest (DeviceContext, request); + } + + // + // Clean up receive packet pool + // + + while ( DeviceContext->ReceivePacketPool.Next != NULL) { + s = PopEntryList (&DeviceContext->ReceivePacketPool); + + // + // HACK: This works because Linkage is the first field in + // ProtocolReserved for a receive packet. + // + + ndisPacket = CONTAINING_RECORD (s, NDIS_PACKET, ProtocolReserved[0]); + + NbfDeallocateReceivePacket (DeviceContext, ndisPacket); + } + + + // + // Clean up receive buffer pool. + // + + while ( DeviceContext->ReceiveBufferPool.Next != NULL ) { + s = PopEntryList( &DeviceContext->ReceiveBufferPool ); + BufferTag = CONTAINING_RECORD (s, BUFFER_TAG, Linkage ); + + NbfDeallocateReceiveBuffer (DeviceContext, BufferTag); + } + + + return; + +} /* NbfFreeResources */ + + +NTSTATUS +NbfDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is the main dispatch routine for the NBF device driver. + It accepts an I/O Request Packet, performs the request, and then + returns with the appropriate status. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + PDEVICE_CONTEXT DeviceContext; + + ENTER_NBF; + + // + // Check to see if NBF has been initialized; if not, don't allow any use. + // Note that this only covers any user mode code use; kernel TDI clients + // will fail on their creation of an endpoint. + // + + DeviceContext = (PDEVICE_CONTEXT)DeviceObject; + if (DeviceContext->State != DEVICECONTEXT_STATE_OPEN) { + LEAVE_NBF; + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + // + // Make sure status information is consistent every time. + // + + IoMarkIrpPending (Irp); + Irp->IoStatus.Status = STATUS_PENDING; + Irp->IoStatus.Information = 0; + + // + // Get a pointer to the current stack location in the IRP. This is where + // the function codes and parameters are stored. + // + + IrpSp = IoGetCurrentIrpStackLocation (Irp); + + // + // Case on the function that is being performed by the requestor. If the + // operation is a valid one for this device, then make it look like it was + // successfully completed, where possible. + // + + + switch (IrpSp->MajorFunction) { + + case IRP_MJ_DEVICE_CONTROL: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatch: IRP_MJ_DEVICE_CONTROL.\n"); + } + + Status = NbfDeviceControl (DeviceObject, Irp, IrpSp); + break; + + default: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatch: OTHER (DEFAULT).\n"); + } + Status = STATUS_INVALID_DEVICE_REQUEST; + + } /* major function switch */ + + if (Status == STATUS_PENDING) { + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatch: request PENDING from handler.\n"); + } + } else { + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatch: request COMPLETED by handler.\n"); + } + + LEAVE_NBF; + IrpSp->Control &= ~SL_PENDING_RETURNED; + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + ENTER_NBF; + } + + // + // Return the immediate status code to the caller. + // + + LEAVE_NBF; + return Status; +} /* NbfDispatch */ + + +NTSTATUS +NbfDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is the main dispatch routine for the NBF device driver. + It accepts an I/O Request Packet, performs the request, and then + returns with the appropriate status. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + KIRQL oldirql; + PDEVICE_CONTEXT DeviceContext; + NTSTATUS Status; + PIO_STACK_LOCATION IrpSp; + PFILE_FULL_EA_INFORMATION openType; + USHORT i; + BOOLEAN found; + PTP_ADDRESS_FILE AddressFile; + PTP_CONNECTION Connection; + + ENTER_NBF; + + // + // Check to see if NBF has been initialized; if not, don't allow any use. + // Note that this only covers any user mode code use; kernel TDI clients + // will fail on their creation of an endpoint. + // + + DeviceContext = (PDEVICE_CONTEXT)DeviceObject; + if (DeviceContext->State != DEVICECONTEXT_STATE_OPEN) { + LEAVE_NBF; + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + // + // Make sure status information is consistent every time. + // + + IoMarkIrpPending (Irp); + Irp->IoStatus.Status = STATUS_PENDING; + Irp->IoStatus.Information = 0; + + // + // Get a pointer to the current stack location in the IRP. This is where + // the function codes and parameters are stored. + // + + IrpSp = IoGetCurrentIrpStackLocation (Irp); + + // + // Case on the function that is being performed by the requestor. If the + // operation is a valid one for this device, then make it look like it was + // successfully completed, where possible. + // + + + switch (IrpSp->MajorFunction) { + + // + // The Create function opens a transport object (either address or + // connection). Access checking is performed on the specified + // address to ensure security of transport-layer addresses. + // + + case IRP_MJ_CREATE: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatch: IRP_MJ_CREATE.\n"); + } + + openType = + (PFILE_FULL_EA_INFORMATION)Irp->AssociatedIrp.SystemBuffer; + + if (openType != NULL) { + + found = TRUE; + + for (i=0;i<(USHORT)openType->EaNameLength;i++) { + if (openType->EaName[i] == TdiTransportAddress[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + Status = NbfOpenAddress (DeviceObject, Irp, IrpSp); + break; + } + + // + // Connection? + // + + found = TRUE; + + for (i=0;i<(USHORT)openType->EaNameLength;i++) { + if (openType->EaName[i] == TdiConnectionContext[i]) { + continue; + } else { + found = FALSE; + break; + } + } + + if (found) { + Status = NbfOpenConnection (DeviceObject, Irp, IrpSp); + break; + } + + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint1 ("NbfDispatchOpenClose: IRP_MJ_CREATE on invalid type, type was: %s\n", + openType->EaName); + } + + } else { + + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchOpenClose: IRP_MJ_CREATE on control channel!\n"); + } + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + IrpSp->FileObject->FsContext = (PVOID)(DeviceContext->ControlChannelIdentifier); + ++DeviceContext->ControlChannelIdentifier; + if (DeviceContext->ControlChannelIdentifier == 0) { + DeviceContext->ControlChannelIdentifier = 1; + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + IrpSp->FileObject->FsContext2 = (PVOID)NBF_FILE_TYPE_CONTROL; + Status = STATUS_SUCCESS; + } + + break; + + case IRP_MJ_CLOSE: + + // + // The Close function closes a transport endpoint, terminates + // all outstanding transport activity on the endpoint, and unbinds + // the endpoint from its transport address, if any. If this + // is the last transport endpoint bound to the address, then + // the address is removed from the provider. + // + + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatch: IRP_MJ_CLOSE.\n"); + } + + switch ((ULONG)IrpSp->FileObject->FsContext2) { + case TDI_TRANSPORT_ADDRESS_FILE: + AddressFile = (PTP_ADDRESS_FILE)IrpSp->FileObject->FsContext; + + // + // This creates a reference to AddressFile->Address + // which is removed by NbfCloseAddress. + // + + Status = NbfVerifyAddressObject(AddressFile); + + if (!NT_SUCCESS (Status)) { + Status = STATUS_INVALID_HANDLE; + } else { + Status = NbfCloseAddress (DeviceObject, Irp, IrpSp); + } + + break; + + case TDI_CONNECTION_FILE: + + // + // This is a connection + // + + Connection = (PTP_CONNECTION)IrpSp->FileObject->FsContext; + Status = NbfVerifyConnectionObject (Connection); + if (NT_SUCCESS (Status)) { + + Status = NbfCloseConnection (DeviceObject, Irp, IrpSp); + NbfDereferenceConnection ("Temporary Use",Connection, CREF_BY_ID); + + } + + break; + + case NBF_FILE_TYPE_CONTROL: + + // + // this always succeeds + // + + Status = STATUS_SUCCESS; + break; + + default: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint1 ("NbfDispatch: IRP_MJ_CLOSE on unknown file type %lx.\n", + IrpSp->FileObject->FsContext2); + } + + Status = STATUS_INVALID_HANDLE; + } + + break; + + case IRP_MJ_CLEANUP: + + // + // Handle the two stage IRP for a file close operation. When the first + // stage hits, run down all activity on the object of interest. This + // do everything to it but remove the creation hold. Then, when the + // CLOSE irp hits, actually close the object. + // + + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatch: IRP_MJ_CLEANUP.\n"); + } + + switch ((ULONG)IrpSp->FileObject->FsContext2) { + case TDI_TRANSPORT_ADDRESS_FILE: + AddressFile = (PTP_ADDRESS_FILE)IrpSp->FileObject->FsContext; + Status = NbfVerifyAddressObject(AddressFile); + if (!NT_SUCCESS (Status)) { + + Status = STATUS_INVALID_HANDLE; + + } else { + + NbfStopAddressFile (AddressFile, AddressFile->Address); + NbfDereferenceAddress ("IRP_MJ_CLEANUP", AddressFile->Address, AREF_VERIFY); + Status = STATUS_SUCCESS; + } + + break; + + case TDI_CONNECTION_FILE: + + Connection = (PTP_CONNECTION)IrpSp->FileObject->FsContext; + Status = NbfVerifyConnectionObject (Connection); + if (NT_SUCCESS (Status)) { + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + NbfStopConnection (Connection, STATUS_LOCAL_DISCONNECT); + KeLowerIrql (oldirql); + Status = STATUS_SUCCESS; + NbfDereferenceConnection ("Temporary Use",Connection, CREF_BY_ID); + } + + break; + + case NBF_FILE_TYPE_CONTROL: + + NbfStopControlChannel( + (PDEVICE_CONTEXT)DeviceObject, + (USHORT)IrpSp->FileObject->FsContext + ); + + Status = STATUS_SUCCESS; + break; + + default: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint1 ("NbfDispatch: IRP_MJ_CLEANUP on unknown file type %lx.\n", + IrpSp->FileObject->FsContext2); + } + + Status = STATUS_INVALID_HANDLE; + } + + break; + + default: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatch: OTHER (DEFAULT).\n"); + } + + Status = STATUS_INVALID_DEVICE_REQUEST; + + } /* major function switch */ + + if (Status == STATUS_PENDING) { + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatch: request PENDING from handler.\n"); + } + } else { + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatch: request COMPLETED by handler.\n"); + } + + LEAVE_NBF; + IrpSp->Control &= ~SL_PENDING_RETURNED; + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + ENTER_NBF; + } + + + // + // Return the immediate status code to the caller. + // + + LEAVE_NBF; + return Status; +} /* NbfDispatchOpenClose */ + + +NTSTATUS +NbfDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + + IrpSp - Pointer to current IRP stack frame. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PDEVICE_CONTEXT DeviceContext = (PDEVICE_CONTEXT)DeviceObject; + + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDeviceControl: Entered.\n"); + } + + + // + // Branch to the appropriate request handler. Preliminary checking of + // the size of the request block is performed here so that it is known + // in the handlers that the minimum input parameters are readable. It + // is *not* determined here whether variable length input fields are + // passed correctly; this is a check which must be made within each routine. + // + + switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) { + +#if MAGIC + case IOCTL_TDI_MAGIC_BULLET: + + // + // Special: send the magic bullet (to trigger the Sniffer). + // + + NbfPrint1 ("NBF: Sending user MagicBullet on %lx\n", DeviceContext); + { + extern VOID NbfSendMagicBullet (PDEVICE_CONTEXT, PTP_LINK); + NbfSendMagicBullet (DeviceContext, NULL); + } + + if (IrpSp->Parameters.DeviceIoControl.Type3InputBuffer != NULL) { + NbfPrint0 ("NBF: DbgBreakPoint after MagicBullet\n"); + DbgBreakPoint(); + } + + Status = STATUS_SUCCESS; + break; +#endif + +#if DBG + case IOCTL_TDI_SEND_TEST: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDeviceControl: Internal IOCTL: start send side test\n"); + } + + (VOID) KeSetEvent( &TdiSendEvent, 0, FALSE ); + + break; + + case IOCTL_TDI_RECEIVE_TEST: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDeviceControl: Internal IOCTL: start receive side test\n"); + } + + (VOID) KeSetEvent( &TdiReceiveEvent, 0, FALSE ); + + break; + + case IOCTL_TDI_SERVER_TEST: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDeviceControl: Internal IOCTL: start receive side test\n"); + } + + (VOID) KeSetEvent( &TdiServerEvent, 0, FALSE ); + + break; +#endif + + default: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDeviceControl: invalid request type.\n"); + } + + // + // Convert the user call to the proper internal device call. + // + + Status = TdiMapUserRequest (DeviceObject, Irp, IrpSp); + + if (Status == STATUS_SUCCESS) { + + // + // If TdiMapUserRequest returns SUCCESS then the IRP + // has been converted into an IRP_MJ_INTERNAL_DEVICE_CONTROL + // IRP, so we dispatch it as usual. The IRP will + // be completed by this call. + // + // NbfDispatchInternal expects to complete the IRP, + // so we change Status to PENDING so we don't. + // + + (VOID)NbfDispatchInternal (DeviceObject, Irp); + Status = STATUS_PENDING; + + } + } + + return Status; +} /* NbfDeviceControl */ + + +NTSTATUS +NbfDispatchInternal ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine dispatches TDI request types to different handlers based + on the minor IOCTL function code in the IRP's current stack location. + In addition to cracking the minor function code, this routine also + reaches into the IRP and passes the packetized parameters stored there + as parameters to the various TDI request handlers so that they are + not IRP-dependent. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + NTSTATUS Status; + PDEVICE_CONTEXT DeviceContext; + PIO_STACK_LOCATION IrpSp; +#if DBG + KIRQL IrqlOnEnter = KeGetCurrentIrql(); +#endif + + ENTER_NBF; + + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfInternalDeviceControl: Entered.\n"); + } + + // + // Get a pointer to the current stack location in the IRP. This is where + // the function codes and parameters are stored. + // + + IrpSp = IoGetCurrentIrpStackLocation (Irp); + + DeviceContext = (PDEVICE_CONTEXT)DeviceObject; + + + if (DeviceContext->State != DEVICECONTEXT_STATE_OPEN) { + LEAVE_NBF; + Irp->IoStatus.Status = STATUS_INVALID_DEVICE_STATE; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + return STATUS_INVALID_DEVICE_STATE; + } + + + // + // Make sure status information is consistent every time. + // + + IoMarkIrpPending (Irp); + Irp->IoStatus.Status = STATUS_PENDING; + Irp->IoStatus.Information = 0; + + + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + { + PULONG Temp=(PULONG)&IrpSp->Parameters; + NbfPrint5 ("Got IrpSp %lx %lx %lx %lx %lx\n", Temp++, Temp++, + Temp++, Temp++, Temp++); + } + } + + // + // Branch to the appropriate request handler. Preliminary checking of + // the size of the request block is performed here so that it is known + // in the handlers that the minimum input parameters are readable. It + // is *not* determined here whether variable length input fields are + // passed correctly; this is a check which must be made within each routine. + // + + switch (IrpSp->MinorFunction) { + + case TDI_ACCEPT: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: TdiAccept request.\n"); + } + + Status = NbfTdiAccept (Irp); + break; + + case TDI_ACTION: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: TdiAction request.\n"); + } + + Status = NbfTdiAction (DeviceContext, Irp); + break; + + case TDI_ASSOCIATE_ADDRESS: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: TdiAccept request.\n"); + } + + Status = NbfTdiAssociateAddress (Irp); + break; + + case TDI_DISASSOCIATE_ADDRESS: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: TdiDisassociateAddress request.\n"); + } + + Status = NbfTdiDisassociateAddress (Irp); + break; + + case TDI_CONNECT: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: TdiConnect request\n"); + } + + Status = NbfTdiConnect (Irp); + + break; + + case TDI_DISCONNECT: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: TdiDisconnect request.\n"); + } + + Status = NbfTdiDisconnect (Irp); + break; + + case TDI_LISTEN: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: TdiListen request.\n"); + } + + Status = NbfTdiListen (Irp); + break; + + case TDI_QUERY_INFORMATION: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: TdiQueryInformation request.\n"); + } + + Status = NbfTdiQueryInformation (DeviceContext, Irp); + break; + + case TDI_RECEIVE: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: TdiReceive request.\n"); + } + + Status = NbfTdiReceive (Irp); + break; + + case TDI_RECEIVE_DATAGRAM: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: TdiReceiveDatagram request.\n"); + } + + Status = NbfTdiReceiveDatagram (Irp); + break; + + case TDI_SEND: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: TdiSend request.\n"); + } + + Status = NbfTdiSend (Irp); + break; + + case TDI_SEND_DATAGRAM: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: TdiSendDatagram request.\n"); + } + + Status = NbfTdiSendDatagram (Irp); + break; + + case TDI_SET_EVENT_HANDLER: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: TdiSetEventHandler request.\n"); + } + + // + // Because this request will enable direct callouts from the + // transport provider at DISPATCH_LEVEL to a client-specified + // routine, this request is only valid in kernel mode, denying + // access to this request in user mode. + // + + Status = NbfTdiSetEventHandler (Irp); + break; + + case TDI_SET_INFORMATION: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: TdiSetInformation request.\n"); + } + + Status = NbfTdiSetInformation (Irp); + break; + +#if DBG + case 0x7f: + + // + // Special: send the magic bullet (to trigger the Sniffer). + // + + NbfPrint1 ("NBF: Sending MagicBullet on %lx\n", DeviceContext); + { + extern VOID NbfSendMagicBullet (PDEVICE_CONTEXT, PTP_LINK); + NbfSendMagicBullet (DeviceContext, NULL); + } + + Status = STATUS_SUCCESS; + break; +#endif + + // + // Something we don't know about was submitted. + // + + default: + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint1 ("NbfDispatchInternal: invalid request type %lx\n", + IrpSp->MinorFunction); + } + Status = STATUS_INVALID_DEVICE_REQUEST; + } + + if (Status == STATUS_PENDING) { + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: request PENDING from handler.\n"); + } + } else { + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint0 ("NbfDispatchInternal: request COMPLETED by handler.\n"); + } + + LEAVE_NBF; + IrpSp->Control &= ~SL_PENDING_RETURNED; + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + ENTER_NBF; + } + + + IF_NBFDBG (NBF_DEBUG_DISPATCH) { + NbfPrint1 ("NbfDispatchInternal: exiting, status: %lx\n",Status); + } + + // + // Return the immediate status code to the caller. + // + + LEAVE_NBF; +#if DBG + ASSERT (KeGetCurrentIrql() == IrqlOnEnter); +#endif + + return Status; + +} /* NbfDispatchInternal */ + + +VOID +NbfWriteResourceErrorLog( + IN PDEVICE_CONTEXT DeviceContext, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN ULONG BytesNeeded, + IN ULONG ResourceId + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + an out of resources condition. It will handle event codes + RESOURCE_POOL, RESOURCE_LIMIT, and RESOURCE_SPECIFIC. + +Arguments: + + DeviceContext - Pointer to the device context. + + ErrorCode - The transport event code. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet. + + BytesNeeded - If applicable, the number of bytes that could not + be allocated. + + ResourceId - The resource ID of the allocated structure. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + PWSTR SecondString; + ULONG SecondStringSize; + PUCHAR StringLoc; + WCHAR ResourceIdBuffer[3]; + WCHAR SizeBuffer[2]; + WCHAR SpecificMaxBuffer[11]; + ULONG SpecificMax; + INT i; + + switch (ErrorCode) { + + case EVENT_TRANSPORT_RESOURCE_POOL: + SecondString = NULL; + SecondStringSize = 0; + break; + + case EVENT_TRANSPORT_RESOURCE_LIMIT: + SecondString = SizeBuffer; + SecondStringSize = sizeof(SizeBuffer); + + switch (DeviceContext->MemoryLimit) { + case 100000: SizeBuffer[0] = L'1'; break; + case 250000: SizeBuffer[0] = L'2'; break; + case 0: SizeBuffer[0] = L'3'; break; + default: SizeBuffer[0] = L'0'; break; + } + SizeBuffer[1] = 0; + break; + + case EVENT_TRANSPORT_RESOURCE_SPECIFIC: + switch (ResourceId) { + case UI_FRAME_RESOURCE_ID: SpecificMax = DeviceContext->SendPacketPoolSize; break; + case PACKET_RESOURCE_ID: SpecificMax = DeviceContext->SendPacketPoolSize; break; + case RECEIVE_PACKET_RESOURCE_ID: SpecificMax = DeviceContext->ReceivePacketPoolSize; break; + case RECEIVE_BUFFER_RESOURCE_ID: SpecificMax = DeviceContext->SendPacketPoolSize+DeviceContext->ReceivePacketPoolSize; break; + case ADDRESS_RESOURCE_ID: SpecificMax = DeviceContext->MaxAddresses; break; + case ADDRESS_FILE_RESOURCE_ID: SpecificMax = DeviceContext->MaxAddressFiles; break; + case CONNECTION_RESOURCE_ID: SpecificMax = DeviceContext->MaxConnections; break; + case LINK_RESOURCE_ID: SpecificMax = DeviceContext->MaxLinks; break; + case REQUEST_RESOURCE_ID: SpecificMax = DeviceContext->MaxRequests; break; + } + + for (i=9; i>=0; i--) { + SpecificMaxBuffer[i] = (WCHAR)((SpecificMax % 10) + L'0'); + SpecificMax /= 10; + if (SpecificMax == 0) { + break; + } + } + SecondString = SpecificMaxBuffer + i; + SecondStringSize = sizeof(SpecificMaxBuffer) - (i * sizeof(WCHAR)); + SpecificMaxBuffer[10] = 0; + break; + + default: + ASSERT (FALSE); + break; + } + + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + + DeviceContext->DeviceNameLength + + sizeof(ResourceIdBuffer) + + SecondStringSize; + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)DeviceContext, + EntrySize + ); + + // + // Convert the resource ID into a buffer. + // + + ResourceIdBuffer[1] = (WCHAR)((ResourceId % 10) + L'0'); + ResourceId /= 10; + ASSERT(ResourceId <= 9); + ResourceIdBuffer[0] = (WCHAR)((ResourceId % 10) + L'0'); + ResourceIdBuffer[2] = 0; + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = sizeof(ULONG); + errorLogEntry->NumberOfStrings = (SecondString == NULL) ? 2 : 3; + errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = 0; + errorLogEntry->FinalStatus = STATUS_INSUFFICIENT_RESOURCES; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + errorLogEntry->DumpData[0] = BytesNeeded; + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + RtlCopyMemory (StringLoc, DeviceContext->DeviceName, DeviceContext->DeviceNameLength); + StringLoc += DeviceContext->DeviceNameLength; + + RtlCopyMemory (StringLoc, ResourceIdBuffer, sizeof(ResourceIdBuffer)); + StringLoc += sizeof(ResourceIdBuffer); + + if (SecondString) { + RtlCopyMemory (StringLoc, SecondString, SecondStringSize); + } + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* NbfWriteResourceErrorLog */ + + +VOID +NbfWriteGeneralErrorLog( + IN PDEVICE_CONTEXT DeviceContext, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN ULONG DumpDataCount, + IN ULONG DumpData[] + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + a general problem as indicated by the parameters. It handles + event codes REGISTER_FAILED, BINDING_FAILED, ADAPTER_NOT_FOUND, + TRANSFER_DATA, TOO_MANY_LINKS, and BAD_PROTOCOL. All these + events have messages with one or two strings in them. + +Arguments: + + DeviceContext - Pointer to the device context, or this may be + a driver object instead. + + ErrorCode - The transport event code. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet. + + FinalStatus - Used as the FinalStatus in the error log packet. + + SecondString - If not NULL, the string to use as the %3 + value in the error log packet. + + DumpDataCount - The number of ULONGs of dump data. + + DumpData - Dump data for the packet. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + ULONG SecondStringSize; + PUCHAR StringLoc; + PWSTR DriverName; + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + + (DumpDataCount * sizeof(ULONG)); + + if (DeviceContext->Type == IO_TYPE_DEVICE) { + EntrySize += (UCHAR)DeviceContext->DeviceNameLength; + } else { + DriverName = L"Nbf"; + EntrySize += 4 * sizeof(WCHAR); + } + + if (SecondString) { + SecondStringSize = (wcslen(SecondString)*sizeof(WCHAR)) + sizeof(UNICODE_NULL); + EntrySize += (UCHAR)SecondStringSize; + } + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)DeviceContext, + EntrySize + ); + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = (USHORT)(DumpDataCount * sizeof(ULONG)); + errorLogEntry->NumberOfStrings = (SecondString == NULL) ? 1 : 2; + errorLogEntry->StringOffset = + sizeof(IO_ERROR_LOG_PACKET) + ((DumpDataCount-1) * sizeof(ULONG)); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = UniqueErrorValue; + errorLogEntry->FinalStatus = FinalStatus; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + + if (DumpDataCount) { + RtlCopyMemory(errorLogEntry->DumpData, DumpData, DumpDataCount * sizeof(ULONG)); + } + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + if (DeviceContext->Type == IO_TYPE_DEVICE) { + RtlCopyMemory (StringLoc, DeviceContext->DeviceName, DeviceContext->DeviceNameLength); + StringLoc += DeviceContext->DeviceNameLength; + } else { + RtlCopyMemory (StringLoc, DriverName, 4 * sizeof(WCHAR)); + StringLoc += 4 * sizeof(WCHAR); + } + if (SecondString) { + RtlCopyMemory (StringLoc, SecondString, SecondStringSize); + } + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* NbfWriteGeneralErrorLog */ + + +VOID +NbfWriteOidErrorLog( + IN PDEVICE_CONTEXT DeviceContext, + IN NTSTATUS ErrorCode, + IN NTSTATUS FinalStatus, + IN PWSTR AdapterString, + IN ULONG OidValue + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + a problem querying or setting an OID on an adapter. It handles + event codes SET_OID_FAILED and QUERY_OID_FAILED. + +Arguments: + + DeviceContext - Pointer to the device context. + + ErrorCode - Used as the ErrorCode in the error log packet. + + FinalStatus - Used as the FinalStatus in the error log packet. + + AdapterString - The name of the adapter we were bound to. + + OidValue - The OID which could not be set or queried. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + ULONG AdapterStringSize; + PUCHAR StringLoc; + WCHAR OidBuffer[9]; + INT i; + UINT CurrentDigit; + + AdapterStringSize = (wcslen(AdapterString)*sizeof(WCHAR)) + sizeof(UNICODE_NULL); + EntrySize = sizeof(IO_ERROR_LOG_PACKET) - + sizeof(ULONG) + + DeviceContext->DeviceNameLength + + AdapterStringSize + + sizeof(OidBuffer); + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)DeviceContext, + EntrySize + ); + + // + // Convert the OID into a buffer. + // + + for (i=7; i>=0; i--) { + CurrentDigit = OidValue & 0xf; + OidValue >>= 4; + if (CurrentDigit >= 0xa) { + OidBuffer[i] = (WCHAR)(CurrentDigit - 0xa + L'A'); + } else { + OidBuffer[i] = (WCHAR)(CurrentDigit + L'0'); + } + } + OidBuffer[8] = 0; + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = 0; + errorLogEntry->NumberOfStrings = 3; + errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET) - sizeof(ULONG); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = ErrorCode; + errorLogEntry->UniqueErrorValue = 0; + errorLogEntry->FinalStatus = FinalStatus; + errorLogEntry->SequenceNumber = (ULONG)-1; + errorLogEntry->IoControlCode = 0; + + StringLoc = ((PUCHAR)errorLogEntry) + errorLogEntry->StringOffset; + RtlCopyMemory (StringLoc, DeviceContext->DeviceName, DeviceContext->DeviceNameLength); + StringLoc += DeviceContext->DeviceNameLength; + + RtlCopyMemory (StringLoc, OidBuffer, sizeof(OidBuffer)); + StringLoc += sizeof(OidBuffer); + + RtlCopyMemory (StringLoc, AdapterString, AdapterStringSize); + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* NbfWriteOidErrorLog */ + +ULONG +NbfInitializeOneDeviceContext( + OUT PNDIS_STATUS NdisStatus, + IN PDRIVER_OBJECT DriverObject, + IN PCONFIG_DATA NbfConfig, + IN INT AdapterIndex + ) +/*++ + +Routine Description: + + This routine creates and initializes one nbf device context. In order to + do this it must successfully open and bind to the adapter described by + nbfconfig->names[adapterindex]. + +Arguments: + + NdisStatus - The outputted status of the operations. + + DriverObject - the nbf driver object. + + NbfConfig - the transport configuration information from the registry. + + AdapterIndex - which adapter to bind with. + +Return Value: + + The number of successful binds. + +--*/ + +{ + ULONG i, j; + PDEVICE_CONTEXT DeviceContext; + PTP_REQUEST Request; + PTP_LINK Link; + PTP_CONNECTION Connection; + PTP_ADDRESS_FILE AddressFile; + PTP_ADDRESS Address; + PTP_UI_FRAME UIFrame; + PTP_PACKET Packet; + PNDIS_PACKET NdisPacket; + PRECEIVE_PACKET_TAG ReceiveTag; + PBUFFER_TAG BufferTag; + NTSTATUS status; + UINT SuccessfulOpens; + UINT MaxUserData; + ULONG InitReceivePackets; + BOOLEAN UniProcessor; +#ifdef _PNP_POWER + PDEVICE_OBJECT DeviceObject; + UNICODE_STRING DeviceString; + UCHAR PermAddr[sizeof(TA_ADDRESS)+TDI_ADDRESS_LENGTH_NETBIOS]; + PTA_ADDRESS pAddress = (PTA_ADDRESS)PermAddr; + PTDI_ADDRESS_NETBIOS NetBIOSAddress = + (PTDI_ADDRESS_NETBIOS)pAddress->Address; + + pAddress->AddressLength = TDI_ADDRESS_LENGTH_NETBIOS; + pAddress->AddressType = TDI_ADDRESS_TYPE_NETBIOS; + NetBIOSAddress->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; +#endif + // + // Determine if we are on a uniprocessor. + // + + if (*KeNumberProcessors == 1) { + UniProcessor = TRUE; + } else { + UniProcessor = FALSE; + } + + { + j = AdapterIndex; + // + // Loop through all the adapters that are in the configuration + // information structure. Allocate a device object for each + // one that we find. + // + + status = NbfCreateDeviceContext( + DriverObject, + &NbfConfig->Names[NbfConfig->DevicesOffset+j], + &DeviceContext + ); + + if (!NT_SUCCESS (status)) { + NbfWriteGeneralErrorLog( + (PVOID)DriverObject, + EVENT_TRANSPORT_BINDING_FAILED, + 707, + status, + NbfConfig->Names[j].Buffer, + 0, + NULL); + *NdisStatus = status; + return(0); + } + + DeviceContext->UniProcessor = UniProcessor; + + // + // Initialize the timer and retry values (note that the link timeouts + // are converted from NT ticks to NBF ticks). These values may + // be modified by NbfInitializeNdis. + // + + DeviceContext->DefaultT1Timeout = NbfConfig->DefaultT1Timeout / SHORT_TIMER_DELTA; + DeviceContext->DefaultT2Timeout = NbfConfig->DefaultT2Timeout / SHORT_TIMER_DELTA; + DeviceContext->DefaultTiTimeout = NbfConfig->DefaultTiTimeout / LONG_TIMER_DELTA; + DeviceContext->LlcRetries = NbfConfig->LlcRetries; + DeviceContext->LlcMaxWindowSize = NbfConfig->LlcMaxWindowSize; + DeviceContext->MaxConsecutiveIFrames = (UCHAR)NbfConfig->MaximumIncomingFrames; + DeviceContext->NameQueryRetries = NbfConfig->NameQueryRetries; + DeviceContext->NameQueryTimeout = NbfConfig->NameQueryTimeout; + DeviceContext->AddNameQueryRetries = NbfConfig->AddNameQueryRetries; + DeviceContext->AddNameQueryTimeout = NbfConfig->AddNameQueryTimeout; + DeviceContext->GeneralRetries = NbfConfig->GeneralRetries; + DeviceContext->GeneralTimeout = NbfConfig->GeneralTimeout; + DeviceContext->MinimumSendWindowLimit = NbfConfig->MinimumSendWindowLimit; + + // + // Initialize our counter that records memory usage. + // + + DeviceContext->MemoryUsage = 0; + DeviceContext->MemoryLimit = NbfConfig->MaxMemoryUsage; + + DeviceContext->MaxRequests = NbfConfig->MaxRequests; + DeviceContext->MaxLinks = NbfConfig->MaxLinks; + DeviceContext->MaxConnections = NbfConfig->MaxConnections; + DeviceContext->MaxAddressFiles = NbfConfig->MaxAddressFiles; + DeviceContext->MaxAddresses = NbfConfig->MaxAddresses; + + // + // Now fire up NDIS so this adapter talks + // + + status = NbfInitializeNdis (DeviceContext, + NbfConfig, + j); + + if (!NT_SUCCESS (status)) { + + // + // Log an error if we were failed to + // open this adapter. + // + + NbfWriteGeneralErrorLog( + DeviceContext, + EVENT_TRANSPORT_BINDING_FAILED, + 601, + status, + NbfConfig->Names[j].Buffer, + 0, + NULL); + + NbfDereferenceDeviceContext ("Initialize NDIS failed", DeviceContext, DCREF_CREATION); + *NdisStatus = status; + return(0); + + } + +#if 0 + DbgPrint("Opened %S as %S\n", &NbfConfig->Names[j], &nameString); +#endif + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint6 ("NbfInitialize: NDIS returned: %x %x %x %x %x %x as local address.\n", + DeviceContext->LocalAddress.Address[0], + DeviceContext->LocalAddress.Address[1], + DeviceContext->LocalAddress.Address[2], + DeviceContext->LocalAddress.Address[3], + DeviceContext->LocalAddress.Address[4], + DeviceContext->LocalAddress.Address[5]); + } + + // + // Initialize our provider information structure; since it + // doesn't change, we just keep it around and copy it to + // whoever requests it. + // + + + MacReturnMaxDataSize( + &DeviceContext->MacInfo, + NULL, + 0, + DeviceContext->MaxSendPacketSize, + TRUE, + &MaxUserData); + + DeviceContext->Information.Version = 0x0100; + DeviceContext->Information.MaxSendSize = 0x1fffe; // 128k - 2 + DeviceContext->Information.MaxConnectionUserData = 0; + DeviceContext->Information.MaxDatagramSize = + MaxUserData - (sizeof(DLC_FRAME) + sizeof(NBF_HDR_CONNECTIONLESS)); + DeviceContext->Information.ServiceFlags = NBF_SERVICE_FLAGS; + if (DeviceContext->MacInfo.MediumAsync) { + DeviceContext->Information.ServiceFlags |= TDI_SERVICE_POINT_TO_POINT; + } + DeviceContext->Information.MinimumLookaheadData = + 240 - (sizeof(DLC_FRAME) + sizeof(NBF_HDR_CONNECTIONLESS)); + DeviceContext->Information.MaximumLookaheadData = + DeviceContext->MaxReceivePacketSize - (sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION)); + DeviceContext->Information.NumberOfResources = NBF_TDI_RESOURCES; + KeQuerySystemTime (&DeviceContext->Information.StartTime); + + + // + // Allocate various structures we will need. + // + + ENTER_NBF; + + // + // The TP_UI_FRAME structure has a CHAR[1] field at the end + // which we expand upon to include all the headers needed; + // the size of the MAC header depends on what the adapter + // told us about its max header size. + // + + DeviceContext->UIFrameHeaderLength = + DeviceContext->MacInfo.MaxHeaderLength + + sizeof(DLC_FRAME) + + sizeof(NBF_HDR_CONNECTIONLESS); + + DeviceContext->UIFrameLength = + FIELD_OFFSET(TP_UI_FRAME, Header[0]) + + DeviceContext->UIFrameHeaderLength; + + + // + // The TP_PACKET structure has a CHAR[1] field at the end + // which we expand upon to include all the headers needed; + // the size of the MAC header depends on what the adapter + // told us about its max header size. TP_PACKETs are used + // for connection-oriented frame as well as for + // control frames, but since DLC_I_FRAME and DLC_S_FRAME + // are the same size, the header is the same size. + // + + ASSERT (sizeof(DLC_I_FRAME) == sizeof(DLC_S_FRAME)); + + DeviceContext->PacketHeaderLength = + DeviceContext->MacInfo.MaxHeaderLength + + sizeof(DLC_I_FRAME) + + sizeof(NBF_HDR_CONNECTION); + + DeviceContext->PacketLength = + FIELD_OFFSET(TP_PACKET, Header[0]) + + DeviceContext->PacketHeaderLength; + + + // + // The BUFFER_TAG structure has a CHAR[1] field at the end + // which we expand upong to include all the frame data. + // + + DeviceContext->ReceiveBufferLength = + DeviceContext->MaxReceivePacketSize + + FIELD_OFFSET(BUFFER_TAG, Buffer[0]); + + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint0 ("NBFDRVR: pre-allocating requests.\n"); + } + for (i=0; i<NbfConfig->InitRequests; i++) { + + NbfAllocateRequest (DeviceContext, &Request); + + if (Request == NULL) { + PANIC ("NbfInitialize: insufficient memory to allocate requests.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + InsertTailList (&DeviceContext->RequestPool, &Request->Linkage); +#if DBG + NbfRequestTable[i+1] = (PVOID)Request; +#endif + } +#if DBG + NbfRequestTable[0] = (PVOID)NbfConfig->InitRequests; + NbfRequestTable[NbfConfig->InitRequests + 1] = (PVOID) + ((NBF_REQUEST_SIGNATURE << 16) | sizeof (TP_REQUEST)); + InitializeListHead (&NbfGlobalRequestList); +#endif + + DeviceContext->RequestInitAllocated = NbfConfig->InitRequests; + DeviceContext->RequestMaxAllocated = NbfConfig->MaxRequests; + + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint2 ("%d requests, %ld\n", NbfConfig->InitRequests, DeviceContext->MemoryUsage); + } + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint0 ("NBFDRVR: allocating links.\n"); + } + for (i=0; i<NbfConfig->InitLinks; i++) { + + NbfAllocateLink (DeviceContext, &Link); + + if (Link == NULL) { + PANIC ("NbfInitialize: insufficient memory to allocate links.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + InsertTailList (&DeviceContext->LinkPool, &Link->Linkage); +#if DBG + NbfLinkTable[i+1] = (PVOID)Link; +#endif + } +#if DBG + NbfLinkTable[0] = (PVOID)NbfConfig->InitLinks; + NbfLinkTable[NbfConfig->InitLinks+1] = (PVOID) + ((NBF_LINK_SIGNATURE << 16) | sizeof (TP_LINK)); +#endif + + DeviceContext->LinkInitAllocated = NbfConfig->InitLinks; + DeviceContext->LinkMaxAllocated = NbfConfig->MaxLinks; + + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint2 ("%d links, %ld\n", NbfConfig->InitLinks, DeviceContext->MemoryUsage); + } + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint0 ("NBFDRVR: allocating connections.\n"); + } + for (i=0; i<NbfConfig->InitConnections; i++) { + + NbfAllocateConnection (DeviceContext, &Connection); + + if (Connection == NULL) { + PANIC ("NbfInitialize: insufficient memory to allocate connections.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + InsertTailList (&DeviceContext->ConnectionPool, &Connection->LinkList); +#if DBG + NbfConnectionTable[i+1] = (PVOID)Connection; +#endif + } +#if DBG + NbfConnectionTable[0] = (PVOID)NbfConfig->InitConnections; + NbfConnectionTable[NbfConfig->InitConnections+1] = (PVOID) + ((NBF_CONNECTION_SIGNATURE << 16) | sizeof (TP_CONNECTION)); +#endif + + DeviceContext->ConnectionInitAllocated = NbfConfig->InitConnections; + DeviceContext->ConnectionMaxAllocated = NbfConfig->MaxConnections; + + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint2 ("%d connections, %ld\n", NbfConfig->InitConnections, DeviceContext->MemoryUsage); + } + + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint0 ("NBFDRVR: allocating AddressFiles.\n"); + } + for (i=0; i<NbfConfig->InitAddressFiles; i++) { + + NbfAllocateAddressFile (DeviceContext, &AddressFile); + + if (AddressFile == NULL) { + PANIC ("NbfInitialize: insufficient memory to allocate Address Files.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + InsertTailList (&DeviceContext->AddressFilePool, &AddressFile->Linkage); +#if DBG + NbfAddressFileTable[i+1] = (PVOID)AddressFile; +#endif + } +#if DBG + NbfAddressFileTable[0] = (PVOID)NbfConfig->InitAddressFiles; + NbfAddressFileTable[NbfConfig->InitAddressFiles + 1] = (PVOID) + ((NBF_ADDRESSFILE_SIGNATURE << 16) | + sizeof (TP_ADDRESS_FILE)); +#endif + + DeviceContext->AddressFileInitAllocated = NbfConfig->InitAddressFiles; + DeviceContext->AddressFileMaxAllocated = NbfConfig->MaxAddressFiles; + + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint2 ("%d address files, %ld\n", NbfConfig->InitAddressFiles, DeviceContext->MemoryUsage); + } + + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint0 ("NBFDRVR: allocating addresses.\n"); + } + for (i=0; i<NbfConfig->InitAddresses; i++) { + + NbfAllocateAddress (DeviceContext, &Address); + if (Address == NULL) { + PANIC ("NbfInitialize: insufficient memory to allocate addresses.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + InsertTailList (&DeviceContext->AddressPool, &Address->Linkage); +#if DBG + NbfAddressTable[i+1] = (PVOID)Address; +#endif + } +#if DBG + NbfAddressTable[0] = (PVOID)NbfConfig->InitAddresses; + NbfAddressTable[NbfConfig->InitAddresses + 1] = (PVOID) + ((NBF_ADDRESS_SIGNATURE << 16) | sizeof (TP_ADDRESS)); +#endif + + DeviceContext->AddressInitAllocated = NbfConfig->InitAddresses; + DeviceContext->AddressMaxAllocated = NbfConfig->MaxAddresses; + + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint2 ("%d addresses, %ld\n", NbfConfig->InitAddresses, DeviceContext->MemoryUsage); + } + + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint0 ("NBFDRVR: allocating UI frames.\n"); + } + + for (i=0; i<NbfConfig->InitUIFrames; i++) { + + NbfAllocateUIFrame (DeviceContext, &UIFrame); + + if (UIFrame == NULL) { + PANIC ("NbfInitialize: insufficient memory to allocate UI frames.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + InsertTailList (&(DeviceContext->UIFramePool), &UIFrame->Linkage); +#if DBG + NbfUiFrameTable[i+1] = UIFrame; +#endif + } +#if DBG + NbfUiFrameTable[0] = (PVOID)NbfConfig->InitUIFrames; +#endif + + DeviceContext->UIFrameInitAllocated = NbfConfig->InitUIFrames; + + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint2 ("%d UI frames, %ld\n", NbfConfig->InitUIFrames, DeviceContext->MemoryUsage); + } + + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint0 ("NBFDRVR: allocating I frames.\n"); + NbfPrint1 ("NBFDRVR: Packet pool header: %lx\n",&DeviceContext->PacketPool); + } + + for (i=0; i<NbfConfig->InitPackets; i++) { + + NbfAllocateSendPacket (DeviceContext, &Packet); + if (Packet == NULL) { + PANIC ("NbfInitialize: insufficient memory to allocate packets.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + PushEntryList (&DeviceContext->PacketPool, (PSINGLE_LIST_ENTRY)&Packet->Linkage); +#if DBG + NbfSendPacketTable[i+1] = Packet; +#endif + } +#if DBG + NbfSendPacketTable[0] = (PVOID)NbfConfig->InitPackets; + NbfSendPacketTable[NbfConfig->InitPackets+1] = (PVOID) + ((NBF_PACKET_SIGNATURE << 16) | sizeof (TP_PACKET)); +#endif + + DeviceContext->PacketInitAllocated = NbfConfig->InitPackets; + + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint2 ("%d send packets, %ld\n", NbfConfig->InitPackets, DeviceContext->MemoryUsage); + } + + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint0 ("NBFDRVR: allocating RR frames.\n"); + NbfPrint1 ("NBFDRVR: Packet pool header: %lx\n",&DeviceContext->RrPacketPool); + } + + for (i=0; i<10; i++) { + + NbfAllocateSendPacket (DeviceContext, &Packet); + if (Packet == NULL) { + PANIC ("NbfInitialize: insufficient memory to allocate packets.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + Packet->Action = PACKET_ACTION_RR; + PushEntryList (&DeviceContext->RrPacketPool, (PSINGLE_LIST_ENTRY)&Packet->Linkage); + } + + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint2 ("%d send packets, %ld\n", 10, DeviceContext->MemoryUsage); + } + + + // Allocate receive Ndis packets + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint0 ("NBFDRVR: allocating Ndis Receive packets.\n"); + } + if (DeviceContext->MacInfo.SingleReceive) { + InitReceivePackets = 2; + } else { + InitReceivePackets = NbfConfig->InitReceivePackets; + } + for (i=0; i<InitReceivePackets; i++) { + + NbfAllocateReceivePacket (DeviceContext, &NdisPacket); + + if (NdisPacket == NULL) { + PANIC ("NbfInitialize: insufficient memory to allocate packet MDLs.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + ReceiveTag = (PRECEIVE_PACKET_TAG)NdisPacket->ProtocolReserved; + PushEntryList (&DeviceContext->ReceivePacketPool, &ReceiveTag->Linkage); + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + PNDIS_BUFFER NdisBuffer; + NdisQueryPacket(NdisPacket, NULL, NULL, &NdisBuffer, NULL); + NbfPrint2 ("NbfInitialize: Created NDIS Pkt: %x Buffer: %x\n", + NdisPacket, NdisBuffer); + } + } + + DeviceContext->ReceivePacketInitAllocated = InitReceivePackets; + + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint2 ("%d receive packets, %ld\n", InitReceivePackets, DeviceContext->MemoryUsage); + } + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint0 ("NBFDRVR: allocating Ndis Receive buffers.\n"); + } + + for (i=0; i<NbfConfig->InitReceiveBuffers; i++) { + + NbfAllocateReceiveBuffer (DeviceContext, &BufferTag); + + if (BufferTag == NULL) { + PANIC ("NbfInitialize: Unable to allocate receive packet.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + PushEntryList (&DeviceContext->ReceiveBufferPool, (PSINGLE_LIST_ENTRY)&BufferTag->Linkage); + + } + + DeviceContext->ReceiveBufferInitAllocated = NbfConfig->InitReceiveBuffers; + + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint2 ("%d receive buffers, %ld\n", NbfConfig->InitReceiveBuffers, DeviceContext->MemoryUsage); + } + + DeviceContext->State = DEVICECONTEXT_STATE_OPEN; + + // + // Start the link-level timers running. + // + + NbfInitializeTimerSystem (DeviceContext); + + + // + // Now link the device into the global list. + // + + InsertTailList (&NbfDeviceList, &DeviceContext->Linkage); + + ++SuccessfulOpens; + +#ifdef _PNP_POWER + DeviceObject = (PDEVICE_OBJECT) DeviceContext; + DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + + RtlInitUnicodeString(&DeviceString, DeviceContext->DeviceName); + status = TdiRegisterDeviceObject(&DeviceString, + &DeviceContext->TdiDeviceHandle); + + if (!NT_SUCCESS (status)) { + --SuccessfulOpens; + RemoveEntryList(&DeviceContext->Linkage); + goto cleanup; + } + + RtlCopyMemory(NetBIOSAddress->NetbiosName, + DeviceContext->ReservedNetBIOSAddress, 16); + + + status = TdiRegisterNetAddress(pAddress, + &DeviceContext->ReservedAddressHandle); + + if (!NT_SUCCESS (status)) { + --SuccessfulOpens; + RemoveEntryList(&DeviceContext->Linkage); + goto cleanup; + } +#endif + + LEAVE_NBF; + *NdisStatus = NDIS_STATUS_SUCCESS; + + return(1);; + +cleanup: + + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 501, + DeviceContext->MemoryUsage, + 0); + + // + // Cleanup whatever device context we were initializing + // when we failed. + // + *NdisStatus = status; + ASSERT(status != STATUS_SUCCESS); + + NbfFreeResources (DeviceContext); + NbfCloseNdis (DeviceContext); + NbfDereferenceDeviceContext ("Load failed", DeviceContext, DCREF_CREATION); + + + LEAVE_NBF; + } + return(0); +} diff --git a/private/ntos/tdi/nbf/nbfhdrs.h b/private/ntos/tdi/nbf/nbfhdrs.h new file mode 100644 index 000000000..9dfa5966f --- /dev/null +++ b/private/ntos/tdi/nbf/nbfhdrs.h @@ -0,0 +1,257 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + nbfhdrs.h + +Abstract: + + This module defines private structure definitions describing the layout + of the NetBIOS Frames Protocol headers for the NT NBF transport + provider. + +Author: + + Stephen E. Jones (stevej) 25-Oct-1989 + +Revision History: + + David Beaver (dbeaver) 24-Sep-1990 + remove pc586 and PDI specific code; add NDIS support + +--*/ + +#ifndef _NBFHDRS_ +#define _NBFHDRS_ + +// +// Pack these headers, as they are sent fully packed on the network. +// + +#ifdef PACKING + +#ifdef __STDC__ +#pragma Off(Align_members) +#else +#pragma pack(1) +#endif // def __STDC__ + +#endif // def PACKING + +#define NETBIOS_SIGNATURE_1 0xef // signature in NetBIOS frames. +#define NETBIOS_SIGNATURE_0 0xff // 1st byte. +#define NETBIOS_SIGNATURE 0xefff + +// +// NetBIOS Frames Protocol Command Codes. +// + +#define NBF_CMD_ADD_GROUP_NAME_QUERY 0x00 +#define NBF_CMD_ADD_NAME_QUERY 0x01 +#define NBF_CMD_NAME_IN_CONFLICT 0x02 +#define NBF_CMD_STATUS_QUERY 0x03 +#define NBF_CMD_TERMINATE_TRACE 0x07 +#define NBF_CMD_DATAGRAM 0x08 +#define NBF_CMD_DATAGRAM_BROADCAST 0x09 +#define NBF_CMD_NAME_QUERY 0x0a +#define NBF_CMD_ADD_NAME_RESPONSE 0x0d +#define NBF_CMD_NAME_RECOGNIZED 0x0e +#define NBF_CMD_STATUS_RESPONSE 0x0f +#define NBF_CMD_TERMINATE_TRACE2 0x13 +#define NBF_CMD_DATA_ACK 0x14 +#define NBF_CMD_DATA_FIRST_MIDDLE 0x15 +#define NBF_CMD_DATA_ONLY_LAST 0x16 +#define NBF_CMD_SESSION_CONFIRM 0x17 +#define NBF_CMD_SESSION_END 0x18 +#define NBF_CMD_SESSION_INITIALIZE 0x19 +#define NBF_CMD_NO_RECEIVE 0x1a +#define NBF_CMD_RECEIVE_OUTSTANDING 0x1b +#define NBF_CMD_RECEIVE_CONTINUE 0x1c +#define NBF_CMD_SESSION_ALIVE 0x1f + +// +// NBF Transport Layer Header. +// + +typedef struct _NBF_HDR_GENERIC { + USHORT Length; // Length of this header in bytes. + UCHAR Signature [2]; // always {0xef, 0xff} for NBF. + UCHAR Command; // command code, NBF_CMD_xxx. + UCHAR Data1; // optional parameter. + USHORT Data2; // optional parameter. + USHORT TransmitCorrelator; // transmit correlator parameter. + USHORT ResponseCorrelator; // response correlator parameter. +} NBF_HDR_GENERIC; +typedef NBF_HDR_GENERIC UNALIGNED *PNBF_HDR_GENERIC; + +typedef struct _NBF_HDR_CONNECTION { + USHORT Length; // length of the header in bytes (14). + USHORT Signature; // always {0xef, 0xff} for NBF. + UCHAR Command; // command code, NBF_CMD_xxx. + UCHAR Data1; // optional parameter. + UCHAR Data2Low, Data2High; // Intel-formatted DW parameter. + USHORT TransmitCorrelator; // Intel-formatted DW param. (transmit correlator). + USHORT ResponseCorrelator; // Intel-formatted DW param. (response correlator). + UCHAR DestinationSessionNumber; // connection identifier of packet receiver. + UCHAR SourceSessionNumber; // connection identifier of packet sender. +} NBF_HDR_CONNECTION; +typedef NBF_HDR_CONNECTION UNALIGNED *PNBF_HDR_CONNECTION; + +typedef struct _NBF_HDR_CONNECTIONLESS { + USHORT Length; // length of the header in bytes (44). + USHORT Signature; // always {0xef, 0xff} for NBF. + UCHAR Command; // command code, NBF_CMD_xxx. + UCHAR Data1; // optional parameter. + UCHAR Data2Low, Data2High; // Intel-formatted DW parameter. + USHORT TransmitCorrelator; // Intel-formatted DW param. (transmit correlator). + USHORT ResponseCorrelator; // Intel-formatted DW param. (response correlator). + UCHAR DestinationName [NETBIOS_NAME_LENGTH]; // name of packet receiver. + UCHAR SourceName [NETBIOS_NAME_LENGTH]; // name of packet sender. +} NBF_HDR_CONNECTIONLESS; +typedef NBF_HDR_CONNECTIONLESS UNALIGNED *PNBF_HDR_CONNECTIONLESS; + +// +// These macros are used to retrieve the transmit and response +// correlators from an NBF_HDR_CONNECTION(LESS). The first two +// are general, the second two are used when the correlators +// are known to be WORD aligned. +// + +#define TRANSMIT_CORR(_Hdr) (*(USHORT UNALIGNED *)(&(_Hdr)->TransmitCorrelator)) +#define RESPONSE_CORR(_Hdr) (*(USHORT UNALIGNED *)(&(_Hdr)->ResponseCorrelator)) + +#define TRANSMIT_CORR_A(_Hdr) ((_Hdr)->TransmitCorrelator) +#define RESPONSE_CORR_A(_Hdr) ((_Hdr)->ResponseCorrelator) + +#define HEADER_LENGTH(_Hdr) (*(USHORT UNALIGNED *)(&(_Hdr)->Length)) +#define HEADER_SIGNATURE(_Hdr) (*(USHORT UNALIGNED *)(&(_Hdr)->Signature)) + +#define HEADER_LENGTH_A(_Hdr) ((_Hdr)->Length) +#define HEADER_SIGNATURE_A(_Hdr) ((_Hdr)->Signature) + +typedef union _NBF_HDR { + NBF_HDR_GENERIC Generic; + NBF_HDR_CONNECTION ConnectionOrientedFrame; + NBF_HDR_CONNECTIONLESS ConnectionlessFrame; +} NBF_HDR; +typedef NBF_HDR UNALIGNED *PNBF_HDR; + +// +// The following structures define I-frame, U-frame, and S-frame DLC headers. +// + +#define DLC_SSAP_RESPONSE 0x0001 // if (ssap & DLC_SSAP_RESP), it's a response. +#define DLC_SSAP_GLOBAL 0x00ff // the global SAP. +#define DLC_SSAP_NULL 0x0000 // the null SAP. +#define DLC_SSAP_MASK 0x00fe // mask to wipe out the response bit. +#define DLC_DSAP_MASK 0x00fe // mask to wipe out the group SAP bit. + +#define DLC_CMD_RR 0x01 // command code for RR. +#define DLC_CMD_RNR 0x05 // command code for RNR. +#define DLC_CMD_REJ 0x09 // command code for REJ. + +#define DLC_CMD_SABME 0x6f // command code for SABME. +#define DLC_CMD_DISC 0x43 // command code for DISC. +#define DLC_CMD_UA 0x63 // command code for UA. +#define DLC_CMD_DM 0x0f // command code for DM. +#define DLC_CMD_FRMR 0x87 // command code for FRMR. +#define DLC_CMD_UI 0x03 // command code for UI. +#define DLC_CMD_XID 0xaf // command code for XID. +#define DLC_CMD_TEST 0xe3 // command code for TEST. + +typedef struct _DLC_XID_INFORMATION { + UCHAR FormatId; // format of this XID frame. + UCHAR Info1; // first information byte. + UCHAR Info2; // second information byte. +} DLC_XID_INFORMATION; +typedef DLC_XID_INFORMATION UNALIGNED *PDLC_XID_INFORMATION; + +typedef struct _DLC_TEST_INFORMATION { + UCHAR Buffer [10]; // this buffer is actually arbitrarily large. +} DLC_TEST_INFORMATION; +typedef DLC_TEST_INFORMATION UNALIGNED *PDLC_TEST_INFORMATION; + +typedef struct _DLC_FRMR_INFORMATION { + UCHAR Command; // format: mmmpmm11, m=modifiers, p=poll/final. + UCHAR Ctrl; // control field of rejected frame. + UCHAR Vs; // our next send when error was detected. + UCHAR Vr; // our next receive when error was detected. + UCHAR Reason; // reason for sending FRMR: 000VZYXW. +} DLC_FRMR_INFORMATION; +typedef DLC_FRMR_INFORMATION UNALIGNED *PDLC_FRMR_INFORMATION; + +typedef struct _DLC_U_FRAME { + UCHAR Dsap; // Destination Service Access Point. + UCHAR Ssap; // Source Service Access Point. + UCHAR Command; // command code. + union { // information field for FRMR, TEST, XID. + DLC_XID_INFORMATION XidInfo; // XID information. + DLC_TEST_INFORMATION TestInfo; // TEST information. + DLC_FRMR_INFORMATION FrmrInfo; // FRMR information. + NBF_HDR_CONNECTIONLESS NbfHeader; // UI frame contains NetBIOS header. + } Information; +} DLC_U_FRAME; +typedef DLC_U_FRAME UNALIGNED *PDLC_U_FRAME; + +#define DLC_U_INDICATOR 0x03 // (cmd & DLC_U_IND) == DLC_U_IND --> U-frame. +#define DLC_U_PF 0x10 // (cmd & DLC_U_PF) -> poll/final set. + +typedef struct _DLC_S_FRAME { + UCHAR Dsap; // Destination Service Access Point. + UCHAR Ssap; // Source Service Access Point. + UCHAR Command; // RR, RNR, REJ command code. + UCHAR RcvSeq; // receive seq #, bottom bit is poll/final. +} DLC_S_FRAME; +typedef DLC_S_FRAME UNALIGNED *PDLC_S_FRAME; + +#define DLC_S_PF 0x01 // (rcvseq & DLC_S_PF) means poll/final set. + +typedef struct _DLC_I_FRAME { + UCHAR Dsap; // Destination Service Access Point. + UCHAR Ssap; // Source Service Access Point. + UCHAR SendSeq; // send sequence number, bottom bit 0. + UCHAR RcvSeq; // rcv sequence number, bottom bit p/f. +} DLC_I_FRAME; +typedef DLC_I_FRAME UNALIGNED *PDLC_I_FRAME; + +#define DLC_I_PF 0x01 // (rcvseq & DLC_I_PF) means poll/final set. +#define DLC_I_INDICATOR 0x01 // !(sndseq & DLC_I_INDICATOR) indicates I-frame. + +typedef struct _DLC_FRAME { + UCHAR Dsap; // Destination Service Access Point. + UCHAR Ssap; // Source Service Access Point. + UCHAR Byte1; // command byte. +} DLC_FRAME; +typedef DLC_FRAME UNALIGNED *PDLC_FRAME; + + +// +// This macro builds a DLC UI-frame header. +// + +#define NbfBuildUIFrameHeader(_Header) \ +{ \ + PDLC_FRAME DlcHeader = (PDLC_FRAME)(_Header); \ + DlcHeader->Dsap = DSAP_NETBIOS_OVER_LLC; \ + DlcHeader->Ssap = DSAP_NETBIOS_OVER_LLC; \ + DlcHeader->Byte1 = DLC_CMD_UI; \ +} + + +// +// Resume previous structure packing method. +// + +#ifdef PACKING + +#ifdef __STDC__ +#pragma Pop(Align_members) +#else +#pragma pack() +#endif // def __STDC__ + +#endif // def PACKING + +#endif // def _NBFHDRS_ diff --git a/private/ntos/tdi/nbf/nbfmac.c b/private/ntos/tdi/nbf/nbfmac.c new file mode 100644 index 000000000..bbc418c19 --- /dev/null +++ b/private/ntos/tdi/nbf/nbfmac.c @@ -0,0 +1,417 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + nbfmac.c + +Abstract: + + This module contains code which implements Mac type dependent code for + the NBF transport. + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode (Actually, unimportant) + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +UCHAR SingleRouteSourceRouting[] = { 0xc2, 0x70 }; +UCHAR GeneralRouteSourceRouting[] = { 0x82, 0x70 }; +ULONG DefaultSourceRoutingLength = 2; + +// +// This is the interpretation of the length bits in +// the 802.5 source-routing information. +// + +ULONG SR802_5Lengths[8] = { 516, 1500, 2052, 4472, + 8144, 11407, 17800, 17800 }; + + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE,MacInitializeMacInfo) +#pragma alloc_text(PAGE,MacSetNetBIOSMulticast) +#endif + + +VOID +MacInitializeMacInfo( + IN NDIS_MEDIUM MacType, + IN BOOLEAN UseDix, + OUT PNBF_NDIS_IDENTIFICATION MacInfo + ) + +/*++ + +Routine Description: + + Fills in the MacInfo table based on MacType. + +Arguments: + + MacType - The MAC type we wish to decode. + + UseDix - TRUE if we should use DIX encoding on 802.3. + + MacInfo - The MacInfo structure to fill in. + +Return Value: + + None. + +--*/ + +{ + switch (MacType) { + case NdisMedium802_3: + MacInfo->DestinationOffset = 0; + MacInfo->SourceOffset = 6; + MacInfo->SourceRouting = FALSE; + MacInfo->AddressLength = 6; + if (UseDix) { + MacInfo->TransferDataOffset = 3; + MacInfo->MaxHeaderLength = 17; + MacInfo->MediumType = NdisMediumDix; + } else { + MacInfo->TransferDataOffset = 0; + MacInfo->MaxHeaderLength = 14; + MacInfo->MediumType = NdisMedium802_3; + } + MacInfo->MediumAsync = FALSE; + break; + case NdisMedium802_5: + MacInfo->DestinationOffset = 2; + MacInfo->SourceOffset = 8; + MacInfo->SourceRouting = TRUE; + MacInfo->AddressLength = 6; + MacInfo->TransferDataOffset = 0; + MacInfo->MaxHeaderLength = 32; + MacInfo->MediumType = NdisMedium802_5; + MacInfo->MediumAsync = FALSE; + break; + case NdisMediumFddi: + MacInfo->DestinationOffset = 1; + MacInfo->SourceOffset = 7; + MacInfo->SourceRouting = FALSE; + MacInfo->AddressLength = 6; + MacInfo->TransferDataOffset = 0; + MacInfo->MaxHeaderLength = 13; + MacInfo->MediumType = NdisMediumFddi; + MacInfo->MediumAsync = FALSE; + break; + case NdisMediumWan: + MacInfo->DestinationOffset = 0; + MacInfo->SourceOffset = 6; + MacInfo->SourceRouting = FALSE; + MacInfo->AddressLength = 6; + MacInfo->TransferDataOffset = 0; + MacInfo->MaxHeaderLength = 14; + MacInfo->MediumType = NdisMedium802_3; + MacInfo->MediumAsync = TRUE; + break; + default: + ASSERT(FALSE); + } +} + +VOID +MacConstructHeader ( + IN PNBF_NDIS_IDENTIFICATION MacInfo, + IN PUCHAR Buffer, + IN PUCHAR DestinationAddress, + IN PUCHAR SourceAddress, + IN UINT PacketLength, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + OUT PUINT HeaderLength + ) + +/*++ + +Routine Description: + + This routine is called to construct the Mac header for the particular + network type we're talking to. + +Arguments: + + MacInfo - Describes the mac we wish to build a header for. + + Buffer - Where to build the header. + + DestinationAddress - the address this packet is to be sent to. + + SourceAddress - Our address. Passing it in as a parameter allows us to play + games with source if we need to. + + PacketLength - The length of this packet. Note that this does not + includes the Mac header. + + SourceRouting - Optional source routing information. + + SourceRoutingLength - The length of SourceRouting. + + HeaderLength - Returns the length of the constructed header. + +Return Value: + + None. + +--*/ +{ + + // + // Note network order of bytes. + // + + switch (MacInfo->MediumType) { + + case NdisMedium802_3: + + *(ULONG UNALIGNED *)&Buffer[6] = *(ULONG UNALIGNED *)&SourceAddress[0]; + Buffer[10] = SourceAddress[4]; + Buffer[11] = SourceAddress[5]; + + *(ULONG UNALIGNED *)&Buffer[0] = *(ULONG UNALIGNED *)&DestinationAddress[0]; + Buffer[4] = DestinationAddress[4]; + Buffer[5] = DestinationAddress[5]; + + Buffer[12] = (UCHAR)(PacketLength >> 8); + Buffer[13] = (UCHAR)PacketLength; + + *HeaderLength = 14; + + break; + + case NdisMediumDix: + + *(ULONG UNALIGNED *)&Buffer[6] = *(ULONG UNALIGNED *)&SourceAddress[0]; + Buffer[10] = SourceAddress[4]; + Buffer[11] = SourceAddress[5]; + + *(ULONG UNALIGNED *)&Buffer[0] = *(ULONG UNALIGNED *)&DestinationAddress[0]; + Buffer[4] = DestinationAddress[4]; + Buffer[5] = DestinationAddress[5]; + + Buffer[12] = 0x80; + Buffer[13] = 0xd5; + + Buffer[14] = (UCHAR)(PacketLength >> 8); + Buffer[15] = (UCHAR)PacketLength; + + Buffer[16] = 0x00; + *HeaderLength = 17; + + break; + + case NdisMedium802_5: + + Buffer[0] = TR_HEADER_BYTE_0; + Buffer[1] = TR_HEADER_BYTE_1; + + ASSERT (TR_ADDRESS_LENGTH == 6); + + *(ULONG UNALIGNED *)&Buffer[8] = *(ULONG UNALIGNED *)&SourceAddress[0]; + Buffer[12] = SourceAddress[4]; + Buffer[13] = SourceAddress[5]; + + *(ULONG UNALIGNED *)&Buffer[2] = *(ULONG UNALIGNED *)&DestinationAddress[0]; + Buffer[6] = DestinationAddress[4]; + Buffer[7] = DestinationAddress[5]; + + *HeaderLength = 14; + if (SourceRouting != NULL) { + RtlCopyMemory (&Buffer[14], SourceRouting, SourceRoutingLength); + Buffer[8] |= 0x80; // add SR bit in source address + *HeaderLength = 14 + SourceRoutingLength; + } + + break; + + case NdisMediumFddi: + + Buffer[0] = FDDI_HEADER_BYTE; + + *(ULONG UNALIGNED *)&Buffer[7] = *(ULONG UNALIGNED *)&SourceAddress[0]; + Buffer[11] = SourceAddress[4]; + Buffer[12] = SourceAddress[5]; + + *(ULONG UNALIGNED *)&Buffer[1] = *(ULONG UNALIGNED *)&DestinationAddress[0]; + Buffer[5] = DestinationAddress[4]; + Buffer[6] = DestinationAddress[5]; + + *HeaderLength = 13; + + break; + + default: + PANIC ("MacConstructHeader: PANIC! called with unsupported Mac type.\n"); + } +} + + +VOID +MacReturnMaxDataSize( + IN PNBF_NDIS_IDENTIFICATION MacInfo, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + IN UINT DeviceMaxFrameSize, + IN BOOLEAN AssumeWorstCase, + OUT PUINT MaxFrameSize + ) + +/*++ + +Routine Description: + + This routine returns the space available for user data in a MAC packet. + This will be the available space after the MAC header; all LLC and NBF + headers will be included in this space. + +Arguments: + + MacInfo - Describes the MAC we wish to decode. + + SourceRouting - If we are concerned about a reply to a specific + frame, then this information is used. + + SourceRouting - The length of SourceRouting. + + MaxFrameSize - The maximum frame size as returned by the adapter. + + AssumeWorstCase - TRUE if we should be pessimistic. + + MaxDataSize - The maximum data size computed. + +Return Value: + + None. + +--*/ + +{ + switch (MacInfo->MediumType) { + + case NdisMedium802_3: + + // + // For 802.3, we always have a 14-byte MAC header. + // + + *MaxFrameSize = DeviceMaxFrameSize - 14; + break; + + case NdisMediumDix: + + // + // For DIX, we have the 14-byte MAC header plus + // the three-byte DIX header. + // + + *MaxFrameSize = DeviceMaxFrameSize - 17; + break; + + case NdisMedium802_5: + + // + // For 802.5, if we have source routing information then + // use that, otherwise assume the worst if told to. + // + + if (SourceRouting && SourceRoutingLength >= 2) { + + UINT SRLength; + + SRLength = SR802_5Lengths[(SourceRouting[1] & 0x70) >> 4]; + DeviceMaxFrameSize -= (SourceRoutingLength + 14); + + if (DeviceMaxFrameSize < SRLength) { + *MaxFrameSize = DeviceMaxFrameSize; + } else { + *MaxFrameSize = SRLength; + } + + } else { + + if (!AssumeWorstCase) { + *MaxFrameSize = DeviceMaxFrameSize - 16; + } else if (DeviceMaxFrameSize < (544+sizeof(DLC_FRAME)+sizeof(NBF_HDR_CONNECTIONLESS))) { + *MaxFrameSize = DeviceMaxFrameSize - 32; + } else { + *MaxFrameSize = 512 + sizeof(DLC_FRAME) + sizeof(NBF_HDR_CONNECTIONLESS); + } + } + + break; + + case NdisMediumFddi: + + // + // For FDDI, we always have a 13-byte MAC header. + // + + *MaxFrameSize = DeviceMaxFrameSize - 13; + break; + + } +} + + + +VOID +MacSetNetBIOSMulticast ( + IN NDIS_MEDIUM Type, + IN PUCHAR Buffer + ) +/*++ + +Routine Description: + + This routine sets the NetBIOS broadcast address into a buffer provided + by the user. + +Arguments: + + Type the Mac Medium type. + + Buffer the buffer to put the multicast address in. + + +Return Value: + + none. + +--*/ +{ + switch (Type) { + case NdisMedium802_3: + case NdisMediumDix: + Buffer[0] = 0x03; + Buffer[ETHERNET_ADDRESS_LENGTH-1] = 0x01; + break; + + case NdisMedium802_5: + Buffer[0] = 0xc0; + Buffer[TR_ADDRESS_LENGTH-1] = 0x80; + break; + + case NdisMediumFddi: + Buffer[0] = 0x03; + Buffer[FDDI_ADDRESS_LENGTH-1] = 0x01; + break; + + default: + PANIC ("MacSetNetBIOSAddress: PANIC! called with unsupported Mac type.\n"); + } +} diff --git a/private/ntos/tdi/nbf/nbfmac.h b/private/ntos/tdi/nbf/nbfmac.h new file mode 100644 index 000000000..8710f1d20 --- /dev/null +++ b/private/ntos/tdi/nbf/nbfmac.h @@ -0,0 +1,766 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + mac.h + +Abstract: + + This header file defines manifest constants and necessary macros for use + by transports dealing with multiple MAC cards through the NDIS interface. + +Author: + + David Beaver (dbeaver) 02-Oct-1990 + +Revision History: + +--*/ + +#ifndef _MAC_ +#define _MAC_ + +// +// MAC-specific definitions, some of which get used below +// + +#define MAX_MAC_HEADER_LENGTH 32 +#define MAX_SOURCE_ROUTING 18 +#define MAX_DEFAULT_SR 2 + +#define ETHERNET_ADDRESS_LENGTH 6 +#define ETHERNET_PACKET_LENGTH 1514 // max size of an ethernet packet +#define ETHERNET_HEADER_LENGTH 14 // size of the ethernet MAC header +#define ETHERNET_DATA_LENGTH_OFFSET 12 +#define ETHERNET_DESTINATION_OFFSET 0 +#define ETHERNET_SOURCE_OFFSET 6 + +#define TR_ADDRESS_LENGTH 6 +#define TR_ADDRESS_OFFSET 2 +#define TR_SPECIFIC_OFFSET 0 +#define TR_PACKET_LENGTH 1514 // max size of a TR packet //BUGBUG +#define TR_HEADER_LENGTH 36 // size of the MAC header w/o source routing +#define TR_DATA_LENGTH_OFFSET 0 +#define TR_DESTINATION_OFFSET 2 +#define TR_SOURCE_OFFSET 8 +#define TR_ROUTING_OFFSET 14 // starts at the 14th byte +#define TR_GR_BCAST_LENGTH 2 +#define TR_GR_BROADCAST 0xC270 // what a general route b'cast looks like +#define TR_ROUTING_LENGTH_MASK 0x1F // low 5 bits in byte +#define TR_DIRECTION_MASK 0x80 // returns direction bit + +#define TR_PREAMBLE_AC 0x10 // how would these be specified? +#define TR_PREAMBLE_FC 0x40 + +#define TR_HEADER_BYTE_0 0x10 +#define TR_HEADER_BYTE_1 0x40 + +#define FDDI_ADDRESS_LENGTH 6 +#define FDDI_HEADER_BYTE 0x57 + + + +// +// We need this to define information about the MAC. Note that +// it is a strange structure in that the first four elements +// are for use internally by the nbfmac routines, while the +// DeviceContext knows about and uses the last two. +// + +typedef struct _NBF_NDIS_IDENTIFICATION { + NDIS_MEDIUM MediumType; + BOOLEAN SourceRouting; + BOOLEAN MediumAsync; + BOOLEAN QueryWithoutSourceRouting; + BOOLEAN AllRoutesNameRecognized; + ULONG DestinationOffset; + ULONG SourceOffset; + ULONG AddressLength; + ULONG TransferDataOffset; + ULONG MaxHeaderLength; + BOOLEAN CopyLookahead; + BOOLEAN ReceiveSerialized; + BOOLEAN TransferSynchronous; + BOOLEAN SingleReceive; +} NBF_NDIS_IDENTIFICATION, *PNBF_NDIS_IDENTIFICATION; + + + +VOID +MacConstructHeader( + IN PNBF_NDIS_IDENTIFICATION MacInfo, + IN PUCHAR Buffer, + IN PUCHAR DestinationAddress, + IN PUCHAR SourceAddress, + IN UINT PacketLength, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + OUT PUINT HeaderLength + ); + +VOID +MacReturnMaxDataSize( + IN PNBF_NDIS_IDENTIFICATION MacInfo, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + IN UINT DeviceMaxFrameSize, + IN BOOLEAN AssumeWorstCase, + OUT PUINT MaxFrameSize + ); + +VOID +MacInitializeMacInfo( + IN NDIS_MEDIUM MacType, + IN BOOLEAN UseDix, + OUT PNBF_NDIS_IDENTIFICATION MacInfo + ); + + +extern UCHAR SingleRouteSourceRouting[]; +extern UCHAR GeneralRouteSourceRouting[]; +extern ULONG DefaultSourceRoutingLength; + + +//++ +// +// VOID +// MacReturnDestinationAddress( +// IN PNBF_NDIS_IDENTIFICATION MacInfo, +// IN PVOID Packet, +// OUT PVOID * DestinationAddress +// ); +// +// Routine Description: +// +// Returns the a pointer to the destination address in the packet. +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// Packet - The packet data. +// +// DestinationAddress - Returns the start of the destination address. +// +// Return Value: +// +// None. +// +//-- + +#define MacReturnDestinationAddress(_MacInfo, _Packet, _DestinationAddress) \ + *(_DestinationAddress) = ((PUCHAR)(_Packet)) + (_MacInfo)->DestinationOffset + + +//++ +// +// VOID +// MacReturnSourceAddress( +// IN PNBF_NDIS_IDENTIFICATION MacInfo, +// IN PVOID Packet, +// OUT PHARDWARE_ADDRESS SourceAddressBuffer, +// OUT PHARDWARE_ADDRESS * SourceAddress, +// OUT BOOLEAN * Multicast OPTIONAL +// ); +// +// Routine Description: +// +// Copies the source address in the packet into SourceAddress. +// NOTE THAT IT MAY COPY THE DATA, UNLIKE ReturnDestinationAddress +// AND ReturnSourceRouting. Optionally, indicates whether the +// destination address is a multicast address. +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// Packet - The packet data. +// +// SourceAddressBuffer - A buffer to hold the source address, +// if needed. +// +// SourceAddress - Returns a pointer to the source address. +// +// Multicast - Optional pointer to a BOOLEAN to receive indication +// of whether the destination was a multicast address. +// +// Return Value: +// +// None. +// +//-- + +// +// NOTE: The default case below handles Ethernet and FDDI. +// + +#define MacReturnSourceAddress(_MacInfo, _Packet, _SourceAddressBuffer, \ + _SourceAddress, _Multicast) \ +{ \ + PUCHAR TmpPacket = (PUCHAR)(_Packet); \ + PUCHAR SrcBuffer = (PUCHAR)(_SourceAddressBuffer); \ + \ + switch ((_MacInfo)->MediumType) { \ + case NdisMedium802_5: \ + if (ARGUMENT_PRESENT(_Multicast)) { \ + *(PBOOLEAN)(_Multicast) = TmpPacket[2] & 0x80; \ + } \ + if (TmpPacket[8] & 0x80) { \ + *(PULONG)SrcBuffer = *(ULONG UNALIGNED *)(&TmpPacket[8]) & ~0x80;\ + SrcBuffer[4] = TmpPacket[12]; \ + SrcBuffer[5] = TmpPacket[13]; \ + *(_SourceAddress) = (PHARDWARE_ADDRESS)SrcBuffer; \ + } else { \ + *(_SourceAddress) = (PHARDWARE_ADDRESS)(TmpPacket + 8); \ + } \ + break; \ + default: \ + if (ARGUMENT_PRESENT(_Multicast)) { \ + *(PBOOLEAN)(_Multicast) = TmpPacket[0] & 0x01; \ + } \ + *(_SourceAddress) = (PHARDWARE_ADDRESS)(TmpPacket + \ + (_MacInfo)->SourceOffset); \ + break; \ + } \ +} + + +//++ +// +// VOID +// MacReturnSourceRouting( +// IN PNBF_NDIS_IDENTIFICATION MacInfo, +// IN PVOID Packet, +// OUT PVOID * SourceRouting +// OUT PUINT SourceRoutingLength +// ); +// +// Routine Description: +// +// Returns the a pointer to the source routing info in the packet. +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// Packet - The packet data. +// +// SourceRouting - Returns the start of the source routing information, +// or NULL if none is present. +// +// SourceRoutingLength - Returns the length of the source routing +// information. +// +// Return Value: +// +// None. +// +//-- + +#define MacReturnSourceRouting(_MacInfo, _Packet, _SourceRouting, _SourceRoutingLength) \ +{ \ + PUCHAR TmpPacket = (PUCHAR)(_Packet); \ + *(_SourceRoutingLength) = 0; \ + if ((_MacInfo)->SourceRouting) { \ + if (TmpPacket[8] & 0x80) { \ + *(_SourceRouting) = TmpPacket + 14; \ + *(_SourceRoutingLength) = TmpPacket[14] & 0x1f; \ + } else { \ + *(_SourceRouting) = NULL; \ + } \ + } else { \ + *(_SourceRouting) = NULL; \ + } \ +} + +//++ +// +// VOID +// MacIsMulticast( +// IN PNBF_NDIS_IDENTIFICATION MacInfo, +// IN PVOID Packet, +// OUT PBOOLEAN Multicast +// ); +// +// Routine Description: +// +// Returns TRUE if the packet is sent to the multicast address. +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// Packet - The packet data. +// +// Multicast - Returns the result. +// +// Return Value: +// +// None. +// +//-- + +#define MacIsMulticast(_MacInfo, _Packet, _Multicast) \ +{ \ + PUCHAR TmpPacket = (PUCHAR)(_Packet); \ + \ + switch ((_MacInfo)->MediumType) { \ + case NdisMedium802_5: \ + *(_Multicast) = ((TmpPacket[2] & 0x80) != 0); \ + break; \ + default: \ + *(_Multicast) = ((TmpPacket[0] & 0x01) != 0); \ + break; \ + } \ +} + +//++ +// +// VOID +// MacReturnPacketLength( +// IN PNBF_NDIS_IDENTIFICATION MacInfo, +// IN PVOID Header, +// IN UINT PacketLength, +// OUT PUINT DataLength +// ); +// +// Routine Description: +// +// Returns the length of data in the packet given the header. +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// Header - The packet header. +// +// PacketLength - The length of the data (not including header). +// +// DataLength - Returns the length of the data. Unchanged if the +// packet is not recognized. Should be initialized by caller to 0. +// +// Return Value: +// +// None. +// +//-- + +#define MacReturnPacketLength(_MacInfo, _Header, _HeaderLength, _PacketLength, _DataLength, _LookaheadBuffer, _LookaheadBufferLength) \ +{ \ + PUCHAR TmpPacket = (PUCHAR)(_Header); \ + UINT TmpLength; \ + \ + switch ((_MacInfo)->MediumType) { \ + case NdisMedium802_3: \ + if ((_HeaderLength) >= 14) { \ + TmpLength = (TmpPacket[12] << 8) | TmpPacket[13]; \ + if (TmpLength <= 0x600) { \ + if (TmpLength <= (_PacketLength)) { \ + *(_DataLength) = TmpLength; \ + } \ + } \ + } \ + break; \ + case NdisMedium802_5: \ + if (((_HeaderLength) >= 14) && \ + (!(TmpPacket[8] & 0x80) || \ + ((_HeaderLength) >= \ + (UINT)(14 + (TmpPacket[14] & 0x1f))))) { \ + *(_DataLength) = (_PacketLength); \ + } \ + break; \ + case NdisMediumFddi: \ + if ((_HeaderLength) >= 13) { \ + *(_DataLength) = (_PacketLength); \ + } \ + break; \ + case NdisMediumDix: \ + if ((TmpPacket[12] == 0x80) && (TmpPacket[13] == 0xd5)) { \ + if (*(_LookaheadBufferLength) >= 3) { \ + TmpPacket = (PUCHAR)(*(_LookaheadBuffer)); \ + TmpLength = (TmpPacket[0] << 8) | TmpPacket[1]; \ + if (TmpLength <= (_PacketLength)-3) { \ + *(_DataLength) = TmpLength; \ + *(_LookaheadBuffer) = (PVOID)(TmpPacket + 3); \ + *(_LookaheadBufferLength) -= 3; \ + } \ + } \ + } \ + break; \ + } \ +} + +//++ +// +// VOID +// MacReturnHeaderLength( +// IN PNBF_NDIS_IDENTIFICATION MacInfo, +// IN PVOID Packet, +// OUT PVOID HeaderLength, +// ); +// +// Routine Description: +// +// Returns the length of the MAC header in a packet (this +// is used for loopback indications to separate header +// and data). +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// Header - The packet header. +// +// HeaderLength - Returns the length of the header. +// +// Return Value: +// +// None. +// +//-- + +#define MacReturnHeaderLength(_MacInfo, _Header, _HeaderLength) \ +{ \ + PUCHAR TmpPacket = (PUCHAR)(_Header); \ + \ + switch ((_MacInfo)->MediumType) { \ + case NdisMedium802_3: \ + case NdisMediumDix: \ + *(_HeaderLength) = 14; \ + break; \ + case NdisMedium802_5: \ + if (TmpPacket[8] & 0x80) { \ + *(_HeaderLength) = (TmpPacket[14] & 0x1f) + 14; \ + } else { \ + *(_HeaderLength) = 14; \ + } \ + break; \ + case NdisMediumFddi: \ + *(_HeaderLength) = 13; \ + break; \ + } \ +} + +//++ +// +// VOID +// MacReturnSingleRouteSR( +// IN PNBF_NDIS_IDENTIFICATION MacInfo, +// OUT PVOID * SingleRouteSR, +// OUT PUINT SingleRouteSRLength +// ); +// +// Routine Description: +// +// Returns the a pointer to the standard single route broadcast +// source routing information for the media type. This is used +// for ADD_NAME_QUERY, DATAGRAM, NAME_IN_CONFLICT, NAME_QUERY, +// and STATUS_QUERY frames. +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// SingleRouteSR - Returns a pointer to the data. +// +// SingleRouteSRLength - The length of SingleRouteSR. +// +// Return Value: +// +// None. +// +//-- + +#define MacReturnSingleRouteSR(_MacInfo, _SingleRouteSR, _SingleRouteSRLength) \ +{ \ + switch ((_MacInfo)->MediumType) { \ + case NdisMedium802_5: \ + *(_SingleRouteSR) = SingleRouteSourceRouting; \ + *(_SingleRouteSRLength) = DefaultSourceRoutingLength; \ + break; \ + default: \ + *(_SingleRouteSR) = NULL; \ + break; \ + } \ +} + + +//++ +// +// VOID +// MacReturnGeneralRouteSR( +// IN PNBF_NDIS_IDENTIFICATION MacInfo, +// OUT PVOID * GeneralRouteSR, +// OUT PUINT GeneralRouteSRLength +// ); +// +// Routine Description: +// +// Returns the a pointer to the standard general route broadcast +// source routing information for the media type. This is used +// for NAME_RECOGNIZED frames. +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// GeneralRouteSR - Returns a pointer to the data. +// +// GeneralRouteSRLength - The length of GeneralRouteSR. +// +// Return Value: +// +// None. +// +//-- + +#define MacReturnGeneralRouteSR(_MacInfo, _GeneralRouteSR, _GeneralRouteSRLength) \ +{ \ + switch ((_MacInfo)->MediumType) { \ + case NdisMedium802_5: \ + *(_GeneralRouteSR) = GeneralRouteSourceRouting; \ + *(_GeneralRouteSRLength) = DefaultSourceRoutingLength; \ + break; \ + default: \ + *(_GeneralRouteSR) = NULL; \ + break; \ + } \ +} + +#if 0 + +//++ +// +// VOID +// MacCreateGeneralRouteReplySR( +// IN PNBF_NDIS_IDENTIFICATION MacInfo, +// IN PUCHAR ExistingSR, +// IN UINT ExistingSRLength, +// OUT PUCHAR * NewSR +// ); +// +// Routine Description: +// +// This modifies an existing source routing entry to make +// it into a general-route source routing entry. The assumption +// is that is to reply to existing source routing, so the +// direction bit is also reversed. In addition, if it is +// determined that no source routing is needed in the reply, +// then NULL is returned. +// +// Note that the information is modified in-place, but a +// separate pointer is returned (to allow NULL to be returned). +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// ExistingSR - The existing source routing to be modified. +// +// Return Value: +// +// None. +// +//-- + +#define MacCreateGeneralRouteReplySR(_MacInfo, _ExistingSR, _ExistingSRLength, _NewSR) \ +{ \ + if (_ExistingSR) { \ + PUCHAR TmpSR = (PUCHAR)(_ExistingSR); \ + switch ((_MacInfo)->MediumType) { \ + case NdisMedium802_5: \ + TmpSR[0] = (TmpSR[0] & 0x1f) | 0x80; \ + TmpSR[1] = (TmpSR[1] ^ 0x80); \ + *(_NewSR) = (_ExistingSR); \ + break; \ + default: \ + *(_NewSR) = (_ExistingSR); \ + break; \ + } \ + } else { \ + *(_NewSR) = NULL; \ + } \ +} +#endif + + +//++ +// +// VOID +// MacCreateNonBroadcastReplySR( +// IN PNBF_NDIS_IDENTIFICATION MacInfo, +// IN PUCHAR ExistingSR, +// IN UINT ExistingSRLength, +// OUT PUCHAR * NewSR +// ); +// +// Routine Description: +// +// This modifies an existing source routing entry to make +// it into a non-broadcast source routing entry. The assumption +// is that is to reply to existing source routing, so the +// direction bit is also reversed. In addition, if it is +// determined that no source routing is needed in the reply, +// then NULL is returned. +// +// Note that the information is modified in-place, but a +// separate pointer is returned (to allow NULL to be returned). +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// ExistingSR - The existing source routing to be modified. +// +// Return Value: +// +// None. +// +//-- + +#define MacCreateNonBroadcastReplySR(_MacInfo, _ExistingSR, _ExistingSRLength, _NewSR) \ +{ \ + if (_ExistingSR) { \ + PUCHAR TmpSR = (PUCHAR)(_ExistingSR); \ + switch ((_MacInfo)->MediumType) { \ + case NdisMedium802_5: \ + if ((_ExistingSRLength) == 2) { \ + *(_NewSR) = NULL; \ + } else { \ + TmpSR[0] = (TmpSR[0] & 0x1f); \ + TmpSR[1] = (TmpSR[1] ^ 0x80); \ + *(_NewSR) = (_ExistingSR); \ + } \ + break; \ + default: \ + *(_NewSR) = (_ExistingSR); \ + break; \ + } \ + } else { \ + *(_NewSR) = NULL; \ + } \ +} + + +//++ +// +// VOID +// MacModifyHeader( +// IN PNBF_NDIS_IDENTIFICATION MacInfo, +// IN PUCHAR Header, +// IN UINT PacketLength +// ); +// +// Routine Description: +// +// Modifies a pre-built packet header to include the +// packet length. Used for connection-oriented traffic +// where the header is pre-built. +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// Header - The header to modify. +// +// PacketLength - Packet length (not including the header). +// Currently this is the only field that cannot be pre-built. +// +// Return Value: +// +// None. +// +//-- + +#define MacModifyHeader(_MacInfo, _Header, _PacketLength) \ +{ \ + switch ((_MacInfo)->MediumType) { \ + case NdisMedium802_3: \ + (_Header)[12] = (UCHAR)((_PacketLength) >> 8); \ + (_Header)[13] = (UCHAR)((_PacketLength) & 0xff); \ + break; \ + case NdisMediumDix: \ + (_Header)[14] = (UCHAR)((_PacketLength) >> 8); \ + (_Header)[15] = (UCHAR)((_PacketLength) & 0xff); \ + break; \ + } \ +} + + +//++ +// +// VOID +// MacReturnMagicAddress( +// IN PNBF_NDIS_IDENTIFICATION MacInfo, +// IN PVOID Address, +// OUT PULARGE_INTEGER Magic +// ); +// +// Routine Description: +// +// MacReturnMagicAddress returns the link as a 64 bit number. +// We then find the link in the link trees by doing a large +// integer comparison. +// +// The number is constructed by assigning the last four bytes of +// the address as the low longword, and the first two bytes as +// the high one. For 802_5 we need to mask off the source routing +// bit in byte 0 of the address. +// +// Arguments: +// +// MacInfo - Describes the MAC we wish to decode. +// +// Address - The address we are encoding. +// +// Magic - Returns the magic number for this address. +// +// Return Value: +// +// None. +// +//-- + +#define MacReturnMagicAddress(_MacInfo, _Address, _Magic) \ +{ \ + PUCHAR TempAddr = (PUCHAR)(_Address); \ + \ + (_Magic)->LowPart = *((LONG UNALIGNED *)(TempAddr + 2)); \ + if ((_MacInfo)->MediumType == NdisMedium802_5) { \ + (_Magic)->HighPart = ((TempAddr[0] & 0x7f) << 8) + TempAddr[1]; \ + } else { \ + (_Magic)->HighPart = (TempAddr[0] << 8) + TempAddr[1]; \ + } \ +} + + +VOID +MacSetNetBIOSMulticast ( + IN NDIS_MEDIUM Type, + IN PUCHAR Buffer + ); + + + +// VOID +// NbfSetNdisPacketLength ( +// IN NDIS_PACKET Packet, +// IN ULONG Length +// ); +// +// NB: This is not a general purpose macro; it assumes that we are setting the +// length of an NDIS packet with only one NDIS_BUFFER chained. We do +// this to save time during the sending of short control packets. +// + +#define NbfSetNdisPacketLength(_packet,_length) { \ + PNDIS_BUFFER NdisBuffer; \ + NdisQueryPacket((_packet), NULL, NULL, &NdisBuffer, NULL); \ + NdisAdjustBufferLength(NdisBuffer, (_length)); \ + NdisRecalculatePacketCounts(_packet); \ +} + +#endif // ifdef _MAC_ + diff --git a/private/ntos/tdi/nbf/nbfndis.c b/private/ntos/tdi/nbf/nbfndis.c new file mode 100644 index 000000000..9dd7902bd --- /dev/null +++ b/private/ntos/tdi/nbf/nbfndis.c @@ -0,0 +1,1956 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + nbfndis.c + +Abstract: + + This module contains code which implements the routines used to interface + NBF and NDIS. All callback routines (except for Transfer Data, + Send Complete, and ReceiveIndication) are here, as well as those routines + called to initialize NDIS. + +Author: + + David Beaver (dbeaver) 13-Feb-1991 + +Environment: + + Kernel mode + +Revision History: + + David Beaver (dbeaver) 1-July-1991 + modify to use new TDI interface + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef NBF_LOCKS // see spnlckdb.c + +VOID +NbfFakeSendCompletionHandler( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus + ); + +VOID +NbfFakeTransferDataComplete ( + IN NDIS_HANDLE BindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus, + IN UINT BytesTransferred + ); + +#endif + + +// +// This is a one-per-driver variable used in binding +// to the NDIS interface. +// + +NDIS_HANDLE NbfNdisProtocolHandle = (NDIS_HANDLE)NULL; + + +NDIS_STATUS +NbfSubmitNdisRequest( + IN PDEVICE_CONTEXT DeviceContext, + IN PNDIS_REQUEST NdisRequest, + IN PNDIS_STRING AdapterName + ); + +VOID +NbfOpenAdapterComplete ( + IN NDIS_HANDLE BindingContext, + IN NDIS_STATUS NdisStatus, + IN NDIS_STATUS OpenErrorStatus + ); + +VOID +NbfCloseAdapterComplete( + IN NDIS_HANDLE NdisBindingContext, + IN NDIS_STATUS Status + ); + +VOID +NbfResetComplete( + IN NDIS_HANDLE NdisBindingContext, + IN NDIS_STATUS Status + ); + +VOID +NbfRequestComplete ( + IN NDIS_HANDLE BindingContext, + IN PNDIS_REQUEST NdisRequest, + IN NDIS_STATUS NdisStatus + ); + +VOID +NbfStatusIndication ( + IN NDIS_HANDLE NdisBindingContext, + IN NDIS_STATUS NdisStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferLength + ); + +VOID +NbfProcessStatusClosing( + IN PVOID Parameter + ); + +VOID +NbfStatusComplete ( + IN NDIS_HANDLE NdisBindingContext + ); + + +#ifdef _PNP_POWER +VOID +NbfProtocolBindAdapter( + OUT PNDIS_STATUS NdisStatus, + IN NDIS_HANDLE BindContext, + IN PNDIS_STRING DeviceName, + IN PVOID SystemSpecific1, + IN PVOID SystemSpecific2 + ); +VOID +NbfProtocolUnbindAdapter( + OUT PNDIS_STATUS NdisStatus, + IN NDIS_HANDLE ProtocolBindContext, + IN PNDIS_HANDLE UnbindContext + ); +#endif + +#ifdef ALLOC_PRAGMA +#ifndef _PNP_POWER +#pragma alloc_text(INIT,NbfRegisterProtocol) +#pragma alloc_text(INIT,NbfSubmitNdisRequest) +#pragma alloc_text(INIT,NbfInitializeNdis) +#else // PNP_POWER +#pragma alloc_text(PAGE,NbfProtocolBindAdapter) +#pragma alloc_text(PAGE,NbfProtocolUnbindAdapter) +#pragma alloc_text(PAGE,NbfRegisterProtocol) +#pragma alloc_text(PAGE,NbfSubmitNdisRequest) +#pragma alloc_text(PAGE,NbfInitializeNdis) +#endif +#endif + + +NTSTATUS +NbfRegisterProtocol ( + IN PUNICODE_STRING NameString + ) + +/*++ + +Routine Description: + + This routine introduces this transport to the NDIS interface. + +Arguments: + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + STATUS_SUCCESS if all goes well, + Failure status if we tried to register and couldn't, + STATUS_INSUFFICIENT_RESOURCES if we couldn't even try to register. + +--*/ + +{ + NDIS_STATUS ndisStatus; + + PNDIS_PROTOCOL_CHARACTERISTICS ProtChars; // Used temporarily to register + + ProtChars = ExAllocatePoolWithTag( + NonPagedPool, +#ifndef _PNP_POWER + sizeof(NDIS_PROTOCOL_CHARACTERISTICS) + +#else + sizeof(NDIS40_PROTOCOL_CHARACTERISTICS) + +#endif + NameString->MaximumLength, + ' FBN'); + + if (ProtChars == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Set up the characteristics of this protocol + // +#ifndef _PNP_POWER + ProtChars->MajorNdisVersion = 3; +#else + ProtChars->MajorNdisVersion = 4; + ProtChars->ReceivePacketHandler = NULL; + ProtChars->TranslateHandler = NULL; + ProtChars->BindAdapterHandler = NbfProtocolBindAdapter; // FIX ME!!! + ProtChars->UnbindAdapterHandler = NbfProtocolUnbindAdapter; // FIX ME!!! +#endif + ProtChars->MinorNdisVersion = 0; + + ProtChars->Name.Length = NameString->Length; + ProtChars->Name.Buffer = (PWCHAR)(ProtChars+1); + RtlCopyMemory (ProtChars->Name.Buffer, NameString->Buffer, NameString->Length); + ProtChars->Name.Buffer[NameString->Length/sizeof(WCHAR)] = UNICODE_NULL; + + ProtChars->OpenAdapterCompleteHandler = NbfOpenAdapterComplete; + ProtChars->CloseAdapterCompleteHandler = NbfCloseAdapterComplete; + ProtChars->ResetCompleteHandler = NbfResetComplete; + ProtChars->RequestCompleteHandler = NbfRequestComplete; + +#ifdef NBF_LOCKS + ProtChars->SendCompleteHandler = NbfFakeSendCompletionHandler; + ProtChars->TransferDataCompleteHandler = NbfFakeTransferDataComplete; +#else + ProtChars->SendCompleteHandler = NbfSendCompletionHandler; + ProtChars->TransferDataCompleteHandler = NbfTransferDataComplete; +#endif + + ProtChars->ReceiveHandler = NbfReceiveIndication; + ProtChars->ReceiveCompleteHandler = NbfReceiveComplete; + ProtChars->StatusHandler = NbfStatusIndication; + ProtChars->StatusCompleteHandler = NbfStatusComplete; + + NdisRegisterProtocol ( + &ndisStatus, + &NbfNdisProtocolHandle, + ProtChars, + (UINT)sizeof(NDIS_PROTOCOL_CHARACTERISTICS) + NameString->MaximumLength); + + ExFreePool (ProtChars); + + if (ndisStatus != NDIS_STATUS_SUCCESS) { +#if DBG + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint1("NbfInitialize: NdisRegisterProtocol failed: %s\n", + NbfGetNdisStatus(ndisStatus)); + } +#endif + return (NTSTATUS)ndisStatus; + } + + return STATUS_SUCCESS; +} + + +VOID +NbfDeregisterProtocol ( + VOID + ) + +/*++ + +Routine Description: + + This routine removes this transport to the NDIS interface. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + NDIS_STATUS ndisStatus; + + if (NbfNdisProtocolHandle != (NDIS_HANDLE)NULL) { + NdisDeregisterProtocol ( + &ndisStatus, + NbfNdisProtocolHandle); + NbfNdisProtocolHandle = (NDIS_HANDLE)NULL; + } +} + + +NDIS_STATUS +NbfSubmitNdisRequest( + IN PDEVICE_CONTEXT DeviceContext, + IN PNDIS_REQUEST NdisRequest, + IN PNDIS_STRING AdapterString + ) + +/*++ + +Routine Description: + + This routine passed an NDIS_REQUEST to the MAC and waits + until it has completed before returning the final status. + +Arguments: + + DeviceContext - Pointer to the device context for this driver. + + NdisRequest - Pointer to the NDIS_REQUEST to submit. + + AdapterString - The name of the adapter, in case an error needs + to be logged. + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + NDIS_STATUS NdisStatus; + + NdisRequest( + &NdisStatus, + DeviceContext->NdisBindingHandle, + NdisRequest); + + if (NdisStatus == NDIS_STATUS_PENDING) { + + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint1 ("OID %lx pended.\n", + NdisRequest->DATA.QUERY_INFORMATION.Oid); + } + + // + // The completion routine will set NdisRequestStatus. + // + + KeWaitForSingleObject( + &DeviceContext->NdisRequestEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + NdisStatus = DeviceContext->NdisRequestStatus; + + KeResetEvent( + &DeviceContext->NdisRequestEvent + ); + + } + + if (NdisStatus == STATUS_SUCCESS) { + + IF_NBFDBG (NBF_DEBUG_NDIS) { + if (NdisRequest->RequestType == NdisRequestSetInformation) { + NbfPrint1 ("Nbfdrvr: Set OID %lx succeeded.\n", + NdisRequest->DATA.SET_INFORMATION.Oid); + } else { + NbfPrint1 ("Nbfdrvr: Query OID %lx succeeded.\n", + NdisRequest->DATA.QUERY_INFORMATION.Oid); + } + } + + } else { +#if DBG + if (NdisRequest->RequestType == NdisRequestSetInformation) { + NbfPrint2 ("Nbfdrvr: Set OID %lx failed: %s.\n", + NdisRequest->DATA.SET_INFORMATION.Oid, NbfGetNdisStatus(NdisStatus)); + } else { + NbfPrint2 ("Nbfdrvr: Query OID %lx failed: %s.\n", + NdisRequest->DATA.QUERY_INFORMATION.Oid, NbfGetNdisStatus(NdisStatus)); + } +#endif + NbfWriteOidErrorLog( + DeviceContext, + NdisRequest->RequestType == NdisRequestSetInformation ? + EVENT_TRANSPORT_SET_OID_FAILED : EVENT_TRANSPORT_QUERY_OID_FAILED, + NdisStatus, + AdapterString->Buffer, + NdisRequest->DATA.QUERY_INFORMATION.Oid); + } + + return NdisStatus; +} + + +NTSTATUS +NbfInitializeNdis ( + IN PDEVICE_CONTEXT DeviceContext, + IN PCONFIG_DATA NbfConfig, + IN UINT ConfigInfoNameIndex + ) + +/*++ + +Routine Description: + + This routine introduces this transport to the NDIS interface and sets up + any necessary NDIS data structures (Buffer pools and such). It will be + called for each adapter opened by this transport. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + + Irp - Pointer to the request packet representing the I/O request. + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + ULONG SendPacketReservedLength; + ULONG ReceivePacketReservedLen; + ULONG SendPacketPoolSize; + ULONG ReceivePacketPoolSize; + NDIS_STATUS NdisStatus; + NDIS_STATUS OpenErrorStatus; + NDIS_MEDIUM NbfSupportedMedia[] = { NdisMedium802_3, NdisMedium802_5, NdisMediumFddi, NdisMediumWan }; + UINT SelectedMedium; + NDIS_REQUEST NbfRequest; + UCHAR NbfDataBuffer[6]; + NDIS_OID NbfOid; + UCHAR WanProtocolId[6] = { 0x80, 0x00, 0x00, 0x00, 0x80, 0xd5 }; + ULONG WanHeaderFormat = NdisWanHeaderEthernet; + ULONG MinimumLookahead = 128 + sizeof(DLC_FRAME) + sizeof(NBF_HDR_CONNECTIONLESS); + ULONG MacOptions; + PNDIS_STRING AdapterString; + + + // + // Initialize this adapter for NBF use through NDIS + // + + // + // This event is used in case any of the NDIS requests + // pend; we wait until it is set by the completion + // routine, which also sets NdisRequestStatus. + // + + KeInitializeEvent( + &DeviceContext->NdisRequestEvent, + NotificationEvent, + FALSE + ); + + DeviceContext->NdisBindingHandle = NULL; + AdapterString = (PNDIS_STRING)&NbfConfig->Names[ConfigInfoNameIndex]; + + NdisOpenAdapter ( + &NdisStatus, + &OpenErrorStatus, + &DeviceContext->NdisBindingHandle, + &SelectedMedium, + NbfSupportedMedia, + sizeof (NbfSupportedMedia) / sizeof(NDIS_MEDIUM), + NbfNdisProtocolHandle, + (NDIS_HANDLE)DeviceContext, + AdapterString, + 0, + NULL); + + if (NdisStatus == NDIS_STATUS_PENDING) { + + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint1 ("Adapter %S open pended.\n", AdapterString); + } + + // + // The completion routine will set NdisRequestStatus. + // + + KeWaitForSingleObject( + &DeviceContext->NdisRequestEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + NdisStatus = DeviceContext->NdisRequestStatus; + + KeResetEvent( + &DeviceContext->NdisRequestEvent + ); + + } + + if (NdisStatus == NDIS_STATUS_SUCCESS) { +#if DBG + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint1 ("Adapter %S successfully opened.\n", AdapterString); + } +#endif + } else { +#if DBG + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint2 ("Adapter open %S failed, status: %s.\n", + AdapterString, + NbfGetNdisStatus (NdisStatus)); + } +#endif + NbfWriteGeneralErrorLog( + DeviceContext, + EVENT_TRANSPORT_ADAPTER_NOT_FOUND, + 807, + NdisStatus, + AdapterString->Buffer, + 0, + NULL); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Get the information we need about the adapter, based on + // the media type. + // + + MacInitializeMacInfo( + NbfSupportedMedia[SelectedMedium], + (BOOLEAN)(NbfConfig->UseDixOverEthernet != 0), + &DeviceContext->MacInfo); + DeviceContext->MacInfo.QueryWithoutSourceRouting = + NbfConfig->QueryWithoutSourceRouting ? TRUE : FALSE; + DeviceContext->MacInfo.AllRoutesNameRecognized = + NbfConfig->AllRoutesNameRecognized ? TRUE : FALSE; + + + // + // Set the multicast/functional addresses first so we avoid windows where we + // receive only part of the addresses. + // + + MacSetNetBIOSMulticast ( + DeviceContext->MacInfo.MediumType, + DeviceContext->NetBIOSAddress.Address); + + + switch (DeviceContext->MacInfo.MediumType) { + + case NdisMedium802_3: + case NdisMediumDix: + + // + // Fill in the data for our multicast list. + // + + RtlCopyMemory(NbfDataBuffer, DeviceContext->NetBIOSAddress.Address, 6); + + // + // Now fill in the NDIS_REQUEST. + // + + NbfRequest.RequestType = NdisRequestSetInformation; + NbfRequest.DATA.SET_INFORMATION.Oid = OID_802_3_MULTICAST_LIST; + NbfRequest.DATA.SET_INFORMATION.InformationBuffer = &NbfDataBuffer; + NbfRequest.DATA.SET_INFORMATION.InformationBufferLength = 6; + + break; + + case NdisMedium802_5: + + // + // For token-ring, we pass the last four bytes of the + // Netbios functional address. + // + + // + // Fill in the OVB for our functional address. + // + + RtlCopyMemory(NbfDataBuffer, ((PUCHAR)(DeviceContext->NetBIOSAddress.Address)) + 2, 4); + + // + // Now fill in the NDIS_REQUEST. + // + + NbfRequest.RequestType = NdisRequestSetInformation; + NbfRequest.DATA.SET_INFORMATION.Oid = OID_802_5_CURRENT_FUNCTIONAL; + NbfRequest.DATA.SET_INFORMATION.InformationBuffer = &NbfDataBuffer; + NbfRequest.DATA.SET_INFORMATION.InformationBufferLength = 4; + + break; + + case NdisMediumFddi: + + // + // Fill in the data for our multicast list. + // + + RtlCopyMemory(NbfDataBuffer, DeviceContext->NetBIOSAddress.Address, 6); + + // + // Now fill in the NDIS_REQUEST. + // + + NbfRequest.RequestType = NdisRequestSetInformation; + NbfRequest.DATA.SET_INFORMATION.Oid = OID_FDDI_LONG_MULTICAST_LIST; + NbfRequest.DATA.SET_INFORMATION.InformationBuffer = &NbfDataBuffer; + NbfRequest.DATA.SET_INFORMATION.InformationBufferLength = 6; + + break; + + } + + NdisStatus = NbfSubmitNdisRequest (DeviceContext, &NbfRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbfCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + + switch (DeviceContext->MacInfo.MediumType) { + + case NdisMedium802_3: + case NdisMediumDix: + + if (DeviceContext->MacInfo.MediumAsync) { + NbfOid = OID_WAN_CURRENT_ADDRESS; + } else { + NbfOid = OID_802_3_CURRENT_ADDRESS; + } + break; + + case NdisMedium802_5: + + NbfOid = OID_802_5_CURRENT_ADDRESS; + break; + + case NdisMediumFddi: + + NbfOid = OID_FDDI_LONG_CURRENT_ADDR; + break; + + default: + + NdisStatus = NDIS_STATUS_FAILURE; + break; + + } + NbfRequest.RequestType = NdisRequestQueryInformation; + NbfRequest.DATA.QUERY_INFORMATION.Oid = NbfOid; + NbfRequest.DATA.QUERY_INFORMATION.InformationBuffer = DeviceContext->LocalAddress.Address; + NbfRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 6; + + NdisStatus = NbfSubmitNdisRequest (DeviceContext, &NbfRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbfCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Set up the reserved Netbios address. + // + + RtlZeroMemory(DeviceContext->ReservedNetBIOSAddress, 10); + RtlCopyMemory(&DeviceContext->ReservedNetBIOSAddress[10], DeviceContext->LocalAddress.Address, 6); + + + + // + // Now query the maximum packet sizes. + // + + NbfRequest.RequestType = NdisRequestQueryInformation; + NbfRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_MAXIMUM_FRAME_SIZE; + NbfRequest.DATA.QUERY_INFORMATION.InformationBuffer = &(DeviceContext->MaxReceivePacketSize); + NbfRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = NbfSubmitNdisRequest (DeviceContext, &NbfRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbfCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + NbfRequest.RequestType = NdisRequestQueryInformation; + NbfRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_MAXIMUM_TOTAL_SIZE; + NbfRequest.DATA.QUERY_INFORMATION.InformationBuffer = &(DeviceContext->MaxSendPacketSize); + NbfRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = NbfSubmitNdisRequest (DeviceContext, &NbfRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbfCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + DeviceContext->CurSendPacketSize = DeviceContext->MaxSendPacketSize; + + + // + // Now set the minimum lookahead size. + // + + NbfRequest.RequestType = NdisRequestSetInformation; + NbfRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_CURRENT_LOOKAHEAD; + NbfRequest.DATA.QUERY_INFORMATION.InformationBuffer = &MinimumLookahead; + NbfRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = NbfSubmitNdisRequest (DeviceContext, &NbfRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbfCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Now query the link speed for non-wan media + // + + if (!DeviceContext->MacInfo.MediumAsync) { + + NbfRequest.RequestType = NdisRequestQueryInformation; + NbfRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_LINK_SPEED; + NbfRequest.DATA.QUERY_INFORMATION.InformationBuffer = &(DeviceContext->MediumSpeed); + NbfRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = NbfSubmitNdisRequest (DeviceContext, &NbfRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbfCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + DeviceContext->MediumSpeedAccurate = TRUE; + + DeviceContext->MinimumT1Timeout = 8; // == 400 ms + + } else { + + // + // On an wan media, this isn't valid until we get an + // WAN_LINE_UP indication. Set the timeouts to + // low values for now. + // + + DeviceContext->DefaultT1Timeout = 8; + DeviceContext->MinimumT1Timeout = 8; + + DeviceContext->MediumSpeedAccurate = FALSE; + + + // + // Back off our connectionless timeouts to 2 seconds. + // + + DeviceContext->NameQueryTimeout = 2 * SECONDS; + DeviceContext->AddNameQueryTimeout = 2 * SECONDS; + DeviceContext->GeneralTimeout = 2 * SECONDS; + + // + // Use the WAN parameter for name query retries. + // + + DeviceContext->NameQueryRetries = NbfConfig->WanNameQueryRetries; + + // + // Use this until we know better. + // + + DeviceContext->RecommendedSendWindow = 1; + + } + + // + // On media that use source routing, we double our name query + // retry count if we are configured to try both ways (with and + // without source routing). + // + + if ((DeviceContext->MacInfo.QueryWithoutSourceRouting) && + (DeviceContext->MacInfo.SourceRouting)) { + DeviceContext->NameQueryRetries *= 2; + } + + + // + // For wan, specify our protocol ID and header format. + // We don't query the medium subtype because we don't + // case (since we require ethernet emulation). + // + + if (DeviceContext->MacInfo.MediumAsync) { + + NbfRequest.RequestType = NdisRequestSetInformation; + NbfRequest.DATA.QUERY_INFORMATION.Oid = OID_WAN_PROTOCOL_TYPE; + NbfRequest.DATA.QUERY_INFORMATION.InformationBuffer = WanProtocolId; + NbfRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 6; + + NdisStatus = NbfSubmitNdisRequest (DeviceContext, &NbfRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbfCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + NbfRequest.RequestType = NdisRequestSetInformation; + NbfRequest.DATA.QUERY_INFORMATION.Oid = OID_WAN_HEADER_FORMAT; + NbfRequest.DATA.QUERY_INFORMATION.InformationBuffer = &WanHeaderFormat; + NbfRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = NbfSubmitNdisRequest (DeviceContext, &NbfRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbfCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + } + + + // + // Now query the MAC's optional characteristics. + // + + NbfRequest.RequestType = NdisRequestQueryInformation; + NbfRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_MAC_OPTIONS; + NbfRequest.DATA.QUERY_INFORMATION.InformationBuffer = &MacOptions; + NbfRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = NbfSubmitNdisRequest (DeviceContext, &NbfRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { +#if 1 + NbfCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; +#else + MacOptions = 0; +#endif + } + + DeviceContext->MacInfo.CopyLookahead = + (BOOLEAN)((MacOptions & NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA) != 0); + DeviceContext->MacInfo.ReceiveSerialized = + (BOOLEAN)((MacOptions & NDIS_MAC_OPTION_RECEIVE_SERIALIZED) != 0); + DeviceContext->MacInfo.TransferSynchronous = + (BOOLEAN)((MacOptions & NDIS_MAC_OPTION_TRANSFERS_NOT_PEND) != 0); + DeviceContext->MacInfo.SingleReceive = + (BOOLEAN)(DeviceContext->MacInfo.ReceiveSerialized && DeviceContext->MacInfo.TransferSynchronous); + + +#if 0 + // + // Now set our options if needed. + // + // Don't allow early indications because we can't determine + // if the CRC has been checked yet. + // + + if ((DeviceContext->MacInfo.MediumType == NdisMedium802_3) || + (DeviceContext->MacInfo.MediumType == NdisMediumDix)) { + + ULONG ProtocolOptions = NDIS_PROT_OPTION_ESTIMATED_LENGTH; + + NbfRequest.RequestType = NdisRequestSetInformation; + NbfRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_PROTOCOL_OPTIONS; + NbfRequest.DATA.QUERY_INFORMATION.InformationBuffer = &ProtocolOptions; + NbfRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = NbfSubmitNdisRequest (DeviceContext, &NbfRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbfCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + } +#endif + + + // + // Calculate the NDIS-related stuff. + // + + SendPacketReservedLength = sizeof (SEND_PACKET_TAG); + ReceivePacketReservedLen = sizeof (RECEIVE_PACKET_TAG); + + + // + // The send packet pool is used for UI frames and regular packets. + // + + SendPacketPoolSize = NbfConfig->SendPacketPoolSize; + + // + // The receive packet pool is used in transfer data. + // + // For a MAC that will only have one receive active, we + // don't need multiple receive packets. Allow an extra + // one for loopback. + // + + if (DeviceContext->MacInfo.SingleReceive) { + ReceivePacketPoolSize = 2; + } else { + ReceivePacketPoolSize = NbfConfig->ReceivePacketPoolSize; + } + + + // Allocate Packet pool descriptors for dynamic packet allocation. + + DeviceContext->SendPacketPoolDesc = ExAllocatePoolWithTag( + NonPagedPool, + sizeof(NBF_POOL_LIST_DESC), + ' FBN'); + + if (DeviceContext->SendPacketPoolDesc == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory(DeviceContext->SendPacketPoolDesc, + sizeof(NBF_POOL_LIST_DESC)); + + DeviceContext->SendPacketPoolDesc->NumElements = + DeviceContext->SendPacketPoolDesc->TotalElements = (USHORT)SendPacketPoolSize; + + NdisAllocatePacketPool ( + &NdisStatus, + &DeviceContext->SendPacketPoolDesc->PoolHandle, + SendPacketPoolSize, + SendPacketReservedLength); + + if (NdisStatus == NDIS_STATUS_SUCCESS) { + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint0 ("NdisInitializePacketPool successful.\n"); + } + + } else { +#if DBG + NbfPrint1 ("NbfInitialize: NdisInitializePacketPool failed, reason: %s.\n", + NbfGetNdisStatus (NdisStatus)); +#endif + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 109, + SendPacketPoolSize, + 0); + ExFreePool (DeviceContext->SendPacketPoolDesc); + DeviceContext->SendPacketPoolDesc = NULL; + NbfCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + DeviceContext->SendPacketPoolSize = SendPacketPoolSize; + + DeviceContext->MemoryUsage += + (SendPacketPoolSize * + (sizeof(NDIS_PACKET) + SendPacketReservedLength)); + +#if DBG + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + DbgPrint ("send pool %d hdr %d, %ld\n", + SendPacketPoolSize, + SendPacketReservedLength, + DeviceContext->MemoryUsage); + } +#endif + + + // Allocate Packet pool descriptors for dynamic packet allocation. + + DeviceContext->ReceivePacketPoolDesc = ExAllocatePoolWithTag( + NonPagedPool, + sizeof(NBF_POOL_LIST_DESC), + ' FBN'); + + if (DeviceContext->ReceivePacketPoolDesc == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory(DeviceContext->ReceivePacketPoolDesc, + sizeof(NBF_POOL_LIST_DESC)); + + DeviceContext->ReceivePacketPoolDesc->NumElements = + DeviceContext->ReceivePacketPoolDesc->TotalElements = (USHORT)ReceivePacketPoolSize; + + NdisAllocatePacketPool( + &NdisStatus, + &DeviceContext->ReceivePacketPoolDesc->PoolHandle, + ReceivePacketPoolSize, + ReceivePacketReservedLen); + + if (NdisStatus == NDIS_STATUS_SUCCESS) { + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint1 ("NdisInitializePacketPool successful, Pool: %lx\n", + DeviceContext->ReceivePacketPoolDesc->PoolHandle); + } + } else { +#if DBG + NbfPrint1 ("NbfInitialize: NdisInitializePacketPool failed, reason: %s.\n", + NbfGetNdisStatus (NdisStatus)); +#endif + ExFreePool (DeviceContext->SendPacketPoolDesc); + ExFreePool(DeviceContext->ReceivePacketPoolDesc); + DeviceContext->SendPacketPoolDesc = NULL; + DeviceContext->ReceivePacketPoolDesc = NULL; + NbfCloseNdis (DeviceContext); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 209, + ReceivePacketPoolSize, + 0); + return STATUS_INSUFFICIENT_RESOURCES; + } + + DeviceContext->ReceivePacketPoolSize = ReceivePacketPoolSize; + + DeviceContext->MemoryUsage += + (ReceivePacketPoolSize * + (sizeof(NDIS_PACKET) + ReceivePacketReservedLen)); + +#if DBG + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + DbgPrint ("receive pool %d hdr %d, %ld\n", + ReceivePacketPoolSize, + ReceivePacketReservedLen, + DeviceContext->MemoryUsage); + } +#endif + + + // + // Allocate the buffer pool; as an estimate, allocate + // one per send or receive packet. + // + + NdisAllocateBufferPool ( + &NdisStatus, + &DeviceContext->NdisBufferPool, + SendPacketPoolSize + ReceivePacketPoolSize); + + if (NdisStatus == NDIS_STATUS_SUCCESS) { + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint0 ("NdisAllocateBufferPool successful.\n"); + } + + } else { +#if DBG + NbfPrint1 ("NbfInitialize: NdisAllocateBufferPool failed, reason: %s.\n", + NbfGetNdisStatus (NdisStatus)); +#endif + ExFreePool(DeviceContext->SendPacketPoolDesc); + ExFreePool(DeviceContext->ReceivePacketPoolDesc); + DeviceContext->SendPacketPoolDesc = NULL; + DeviceContext->ReceivePacketPoolDesc = NULL; + DeviceContext->NdisBufferPool = NULL; + NbfCloseNdis (DeviceContext); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 309, + SendPacketPoolSize + ReceivePacketPoolSize, + 0); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Now that everything is set up, we enable the filter + // for packet reception. + // + + // + // Fill in the OVB for packet filter. + // + + switch (DeviceContext->MacInfo.MediumType) { + + case NdisMedium802_3: + case NdisMediumDix: + case NdisMediumFddi: + + RtlStoreUlong((PULONG)NbfDataBuffer, + (NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_MULTICAST)); + break; + + case NdisMedium802_5: + + RtlStoreUlong((PULONG)NbfDataBuffer, + (NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_FUNCTIONAL)); + break; + + default: + + NdisStatus = NDIS_STATUS_FAILURE; + break; + + } + + // + // Now fill in the NDIS_REQUEST. + // + + NbfRequest.RequestType = NdisRequestSetInformation; + NbfRequest.DATA.SET_INFORMATION.Oid = OID_GEN_CURRENT_PACKET_FILTER; + NbfRequest.DATA.SET_INFORMATION.InformationBuffer = &NbfDataBuffer; + NbfRequest.DATA.SET_INFORMATION.InformationBufferLength = sizeof(ULONG); + + NbfSubmitNdisRequest (DeviceContext, &NbfRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbfCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + return STATUS_SUCCESS; + +} /* NbfInitializeNdis */ + + +VOID +NbfCloseNdis ( + IN PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + + This routine unbinds the transport from the NDIS interface and does + any other work required to undo what was done in NbfInitializeNdis. + It is written so that it can be called from within NbfInitializeNdis + if it fails partway through. + +Arguments: + + DeviceObject - Pointer to the device object for this driver. + +Return Value: + + The function value is the status of the operation. + +--*/ +{ + NDIS_STATUS ndisStatus; + + // + // Close the NDIS binding. + // + + if (DeviceContext->NdisBindingHandle != (NDIS_HANDLE)NULL) { + + // + // This event is used in case any of the NDIS requests + // pend; we wait until it is set by the completion + // routine, which also sets NdisRequestStatus. + // + + KeInitializeEvent( + &DeviceContext->NdisRequestEvent, + NotificationEvent, + FALSE + ); + + NdisCloseAdapter( + &ndisStatus, + DeviceContext->NdisBindingHandle); + + if (ndisStatus == NDIS_STATUS_PENDING) { + + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint0 ("Adapter close pended.\n"); + } + + // + // The completion routine will set NdisRequestStatus. + // + + KeWaitForSingleObject( + &DeviceContext->NdisRequestEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + ndisStatus = DeviceContext->NdisRequestStatus; + + KeResetEvent( + &DeviceContext->NdisRequestEvent + ); + + } + + // + // We ignore ndisStatus. + // + + } + + if (DeviceContext->SendPacketPoolDesc != NULL && + DeviceContext->SendPacketPoolDesc->PoolHandle != NULL) { + NdisFreePacketPool (DeviceContext->SendPacketPoolDesc->PoolHandle); + ExFreePool(DeviceContext->SendPacketPoolDesc); + DeviceContext->SendPacketPoolDesc = NULL; + } + + if (DeviceContext->ReceivePacketPoolDesc != NULL && + DeviceContext->ReceivePacketPoolDesc->PoolHandle != NULL) { + NdisFreePacketPool (DeviceContext->ReceivePacketPoolDesc->PoolHandle); + ExFreePool(DeviceContext->ReceivePacketPoolDesc); + DeviceContext->ReceivePacketPoolDesc = NULL; + } + + if (DeviceContext->NdisBufferPool != NULL) { + NdisFreeBufferPool (DeviceContext->NdisBufferPool); + } + +} /* NbfCloseNdis */ + + +VOID +NbfOpenAdapterComplete ( + IN NDIS_HANDLE BindingContext, + IN NDIS_STATUS NdisStatus, + IN NDIS_STATUS OpenErrorStatus + ) + +/*++ + +Routine Description: + + This routine is called by NDIS to indicate that an open adapter + is complete. Since we only ever have one outstanding, and then only + during initialization, all we do is record the status and set + the event to signalled to unblock the initialization thread. + +Arguments: + + BindingContext - Pointer to the device object for this driver. + + NdisStatus - The request completion code. + + OpenErrorStatus - More status information. + +Return Value: + + None. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext = (PDEVICE_CONTEXT)BindingContext; + +#if DBG + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint1 ("Nbfdrvr: NbfOpenAdapterCompleteNDIS Status: %s\n", + NbfGetNdisStatus (NdisStatus)); + } +#endif + + ENTER_NBF; + + DeviceContext->NdisRequestStatus = NdisStatus; + KeSetEvent( + &DeviceContext->NdisRequestEvent, + 0L, + FALSE); + + LEAVE_NBF; + return; +} + +VOID +NbfCloseAdapterComplete ( + IN NDIS_HANDLE BindingContext, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called by NDIS to indicate that a close adapter + is complete. Currently we don't close adapters, so this is not + a problem. + +Arguments: + + BindingContext - Pointer to the device object for this driver. + + NdisStatus - The request completion code. + +Return Value: + + None. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext = (PDEVICE_CONTEXT)BindingContext; + +#if DBG + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint1 ("Nbfdrvr: NbfCloseAdapterCompleteNDIS Status: %s\n", + NbfGetNdisStatus (NdisStatus)); + } +#endif + + ENTER_NBF; + + DeviceContext->NdisRequestStatus = NdisStatus; + KeSetEvent( + &DeviceContext->NdisRequestEvent, + 0L, + FALSE); + + LEAVE_NBF; + return; +} + +VOID +NbfResetComplete ( + IN NDIS_HANDLE BindingContext, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called by NDIS to indicate that a reset adapter + is complete. Currently we don't reset adapters, so this is not + a problem. + +Arguments: + + BindingContext - Pointer to the device object for this driver. + + NdisStatus - The request completion code. + +Return Value: + + None. + +--*/ + +{ + UNREFERENCED_PARAMETER(BindingContext); + UNREFERENCED_PARAMETER(NdisStatus); + +#if DBG + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint1 ("Nbfdrvr: NbfResetCompleteNDIS Status: %s\n", + NbfGetNdisStatus (NdisStatus)); + } +#endif + + return; +} + +VOID +NbfRequestComplete ( + IN NDIS_HANDLE BindingContext, + IN PNDIS_REQUEST NdisRequest, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called by NDIS to indicate that a request is complete. + Since we only ever have one request outstanding, and then only + during initialization, all we do is record the status and set + the event to signalled to unblock the initialization thread. + +Arguments: + + BindingContext - Pointer to the device object for this driver. + + NdisRequest - The object describing the request. + + NdisStatus - The request completion code. + +Return Value: + + None. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext = (PDEVICE_CONTEXT)BindingContext; + +#if DBG + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint2 ("Nbfdrvr: NbfRequestComplete request: %i, NDIS Status: %s\n", + NdisRequest->RequestType,NbfGetNdisStatus (NdisStatus)); + } +#endif + + ENTER_NBF; + + DeviceContext->NdisRequestStatus = NdisStatus; + KeSetEvent( + &DeviceContext->NdisRequestEvent, + 0L, + FALSE); + + LEAVE_NBF; + return; +} + +VOID +NbfStatusIndication ( + IN NDIS_HANDLE NdisBindingContext, + IN NDIS_STATUS NdisStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferSize + ) + +{ + PDEVICE_CONTEXT DeviceContext; + PNDIS_WAN_LINE_UP LineUp; + KIRQL oldirql; + PTP_LINK Link; + + DeviceContext = (PDEVICE_CONTEXT)NdisBindingContext; + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + + switch (NdisStatus) { + + case NDIS_STATUS_WAN_LINE_UP: + + // + // A wan line is connected. + // + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + // + // If this happens before we are ready, then make + // a note of it, otherwise make the device ready. + // + + DeviceContext->MediumSpeedAccurate = TRUE; + + LineUp = (PNDIS_WAN_LINE_UP)StatusBuffer; + + // + // See if this is a new lineup for this protocol type + // + if (LineUp->ProtocolType == 0x80D5) { + NDIS_HANDLE TransportHandle; + + *((ULONG UNALIGNED *)(&TransportHandle)) = + *((ULONG UNALIGNED *)(&LineUp->LocalAddress[2])); + + // + // See if this is a new lineup + // + if (TransportHandle == NULL) { + *((ULONG UNALIGNED *)(&LineUp->LocalAddress[2])) = *((ULONG UNALIGNED *)(&DeviceContext)); +// ETH_COPY_NETWORK_ADDRESS(DeviceContext->LocalAddress.Address, LineUp->LocalAddress); +// ETH_COPY_NETWORK_ADDRESS(&DeviceContext->ReservedNetBIOSAddress[10], DeviceContext->LocalAddress.Address); + } + + // + // Calculate minimum link timeouts based on the speed, + // which is passed in StatusBuffer. + // + // The formula is (max_frame_size * 2) / speed + 0.4 sec. + // This expands to + // + // MFS (bytes) * 2 8 bits + // ------------------- x ------ == timeout (sec), + // speed (100 bits/sec) byte + // + // which is (MFS * 16 / 100) / speed. We then convert it into + // the 50 ms units that NBF uses and add 8 (which is + // 0.4 seconds in 50 ms units). + // + // As a default timeout we use the min + 0.2 seconds + // unless the configured default is more. + // + + if (LineUp->LinkSpeed > 0) { + DeviceContext->MediumSpeed = LineUp->LinkSpeed; + } + + if (LineUp->MaximumTotalSize > 0) { +#if DBG + if (LineUp->MaximumTotalSize > DeviceContext->MaxSendPacketSize) { + DbgPrint ("Nbf: Bad LINE_UP size, %d (> %d)\n", + LineUp->MaximumTotalSize, DeviceContext->MaxSendPacketSize); + } + if (LineUp->MaximumTotalSize < 128) { + DbgPrint ("NBF: Bad LINE_UP size, %d (< 128)\n", + LineUp->MaximumTotalSize); + } +#endif + DeviceContext->CurSendPacketSize = LineUp->MaximumTotalSize; + } + + if (LineUp->SendWindow == 0) { + DeviceContext->RecommendedSendWindow = 3; + } else { + DeviceContext->RecommendedSendWindow = LineUp->SendWindow + 1; + } + + DeviceContext->MinimumT1Timeout = + ((((DeviceContext->CurSendPacketSize * 16) / 100) / DeviceContext->MediumSpeed) * + ((1 * SECONDS) / (50 * MILLISECONDS))) + 8; + + if (DeviceContext->DefaultT1Timeout < DeviceContext->MinimumT1Timeout) { + DeviceContext->DefaultT1Timeout = DeviceContext->MinimumT1Timeout + 4; + } + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + break; + + case NDIS_STATUS_WAN_LINE_DOWN: + + // + // An wan line is disconnected. + // + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + DeviceContext->MediumSpeedAccurate = FALSE; + + // + // Set the timeouts to small values (0.4 seconds) + // + + DeviceContext->DefaultT1Timeout = 8; + DeviceContext->MinimumT1Timeout = 8; + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + + // + // Stop the link on this device context (there + // will only be one). + // + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + if (DeviceContext->LinkTreeElements > 0) { + + Link = (PTP_LINK)DeviceContext->LinkTreeRoot; + if ((Link->DeferredFlags & LINK_FLAGS_DEFERRED_DELETE) == 0) { + + NbfReferenceLink ("Wan line down", Link, LREF_TREE); + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + // + // Put the link in ADM to shut it down. + // + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + if (Link->State != LINK_STATE_ADM) { + Link->State = LINK_STATE_ADM; + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + NbfDereferenceLinkSpecial ("Wan line down", Link, LREF_NOT_ADM); + } else { + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + } + + // + // Now stop it to destroy all connections on it. + // + + NbfStopLink (Link); + + NbfDereferenceLink ("Wan line down", Link, LREF_TREE); + + } else { + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + } + + } else { + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + } + + break; + + case NDIS_STATUS_WAN_FRAGMENT: + + // + // A fragment has been received on the wan line. + // Send a reject back to him. + // + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + if (DeviceContext->LinkTreeElements > 0) { + + Link = (PTP_LINK)DeviceContext->LinkTreeRoot; + NbfReferenceLink ("Async line down", Link, LREF_TREE); + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + NbfSendRej (Link, FALSE, FALSE); // release lock + NbfDereferenceLink ("Async line down", Link, LREF_TREE); + + } else { + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + } + + break; + + case NDIS_STATUS_CLOSING: + + // + // The adapter is shutting down. We queue a worker + // thread to handle this. + // + + ExInitializeWorkItem( + &DeviceContext->StatusClosingQueueItem, + NbfProcessStatusClosing, + (PVOID)DeviceContext); + ExQueueWorkItem(&DeviceContext->StatusClosingQueueItem, DelayedWorkQueue); + + break; + + default: + break; + + } + + KeLowerIrql (oldirql); + +} + + +VOID +NbfProcessStatusClosing( + IN PVOID Parameter + ) + +/*++ + +Routine Description: + + This is the thread routine which restarts packetizing + that has been delayed on WAN to allow RRs to come in. + This is very similar to PacketizeConnections. + +Arguments: + + Parameter - A pointer to the device context. + +Return Value: + + None. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + PLIST_ENTRY p; +#if 0 + PTP_ADDRESS Address; +#endif + PTP_LINK Link; + PTP_REQUEST Request; + NDIS_STATUS ndisStatus; + KIRQL oldirql; + + DeviceContext = (PDEVICE_CONTEXT)Parameter; + + // + // Prevent new activity on the connection. + // + + DeviceContext->State = DEVICECONTEXT_STATE_DOWN; + + +#if 0 + // + // Stop all the addresses. + // + + while ((p = ExInterlockedRemoveHeadList( + &DeviceContext->AddressDatabase, + &DeviceContext->SpinLock)) != NULL) { + + Address = CONTAINING_RECORD (p, TP_ADDRESS, Linkage); + InitializeListHead(p); + + NbfStopAddress (Address); + + } +#endif + + // + // To speed things along, stop all the links too. + // + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + DeviceContext->LastLink = NULL; + + while (DeviceContext->LinkTreeRoot != NULL) { + + Link = (PTP_LINK)DeviceContext->LinkTreeRoot; + DeviceContext->LinkTreeRoot = RtlDelete ((PRTL_SPLAY_LINKS)Link); + DeviceContext->LinkTreeElements--; + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + if (Link->OnShortList) { + RemoveEntryList (&Link->ShortList); + } + if (Link->OnLongList) { + RemoveEntryList (&Link->LongList); + } + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + if (Link->State != LINK_STATE_ADM) { + Link->State = LINK_STATE_ADM; + NbfSendDm (Link, FALSE); // send DM/0, release lock + // moving to ADM, remove reference + NbfDereferenceLinkSpecial("Expire T1 in CONNECTING mode", Link, LREF_NOT_ADM); + } else { + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + } + NbfStopLink (Link); + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + KeLowerIrql (oldirql); + + + // + // Shutdown the control channel. + // + + while ((p = ExInterlockedRemoveHeadList( + &DeviceContext->QueryIndicationQueue, + &DeviceContext->SpinLock)) != NULL) { + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + NbfCompleteRequest (Request, STATUS_INVALID_DEVICE_STATE, 0); + } + + while ((p = ExInterlockedRemoveHeadList( + &DeviceContext->DatagramIndicationQueue, + &DeviceContext->SpinLock)) != NULL) { + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + NbfCompleteRequest (Request, STATUS_INVALID_DEVICE_STATE, 0); + } + + while ((p = ExInterlockedRemoveHeadList( + &DeviceContext->StatusQueryQueue, + &DeviceContext->SpinLock)) != NULL) { + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + NbfCompleteRequest (Request, STATUS_INVALID_DEVICE_STATE, 0); + } + + while ((p = ExInterlockedRemoveHeadList( + &DeviceContext->FindNameQueue, + &DeviceContext->SpinLock)) != NULL) { + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + NbfCompleteRequest (Request, STATUS_INVALID_DEVICE_STATE, 0); + } + + + // + // Close the NDIS binding. + // + + KeInitializeEvent( + &DeviceContext->NdisRequestEvent, + NotificationEvent, + FALSE + ); + + NdisCloseAdapter( + &ndisStatus, + DeviceContext->NdisBindingHandle); + + if (ndisStatus == NDIS_STATUS_PENDING) { + + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint0 ("Adapter close pended.\n"); + } + + // + // The completion routine will set NdisRequestStatus. + // + + KeWaitForSingleObject( + &DeviceContext->NdisRequestEvent, + Executive, + KernelMode, + TRUE, + (PLARGE_INTEGER)NULL + ); + + ndisStatus = DeviceContext->NdisRequestStatus; + + KeResetEvent( + &DeviceContext->NdisRequestEvent + ); + + } + + DeviceContext->NdisBindingHandle = NULL; + + // + // We ignore ndisStatus. + // + +#if 0 + // + // Remove all the storage associated with the device. + // + + NbfFreeResources (DeviceContext); + + NdisFreePacketPool (DeviceContext->SendPacketPoolHandle); + NdisFreePacketPool (DeviceContext->ReceivePacketPoolHandle); + NdisFreeBufferPool (DeviceContext->NdisBufferPoolHandle); +#endif + + // + // And remove the creation reference from the device + // context. + // + + NbfDereferenceDeviceContext ("Unload", DeviceContext, DCREF_CREATION); + +} /* NbfProcessStatusClosing */ + + +VOID +NbfStatusComplete ( + IN NDIS_HANDLE NdisBindingContext + ) +{ + UNREFERENCED_PARAMETER (NdisBindingContext); + +} + +#if DBG + +PUCHAR +NbfGetNdisStatus( + NDIS_STATUS GeneralStatus + ) +/*++ + +Routine Description: + + This routine returns a pointer to the string describing the NDIS error + denoted by GeneralStatus. + +Arguments: + + GeneralStatus - the status you wish to make readable. + +Return Value: + + None. + +--*/ +{ + static NDIS_STATUS Status[] = { + NDIS_STATUS_SUCCESS, + NDIS_STATUS_PENDING, + + NDIS_STATUS_ADAPTER_NOT_FOUND, + NDIS_STATUS_ADAPTER_NOT_OPEN, + NDIS_STATUS_ADAPTER_NOT_READY, + NDIS_STATUS_ADAPTER_REMOVED, + NDIS_STATUS_BAD_CHARACTERISTICS, + NDIS_STATUS_BAD_VERSION, + NDIS_STATUS_CLOSING, + NDIS_STATUS_DEVICE_FAILED, + NDIS_STATUS_FAILURE, + NDIS_STATUS_INVALID_DATA, + NDIS_STATUS_INVALID_LENGTH, + NDIS_STATUS_INVALID_OID, + NDIS_STATUS_INVALID_PACKET, + NDIS_STATUS_MULTICAST_FULL, + NDIS_STATUS_NOT_INDICATING, + NDIS_STATUS_NOT_RECOGNIZED, + NDIS_STATUS_NOT_RESETTABLE, + NDIS_STATUS_NOT_SUPPORTED, + NDIS_STATUS_OPEN_FAILED, + NDIS_STATUS_OPEN_LIST_FULL, + NDIS_STATUS_REQUEST_ABORTED, + NDIS_STATUS_RESET_IN_PROGRESS, + NDIS_STATUS_RESOURCES, + NDIS_STATUS_UNSUPPORTED_MEDIA + }; + static PUCHAR String[] = { + "SUCCESS", + "PENDING", + + "ADAPTER_NOT_FOUND", + "ADAPTER_NOT_OPEN", + "ADAPTER_NOT_READY", + "ADAPTER_REMOVED", + "BAD_CHARACTERISTICS", + "BAD_VERSION", + "CLOSING", + "DEVICE_FAILED", + "FAILURE", + "INVALID_DATA", + "INVALID_LENGTH", + "INVALID_OID", + "INVALID_PACKET", + "MULTICAST_FULL", + "NOT_INDICATING", + "NOT_RECOGNIZED", + "NOT_RESETTABLE", + "NOT_SUPPORTED", + "OPEN_FAILED", + "OPEN_LIST_FULL", + "REQUEST_ABORTED", + "RESET_IN_PROGRESS", + "RESOURCES", + "UNSUPPORTED_MEDIA" + }; + + static UCHAR BadStatus[] = "UNDEFINED"; +#define StatusCount (sizeof(Status)/sizeof(NDIS_STATUS)) + INT i; + + for (i=0; i<StatusCount; i++) + if (GeneralStatus == Status[i]) + return String[i]; + return BadStatus; +#undef StatusCount +} +#endif diff --git a/private/ntos/tdi/nbf/nbfpnp.c b/private/ntos/tdi/nbf/nbfpnp.c new file mode 100644 index 000000000..844abf8ad --- /dev/null +++ b/private/ntos/tdi/nbf/nbfpnp.c @@ -0,0 +1,210 @@ +/*++ + +Copyright (c) 1996 Microsoft Corporation + +Module Name: + + nbfpnp.c + +Abstract: + + This module contains code which allocates and initializes all data + structures needed to activate a plug and play binding. It also informs + tdi (and thus nbf clients) of new devices and protocol addresses. + +Author: + + Jim McNelis (jimmcn) 1-Jan-1996 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef _PNP_POWER +PCONFIG_DATA NbfConfig = NULL; +SuccessfulOpens = 0; + +#ifdef RASAUTODIAL +VOID +NbfAcdBind(); + +VOID +NbfAcdUnbind(); +#endif // RASAUTODIAL + +VOID +NbfProtocolBindAdapter( + OUT PNDIS_STATUS NdisStatus, + IN NDIS_HANDLE BindContext, + IN PNDIS_STRING DeviceName, + IN PVOID SystemSpecific1, + IN PVOID SystemSpecific2 + ) +/*++ + +Routine Description: + + This routine activates a transport binding and exposes the new device + and associated addresses to transport clients. This is done by reading + the registry, and performing any one time initialization of the transport + and then natching the device to bind to with the linkage information from + the registry. If we have a match for that device the bind will be + performed. + +Arguments: + + NdisStatus - The status of the bind. + + BindContext - A context used for NdisCompleteBindAdapter() if + STATUS_PENDING is returned. + + DeviceName - The name of the device that we are binding with. + + SystemSpecific1 - Unused (a pointer to an NDIS_STRING to use with + NdisOpenProtocolConfiguration. This is not used by nbf + since there is no adapter specific information when + configuring the protocol via the registry. + + SystemSpecific2 - Currently unused. + +Return Value: + + None. + +--*/ +{ + + ULONG j; + NTSTATUS status; + + if (NbfConfig == NULL) { + // + // This allocates the CONFIG_DATA structure and returns + // it in NbfConfig. + // + + + + status = NbfConfigureTransport(&NbfRegistryPath, &NbfConfig); + + if (!NT_SUCCESS (status)) { + PANIC (" Failed to initialize transport, Nbf binding failed.\n"); + *NdisStatus = NDIS_STATUS_RESOURCES; + return; + } + +#if DBG + + // + // Allocate the debugging tables. + // + + NbfConnectionTable = (PVOID *)ExAllocatePoolWithTag(NonPagedPool, + sizeof(PVOID) * + (NbfConfig->InitConnections + 2 + + NbfConfig->InitRequests + 2 + + NbfConfig->InitUIFrames + 2 + + NbfConfig->InitPackets + 2 + + NbfConfig->InitLinks + 2 + + NbfConfig->InitAddressFiles + 2 + + NbfConfig->InitAddresses + 2), + ' FBN'); + + ASSERT (NbfConnectionTable); + +#if 0 + if (NbfConnectionTable == NULL) { + *NdisStatus = NDIS_STATUS_RESOURCES; + return; + } +#endif + + + NbfRequestTable = NbfConnectionTable + (NbfConfig->InitConnections + 2); + NbfUiFrameTable = NbfRequestTable + (NbfConfig->InitRequests + 2); + NbfSendPacketTable = NbfUiFrameTable + (NbfConfig->InitUIFrames + 2); + NbfLinkTable = NbfSendPacketTable + (NbfConfig->InitPackets + 2); + NbfAddressFileTable = NbfLinkTable + (NbfConfig->InitLinks + 2); + NbfAddressTable = NbfAddressFileTable + + (NbfConfig->InitAddressFiles + 2); +#endif + } + + for (j=0;j<NbfConfig->NumAdapters;j++ ) { + + // + // Loop through all the adapters that are in the configuration + // information structure until we find the one that ndis is calling + // Protocol bind adapter for. + // + if (NdisEqualString(DeviceName, &NbfConfig->Names[j], TRUE)) { + break; + } + + } + + SuccessfulOpens += NbfInitializeOneDeviceContext(NdisStatus, + NbfDriverObject, + NbfConfig, j + ); + + if (SuccessfulOpens == 1 && *NdisStatus == NDIS_STATUS_SUCCESS) { + +#if DBG + DbgPrint("Calling NbfAcdBind()\n"); +#endif + // + // If this is the first successful open. + // +#ifdef RASAUTODIAL + // + // Get the automatic connection + // driver entry points. + // + NbfAcdBind(); +#endif // RASAUTODIAL + } + return; +} +VOID +NbfProtocolUnbindAdapter( + OUT PNDIS_STATUS NdisStatus, + IN NDIS_HANDLE ProtocolBindContext, + IN PNDIS_HANDLE UnbindContext + ) +/*++ + +Routine Description: + + This routine deactivates a transport binding. Currently unimplemented. + +Arguments: + + NdisStatus - The status of the bind. + + ProtocolBindContext - the context from the openadapter call + + UnbindContext - A context for async unbinds. + + +Return Value: + + None. + +--*/ + +{ + + *NdisStatus = STATUS_NOT_IMPLEMENTED; + return; +} + +#endif // _PNP_POWER diff --git a/private/ntos/tdi/nbf/nbfprocs.h b/private/ntos/tdi/nbf/nbfprocs.h new file mode 100644 index 000000000..9449f0563 --- /dev/null +++ b/private/ntos/tdi/nbf/nbfprocs.h @@ -0,0 +1,2322 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + nbfprocs.h + +Abstract: + + This header file defines private functions for the NT NBF transport + provider. + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Revision History: + +--*/ + +#ifndef _NBFPROCS_ +#define _NBFPROCS_ + +// +// MACROS. +// +// +// Debugging aids +// + +// +// VOID +// IF_NBFDBG( +// IN PSZ Message +// ); +// + +#if DBG +#define IF_NBFDBG(flags) \ + if (NbfDebug & (flags)) +#else +#define IF_NBFDBG(flags) \ + if (0) +#endif + +// +// VOID +// PANIC( +// IN PSZ Message +// ); +// + +#if DBG +#define PANIC(Msg) \ + DbgPrint ((Msg)) +#else +#define PANIC(Msg) +#endif + + +// +// These are define to allow DbgPrints that disappear when +// DBG is 0. +// + +#if DBG +#define NbfPrint0(fmt) DbgPrint(fmt) +#define NbfPrint1(fmt,v0) DbgPrint(fmt,v0) +#define NbfPrint2(fmt,v0,v1) DbgPrint(fmt,v0,v1) +#define NbfPrint3(fmt,v0,v1,v2) DbgPrint(fmt,v0,v1,v2) +#define NbfPrint4(fmt,v0,v1,v2,v3) DbgPrint(fmt,v0,v1,v2,v3) +#define NbfPrint5(fmt,v0,v1,v2,v3,v4) DbgPrint(fmt,v0,v1,v2,v3,v4) +#define NbfPrint6(fmt,v0,v1,v2,v3,v4,v5) DbgPrint(fmt,v0,v1,v2,v3,v4,v5) +#else +#define NbfPrint0(fmt) +#define NbfPrint1(fmt,v0) +#define NbfPrint2(fmt,v0,v1) +#define NbfPrint3(fmt,v0,v1,v2) +#define NbfPrint4(fmt,v0,v1,v2,v3) +#define NbfPrint5(fmt,v0,v1,v2,v3,v4) +#define NbfPrint6(fmt,v0,v1,v2,v3,v4,v5) +#endif + +// +// The REFCOUNTS message take up a lot of room, so make +// removing them easy. +// + +#if 1 +#define IF_REFDBG IF_NBFDBG (NBF_DEBUG_REFCOUNTS) +#else +#define IF_REFDBG if (0) +#endif + +#if DBG +#define NbfReferenceLink( Reason, Link, Type)\ + if ((Link)->Destroyed) { \ + DbgPrint("NBF: Attempt to reference destroyed link %lx\n", Link); \ + DbgBreakPoint(); \ + } \ + IF_REFDBG { \ + DbgPrint ("RefL %x: %s %s, %ld : %ld\n", Link, Reason, __FILE__, __LINE__, (Link)->ReferenceCount);\ + }\ + (VOID)ExInterlockedAddUlong ( \ + (PULONG)(&(Link)->RefTypes[Type]), \ + 1, \ + &NbfGlobalInterlock); \ + NbfRefLink (Link) + +#define NbfDereferenceLink(Reason, Link, Type)\ + if ((Link)->Destroyed) { \ + DbgPrint("NBF: Attempt to dereference destroyed link %lx\n", Link); \ + DbgBreakPoint(); \ + } \ + IF_REFDBG { \ + DbgPrint ("DeRefL %x: %s %s, %ld : %ld\n", Link, Reason, __FILE__, __LINE__, (Link)->ReferenceCount);\ + } \ + (VOID)ExInterlockedAddUlong ( \ + (PULONG)(&(Link)->RefTypes[Type]), \ + (ULONG)-1, \ + &NbfGlobalInterlock); \ + NbfDerefLink (Link) + +#define NbfDereferenceLinkMacro(Reason, Link, Type)\ + NbfDereferenceLink(Reason, Link, Type) + +#define NbfReferenceLinkSpecial( Reason, Link, Type)\ + if ((Link)->Destroyed) { \ + DbgPrint("NBF: Attempt to special reference destroyed link %lx\n", Link); \ + DbgBreakPoint(); \ + } \ + IF_REFDBG { \ + DbgPrint ("RefLS %x: %s %s, %ld : %ld\n", Link, Reason, __FILE__, __LINE__, (Link)->SpecialRefCount);\ + }\ + (VOID)ExInterlockedAddUlong ( \ + (PULONG)(&(Link)->RefTypes[Type]), \ + 1, \ + &NbfGlobalInterlock); \ + NbfRefLinkSpecial (Link) + +#define NbfDereferenceLinkSpecial(Reason, Link, Type)\ + if ((Link)->Destroyed) { \ + DbgPrint("NBF: Attempt to special dereference destroyed link %lx\n", Link); \ + DbgBreakPoint(); \ + } \ + IF_REFDBG { \ + DbgPrint ("DeRefLS %x: %s %s, %ld : %ld\n", Link, Reason, __FILE__, __LINE__, (Link)->SpecialRefCount);\ + } \ + (VOID)ExInterlockedAddUlong ( \ + (PULONG)(&(Link)->RefTypes[Type]), \ + (ULONG)-1, \ + &NbfGlobalInterlock); \ + NbfDerefLinkSpecial (Link) + +#define NbfReferenceConnection(Reason, Connection, Type)\ + if ((Connection)->Destroyed) { \ + DbgPrint("NBF: Attempt to reference destroyed conn %lx\n", Connection); \ + DbgBreakPoint(); \ + } \ + IF_REFDBG { \ + DbgPrint ("RefC %x: %s %s, %ld : %ld\n", Connection, Reason, __FILE__, __LINE__, (Connection)->ReferenceCount);\ + } \ + (VOID)ExInterlockedAddUlong ( \ + (PULONG)(&(Connection)->RefTypes[Type]), \ + 1, \ + &NbfGlobalInterlock); \ + NbfRefConnection (Connection) + +#define NbfDereferenceConnection(Reason, Connection, Type)\ + if ((Connection)->Destroyed) { \ + DbgPrint("NBF: Attempt to dereference destroyed conn %lx\n", Connection); \ + DbgBreakPoint(); \ + } \ + IF_REFDBG { \ + DbgPrint ("DeRefC %x: %s %s, %ld : %ld\n", Connection, Reason, __FILE__, __LINE__, (Connection)->ReferenceCount);\ + } \ + (VOID)ExInterlockedAddUlong ( \ + (PULONG)&((Connection)->RefTypes[Type]), \ + (ULONG)-1, \ + &NbfGlobalInterlock); \ + NbfDerefConnection (Connection) + +#define NbfDereferenceConnectionMacro(Reason, Connection, Type)\ + NbfDereferenceConnection(Reason, Connection, Type) + +#define NbfDereferenceConnectionSpecial(Reason, Connection, Type)\ + IF_REFDBG { \ + DbgPrint ("DeRefCL %x: %s %s, %ld : %ld\n", Connection, Reason, __FILE__, __LINE__, (Connection)->ReferenceCount);\ + } \ + (VOID)ExInterlockedAddUlong ( \ + (PULONG)&((Connection)->RefTypes[Type]), \ + (ULONG)-1, \ + &NbfGlobalInterlock); \ + NbfDerefConnectionSpecial (Connection) + +#define NbfReferenceRequest( Reason, Request, Type)\ + if ((Request)->Destroyed) { \ + DbgPrint("NBF: Attempt to reference destroyed req %lx\n", Request); \ + DbgBreakPoint(); \ + } \ + IF_REFDBG { \ + DbgPrint ("RefR %x: %s %s, %ld : %ld\n", Request, Reason, __FILE__, __LINE__, (Request)->ReferenceCount);}\ + (VOID)ExInterlockedAddUlong ( \ + (PULONG)(&(Request)->RefTypes[Type]), \ + 1, \ + &NbfGlobalInterlock); \ + NbfRefRequest (Request) + +#define NbfDereferenceRequest(Reason, Request, Type)\ + if ((Request)->Destroyed) { \ + DbgPrint("NBF: Attempt to dereference destroyed req %lx\n", Request); \ + DbgBreakPoint(); \ + } \ + IF_REFDBG { \ + DbgPrint ("DeRefR %x: %s %s, %ld : %ld\n", Request, Reason, __FILE__, __LINE__, (Request)->ReferenceCount);\ + } \ + (VOID)ExInterlockedAddUlong ( \ + (PULONG)(&(Request)->RefTypes[Type]), \ + (ULONG)-1, \ + &NbfGlobalInterlock); \ + NbfDerefRequest (Request) + +#define NbfReferenceSendIrp( Reason, IrpSp, Type)\ + IF_REFDBG { \ + DbgPrint ("RefSI %x: %s %s, %ld : %ld\n", IrpSp, Reason, __FILE__, __LINE__, IRP_SEND_REFCOUNT(IrpSp));}\ + NbfRefSendIrp (IrpSp) + +#define NbfDereferenceSendIrp(Reason, IrpSp, Type)\ + IF_REFDBG { \ + DbgPrint ("DeRefSI %x: %s %s, %ld : %ld\n", IrpSp, Reason, __FILE__, __LINE__, IRP_SEND_REFCOUNT(IrpSp));\ + } \ + NbfDerefSendIrp (IrpSp) + +#define NbfReferenceReceiveIrpLocked( Reason, IrpSp, Type)\ + IF_REFDBG { \ + DbgPrint ("RefRI %x: %s %s, %ld : %ld\n", IrpSp, Reason, __FILE__, __LINE__, IRP_RECEIVE_REFCOUNT(IrpSp));}\ + NbfRefReceiveIrpLocked (IrpSp) + +#define NbfDereferenceReceiveIrp(Reason, IrpSp, Type)\ + IF_REFDBG { \ + DbgPrint ("DeRefRI %x: %s %s, %ld : %ld\n", IrpSp, Reason, __FILE__, __LINE__, IRP_RECEIVE_REFCOUNT(IrpSp));\ + } \ + NbfDerefReceiveIrp (IrpSp) + +#define NbfDereferenceReceiveIrpLocked(Reason, IrpSp, Type)\ + IF_REFDBG { \ + DbgPrint ("DeRefRILocked %x: %s %s, %ld : %ld\n", IrpSp, Reason, __FILE__, __LINE__, IRP_RECEIVE_REFCOUNT(IrpSp));\ + } \ + NbfDerefReceiveIrpLocked (IrpSp) + +#define NbfReferenceAddress( Reason, Address, Type)\ + IF_REFDBG { \ + DbgPrint ("RefA %x: %s %s, %ld : %ld\n", Address, Reason, __FILE__, __LINE__, (Address)->ReferenceCount);}\ + (VOID)ExInterlockedAddUlong ( \ + (PULONG)(&(Address)->RefTypes[Type]), \ + 1, \ + &NbfGlobalInterlock); \ + NbfRefAddress (Address) + +#define NbfDereferenceAddress(Reason, Address, Type)\ + IF_REFDBG { \ + DbgPrint ("DeRefA %x: %s %s, %ld : %ld\n", Address, Reason, __FILE__, __LINE__, (Address)->ReferenceCount);\ + } \ + (VOID)ExInterlockedAddUlong ( \ + (PULONG)(&(Address)->RefTypes[Type]), \ + (ULONG)-1, \ + &NbfGlobalInterlock); \ + NbfDerefAddress (Address) + +#define NbfReferenceDeviceContext( Reason, DeviceContext, Type)\ + IF_REFDBG { \ + DbgPrint ("RefDC %x: %s %s, %ld : %ld\n", DeviceContext, Reason, __FILE__, __LINE__, (DeviceContext)->ReferenceCount);}\ + (VOID)ExInterlockedAddUlong ( \ + (PULONG)(&(DeviceContext)->RefTypes[Type]), \ + 1, \ + &NbfGlobalInterlock); \ + NbfRefDeviceContext (DeviceContext) + +#define NbfDereferenceDeviceContext(Reason, DeviceContext, Type)\ + IF_REFDBG { \ + DbgPrint ("DeRefDC %x: %s %s, %ld : %ld\n", DeviceContext, Reason, __FILE__, __LINE__, (DeviceContext)->ReferenceCount);\ + } \ + (VOID)ExInterlockedAddUlong ( \ + (PULONG)(&(DeviceContext)->RefTypes[Type]), \ + (ULONG)-1, \ + &NbfGlobalInterlock); \ + NbfDerefDeviceContext (DeviceContext) + +#else +#if defined(NBF_UP) +#define NbfReferenceLink(Reason, Link, Type) \ + { \ + ULONG _ref; \ + _ref = ++(Link)->ReferenceCount; \ + if ( _ref == 0 ) { \ + NbfReferenceLinkSpecial ("first ref", (Link), LREF_SPECIAL_TEMP); \ + } \ + } +#else +#define NbfReferenceLink(Reason, Link, Type) \ + if (InterlockedIncrement( \ + &(Link)->ReferenceCount) == 0) { \ + NbfReferenceLinkSpecial ("first ref", (Link), LREF_SPECIAL_TEMP); \ + } +#endif + +#define NbfDereferenceLink(Reason, Link, Type)\ + NbfDereferenceLinkMacro(Reason,Link,Type) + +#if defined(NBF_UP) +#define NbfDereferenceLinkMacro(Reason, Link, Type){ \ + ULONG _ref; \ + _ref = --(Link)->ReferenceCount; \ + if (_ref < 0) { \ + NbfDisconnectLink (Link); \ + NbfDerefLinkSpecial (Link); \ + } \ +} +#else +#define NbfDereferenceLinkMacro(Reason, Link, Type){ \ + if (InterlockedDecrement( \ + &(Link)->ReferenceCount) < 0) { \ + NbfDisconnectLink (Link); \ + NbfDerefLinkSpecial (Link); \ + } \ +} +#endif + +#define NbfReferenceLinkSpecial(Reason, Link, Type)\ + NbfRefLinkSpecial (Link) + +#define NbfDereferenceLinkSpecial(Reason, Link, Type)\ + NbfDerefLinkSpecial (Link) + +#define NbfReferenceConnection(Reason, Connection, Type)\ + if (InterlockedIncrement( \ + &(Connection)->ReferenceCount) == 0) { \ + ExInterlockedAddUlong( \ + (PULONG)(&(Connection)->SpecialRefCount), \ + 1, \ + (Connection)->ProviderInterlock); \ + } + +#define NbfDereferenceConnection(Reason, Connection, Type)\ + NbfDerefConnection (Connection) + +#define NbfDereferenceConnectionMacro(Reason, Connection, Type){ \ + if (InterlockedDecrement( \ + &(Connection)->ReferenceCount) < 0) { \ + if (NbfDisconnectFromLink (Connection, TRUE)) { \ + NbfIndicateDisconnect (Connection); \ + } \ + NbfDerefConnectionSpecial (Connection); \ + } \ +} + +#define NbfDereferenceConnectionSpecial(Reason, Connection, Type)\ + NbfDerefConnectionSpecial (Connection) + +#define NbfReferenceRequest(Reason, Request, Type)\ + (VOID)InterlockedIncrement( \ + &(Request)->ReferenceCount) + +#define NbfDereferenceRequest(Reason, Request, Type)\ + NbfDerefRequest (Request) + +#define NbfReferenceSendIrp(Reason, IrpSp, Type)\ + (VOID)InterlockedIncrement( \ + &IRP_SEND_REFCOUNT(IrpSp)) + +#define NbfDereferenceSendIrp(Reason, IrpSp, Type) {\ + PIO_STACK_LOCATION _IrpSp = (IrpSp); \ + if (InterlockedDecrement( \ + &IRP_SEND_REFCOUNT(_IrpSp)) == 0) { \ + PIRP _Irp = IRP_SEND_IRP(_IrpSp); \ + IRP_SEND_REFCOUNT(_IrpSp) = 0; \ + IRP_SEND_IRP (_IrpSp) = NULL; \ + IoCompleteRequest (_Irp, IO_NETWORK_INCREMENT); \ + } \ +} + +#define NbfReferenceReceiveIrpLocked(Reason, IrpSp, Type)\ + ++IRP_RECEIVE_REFCOUNT(IrpSp) + +#define NbfDereferenceReceiveIrp(Reason, IrpSp, Type)\ + NbfDerefReceiveIrp (IrpSp) + +#define NbfDereferenceReceiveIrpLocked(Reason, IrpSp, Type) { \ + if (--IRP_RECEIVE_REFCOUNT(IrpSp) == 0) { \ + ExInterlockedInsertTailList( \ + &(IRP_DEVICE_CONTEXT(IrpSp)->IrpCompletionQueue), \ + &(IRP_RECEIVE_IRP(IrpSp))->Tail.Overlay.ListEntry, \ + &(IRP_DEVICE_CONTEXT(IrpSp)->Interlock)); \ + } \ +} + +#define NbfReferenceAddress(Reason, Address, Type)\ + (VOID)InterlockedIncrement(&(Address)->ReferenceCount) + +#define NbfDereferenceAddress(Reason, Address, Type)\ + NbfDerefAddress (Address) + +#define NbfReferenceDeviceContext(Reason, DeviceContext, Type)\ + NbfRefDeviceContext (DeviceContext) + +#define NbfDereferenceDeviceContext(Reason, DeviceContext, Type)\ + NbfDerefDeviceContext (DeviceContext) + +#define NbfReferencePacket(Packet) \ + (VOID)InterlockedIncrement(&(Packet)->ReferenceCount) + +#define NbfDereferencePacket(Packet){ \ + if (InterlockedDecrement ( \ + &(Packet)->ReferenceCount) == 0) { \ + NbfDestroyPacket (Packet); \ + } \ +} + +#endif + + +// +// Error and statistics Macros +// + + +// VOID +// LogErrorToSystem( +// NTSTATUS ErrorType, +// PUCHAR ErrorDescription +// ) + +/*++ + +Routine Description: + + This routine is called to log an error from the transport to the system. + Errors that are of system interest should be logged using this interface. + For now, this macro is defined trivially. (BUGBUG) + +Arguments: + + ErrorType - The error type, a conventional NT status + + ErrorDescription - A pointer to a string describing the error. + +Return Value: + + none. + +--*/ + +#if DBG +#define LogErrorToSystem( ErrorType, ErrorDescription) \ + DbgPrint ("Logging error: File: %s Line: %ld \n Description: %s\n",__FILE__, __LINE__, ErrorDescription) +#else +#define LogErrorToSystem( ErrorType, ErrorDescription) +#endif + + +// +// Routines in TIMER.C (lightweight timer system package). +// Note that all the start and stop routines for the timers assume that you +// have the link spinlock when you call them! +// Note also that, with the latest revisions, the timer system now works by +// putting those links that have timers running on a list of links to be looked +// at for each clock tick. This list is ordered, with the most recently inserted +// elements at the tail of the list. Note further that anything already on the +// is moved to the end of the list if the timer is restarted; thus, the list +// order is preserved. +// + +VOID +NbfStartShortTimer( + IN PDEVICE_CONTEXT DeviceContext + ); + +VOID +NbfInitializeTimerSystem( + IN PDEVICE_CONTEXT DeviceContext + ); + +VOID +NbfStopTimerSystem( + IN PDEVICE_CONTEXT DeviceContext + ); + + +VOID +StartT1( + IN PTP_LINK Link, + IN ULONG PacketSize + ); + +VOID +StartT2( + IN PTP_LINK Link + ); + +VOID +StartTi( + IN PTP_LINK Link + ); + +#if DBG + +VOID +StopT1( + IN PTP_LINK Link + ); + +VOID +StopT2( + IN PTP_LINK Link + ); + +VOID +StopTi( + IN PTP_LINK Link + ); + +#else + +#define StopT1(_Link) \ + { \ + (_Link)->CurrentPollOutstanding = FALSE; \ + (_Link)->T1 = 0; \ + } + +#define StopT2(_Link) \ + { \ + (_Link)->ConsecutiveIFrames = 0; \ + (_Link)->T2 = 0; \ + } + +#define StopTi(_Link) \ + (_Link)->Ti = 0; + +#endif + + +// +// These functions may become macros once they are finished. +// + +ULONG +GetTimerInterval( + IN PTP_LINK Link + ); + +VOID +BackoffCurrentT1Timeout( + IN PTP_LINK Link + ); + +VOID +UpdateBaseT1Timeout( + IN PTP_LINK Link + ); + +VOID +CancelT1Timeout( + IN PTP_LINK Link + ); + +VOID +UpdateDelayAndThroughput( + IN PTP_LINK Link, + IN ULONG TimerInterval + ); + +VOID +FakeStartT1( + IN PTP_LINK Link, + IN ULONG PacketSize + ); + +VOID +FakeUpdateBaseT1Timeout( + IN PTP_LINK Link + ); + + +// +// These macros are used to create and destroy packets, due +// to the allocation or deallocation of structure which +// need them. +// + +#define NbfAddUIFrame(DeviceContext) { \ + PTP_UI_FRAME _UIFrame; \ + NbfAllocateUIFrame ((DeviceContext), &_UIFrame); \ + if (_UIFrame != NULL) { \ + ExInterlockedInsertTailList( \ + &(DeviceContext)->UIFramePool, \ + &_UIFrame->Linkage, \ + &(DeviceContext)->Interlock); \ + } \ +} + +#define NbfRemoveUIFrame(DeviceContext) { \ + PLIST_ENTRY p; \ + if (DeviceContext->UIFrameAllocated > DeviceContext->UIFrameInitAllocated) { \ + p = ExInterlockedRemoveHeadList( \ + &(DeviceContext)->UIFramePool, \ + &(DeviceContext)->Interlock); \ + if (p != NULL) { \ + NbfDeallocateUIFrame((DeviceContext), \ + (PTP_UI_FRAME)CONTAINING_RECORD(p, TP_UI_FRAME, Linkage)); \ + } \ + } \ +} + + +#define NbfAddSendPacket(DeviceContext) { \ + PTP_PACKET _SendPacket; \ + NbfAllocateSendPacket ((DeviceContext), &_SendPacket); \ + if (_SendPacket != NULL) { \ + ExInterlockedPushEntryList( \ + &(DeviceContext)->PacketPool, \ + (PSINGLE_LIST_ENTRY)&_SendPacket->Linkage, \ + &(DeviceContext)->Interlock); \ + } \ +} + +#define NbfRemoveSendPacket(DeviceContext) { \ + PSINGLE_LIST_ENTRY s; \ + if (DeviceContext->PacketAllocated > DeviceContext->PacketInitAllocated) { \ + s = ExInterlockedPopEntryList( \ + &(DeviceContext)->PacketPool, \ + &(DeviceContext)->Interlock); \ + if (s != NULL) { \ + NbfDeallocateSendPacket((DeviceContext), \ + (PTP_PACKET)CONTAINING_RECORD(s, TP_PACKET, Linkage)); \ + } \ + } \ +} + + +#define NbfAddReceivePacket(DeviceContext) { \ + if (!(DeviceContext)->MacInfo.SingleReceive) { \ + PNDIS_PACKET _ReceivePacket; \ + NbfAllocateReceivePacket ((DeviceContext), &_ReceivePacket); \ + if (_ReceivePacket != NULL) { \ + ExInterlockedPushEntryList( \ + &(DeviceContext)->ReceivePacketPool, \ + &((PRECEIVE_PACKET_TAG)_ReceivePacket->ProtocolReserved)->Linkage, \ + &(DeviceContext)->Interlock); \ + } \ + } \ +} + +#define NbfRemoveReceivePacket(DeviceContext) { \ + PSINGLE_LIST_ENTRY s; \ + if (DeviceContext->ReceivePacketAllocated > DeviceContext->ReceivePacketInitAllocated) { \ + s = ExInterlockedPopEntryList( \ + &(DeviceContext)->ReceivePacketPool, \ + &(DeviceContext)->Interlock); \ + if (s != NULL) { \ + NbfDeallocateReceivePacket((DeviceContext), \ + (PNDIS_PACKET)CONTAINING_RECORD(s, NDIS_PACKET, ProtocolReserved[0])); \ + } \ + } \ +} + + +#define NbfAddReceiveBuffer(DeviceContext) { \ + if (!(DeviceContext)->MacInfo.SingleReceive) { \ + PBUFFER_TAG _ReceiveBuffer; \ + NbfAllocateReceiveBuffer ((DeviceContext), &_ReceiveBuffer); \ + if (_ReceiveBuffer != NULL) { \ + ExInterlockedPushEntryList( \ + &(DeviceContext)->ReceiveBufferPool, \ + (PSINGLE_LIST_ENTRY)&_ReceiveBuffer->Linkage, \ + &(DeviceContext)->Interlock); \ + } \ + } \ +} + +#define NbfRemoveReceiveBuffer(DeviceContext) { \ + PSINGLE_LIST_ENTRY s; \ + if (DeviceContext->ReceiveBufferAllocated > DeviceContext->ReceiveBufferInitAllocated) { \ + s = ExInterlockedPopEntryList( \ + &(DeviceContext)->ReceiveBufferPool, \ + &(DeviceContext)->Interlock); \ + if (s != NULL) { \ + NbfDeallocateReceiveBuffer(DeviceContext, \ + (PBUFFER_TAG)CONTAINING_RECORD(s, BUFFER_TAG, Linkage)); \ + } \ + } \ +} + + +// +// These routines are used to maintain counters. +// + +#define INCREMENT_COUNTER(_DeviceContext,_Field) \ + ++(_DeviceContext)->Statistics._Field + +#define DECREMENT_COUNTER(_DeviceContext,_Field) \ + --(_DeviceContext)->Statistics._Field + +#define ADD_TO_LARGE_INTEGER(_LargeInteger,_Ulong) \ + ExInterlockedAddLargeStatistic((_LargeInteger), (ULONG)(_Ulong)) + + + +// +// Routines in PACKET.C (TP_PACKET object manager). +// + +VOID +NbfAllocateUIFrame( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_UI_FRAME *TransportUIFrame + ); + +VOID +NbfAllocateSendPacket( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_PACKET *TransportSendPacket + ); + +VOID +NbfAllocateReceivePacket( + IN PDEVICE_CONTEXT DeviceContext, + OUT PNDIS_PACKET *TransportReceivePacket + ); + +VOID +NbfAllocateReceiveBuffer( + IN PDEVICE_CONTEXT DeviceContext, + OUT PBUFFER_TAG *TransportReceiveBuffer + ); + +VOID +NbfDeallocateUIFrame( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_UI_FRAME TransportUIFrame + ); + +VOID +NbfDeallocateSendPacket( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_PACKET TransportSendPacket + ); + +VOID +NbfDeallocateReceivePacket( + IN PDEVICE_CONTEXT DeviceContext, + IN PNDIS_PACKET TransportReceivePacket + ); + +VOID +NbfDeallocateReceiveBuffer( + IN PDEVICE_CONTEXT DeviceContext, + IN PBUFFER_TAG TransportReceiveBuffer + ); + +NTSTATUS +NbfCreatePacket( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_LINK Link, + OUT PTP_PACKET *Packet + ); + +NTSTATUS +NbfCreateRrPacket( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_LINK Link, + OUT PTP_PACKET *Packet + ); + +VOID +NbfDestroyPacket( + IN PTP_PACKET Packet + ); +VOID +NbfGrowSendPacketPool( + IN PDEVICE_CONTEXT DeviceContext + ); + +#if DBG +VOID +NbfReferencePacket( + IN PTP_PACKET Packet + ); + +VOID +NbfDereferencePacket( + IN PTP_PACKET Packet + ); +#endif + +VOID +NbfWaitPacket( + IN PTP_CONNECTION Connection, + IN ULONG Flags + ); + +#if DBG +#define MAGIC 1 +extern BOOLEAN NbfEnableMagic; +#else +#define MAGIC 0 +#endif + +#if MAGIC +VOID +NbfSendMagicBullet ( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_LINK Link + ); +#endif + +// +// Routines in RCVENG.C (Receive engine). +// + +VOID +AwakenReceive( + IN PTP_CONNECTION Connection + ); + +VOID +ActivateReceive( + IN PTP_CONNECTION Connection + ); + +VOID +CompleteReceive ( + IN PTP_CONNECTION Connection, + IN BOOLEAN EndOfMessage, + IN ULONG BytesTransferred + ); + +VOID +NbfCancelReceive( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +NbfCancelReceiveDatagram( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +// +// Routines in SEND.C (Receive engine). +// + +NTSTATUS +NbfTdiSend( + IN PIRP Irp + ); + +NTSTATUS +NbfTdiSendDatagram( + IN PIRP Irp + ); + +// +// Routines in SENDENG.C (Send engine). +// + +#if DBG + +VOID +InitializeSend( + PTP_CONNECTION Connection + ); + +#else + +// See SENDENG.C for the fully-commented description of InitializeSend. + +#define InitializeSend(_conn_) { \ + PIRP _irp_; \ + (_conn_)->SendState = CONNECTION_SENDSTATE_PACKETIZE; \ + _irp_ = CONTAINING_RECORD ((_conn_)->SendQueue.Flink, \ + IRP, \ + Tail.Overlay.ListEntry); \ + (_conn_)->FirstSendIrp = (_conn_)->sp.CurrentSendIrp = _irp_; \ + (_conn_)->FirstSendMdl = (_conn_)->sp.CurrentSendMdl = \ + _irp_->MdlAddress; \ + (_conn_)->FirstSendByteOffset = (_conn_)->sp.SendByteOffset = 0; \ + (_conn_)->sp.MessageBytesSent = 0; \ + (_conn_)->CurrentSendLength = \ + IRP_SEND_LENGTH(IoGetCurrentIrpStackLocation(_irp_)); \ + (_conn_)->StallCount = 0; \ + (_conn_)->StallBytesSent = 0; \ + if ((_conn_)->NetbiosHeader.ResponseCorrelator == 0xffff) { \ + (_conn_)->NetbiosHeader.ResponseCorrelator = 1; \ + } else { \ + ++((_conn_)->NetbiosHeader.ResponseCorrelator); \ + } \ +} + +#endif + +// See SENDENG.C for the fully-commented description of +// StartPacketizingConnection. On a free build this is a +// macro for speed. + +#if DBG + +VOID +StartPacketizingConnection( + PTP_CONNECTION Connection, + IN BOOLEAN Immediate + ); + +#else + +#define StartPacketizingConnection(_conn_,_immed_) { \ + PDEVICE_CONTEXT _devctx_; \ + _devctx_ = (_conn_)->Provider; \ + if (((_conn_)->SendState == CONNECTION_SENDSTATE_PACKETIZE) && \ + !((_conn_)->Flags & CONNECTION_FLAGS_PACKETIZE)) { \ + (_conn_)->Flags |= CONNECTION_FLAGS_PACKETIZE; \ + if (!(_immed_)) { \ + NbfReferenceConnection("Packetize", \ + (_conn_), \ + CREF_PACKETIZE_QUEUE); \ + } \ + ExInterlockedInsertTailList (&_devctx_->PacketizeQueue, \ + &(_conn_)->PacketizeLinkage, \ + &_devctx_->SpinLock); \ + RELEASE_DPC_SPIN_LOCK ((_conn_)->LinkSpinLock); \ + } else { \ + RELEASE_DPC_SPIN_LOCK ((_conn_)->LinkSpinLock); \ + if (_immed_) { \ + NbfDereferenceConnection("temp TdiSend", (_conn_), CREF_BY_ID); \ + } \ + } \ + if (_immed_) { \ + PacketizeConnections (_devctx_); \ + } \ +} + +#endif + +VOID +PacketizeConnections( + IN PDEVICE_CONTEXT DeviceContext + ); + +VOID +PacketizeSend( + IN PTP_CONNECTION Connection, + IN BOOLEAN Direct + ); + +BOOLEAN +ResendLlcPackets( + IN PTP_LINK Link, + IN UCHAR AckSequenceNumber, + IN BOOLEAN Resend + ); + +VOID +CompleteSend( + IN PTP_CONNECTION Connection, + IN USHORT Correlator + ); + +VOID +FailSend( + IN PTP_CONNECTION Connection, + IN NTSTATUS RequestStatus, + IN BOOLEAN StopConnection + ); + +VOID +ReframeSend( + IN PTP_CONNECTION Connection, + IN ULONG BytesReceived + ); + +VOID +NbfCancelSend( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +SendOnePacket( + IN PTP_CONNECTION Connection, + IN PTP_PACKET Packet, + IN BOOLEAN ForceAck, + OUT PBOOLEAN LinkCheckpoint OPTIONAL + ); + +VOID +SendControlPacket( + IN PTP_LINK Link, + IN PTP_PACKET Packet + ); + +VOID +NbfNdisSend( + IN PTP_LINK Link, + IN PTP_PACKET Packet + ); + +VOID +RestartLinkTraffic( + IN PTP_LINK Link + ); + +VOID +NbfSendCompletionHandler( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus + ); + +NTSTATUS +BuildBufferChainFromMdlChain ( + IN PDEVICE_CONTEXT DeviceContext, + IN PMDL CurrentMdl, + IN ULONG ByteOffset, + IN ULONG DesiredLength, + OUT PNDIS_BUFFER *Destination, + OUT PMDL *NewCurrentMdl, + OUT ULONG *NewByteOffset, + OUT ULONG *TrueLength + ); + +// +// Routines in DEVCTX.C (TP_DEVCTX object manager). +// + +VOID +NbfRefDeviceContext( + IN PDEVICE_CONTEXT DeviceContext + ); + +VOID +NbfDerefDeviceContext( + IN PDEVICE_CONTEXT DeviceContext + ); + +NTSTATUS +NbfCreateDeviceContext( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING DeviceName, + IN OUT PDEVICE_CONTEXT *DeviceContext + ); + +VOID +NbfDestroyDeviceContext( + IN PDEVICE_CONTEXT DeviceContext + ); + + +// +// Routines in ADDRESS.C (TP_ADDRESS object manager). +// + +#if DBG +VOID +NbfRefAddress( + IN PTP_ADDRESS Address + ); +#endif + +VOID +NbfDerefAddress( + IN PTP_ADDRESS Address + ); + +VOID +NbfAllocateAddressFile( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_ADDRESS_FILE *TransportAddressFile + ); + +VOID +NbfDeallocateAddressFile( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS_FILE TransportAddressFile + ); + +NTSTATUS +NbfCreateAddressFile( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_ADDRESS_FILE * AddressFile + ); + +VOID +NbfReferenceAddressFile( + IN PTP_ADDRESS_FILE AddressFile + ); + +VOID +NbfDereferenceAddressFile( + IN PTP_ADDRESS_FILE AddressFile + ); + +VOID +NbfDestroyAddress( + IN PVOID Parameter + ); + +NTSTATUS +NbfOpenAddress( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +NbfCloseAddress( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +VOID +NbfStopAddress( + IN PTP_ADDRESS Address + ); + +VOID +NbfRegisterAddress( + IN PTP_ADDRESS Address + ); + +BOOLEAN +NbfMatchNetbiosAddress( + IN PTP_ADDRESS Address, + IN UCHAR NameType, + IN PUCHAR NetBIOSName + ); + +VOID +NbfAllocateAddress( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_ADDRESS *TransportAddress + ); + +VOID +NbfDeallocateAddress( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS TransportAddress + ); + +NTSTATUS +NbfCreateAddress( + IN PDEVICE_CONTEXT DeviceContext, + IN PNBF_NETBIOS_ADDRESS NetworkName, + OUT PTP_ADDRESS *Address + ); + +PTP_ADDRESS +NbfLookupAddress( + IN PDEVICE_CONTEXT DeviceContext, + IN PNBF_NETBIOS_ADDRESS NetworkName + ); + +PTP_CONNECTION +NbfLookupRemoteName( + IN PTP_ADDRESS Address, + IN PUCHAR RemoteName, + IN UCHAR RemoteSessionNumber + ); + +NTSTATUS +NbfStopAddressFile( + IN PTP_ADDRESS_FILE AddressFile, + IN PTP_ADDRESS Address + ); + +VOID +AddressTimeoutHandler( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); + +TDI_ADDRESS_NETBIOS UNALIGNED * +NbfParseTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN BOOLEAN BroadcastAddressOk +); + +BOOLEAN +NbfValidateTdiAddress( + IN TRANSPORT_ADDRESS UNALIGNED * TransportAddress, + IN ULONG TransportAddressLength +); + +NTSTATUS +NbfVerifyAddressObject ( + IN PTP_ADDRESS_FILE AddressFile + ); + +NTSTATUS +NbfSendDatagramsOnAddress( + PTP_ADDRESS Address + ); + +// +// Routines in CONNECT.C. +// + +NTSTATUS +NbfTdiAccept( + IN PIRP Irp + ); + +NTSTATUS +NbfTdiConnect( + IN PIRP Irp + ); + +NTSTATUS +NbfTdiDisconnect( + IN PIRP Irp + ); + +NTSTATUS +NbfTdiDisassociateAddress ( + IN PIRP Irp + ); + +NTSTATUS +NbfTdiAssociateAddress( + IN PIRP Irp + ); + +NTSTATUS +NbfTdiListen( + IN PIRP Irp + ); + +NTSTATUS +NbfOpenConnection( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +NbfCloseConnection( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +// +// +// Routines in CONNOBJ.C (TP_CONNECTION object manager). +// + +#if DBG +VOID +NbfRefConnection( + IN PTP_CONNECTION TransportConnection + ); +#endif + +VOID +NbfDerefConnection( + IN PTP_CONNECTION TransportConnection + ); + +VOID +NbfDerefConnectionSpecial( + IN PTP_CONNECTION TransportConnection + ); + +VOID +NbfClearConnectionLsn( + IN PTP_CONNECTION TransportConnection + ); + +VOID +NbfStopConnection( + IN PTP_CONNECTION TransportConnection, + IN NTSTATUS Status + ); + +VOID +NbfCancelConnection( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +NbfStartConnectionTimer( + IN PTP_CONNECTION TransportConnection, + IN PKDEFERRED_ROUTINE TimeoutFunction, + IN ULONG WaitTime + ); + +PTP_CONNECTION +NbfLookupListeningConnection( + IN PTP_ADDRESS Address, + IN PUCHAR RemoteName + ); + +PTP_CONNECTION +NbfLookupConnectingConnection( + IN PTP_ADDRESS Address + ); + +VOID +NbfAllocateConnection( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_CONNECTION *TransportConnection + ); + +VOID +NbfDeallocateConnection( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_CONNECTION TransportConnection + ); + +NTSTATUS +NbfCreateConnection( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_CONNECTION *TransportConnection + ); + +PTP_CONNECTION +NbfLookupConnectionById( + IN PTP_ADDRESS Address, + IN USHORT ConnectionId + ); + +PTP_CONNECTION +NbfLookupConnectionByContext( + IN PTP_ADDRESS Address, + IN CONNECTION_CONTEXT ConnectionContext + ); + +#if 0 +VOID +NbfWaitConnectionOnLink( + IN PTP_CONNECTION Connection, + IN ULONG Flags + ); +#endif + +VOID +ConnectionEstablishmentTimeout( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); + +NTSTATUS +NbfVerifyConnectionObject ( + IN PTP_CONNECTION Connection + ); + +NTSTATUS +NbfIndicateDisconnect( + IN PTP_CONNECTION TransportConnection + ); + +// +// Routines in INFO.C (QUERY_INFO manager). +// + +NTSTATUS +NbfTdiQueryInformation( + IN PDEVICE_CONTEXT DeviceContext, + IN PIRP Irp + ); + +NTSTATUS +NbfTdiSetInformation( + IN PIRP Irp + ); + +VOID +NbfSendQueryFindName( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_REQUEST Request + ); + +NTSTATUS +NbfProcessQueryNameRecognized( + IN PDEVICE_CONTEXT DeviceContext, + IN PUCHAR Packet, + PNBF_HDR_CONNECTIONLESS UiFrame + ); + +VOID +NbfSendStatusQuery( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_REQUEST Request, + IN PHARDWARE_ADDRESS DestinationAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ); + +NTSTATUS +NbfProcessStatusResponse( + IN PDEVICE_CONTEXT DeviceContext, + IN NDIS_HANDLE ReceiveContext, + IN PNBF_HDR_CONNECTIONLESS UiFrame, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ); + +NTSTATUS +NbfProcessStatusQuery( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS Address OPTIONAL, + IN PNBF_HDR_CONNECTIONLESS UiFrame, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ); + +// +// Routines in EVENT.C. +// + +NTSTATUS +NbfTdiSetEventHandler( + IN PIRP Irp + ); + +// +// Routines in REQUEST.C (TP_REQUEST object manager). +// + + +VOID +TdiRequestTimeoutHandler( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); + +#if DBG +VOID +NbfRefRequest( + IN PTP_REQUEST Request + ); +#endif + +VOID +NbfDerefRequest( + IN PTP_REQUEST Request + ); + +VOID +NbfCompleteRequest( + IN PTP_REQUEST Request, + IN NTSTATUS Status, + IN ULONG Information + ); + +#if DBG +VOID +NbfRefSendIrp( + IN PIO_STACK_LOCATION IrpSp + ); + +VOID +NbfDerefSendIrp( + IN PIO_STACK_LOCATION IrpSp + ); +#endif + +VOID +NbfCompleteSendIrp( + IN PIRP Irp, + IN NTSTATUS Status, + IN ULONG Information + ); + +#if DBG +VOID +NbfRefReceiveIrpLocked( + IN PIO_STACK_LOCATION IrpSp + ); +#endif + +VOID +NbfDerefReceiveIrp( + IN PIO_STACK_LOCATION IrpSp + ); + +#if DBG +VOID +NbfDerefReceiveIrpLocked( + IN PIO_STACK_LOCATION IrpSp + ); +#endif + +VOID +NbfCompleteReceiveIrp( + IN PIRP Irp, + IN NTSTATUS Status, + IN ULONG Information + ); + +VOID +NbfAllocateRequest( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_REQUEST *TransportRequest + ); + +VOID +NbfDeallocateRequest( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_REQUEST TransportRequest + ); + +NTSTATUS +NbfCreateRequest( + IN PIRP Irp, + IN PVOID Context, + IN ULONG Flags, + IN PMDL Buffer2, + IN ULONG Buffer2Length, + IN LARGE_INTEGER Timeout, + OUT PTP_REQUEST * TpRequest + ); + +// +// Routines in LINK.C (TP_LINK object manager). +// + +NTSTATUS +NbfDestroyLink( + IN PTP_LINK TransportLink + ); + +VOID +NbfDisconnectLink( + IN PTP_LINK Link + ); + +#if DBG +VOID +NbfRefLink( + IN PTP_LINK TransportLink + ); +#endif + +VOID +NbfDerefLink( + IN PTP_LINK TransportLink + ); + +VOID +NbfRefLinkSpecial( + IN PTP_LINK TransportLink + ); + +VOID +NbfDerefLinkSpecial( + IN PTP_LINK TransportLink + ); + +VOID +NbfResetLink( + IN PTP_LINK Link + ); + +VOID +NbfStopLink( + IN PTP_LINK Link + ); + +VOID +NbfCompleteLink( + IN PTP_LINK Link + ); + +VOID +NbfActivateLink( + IN PTP_LINK Link + ); + +VOID +NbfWaitLink( + IN PTP_LINK Link + ); + +BOOLEAN +NbfDisconnectFromLink( + IN PTP_CONNECTION TransportConnection, + IN BOOLEAN VerifyReferenceCount + ); + +NTSTATUS +NbfAssignGroupLsn( + IN PTP_CONNECTION TransportConnection + ); + +NTSTATUS +NbfConnectToLink( + IN PTP_LINK Link, + IN PTP_CONNECTION TransportConnection + ); + +PTP_CONNECTION +NbfLookupPendingConnectOnLink( + IN PTP_LINK Link + ); + +PTP_CONNECTION +NbfLookupPendingListenOnLink( + IN PTP_LINK Link + ); + +VOID +NbfAllocateLink( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_LINK *TransportLink + ); + +VOID +NbfDeallocateLink( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_LINK TransportLink + ); + +NTSTATUS +NbfCreateLink( + IN PDEVICE_CONTEXT DeviceContext, + IN PHARDWARE_ADDRESS HardwareAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + IN USHORT LoopbackLinkIndex, + OUT PTP_LINK *TransportLink + ); + +VOID +NbfDumpLinkInfo ( + IN PTP_LINK Link + ); + +// +// routines in linktree.c +// + + +NTSTATUS +NbfAddLinkToTree ( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_LINK Link + ); + +NTSTATUS +NbfRemoveLinkFromTree( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_LINK Link + ); + +PTP_LINK +NbfFindLinkInTree( + IN PDEVICE_CONTEXT DeviceContext, + IN PUCHAR Remote + ); + +PTP_LINK +NbfFindLink( + IN PDEVICE_CONTEXT DeviceContext, + IN PUCHAR Remote + ); + +// +// Routines in DLC.C (LLC frame cracker, entrypoints from NDIS interface). +// + +VOID +NbfInsertInLoopbackQueue ( + IN PDEVICE_CONTEXT DeviceContext, + IN PNDIS_PACKET NdisPacket, + IN UCHAR LinkIndex + ); + +VOID +NbfProcessLoopbackQueue ( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); + +NDIS_STATUS +NbfReceiveIndication( + IN NDIS_HANDLE BindingContext, + IN NDIS_HANDLE ReceiveContext, + IN PVOID HeaderBuffer, + IN UINT HeaderBufferSize, + IN PVOID LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT PacketSize + ); + +VOID +NbfGeneralReceiveHandler ( + IN PDEVICE_CONTEXT DeviceContext, + IN NDIS_HANDLE ReceiveContext, + IN PHARDWARE_ADDRESS SourceAddress, + IN PTP_LINK Link, + IN PVOID HeaderBuffer, + IN UINT PacketSize, + IN PDLC_FRAME DlcHeader, + IN UINT DlcSize, + IN BOOLEAN Loopback + ); + +VOID +NbfReceiveComplete ( + IN NDIS_HANDLE BindingContext + ); + +VOID +NbfProcessWanDelayedQueue( + IN PVOID Parameter + ); + +VOID +NbfTransferDataComplete( + IN NDIS_HANDLE BindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS Status, + IN UINT BytesTransferred + ); + + +VOID +NbfTransferLoopbackData ( + OUT PNDIS_STATUS NdisStatus, + IN PDEVICE_CONTEXT DeviceContext, + IN NDIS_HANDLE ReceiveContext, + IN UINT ByteOffset, + IN UINT BytesToTransfer, + IN PNDIS_PACKET Packet, + OUT PUINT BytesTransferred + ); + + +// +// Routines in UFRAMES.C, the UI-frame NBF frame processor. +// + +NTSTATUS +NbfIndicateDatagram( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS Address, + IN PUCHAR Dsdu, + IN ULONG Length + ); + +NTSTATUS +NbfProcessUi( + IN PDEVICE_CONTEXT DeviceContext, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR Header, + IN PUCHAR DlcHeader, + IN ULONG DlcLength, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + OUT PTP_ADDRESS * DatagramAddress + ); + +// +// Routines in IFRAMES.C, the I-frame NBF frame processor. +// + +VOID +NbfAcknowledgeDataOnlyLast( + IN PTP_CONNECTION Connection, + IN ULONG MessageLength + ); + +VOID +NbfProcessIIndicate( + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PTP_LINK Link, + IN PUCHAR DlcHeader, + IN UINT DlcIndicatedLength, + IN UINT DlcTotalLength, + IN NDIS_HANDLE ReceiveContext, + IN BOOLEAN Loopback + ); + +NTSTATUS +ProcessIndicateData( + IN PTP_CONNECTION Connection, + IN PUCHAR DlcHeader, + IN UINT DlcIndicatedLength, + IN PUCHAR DataHeader, + IN UINT DataTotalLength, + IN NDIS_HANDLE ReceiveContext, + IN BOOLEAN Last, + IN BOOLEAN Loopback + ); + +// +// Routines in RCV.C (data copying routines for receives). +// + +NTSTATUS +NbfTdiReceive( + IN PIRP Irp + ); + +NTSTATUS +NbfTdiReceiveDatagram( + IN PIRP Irp + ); + +// +// Routines in FRAMESND.C, the UI-frame (non-link) shipper. +// + +VOID +NbfSendNameQuery( + IN PTP_CONNECTION Connection, + IN BOOLEAN SourceRoutingOptional + ); + +VOID +NbfSendNameRecognized( + IN PTP_ADDRESS Address, + IN UCHAR LocalSessionNumber, // LSN assigned to session. + IN PNBF_HDR_CONNECTIONLESS Header, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ); + +VOID +NbfSendNameInConflict( + IN PTP_ADDRESS Address, + IN PUCHAR ConflictingName + ); + +NTSTATUS +NbfSendAddNameQuery( + IN PTP_ADDRESS Address + ); + +VOID +NbfSendSessionInitialize( + IN PTP_CONNECTION Connection + ); + +VOID +NbfSendSessionConfirm( + IN PTP_CONNECTION Connection + ); + +VOID +NbfSendSessionEnd( + IN PTP_CONNECTION Connection, + IN BOOLEAN Abort + ); + +VOID +NbfSendNoReceive( + IN PTP_CONNECTION Connection + ); + +VOID +NbfSendReceiveContinue( + IN PTP_CONNECTION Connection + ); + +VOID +NbfSendReceiveOutstanding( + IN PTP_CONNECTION Connection + ); + +VOID +NbfSendDataAck( + IN PTP_CONNECTION Connection + ); + +VOID +NbfSendSabme( + IN PTP_LINK Link, + IN BOOLEAN PollFinal + ); + +VOID +NbfSendDisc( + IN PTP_LINK Link, + IN BOOLEAN PollFinal + ); + +VOID +NbfSendUa( + IN PTP_LINK Link, + IN BOOLEAN PollFinal + ); + +VOID +NbfSendDm( + IN PTP_LINK Link, + IN BOOLEAN PollFinal + ); + +VOID +NbfSendRr( + IN PTP_LINK Link, + IN BOOLEAN Command, + IN BOOLEAN PollFinal + ); + +#if 0 + +// +// These functions are not currently called, so they are commented +// out. +// + +VOID +NbfSendRnr( + IN PTP_LINK Link, + IN BOOLEAN Command, + IN BOOLEAN PollFinal + ); + +VOID +NbfSendTest( + IN PTP_LINK Link, + IN BOOLEAN Command, + IN BOOLEAN PollFinal, + IN PMDL Psdu + ); + +VOID +NbfSendFrmr( + IN PTP_LINK Link, + IN BOOLEAN PollFinal + ); + +#endif + +VOID +NbfSendXid( + IN PTP_LINK Link, + IN BOOLEAN Command, + IN BOOLEAN PollFinal + ); + +VOID +NbfSendRej( + IN PTP_LINK Link, + IN BOOLEAN Command, + IN BOOLEAN PollFinal + ); + +NTSTATUS +NbfCreateConnectionlessFrame( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_UI_FRAME *OuterFrame + ); + +VOID +NbfDestroyConnectionlessFrame( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_UI_FRAME RawFrame + ); + +VOID +NbfSendUIFrame( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_UI_FRAME RawFrame, + IN BOOLEAN Loopback + ); + +VOID +NbfSendUIMdlFrame( + IN PTP_ADDRESS Address + ); + +VOID +NbfSendDatagramCompletion( + IN PTP_ADDRESS Address, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus + ); + +// +// Routines in FRAMECON.C, the NetBIOS Frames Protocol Frame Constructors. +// To understand the various constant parameters to these functions (such +// as special data1 & data2 values, see NBFCONST.H for details. +// + +VOID +ConstructAddGroupNameQuery( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN USHORT Correlator, // correlator for ADD_NAME_RESPONSE. + IN PNAME GroupName // NetBIOS group name to be added. + ); + +VOID +ConstructAddNameQuery( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN USHORT Correlator, // correlator for ADD_NAME_RESPONSE. + IN PNAME Name // NetBIOS name to be added. + ); + +VOID +ConstructNameInConflict( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN PNAME ConflictingName, // NetBIOS name that is conflicting. + IN PNAME SendingPermanentName // NetBIOS permanent node name of sender. + ); + +VOID +ConstructStatusQuery( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN UCHAR RequestType, // type of request, defined below. + IN USHORT BufferLength, // length of user's status buffer. + IN USHORT Correlator, // correlator for STATUS_RESPONSE. + IN PNAME ReceiverName, // NetBIOS name of receiver. + IN PNAME SendingPermanentName // NetBIOS permanent node name of sender. + ); + +VOID +ConstructTerminateTrace( + IN PNBF_HDR_CONNECTIONLESS RawFrame // frame buffer to format. + ); + +VOID +ConstructDatagram( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN PNAME ReceiverName, // NetBIOS name of receiver. + IN PNAME SenderName // NetBIOS name of sender. + ); + +VOID +ConstructDatagramBroadcast( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN PNAME SenderName // NetBIOS name of sender. + ); + +VOID +ConstructNameQuery( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN UCHAR NameType, // type of name. + IN UCHAR LocalSessionNumber, // LSN assigned to session (0=FIND_NAME). + IN USHORT Correlator, // correlator in NAME_RECOGNIZED. + IN PNAME SenderName, // NetBIOS name of sender. + IN PNAME ReceiverName // NetBIOS name of sender. + ); + +VOID +ConstructAddNameResponse( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN UCHAR NameType, // type of name. + IN USHORT Correlator, // correlator from ADD_[GROUP_]NAME_QUERY. + IN PNAME Name // NetBIOS name being responded to. + ); + +VOID +ConstructNameRecognized( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN UCHAR NameType, // type of name. + IN UCHAR LocalSessionNumber, // LSN assigned to session. + IN USHORT NameQueryCorrelator, // correlator from NAME_QUERY. + IN USHORT Correlator, // correlator expected from next response. + IN PNAME SenderName, // NetBIOS name of sender. + IN PNAME ReceiverName // NetBIOS name of receiver. + ); + +VOID +ConstructStatusResponse( + IN PNBF_HDR_CONNECTIONLESS RawFrame,// frame buffer to format. + IN UCHAR RequestType, // type of request, defined below. + IN BOOLEAN Truncated, // data is truncated. + IN BOOLEAN DataOverflow, // too much data for user's buffer. + IN USHORT DataLength, // length of data sent. + IN USHORT Correlator, // correlator from STATUS_QUERY. + IN PNAME ReceivingPermanentName, // NetBIOS permanent node name of receiver. + IN PNAME SenderName // NetBIOS name of sender. + ); + +VOID +ConstructDataAck( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN USHORT Correlator, // correlator from DATA_ONLY_LAST. + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ); + +VOID +ConstructDataOnlyLast( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN BOOLEAN Resynched, // TRUE if we are resynching. + IN USHORT Correlator, // correlator for RECEIVE_CONTINUE. + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ); + +VOID +ConstructSessionConfirm( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN UCHAR Options, // bitflag options, defined below. + IN USHORT MaximumUserBufferSize, // max size of user frame on session. + IN USHORT Correlator, // correlator from SESSION_INITIALIZE. + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ); + +VOID +ConstructSessionEnd( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN USHORT Reason, // reason for termination, defined below. + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ); + +VOID +ConstructSessionInitialize( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN UCHAR Options, // bitflag options, defined below. + IN USHORT MaximumUserBufferSize, // max size of user frame on session. + IN USHORT NameRecognizedCorrelator, // correlator from NAME_RECOGNIZED. + IN USHORT Correlator, // correlator for SESSION_CONFIRM. + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ); + +VOID +ConstructNoReceive( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN USHORT Options, // option bitflags, defined below. + IN USHORT BytesAccepted, // number of bytes accepted. + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ); + +VOID +ConstructReceiveOutstanding( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN USHORT BytesAccepted, // number of bytes accepted. + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ); + +VOID +ConstructReceiveContinue( + IN PNBF_HDR_CONNECTION RawFrame, // frame buffer to format. + IN USHORT Correlator, // correlator from DATA_FIRST_MIDDLE + IN UCHAR LocalSessionNumber, // session number of SENDER. + IN UCHAR RemoteSessionNumber // session number of RECEIVER. + ); + +#if 0 +VOID +ConstructSessionAlive( + IN PNBF_HDR_CONNECTION RawFrame // frame buffer to format. + ); +#endif + +// +// Routines in nbfndis.c. +// + +#if DBG +PUCHAR +NbfGetNdisStatus ( + IN NDIS_STATUS NdisStatus + ); +#endif + +// +// Routines in nbfdrvr.c +// + +VOID +NbfWriteResourceErrorLog( + IN PDEVICE_CONTEXT DeviceContext, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN ULONG BytesNeeded, + IN ULONG ResourceId + ); + +VOID +NbfWriteGeneralErrorLog( + IN PDEVICE_CONTEXT DeviceContext, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN ULONG DumpDataCount, + IN ULONG DumpData[] + ); + +VOID +NbfWriteOidErrorLog( + IN PDEVICE_CONTEXT DeviceContext, + IN NTSTATUS ErrorCode, + IN NTSTATUS FinalStatus, + IN PWSTR AdapterString, + IN ULONG OidValue + ); + +VOID +NbfFreeResources( + IN PDEVICE_CONTEXT DeviceContext + ); + + +extern +ULONG +NbfInitializeOneDeviceContext( + OUT PNDIS_STATUS NdisStatus, + IN PDRIVER_OBJECT DriverObject, + IN PCONFIG_DATA NbfConfig, + IN INT AdapterIndex + ); +// +// routines in nbfcnfg.c +// + +NTSTATUS +NbfConfigureTransport ( + IN PUNICODE_STRING RegistryPath, + IN PCONFIG_DATA * ConfigData + ); + +// +// Routines in nbfndis.c +// + +NTSTATUS +NbfRegisterProtocol ( + IN PUNICODE_STRING NameString + ); + +VOID +NbfDeregisterProtocol ( + VOID + ); + + +NTSTATUS +NbfInitializeNdis ( + IN PDEVICE_CONTEXT DeviceContext, + IN PCONFIG_DATA ConfigInfo, + IN UINT ConfigInfoNameIndex + ); + +VOID +NbfCloseNdis ( + IN PDEVICE_CONTEXT DeviceContext + ); + + +// +// Routines in action.c +// + +NTSTATUS +NbfTdiAction( + IN PDEVICE_CONTEXT DeviceContext, + IN PIRP Irp + ); + +VOID +NbfActionQueryIndication( + PDEVICE_CONTEXT DeviceContext, + PNBF_HDR_CONNECTIONLESS UiFrame + ); + +VOID +NbfActionDatagramIndication( + PDEVICE_CONTEXT DeviceContext, + PNBF_HDR_CONNECTIONLESS UiFrame, + ULONG Length + ); + +VOID +NbfStopControlChannel( + IN PDEVICE_CONTEXT DeviceContext, + IN USHORT ChannelIdentifier + ); + + +// +// Routines in nbfdebug.c +// + +#if DBG + +VOID +DisplayOneFrame( + PTP_PACKET Packet + ); + +VOID +NbfDisplayUIFrame( + PTP_UI_FRAME OuterFrame + ); + +VOID +NbfFormattedDump( + PCHAR far_p, + ULONG len + ); + +#endif + +#endif // def _NBFPROCS_ diff --git a/private/ntos/tdi/nbf/nbftypes.h b/private/ntos/tdi/nbf/nbftypes.h new file mode 100644 index 000000000..a72e9fe1b --- /dev/null +++ b/private/ntos/tdi/nbf/nbftypes.h @@ -0,0 +1,2202 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + nbftypes.h + +Abstract: + + This module defines private data structures and types for the NT + NBF transport provider. + +Author: + + David Beaver (dbeaver) 1 July 1991 + +Revision History: + +--*/ + +#ifndef _NBFTYPES_ +#define _NBFTYPES_ + +// +// This structure defines a NETBIOS name as a character array for use when +// passing preformatted NETBIOS names between internal routines. It is +// not a part of the external interface to the transport provider. +// + +#define NETBIOS_NAME_SIZE 16 + +typedef struct _NBF_NETBIOS_ADDRESS { + UCHAR NetbiosName[NETBIOS_NAME_SIZE]; + USHORT NetbiosNameType; +} NBF_NETBIOS_ADDRESS, *PNBF_NETBIOS_ADDRESS; + +typedef UCHAR NAME; +typedef NAME UNALIGNED *PNAME; + + +// +// This structure defines things associated with a TP_REQUEST, or outstanding +// TDI request, maintained on a queue somewhere in the transport. All +// requests other than open/close require that a TP_REQUEST block be built. +// + +#if DBG +#define REQUEST_HISTORY_LENGTH 20 +extern KSPIN_LOCK NbfGlobalInterlock; +#endif + +// +// the types of potential owners of requests +// + +typedef enum _REQUEST_OWNER { + ConnectionType, + AddressType, + DeviceContextType +} REQUEST_OWNER; + +//typedef +//NTSTATUS +//(*PTDI_TIMEOUT_ACTION)( +// IN PTP_REQUEST Request +// ); + +// +// The request itself +// + +#if DBG +#define RREF_CREATION 0 +#define RREF_PACKET 1 +#define RREF_TIMER 2 +#define RREF_RECEIVE 3 +#define RREF_FIND_NAME 4 +#define RREF_STATUS 5 + +#define NUMBER_OF_RREFS 8 +#endif + +typedef struct _TP_REQUEST { + CSHORT Type; // type of this structure + USHORT Size; // size of this structure + LIST_ENTRY Linkage; // used by ExInterlocked routines. + KSPIN_LOCK SpinLock; // spinlock for other fields. + // (used in KeAcquireSpinLock calls) +#if DBG + LONG RefTypes[NUMBER_OF_RREFS]; +#endif + LONG ReferenceCount; // reasons why we can't destroy this req. + + struct _DEVICE_CONTEXT *Provider; // pointer to the device context. + PKSPIN_LOCK ProviderInterlock; // &Provider->Interlock. + + PIRP IoRequestPacket; // pointer to IRP for this request. + + // + // The following two fields are used to quickly reference the basic + // components of the requests without worming through the IRP's stack. + // + + PVOID Buffer2; // second buffer in the request. + ULONG Buffer2Length; // length of the second buffer. + + // + // The following two fields (Flags and Context) are used to clean up + // queued requests which must be canceled or abnormally completed. + // The Flags field contains bitflags indicating the state of the request, + // and the specific queue type that the request is located on. The + // Context field contains a pointer to the owning structure (TP_CONNECTION + // or TP_ADDRESS) so that the cleanup routines can perform post-cleanup + // operations on the owning structure, such as dereferencing, etc. + // + + ULONG Flags; // disposition of this request. + PVOID Context; // context of this request. + REQUEST_OWNER Owner; // what type of owner this request has. + +#if DBG + LARGE_INTEGER Time; // time when request created +#endif + + KTIMER Timer; // kernel timer for this request. + KDPC Dpc; // DPC object for timeouts. + + // + // These fields are used for FIND.NAME and STATUS.QUERY requests. + // + + ULONG Retries; // timeouts remaining. + USHORT BytesWritten; // usage varies. + USHORT FrameContext; // identifies request. + PVOID ResponseBuffer; // temp alloc to hold data. + +#if DBG + LIST_ENTRY GlobalLinkage; + ULONG TotalReferences; + ULONG TotalDereferences; + ULONG NextRefLoc; + struct { + PVOID Caller; + PVOID CallersCaller; + } History[REQUEST_HISTORY_LENGTH]; + BOOLEAN Completed; + BOOLEAN Destroyed; +#endif + +} TP_REQUEST, *PTP_REQUEST; + +#ifdef _PNP_POWER +// +// in nbfdrvr.c +// + +extern UNICODE_STRING NbfRegistryPath; + +// +// We need the driver object to create device context structures. +// + +extern PDRIVER_OBJECT NbfDriverObject; + +#endif // _PNP_POWER +#if DBG +extern KSPIN_LOCK NbfGlobalHistoryLock; +extern LIST_ENTRY NbfGlobalRequestList; +#define StoreRequestHistory(_req,_ref) { \ + KIRQL oldIrql; \ + KeAcquireSpinLock (&NbfGlobalHistoryLock, &oldIrql); \ + if ((_req)->Destroyed) { \ + DbgPrint ("request touched after being destroyed 0x%lx\n", \ + (_req)); \ + DbgBreakPoint(); \ + } \ + RtlGetCallersAddress( \ + &(_req)->History[(_req)->NextRefLoc].Caller, \ + &(_req)->History[(_req)->NextRefLoc].CallersCaller \ + ); \ + if ((_ref)) { \ + (_req)->TotalReferences++; \ + } else { \ + (_req)->TotalDereferences++; \ + (_req)->History[(_req)->NextRefLoc].Caller = \ + (PVOID)((ULONG)(_req)->History[(_req)->NextRefLoc].Caller & \ + ~0x80000000); \ + } \ + if (++(_req)->NextRefLoc == REQUEST_HISTORY_LENGTH) { \ + (_req)->NextRefLoc = 0; \ + } \ + KeReleaseSpinLock (&NbfGlobalHistoryLock, oldIrql); \ +} +#endif + +#define NBF_ALLOCATION_TYPE_REQUEST 1 + +#define REQUEST_FLAGS_TIMER 0x0001 // a timer is active for this request. +#define REQUEST_FLAGS_TIMED_OUT 0x0002 // a timer expiration occured on this request. +#define REQUEST_FLAGS_ADDRESS 0x0004 // request is attached to a TP_ADDRESS. +#define REQUEST_FLAGS_CONNECTION 0x0008 // request is attached to a TP_CONNECTION. +#define REQUEST_FLAGS_STOPPING 0x0010 // request is being killed. +#define REQUEST_FLAGS_EOR 0x0020 // TdiSend request has END_OF_RECORD mark. +#define REQUEST_FLAGS_PIGGYBACK 0x0040 // TdiSend that can be piggyback ack'ed. +#define REQUEST_FLAGS_DC 0x0080 // request is attached to a TP_DEVICE_CONTEXT + +// +// This defines the TP_SEND_IRP_PARAMETERS, which is masked onto the +// Parameters section of a send IRP's stack location. +// + +typedef struct _TP_SEND_IRP_PARAMETERS { + TDI_REQUEST_KERNEL_SEND Request; + LONG ReferenceCount; + PVOID Irp; +} TP_SEND_IRP_PARAMETERS, *PTP_SEND_IRP_PARAMETERS; + +#define IRP_SEND_LENGTH(_IrpSp) \ + (((PTP_SEND_IRP_PARAMETERS)&(_IrpSp)->Parameters)->Request.SendLength) + +#define IRP_SEND_FLAGS(_IrpSp) \ + (((PTP_SEND_IRP_PARAMETERS)&(_IrpSp)->Parameters)->Request.SendFlags) + +#define IRP_SEND_REFCOUNT(_IrpSp) \ + (((PTP_SEND_IRP_PARAMETERS)&(_IrpSp)->Parameters)->ReferenceCount) + +#define IRP_SEND_IRP(_IrpSp) \ + (((PTP_SEND_IRP_PARAMETERS)&(_IrpSp)->Parameters)->Irp) + +#define IRP_SEND_CONNECTION(_IrpSp) \ + ((PTP_CONNECTION)((_IrpSp)->FileObject->FsContext)) + +#define IRP_DEVICE_CONTEXT(_IrpSp) \ + ((PDEVICE_CONTEXT)((_IrpSp)->DeviceObject)) + + +// +// This defines the TP_RECEIVE_IRP_PARAMETERS, which is masked onto the +// Parameters section of a receive IRP's stack location. +// + +typedef struct _TP_RECEIVE_IRP_PARAMETERS { + TDI_REQUEST_KERNEL_RECEIVE Request; + LONG ReferenceCount; + PIRP Irp; +} TP_RECEIVE_IRP_PARAMETERS, *PTP_RECEIVE_IRP_PARAMETERS; + +#define IRP_RECEIVE_LENGTH(_IrpSp) \ + (((PTP_RECEIVE_IRP_PARAMETERS)&(_IrpSp)->Parameters)->Request.ReceiveLength) + +#define IRP_RECEIVE_FLAGS(_IrpSp) \ + (((PTP_RECEIVE_IRP_PARAMETERS)&(_IrpSp)->Parameters)->Request.ReceiveFlags) + +#define IRP_RECEIVE_REFCOUNT(_IrpSp) \ + (((PTP_RECEIVE_IRP_PARAMETERS)&(_IrpSp)->Parameters)->ReferenceCount) + +#define IRP_RECEIVE_IRP(_IrpSp) \ + (((PTP_RECEIVE_IRP_PARAMETERS)&(_IrpSp)->Parameters)->Irp) + +#define IRP_RECEIVE_CONNECTION(_IrpSp) \ + ((PTP_CONNECTION)((_IrpSp)->FileObject->FsContext)) + + + +// +// This structure defines a TP_UI_FRAME, or connectionless frame header, +// that is manipulated by the FRAME.C routines. +// + +typedef struct _TP_UI_FRAME { + PNDIS_PACKET NdisPacket; + LIST_ENTRY Linkage; // used by ExInterLocked routines. + PVOID DataBuffer; // for transport-created data. + UCHAR Header[1]; // the header in the frame (MAC + DLC + NBF) +} TP_UI_FRAME, *PTP_UI_FRAME; + + +// +// This structure defines a TP_VARIABLE, or network managable variable, +// maintained in a linked list on the device context. +// + +typedef struct _TP_VARIABLE { + + struct _TP_VARIABLE *Fwdlink; // next variable in provider's chain. + + ULONG VariableSerialNumber; // identifier for this variable. + ULONG VariableType; // type of this variable (see TDI.H). + STRING VariableName; // allocated variable name. + + union { + ULONG LongValue; + HARDWARE_ADDRESS HardwareAddressValue; + STRING StringValue; // allocated string value, if of that type. + } Value; + +} TP_VARIABLE, *PTP_VARIABLE; + + +// +// This structure defines a TP_CONNECTION, or active transport connection, +// maintained on a transport address. +// + +#if DBG +#define CONNECTION_HISTORY_LENGTH 50 + +#define CREF_SPECIAL_CREATION 0 +#define CREF_SPECIAL_TEMP 1 +#define CREF_COMPLETE_SEND 2 +#define CREF_SEND_IRP 3 +#define CREF_ADM_SESS 4 +#define CREF_TRANSFER_DATA 5 +#define CREF_FRAME_SEND 6 +#define CREF_TIMER 7 +#define CREF_BY_ID 8 +#define CREF_LINK 9 +#define CREF_SESSION_END 10 +#define CREF_LISTENING 11 +#define CREF_P_LINK 12 +#define CREF_P_CONNECT 13 +#define CREF_PACKETIZE 14 +#define CREF_RECEIVE_IRP 15 +#define CREF_PROCESS_DATA 16 +#define CREF_REQUEST 17 +#define CREF_TEMP 18 +#define CREF_DATA_ACK_QUEUE 19 +#define CREF_ASSOCIATE 20 +#define CREF_STOP_ADDRESS 21 +#define CREF_PACKETIZE_QUEUE 22 +#define CREF_STALLED 23 + +#define NUMBER_OF_CREFS 24 +#endif + +// +// This structure holds our "complex send pointer" indicating +// where we are in the packetization of a send. +// + +typedef struct _TP_SEND_POINTER { + ULONG MessageBytesSent; // up count, bytes sent/this msg. + PIRP CurrentSendIrp; // ptr, current send request in chain. + PMDL CurrentSendMdl; // ptr, current MDL in send chain. + ULONG SendByteOffset; // current byte offset in current MDL. +} TP_SEND_POINTER, *PTP_SEND_POINTER; + +typedef struct _TP_CONNECTION { + +#if DBG + ULONG RefTypes[NUMBER_OF_CREFS]; +#endif + +#if DBG + ULONG LockAcquired; + UCHAR LastAcquireFile[8]; + ULONG LastAcquireLine; + ULONG Padding; + UCHAR LastReleaseFile[8]; + ULONG LastReleaseLine; +#endif + + CSHORT Type; + USHORT Size; + + LIST_ENTRY LinkList; // used for link thread or for free + // resource list + KSPIN_LOCK SpinLock; // spinlock for connection protection. + PKSPIN_LOCK LinkSpinLock; // pointer to link's spinlock + + LONG ReferenceCount; // number of references to this object. + LONG SpecialRefCount; // controls freeing of connection. + + // + // The following lists are used to associate this connection with a + // particular address. + // + + LIST_ENTRY AddressList; // list of connections for given address + LIST_ENTRY AddressFileList; // list for connections bound to a + // given address reference + + // + // The following field is used as linkage in the device context's + // PacketizeQueue + // + + LIST_ENTRY PacketizeLinkage; + + // + // The following field is used as linkage in the device context's + // PacketWaitQueue. + // + + LIST_ENTRY PacketWaitLinkage; + + // + // The following field points to the TP_LINK object that describes the + // (active) data link connection for this transport connection. To be + // valid, this field is non-NULL. + // + + struct _TP_LINK *Link; // pointer to transport link object. + struct _TP_ADDRESS_FILE *AddressFile; // pointer to owning Address. + struct _DEVICE_CONTEXT *Provider; // device context to which we are attached. + PKSPIN_LOCK ProviderInterlock; // &Provider->Interlock + PFILE_OBJECT FileObject; // easy backlink to file object. + + // + // The following field contains the actual ID we expose to the TDI client + // to represent this connection. A unique one is created from the address. + // + + USHORT ConnectionId; // unique identifier. + UCHAR SessionNumber; // the session number used in the packet header + + // + // This field is used to keep the reason for the connection disconnect + // around until connection deletion time. + // + + BOOLEAN RemoteDisconnect; // was this connection remotely disonnected? + + // + // The following field is specified by the user at connection open time. + // It is the context that the user associates with the connection so that + // indications to and from the client can be associated with a particular + // connection. + // + + CONNECTION_CONTEXT Context; // client-specified value. + + // + // The following two queues are used to associate TdiSend and TdiReceive + // IRPs with this connection. New arrivals are placed at the end of + // the queues (really a linked list) and IRPs are processed at the + // front of the queues. The first TdiSend IRP on the SendQueue is + // the current TdiSend being processed, and the first TdiReceive IRP + // on the ReceiveQueue is the first TdiReceive being processed, PROVIDED + // the CONNECTION_FLAGS_ACTIVE_RECEIVE flag is set. If this flag is not + // set, then the first TdiReceive IRP on the ReceiveQueue is not active. + // These queues are managed by the EXECUTIVE interlocked list manipuation + // routines. + // + + LIST_ENTRY SendQueue; // FIFO of outstanding TdiSends. + LIST_ENTRY ReceiveQueue; // FIFO of outstanding TdiReceives. + + // + // The following fields are used to maintain state for the current receive. + // + + ULONG MessageBytesReceived; // up count, bytes recd/this msg. + ULONG MessageBytesAcked; // bytes acked (NR or RO) this msg. + ULONG MessageInitAccepted; // bytes accepted during indication. + + // + // These fields are only valid if the CONNECTION_FLAGS_ACTIVE_RECEIVE + // flag is set. + // + + PIRP SpecialReceiveIrp; // a "no-request" receive IRP exists. + PIRP CurrentReceiveIrp; // ptr, current receive IRP. + PMDL CurrentReceiveMdl; // ptr, current MDL in receive chain. + ULONG ReceiveByteOffset; // current byte offset in current MDL. + ULONG ReceiveLength; // current receive length, in bytes (total) + ULONG ReceiveBytesUnaccepted; // by client...only indicate when == 0 + + // + // The following fields are used to maintain state for the active send. + // They only have meaning if the connection's SendState is not IDLE. + // Because the TDI client may submit multiple TdiSend requests to comprise + // a full message, we have to keep a complex pointer to the first byte of + // unACKed data (hence the first three fields). We also have a complex + // pointer to the first byte of unsent data (hence the last three fields). + // + + ULONG SendState; // send state machine variable. + + PIRP FirstSendIrp; // ptr, 1st TdiSend's IRP. + PMDL FirstSendMdl; // ptr, 1st unacked MDL in chain/this msg. + ULONG FirstSendByteOffset; // pre-acked bytes in that MDL. + + TP_SEND_POINTER sp; // current send loc, defined above. + ULONG CurrentSendLength; // how long is this send (total) + ULONG StallCount; // times in a row we looked stalled. + ULONG StallBytesSent; // bytes sent last time we checked. + + // + // This is TRUE if we need don't need to reference the current + // receive IRP during transfers (because it is a special + // receive or the driver doesn't pend transfers). + // + + BOOLEAN CurrentReceiveSynchronous; + + // + // This field will be TRUE if the last DOL received allowed + // piggyback acks. + // + + BOOLEAN CurrentReceiveAckQueueable; + + // + // + // This field will be TRUE if the last DOL received was + // sent NO.ACK. + // + + BOOLEAN CurrentReceiveNoAck; + + // + // These fields handle asynchronous TransferData calls. + // + + ULONG TransferBytesPending; // bytes pending in current transfers + ULONG TotalTransferBytesPending; // bytes since TransferBytesPending was 0; + // how much we back off if a transfer fails + PMDL SavedCurrentReceiveMdl; // used to back off by TotalTransferPending bytes + ULONG SavedReceiveByteOffset; // used to back off by TotalTransferPending bytes + + // + // This field will be TRUE if we are in the middle of + // processing a receive indication on this connection and + // we are not yet in a state where another indication + // can be handled. + // + // It is stored as a INT since access to it is guarded + // by the connection's link spinlock, unlike the variables + // around it (BUGBUG: What about Alpha?) + // + + UINT IndicationInProgress; + + // + // The following field is used as a linkage when on the device + // context's DataAckQueue. + // + + LIST_ENTRY DataAckLinkage; + + // + // TRUE if the connection is on the data ack queue. + // Also an INT so access can be non-guarded. + // + + UINT OnDataAckQueue; + + // + // These keep track of the number of consecutive sends or + // receives on this connection. This is used in determining when + // to queue a data ack. + // + + ULONG ConsecutiveSends; + ULONG ConsecutiveReceives; + + // + // The following list head is used as a pointer to a TdiListen/TdiConnect + // request which is in progress. Although manipulated + // with queue instructions, there will only be one request in the queue. + // This is done for consistency with respect to TpCreateRequest, which + // does a great job of creating a request and associating it atomically + // with a supervisory object. + // + + LIST_ENTRY InProgressRequest; // TdiListen/TdiConnect + + // + // If the connection is being disconnected as a result of + // a TdiDisconnect call (RemoteDisconnect is FALSE) then this + // will hold the IRP passed to TdiDisconnect. It is needed + // when the TdiDisconnect request is completed. + // + + PIRP DisconnectIrp; + + // + // If the connection is being closed, this will hold + // the IRP passed to TdiCloseConnection. It is needed + // when the request is completed. + // + + PIRP CloseIrp; + + // + // These fields are used for deferred operations on connections; the only + // deferred operation currently supported is piggyback ACK + // + + ULONG DeferredFlags; +#if DBG + ULONG DeferredPasses; +#endif + LIST_ENTRY DeferredQueue; + + // + // The following fields are used for connection housekeeping. + // + + ULONG Flags; // attributes guarded by LinkSpinLock + ULONG Flags2; // attributes guarded by SpinLock + UINT OnPacketWaitQueue; // TRUE if on PacketWaitQueue + UCHAR Lsn; // local session number (1-254). + UCHAR Rsn; // remote session number (1-254). + USHORT Retries; // retry limit for NAME_QUERY shipments. + KTIMER Timer; // kernel timer for timeouts on NQ/NR. + LARGE_INTEGER ConnectStartTime; // when we sent the committed NQ. + KDPC Dpc; // DPC object for timeouts. + NTSTATUS Status; // status code for connection rundown. + ULONG LastPacketsSent; // The value that was in Link->XXX the + ULONG LastPacketsResent; // last time we calculated the throughput. + NBF_NETBIOS_ADDRESS CalledAddress; // TdiConnect request's T.A. + USHORT MaximumDataSize; // maximum I-frame data size for NBF. + + NBF_HDR_CONNECTION NetbiosHeader; // pre-built Netbios header; we store + // the current send and reply correlators + // in the appropriate spots in this. + + // + // These are for CONNECTION_INFO queries. + // + + ULONG TransmittedTsdus; // TSDUs sent on this connection. + ULONG ReceivedTsdus; // TSDUs received on this connection. + ULONG TransmissionErrors; // TSDUs transmitted in error/this connection. + ULONG ReceiveErrors; // TSDUs received in error/this connection. + + // + // The following structure contains statistics counters for use + // by TdiQueryInformation and TdiSetInformation. They should not + // be used for maintenance of internal data structures. + // + + // TDI_CONNECTION_INFO Information; // information about this connection. + +#if DBG + LIST_ENTRY GlobalLinkage; + ULONG TotalReferences; + ULONG TotalDereferences; + ULONG NextRefLoc; + struct { + PVOID Caller; + PVOID CallersCaller; + } History[CONNECTION_HISTORY_LENGTH]; + BOOLEAN Destroyed; +#endif + CHAR RemoteName[16]; + +} TP_CONNECTION, *PTP_CONNECTION; + +#if DBG +extern KSPIN_LOCK NbfGlobalHistoryLock; +extern LIST_ENTRY NbfGlobalConnectionList; +#define StoreConnectionHistory(_conn,_ref) { \ + KIRQL oldIrql; \ + KeAcquireSpinLock (&NbfGlobalHistoryLock, &oldIrql); \ + if ((_conn)->Destroyed) { \ + DbgPrint ("connection touched after being destroyed 0x%lx\n", \ + (_conn)); \ + DbgBreakPoint(); \ + } \ + RtlGetCallersAddress( \ + &(_conn)->History[(_conn)->NextRefLoc].Caller, \ + &(_conn)->History[(_conn)->NextRefLoc].CallersCaller \ + ); \ + if ((_ref)) { \ + (_conn)->TotalReferences++; \ + } else { \ + (_conn)->TotalDereferences++; \ + (_conn)->History[(_conn)->NextRefLoc].Caller = \ + (PVOID)((ULONG)(_conn)->History[(_conn)->NextRefLoc].Caller | 1); \ + } \ + if (++(_conn)->NextRefLoc == CONNECTION_HISTORY_LENGTH) { \ + (_conn)->NextRefLoc = 0; \ + } \ + KeReleaseSpinLock (&NbfGlobalHistoryLock, oldIrql); \ +} +#endif + +#define CONNECTION_FLAGS_VERSION2 0x00000001 // remote netbios is version 2.0. +#define CONNECTION_FLAGS_RECEIVE_WAKEUP 0x00000002 // send a RECEIVE_OUTSTANDING when a receive arrives. +#define CONNECTION_FLAGS_ACTIVE_RECEIVE 0x00000004 // a receive is active. +#define CONNECTION_FLAGS_WAIT_SI 0x00000020 // waiting for a SESSION_INITIALIZE. +#define CONNECTION_FLAGS_WAIT_SC 0x00000040 // waiting for a SESSION_CONFIRM. +#define CONNECTION_FLAGS_WAIT_LINK_UP 0x00000080 // waiting for DDI to est. connection. +#define CONNECTION_FLAGS_READY 0x00000200 // sends/rcvs/discons valid. +#define CONNECTION_FLAGS_RC_PENDING 0x00001000 // a receive is pending completion +#define CONNECTION_FLAGS_W_PACKETIZE 0x00002000 // w/for a packet to packetize. +#define CONNECTION_FLAGS_PACKETIZE 0x00004000 // we're on the PacketizeQueue. +#define CONNECTION_FLAGS_W_RESYNCH 0x00008000 // waiting for resynch indicator. (receive) +#define CONNECTION_FLAGS_SEND_SI 0x00010000 // w/for a packet to send SI. +#define CONNECTION_FLAGS_SEND_SC 0x00020000 // w/for a packet to send SC. +#define CONNECTION_FLAGS_SEND_DA 0x00040000 // w/for a packet to send DA. +#define CONNECTION_FLAGS_SEND_RO 0x00080000 // w/for a packet to send RO. +#define CONNECTION_FLAGS_SEND_RC 0x00100000 // w/for a packet to send RC. +#define CONNECTION_FLAGS_SEND_SE 0x00200000 // w/for a packet to send SE. +#define CONNECTION_FLAGS_SEND_NR 0x00400000 // w/for a packet to send NR. +#define CONNECTION_FLAGS_NO_INDICATE 0x00800000 // don't take packets at indication time +#define CONNECTION_FLAGS_FAILING_TO_EOR 0x01000000 // wait for an EOF in an incoming request before sending +#define CONNECTION_FLAGS_RESYNCHING 0x02000000 // engaged send side resynch +#define CONNECTION_FLAGS_RCV_CANCELLED 0x10000000 // current receive was cancelled +#define CONNECTION_FLAGS_PEND_INDICATE 0x20000000 // new data received during RC_PENDING +#define CONNECTION_FLAGS_TRANSFER_FAIL 0x40000000 // a transfer data call failed + +#define CONNECTION_FLAGS2_STOPPING 0x00000001 // connection is running down. +#define CONNECTION_FLAGS2_WAIT_NR 0x00000002 // waiting for NAME_RECOGNIZED. +#define CONNECTION_FLAGS2_WAIT_NQ 0x00000004 // waiting for NAME_QUERY. +#define CONNECTION_FLAGS2_WAIT_NR_FN 0x00000008 // waiting for FIND NAME response. +#define CONNECTION_FLAGS2_CLOSING 0x00000010 // connection is closing +#define CONNECTION_FLAGS2_ASSOCIATED 0x00000020 // associated with address +#define CONNECTION_FLAGS2_DISCONNECT 0x00000040 // disconnect done on connection +#define CONNECTION_FLAGS2_ACCEPTED 0x00000080 // accept done on connection +#define CONNECTION_FLAGS2_REQ_COMPLETED 0x00000100 // Listen/Connect request completed. +#define CONNECTION_FLAGS2_DISASSOCIATED 0x00000200 // associate CRef has been removed +#define CONNECTION_FLAGS2_DISCONNECTED 0x00000400 // disconnect has been indicated +#define CONNECTION_FLAGS2_NO_LISTEN 0x00000800 // no_listen received during setup +#define CONNECTION_FLAGS2_REMOTE_VALID 0x00001000 // Connection->RemoteName is valid +#define CONNECTION_FLAGS2_GROUP_LSN 0x00002000 // connection LSN is globally assigned +#define CONNECTION_FLAGS2_W_ADDRESS 0x00004000 // waiting for address reregistration. +#define CONNECTION_FLAGS2_PRE_ACCEPT 0x00008000 // no TdiAccept after listen completes +#define CONNECTION_FLAGS2_ABORT 0x00010000 // abort this connection. +#define CONNECTION_FLAGS2_ORDREL 0x00020000 // we're in orderly release. +#define CONNECTION_FLAGS2_DESTROY 0x00040000 // destroy this connection. +#define CONNECTION_FLAGS2_LISTENER 0x00100000 // we were the passive listener. +#define CONNECTION_FLAGS2_CONNECTOR 0x00200000 // we were the active connector. +#define CONNECTION_FLAGS2_WAITING_SC 0x00400000 // the connection is waiting for + // and accept to send the + // session confirm +#define CONNECTION_FLAGS2_INDICATING 0x00800000 // connection was manipulated while + // indication was in progress + +#define CONNECTION_FLAGS2_LDISC 0x01000000 // Local disconnect req. +#ifdef RASAUTODIAL +#define CONNECTION_FLAGS2_AUTOCONNECTING 0x02000000 // RAS autodial in progress +#define CONNECTION_FLAGS2_AUTOCONNECTED 0x04000000 // RAS autodial done +#endif // RASAUTODIAL + +#define CONNECTION_FLAGS_STARVED ( \ + CONNECTION_FLAGS_SEND_SI | \ + CONNECTION_FLAGS_SEND_SC | \ + CONNECTION_FLAGS_SEND_DA | \ + CONNECTION_FLAGS_SEND_RO | \ + CONNECTION_FLAGS_SEND_RC | \ + CONNECTION_FLAGS_SEND_NR | \ + CONNECTION_FLAGS_SEND_SE \ + ) + +#define CONNECTION_FLAGS_DEFERRED_ACK 0x00000001 // send piggyback ack first opportunity +#define CONNECTION_FLAGS_DEFERRED_ACK_2 0x00000002 // deferred ack wasn't sent +#define CONNECTION_FLAGS_DEFERRED_NOT_Q 0x00000004 // DEFERRED_ACK set, but not on DataAckQueue +#define CONNECTION_FLAGS_DEFERRED_SENDS 0x80000000 // print completed sends + +#define CONNECTION_SENDSTATE_IDLE 0 // no sends being processed. +#define CONNECTION_SENDSTATE_PACKETIZE 1 // send being packetized. +#define CONNECTION_SENDSTATE_W_PACKET 2 // waiting for free packet. +#define CONNECTION_SENDSTATE_W_LINK 3 // waiting for good link conditions. +#define CONNECTION_SENDSTATE_W_EOR 4 // waiting for TdiSend(EOR). +#define CONNECTION_SENDSTATE_W_ACK 5 // waiting for DATA_ACK. +#define CONNECTION_SENDSTATE_W_RCVCONT 6 // waiting for RECEIVE_CONTINUE. + + +// +// This structure is pointed to by the FsContext field in the FILE_OBJECT +// for this Address. This structure is the base for all activities on +// the open file object within the transport provider. All active connections +// on the address point to this structure, although no queues exist here to do +// work from. This structure also maintains a reference to a TP_ADDRESS +// structure, which describes the address that it is bound to. Thus, a +// connection will point to this structure, which describes the address the +// connection was associated with. When the address file closes, all connections +// opened on this address file get closed, too. Note that this may leave an +// address hanging around, with other references. +// + +typedef struct _TP_ADDRESS_FILE { + + CSHORT Type; + CSHORT Size; + + LIST_ENTRY Linkage; // next address file on this address. + // also used for linkage in the + // look-aside list + + LONG ReferenceCount; // number of references to this object. + + // + // This structure is edited after taking the Address spinlock for the + // owning address. This ensures that the address and this structure + // will never get out of syncronization with each other. + // + + // + // The following field points to a list of TP_CONNECTION structures, + // one per connection open on this address. This list of connections + // is used to help the cleanup process if a process closes an address + // before disassociating all connections on it. By design, connections + // will stay around until they are explicitly + // closed; we use this database to ensure that we clean up properly. + // + + LIST_ENTRY ConnectionDatabase; // list of defined transport connections. + + // + // the current state of the address file structure; this is either open or + // closing + // + + UCHAR State; + + // + // The following fields are kept for housekeeping purposes. + // + + PIRP Irp; // the irp used for open or close + struct _TP_ADDRESS *Address; // address to which we are bound. + PFILE_OBJECT FileObject; // easy backlink to file object. + struct _DEVICE_CONTEXT *Provider; // device context to which we are attached. + + // + // The following queue is used to queue receive datagram requests + // on this address file. Send datagram requests are queued on the + // address itself. These queues are managed by the EXECUTIVE interlocked + // list management routines. The actual objects which get queued to this + // structure are request control blocks (RCBs). + // + + LIST_ENTRY ReceiveDatagramQueue; // FIFO of outstanding TdiReceiveDatagrams. + + // + // This holds the Irp used to close this address file, + // for pended completion. + // + + PIRP CloseIrp; + + // + // is this address file currently indicating a connection request? if yes, we + // need to mark connections that are manipulated during this time. + // + + BOOLEAN ConnectIndicationInProgress; + + // + // handler for kernel event actions. First we have a set of booleans that + // indicate whether or not this address has an event handler of the given + // type registered. + // + + BOOLEAN RegisteredConnectionHandler; + BOOLEAN RegisteredDisconnectHandler; + BOOLEAN RegisteredReceiveHandler; + BOOLEAN RegisteredReceiveDatagramHandler; + BOOLEAN RegisteredExpeditedDataHandler; + BOOLEAN RegisteredErrorHandler; + + // + // This function pointer points to a connection indication handler for this + // Address. Any time a connect request is received on the address, this + // routine is invoked. + // + // + + PTDI_IND_CONNECT ConnectionHandler; + PVOID ConnectionHandlerContext; + + // + // The following function pointer always points to a TDI_IND_DISCONNECT + // handler for the address. If the NULL handler is specified in a + // TdiSetEventHandler, this this points to an internal routine which + // simply returns successfully. + // + + PTDI_IND_DISCONNECT DisconnectHandler; + PVOID DisconnectHandlerContext; + + // + // The following function pointer always points to a TDI_IND_RECEIVE + // event handler for connections on this address. If the NULL handler + // is specified in a TdiSetEventHandler, then this points to an internal + // routine which does not accept the incoming data. + // + + PTDI_IND_RECEIVE ReceiveHandler; + PVOID ReceiveHandlerContext; + + // + // The following function pointer always points to a TDI_IND_RECEIVE_DATAGRAM + // event handler for the address. If the NULL handler is specified in a + // TdiSetEventHandler, this this points to an internal routine which does + // not accept the incoming data. + // + + PTDI_IND_RECEIVE_DATAGRAM ReceiveDatagramHandler; + PVOID ReceiveDatagramHandlerContext; + + // + // An expedited data handler. This handler is used if expedited data is + // expected; it never is in NBF, thus this handler should always point to + // the default handler. + // + + PTDI_IND_RECEIVE_EXPEDITED ExpeditedDataHandler; + PVOID ExpeditedDataHandlerContext; + + // + // The following function pointer always points to a TDI_IND_ERROR + // handler for the address. If the NULL handler is specified in a + // TdiSetEventHandler, this this points to an internal routine which + // simply returns successfully. + // + + PTDI_IND_ERROR ErrorHandler; + PVOID ErrorHandlerContext; + PVOID ErrorHandlerOwner; + + +} TP_ADDRESS_FILE, *PTP_ADDRESS_FILE; + +#define ADDRESSFILE_STATE_OPENING 0x00 // not yet open for business +#define ADDRESSFILE_STATE_OPEN 0x01 // open for business +#define ADDRESSFILE_STATE_CLOSING 0x02 // closing + + +// +// This structure defines a TP_ADDRESS, or active transport address, +// maintained by the transport provider. It contains all the visible +// components of the address (such as the TSAP and network name components), +// and it also contains other maintenance parts, such as a reference count, +// ACL, and so on. All outstanding connection-oriented and connectionless +// data transfer requests are queued here. +// + +#if DBG +#define AREF_TIMER 0 +#define AREF_TEMP_CREATE 1 +#define AREF_OPEN 2 +#define AREF_VERIFY 3 +#define AREF_LOOKUP 4 +#define AREF_FRAME_SEND 5 +#define AREF_CONNECTION 6 +#define AREF_TEMP_STOP 7 +#define AREF_REQUEST 8 +#define AREF_PROCESS_UI 9 +#define AREF_PROCESS_DATAGRAM 10 +#define AREF_TIMER_SCAN 11 + +#define NUMBER_OF_AREFS 12 +#endif + +typedef struct _TP_ADDRESS { + +#if DBG + ULONG RefTypes[NUMBER_OF_AREFS]; +#endif + + USHORT Size; + CSHORT Type; + + LIST_ENTRY Linkage; // next address/this device object. + LONG ReferenceCount; // number of references to this object. + + // + // The following spin lock is acquired to edit this TP_ADDRESS structure + // or to scan down or edit the list of address files. + // + + KSPIN_LOCK SpinLock; // lock to manipulate this structure. + + // + // The following fields comprise the actual address itself. + // + + PIRP Irp; // pointer to address creation IRP. + PNBF_NETBIOS_ADDRESS NetworkName; // this address + + // + // The following fields are used to maintain state about this address. + // + + ULONG Flags; // attributes of the address. + struct _DEVICE_CONTEXT *Provider; // device context to which we are attached. + + // + // The following queues is used to hold send datagrams for this + // address. Receive datagrams are queued to the address file. Requests are + // processed in a first-in, first-out manner, so that the very next request + // to be serviced is always at the head of its respective queue. These + // queues are managed by the EXECUTIVE interlocked list management routines. + // The actual objects which get queued to this structure are request control + // blocks (RCBs). + // + + LIST_ENTRY SendDatagramQueue; // FIFO of outstanding TdiSendDatagrams. + + // + // The following field points to a list of TP_CONNECTION structures, + // one per active, connecting, or disconnecting connections on this + // address. By definition, if a connection is on this list, then + // it is visible to the client in terms of receiving events and being + // able to post requests by naming the ConnectionId. If the connection + // is not on this list, then it is not valid, and it is guaranteed that + // no indications to the client will be made with reference to it, and + // no requests specifying its ConnectionId will be accepted by the transport. + // + + LIST_ENTRY ConnectionDatabase; // list of defined transport connections. + LIST_ENTRY AddressFileDatabase; // list of defined address file objects + + // + // The packet pool of size 1 that holds the UI frame, and the + // frame that is allocated out of it. + // + + NDIS_HANDLE UIFramePoolHandle; + PTP_UI_FRAME UIFrame; // DLC-UI/NBF header for datagram sends. + + // + // The following fields are used to register this address on the network. + // + + ULONG Retries; // retries of ADD_NAME_QUERY left to go. + KTIMER Timer; // kernel timer for timeouts on ANQ/ANR. + KDPC Dpc; // DPC object for timeout. + + // + // These two can be a union because they are not used + // concurrently. + // + + union { + + // + // This structure is used for checking share access. + // + + SHARE_ACCESS ShareAccess; + + // + // Used for delaying NbfDestroyAddress to a thread so + // we can access the security descriptor. + // + + WORK_QUEUE_ITEM DestroyAddressQueueItem; + + } u; + + // + // This structure is used to hold ACLs on the address. + + PSECURITY_DESCRIPTOR SecurityDescriptor; + + // + // If we get an ADD_NAME_RESPONSE frame, this holds the address + // of the remote we got it from (used to check for duplicate names). + // + + UCHAR UniqueResponseAddress[6]; + + // + // Set to TRUE once we send a name in conflict frame, so that + // we don't flood the network with them on every response. + // + + BOOLEAN NameInConflictSent; + +} TP_ADDRESS, *PTP_ADDRESS; + +#define ADDRESS_FLAGS_GROUP 0x00000001 // set if group, otherwise unique. +#define ADDRESS_FLAGS_CONFLICT 0x00000002 // address in conflict detected. +#define ADDRESS_FLAGS_REGISTERING 0x00000004 // registration in progress. +#define ADDRESS_FLAGS_DEREGISTERING 0x00000008 // deregistration in progress. +#define ADDRESS_FLAGS_DUPLICATE_NAME 0x00000010 // duplicate name was found on net. +#define ADDRESS_FLAGS_NEEDS_REG 0x00000020 // address must be registered. +#define ADDRESS_FLAGS_STOPPING 0x00000040 // TpStopAddress is in progress. +#define ADDRESS_FLAGS_BAD_ADDRESS 0x00000080 // name in conflict on associated address. +#define ADDRESS_FLAGS_SEND_IN_PROGRESS 0x00000100 // send datagram process active. +#define ADDRESS_FLAGS_CLOSED 0x00000200 // address has been closed; + // existing activity can + // complete, nothing new can start +#define ADDRESS_FLAGS_NEED_REREGISTER 0x00000400 // quick-reregister on next connect. +#define ADDRESS_FLAGS_QUICK_REREGISTER 0x00000800 // address is quick-reregistering. + + +// +// This structure defines a TP_LINK, or established data link object, +// maintained by the transport provider. Each data link connection with +// a remote machine is represented by this object. Zero, one, or several +// transport connections can be multiplexed over the same data link connection. +// This object is managed by routines in LINK.C. +// + +#if DBG +#define LREF_SPECIAL_CONN 0 +#define LREF_SPECIAL_TEMP 1 +#define LREF_CONNECTION 2 +#define LREF_STOPPING 3 +#define LREF_START_T1 4 +#define LREF_TREE 5 +#define LREF_NOT_ADM 6 +#define LREF_NDIS_SEND 7 + +#define NUMBER_OF_LREFS 8 +#endif + +#if DBG +#define LINK_HISTORY_LENGTH 20 +#endif + +typedef struct _TP_LINK { + + RTL_SPLAY_LINKS SplayLinks; // for the link splay tree + CSHORT Type; // type of this structure + USHORT Size; // size of this structure + +#if DBG + ULONG RefTypes[NUMBER_OF_LREFS]; +#endif + + LIST_ENTRY Linkage; // for list of free links or deferred + // operation queue + KSPIN_LOCK SpinLock; // lock to manipulate this structure. + + LONG ReferenceCount; // number of references to this object. + LONG SpecialRefCount; // controls freeing of the link. + + // + // information about the remote hardware this link is talking to. + // + + BOOLEAN Loopback; // TRUE if this is a loopback link. + UCHAR LoopbackDestinationIndex; // if Loopback, the index. + + HARDWARE_ADDRESS HardwareAddress; // hardware address of remote. + ULARGE_INTEGER MagicAddress; // numerical representation of the + // hardware address used for quick + // comparisons + UCHAR Header[MAX_MAC_HEADER_LENGTH]; // a place to stick a prebuilt packet + // header. + ULONG HeaderLength; // length of Header for this link + + // + // Vital conditions surrounding the data link connnection. + // + + ULONG MaxFrameSize; // maximum size of NetBIOS frame, MAC + // dependent. + + // + // Connections associated with this link. We keep a simple list of + // connections because it's unlikely we'll get more than a few connections + // on a given link (we're assuming that the server or redir will be the + // biggest user of the net in the vast majority of environments). We've + // made the link lookup be via a splay tree, which vastly speeds the + // process of getting to the proper link; as long as there are only a few + // connections, the connection lookup will be fast. If this becomes a + // problem down the road, we can make this connection list be a splay tree + // also. + // + + LIST_ENTRY ConnectionDatabase; + ULONG ActiveConnectionCount; // # connections in above list. + + // + // The following fields are used to maintain state about this link. + // One other field is implicit-- the address of this object is the + // ConnectionContext value as described in the PDI spec. + // + + ULONG Flags; // attributes of the link. + ULONG DeferredFlags; // when on the deferred queue. + ULONG State; // link state variable. + + // + // Send-side state. + // + + ULONG PacketsSent; // number of packets sent. + ULONG PacketsResent; // number of packets resent. + UCHAR SendState; // send-side state variable. + UCHAR NextSend; // next N(S) we should send. + UCHAR LastAckReceived; // last N(R) we received. + UCHAR SendWindowSize; // current send window size. + UCHAR PrevWindowSize; // size last time we dropped a frame. + UCHAR WindowsUntilIncrease; // how many windows until size increases. + UCHAR SendRetries; // number of retries left/this checkpoint. + UCHAR ConsecutiveLastPacketLost; // consecutive windows with last packet dropped. + ULONG NdisSendsInProgress; // >0 if sends queued to NdisSendQueue. + LIST_ENTRY NdisSendQueue; // queue of sends to pass to NdisSend. + LIST_ENTRY WackQ; // sent packets waiting LLC acks. + + BOOLEAN OnDeferredRrQueue; + LIST_ENTRY DeferredRrLinkage; + + // + // Receive-side state. + // + + ULONG PacketsReceived; // number of packets received. + UCHAR ReceiveState; // receive-side state variable. + UCHAR NextReceive; // next expected N(S) we should receive. + UCHAR LastAckSent; // last N(R) we sent. + UCHAR ReceiveWindowSize; // current receive window size. + BOOLEAN RespondToPoll; // remote guy is polling-- we must final. + BOOLEAN ResendingPackets; // ResendLlcPackets in progress + BOOLEAN LinkBusy; // received RNR (really send-side state). + + // + // Timer, used to determine delay and throughput. + // + + ULONG Delay; // an NT time, but only LowPart is saved. + LARGE_INTEGER Throughput; + + // + // These are counters needed by ADAPTER_STATUS queries. + // + + USHORT FrmrsReceived; + USHORT FrmrsTransmitted; + USHORT ErrorIFramesReceived; + USHORT ErrorIFramesTransmitted; + USHORT AbortedTransmissions; + USHORT BuffersNotAvailable; + ULONG SuccessfulTransmits; + ULONG SuccessfulReceives; + USHORT T1Expirations; + USHORT TiExpirations; + + // + // Timeout state. There is one kernel timer for this transport that is set + // to go off at regular intervals. This timer increments the current time, + // which is then used to compare against the timer queues. The timer queues + // are ordered, so whenever the first element is not expired, the rest of + // the queue is not expired. This allows us to have hundreds of timers + // running with very little system overhead. + // A value of 0 indicates that the timer is not active. + // + + ULONG T1; // retry timer. + ULONG T2; // delayed ack timer. + ULONG Ti; // inactivity timer. + BOOLEAN OnShortList; // TRUE if link is in ShortList + BOOLEAN OnLongList; // TRUE if link is in LongList + LIST_ENTRY ShortList; // list of links waiting t1 or t2 + LIST_ENTRY LongList; // list of links waiting ti + + LIST_ENTRY PurgeList; + + // + // This counter is used to keep track of whether there are + // any "connectors" (connections initiated by this side) on + // this link. If there are none, and we are on an easily + // disconnected link, then we handle the inactivity timeout + // differently. + // + + LONG NumberOfConnectors; + + // + // BaseT1Timeout is the current T1 timeout computed based on + // the response to previous poll frames. T1Timeout is the + // value to be used for the next T1, and will generally be + // based on BaseT1Timeout but may be more if T1 is backing + // off. T2Timeout and TiTimeout are independent of these. + // + + ULONG BaseT1Timeout; // Timeout value for T1, << 16. + ULONG CurrentT1Timeout; // Current backed-off T1 timeout. + ULONG MinimumBaseT1Timeout; // Minimum value, based on link speed. + ULONG BaseT1RecalcThreshhold; // Only recalc BaseT1 on frames > this. + ULONG CurrentPollRetransmits; // Current retransmits waiting for final. + BOOLEAN ThroughputAccurate; // Is the throughput on this link accurate? + BOOLEAN CurrentT1Backoff; // the last poll frame had retransmits + BOOLEAN CurrentPollOutstanding; // Check that we have a poll outstanding. + LARGE_INTEGER CurrentTimerStart; // Time that current timing was begun. + ULONG CurrentPollSize; // Size of current poll packet. + ULONG T2Timeout; // Timeout value for T2. + ULONG TiTimeout; // Timeout value for Ti. + ULONG LlcRetries; // total retry count for this link. + ULONG MaxWindowSize; // maximum send window size. + ULONG TiStartPacketsReceived; // PacketsReceived when Ti was started. + + // + // Adaptive window algorithm state. + // + + ULONG WindowErrors; // # retransmissions/this adaptive run. + UCHAR BestWindowSize; // our best window from experience. + UCHAR WorstWindowSize; // our worst window from experience. + + // + // Keep track of remotes that never poll so we can send + // an RR every two frames. + // + + BOOLEAN RemoteNoPoll; // We think remote doesn't poll + UCHAR ConsecutiveIFrames; // number received since polling + +#if DBG + UCHAR CreatePacketFailures; // consecutive failures +#endif + + LIST_ENTRY DeferredList; // for threading on deferred list + + struct _DEVICE_CONTEXT *Provider; + PKSPIN_LOCK ProviderInterlock; // &Provider->Interlock + +#if DBG + LIST_ENTRY GlobalLinkage; + ULONG TotalReferences; + ULONG TotalDereferences; + ULONG NextRefLoc; + struct { + PVOID Caller; + PVOID CallersCaller; + } History[LINK_HISTORY_LENGTH]; + BOOLEAN Destroyed; +#endif + +} TP_LINK, *PTP_LINK; + +#if DBG +extern KSPIN_LOCK NbfGlobalHistoryLock; +extern LIST_ENTRY NbfGlobalLinkList; +#define StoreLinkHistory(_link,_ref) { \ + KIRQL oldIrql; \ + KeAcquireSpinLock (&NbfGlobalHistoryLock, &oldIrql); \ + if ((_link)->Destroyed) { \ + DbgPrint ("link touched after being destroyed 0x%lx\n", (_link)); \ + DbgBreakPoint(); \ + } \ + RtlGetCallersAddress( \ + &(_link)->History[(_link)->NextRefLoc].Caller, \ + &(_link)->History[(_link)->NextRefLoc].CallersCaller \ + ); \ + if ((_ref)) { \ + (_link)->TotalReferences++; \ + } else { \ + (_link)->TotalDereferences++; \ + (_link)->History[(_link)->NextRefLoc].Caller = \ + (PVOID)((ULONG)(_link)->History[(_link)->NextRefLoc].Caller & \ + ~0x80000000); \ + } \ + if (++(_link)->NextRefLoc == LINK_HISTORY_LENGTH) { \ + (_link)->NextRefLoc = 0; \ + } \ + KeReleaseSpinLock (&NbfGlobalHistoryLock, oldIrql); \ +} +#endif + +#define LINK_FLAGS_JUMP_START 0x00000040 // run adaptive alg/every sent window. +#define LINK_FLAGS_LOCAL_DISC 0x00000080 // link was stopped locally. + +// +// deferred flags, used for processing at timer tick if needed +// + +#define LINK_FLAGS_DEFERRED_DELETE 0x00010000 // delete at next opportunity +#define LINK_FLAGS_DEFERRED_ADD 0x00020000 // add to splay tree, next opportunity +#define LINK_FLAGS_DEFERRED_MASK 0x00030000 // (LINK_FLAGS_DEFERRED_DELETE | LINK_FLAGS_DEFERRED_ADD) + +#define LINK_STATE_ADM 1 // asynchronous disconnected mode. +#define LINK_STATE_READY 2 // asynchronous balanced mode extended. +#define LINK_STATE_BUSY 3 // all link buffers are busy, sent RNR +#define LINK_STATE_CONNECTING 4 // waiting SABME response (UA-r/f). +#define LINK_STATE_W_POLL 5 // waiting initial checkpoint. +#define LINK_STATE_W_FINAL 6 // waiting final from initial checkpoint. +#define LINK_STATE_W_DISC_RSP 7 // waiting disconnect response. + +#define SEND_STATE_DOWN 0 // asynchronous disconnected mode. +#define SEND_STATE_READY 1 // completely ready to send. +#define SEND_STATE_REJECTING 2 // other guy is rejecting. +#define SEND_STATE_CHECKPOINTING 3 // we're checkpointing (can't send data). + +#define RECEIVE_STATE_DOWN 0 // asynchronous disconnected mode. +#define RECEIVE_STATE_READY 1 // we're ready to receive. +#define RECEIVE_STATE_REJECTING 2 // we're rejecting. + + +// +// This structure defines the DEVICE_OBJECT and its extension allocated at +// the time the transport provider creates its device object. +// + +#if DBG +#define DCREF_CREATION 0 +#define DCREF_ADDRESS 1 +#define DCREF_CONNECTION 2 +#define DCREF_LINK 3 +#define DCREF_QUERY_INFO 4 +#define DCREF_SCAN_TIMER 5 +#define DCREF_REQUEST 6 + +#define NUMBER_OF_DCREFS 8 +#endif + + +typedef struct _NBF_POOL_LIST_DESC { + NDIS_HANDLE PoolHandle; + USHORT NumElements; + USHORT TotalElements; + struct _NBF_POOL_LIST_DESC *Next; +} NBF_POOL_LIST_DESC, *PNBF_POOL_LIST_DESC; + +typedef struct _DEVICE_CONTEXT { + + DEVICE_OBJECT DeviceObject; // the I/O system's device object. + +#if DBG + ULONG RefTypes[NUMBER_OF_DCREFS]; +#endif + + CSHORT Type; // type of this structure + USHORT Size; // size of this structure + + LIST_ENTRY Linkage; // links them on NbfDeviceList; + + KSPIN_LOCK Interlock; // GLOBAL spinlock for reference count. + // (used in ExInterlockedXxx calls) + LONG ReferenceCount; // activity count/this provider. + + + // + // This protects the LoopbackQueue. + // + + KSPIN_LOCK LoopbackSpinLock; + + // + // The queue of packets waiting to be looped back. + // + + LIST_ENTRY LoopbackQueue; + + // + // These two links are used for loopback. + // + + PTP_LINK LoopbackLinks[2]; + + // + // This buffer is used for loopback indications; a + // contiguous piece is copied into it. It is allocated + // (of size NBF_MAX_LOOPBACK_LOOKAHEAD) when one of + // the LoopbackLinks become non-NULL. + // + + PUCHAR LookaheadContiguous; + + // + // This holds the length of the header in the currently + // indicating loopback packet. + // + + ULONG LoopbackHeaderLength; + + // + // Used for processing the loopback queue. + // + + KDPC LoopbackDpc; + + // + // Determines if a LoopbackDpc is in progress. + // + + BOOLEAN LoopbackInProgress; + + // + // Determines if a WanDelayedDpc is in progress. + // + + BOOLEAN WanThreadQueued; + + // + // Used for momentarily delaying WAN packetizing to + // allow RR's to be received. + // + + WORK_QUEUE_ITEM WanDelayedQueueItem; + + // + // The queue of FIND.NAME requests waiting to be processed. + // + + LIST_ENTRY FindNameQueue; + + // + // The queue of STATUS.QUERY requests waiting to be processed. + // + + LIST_ENTRY StatusQueryQueue; + + // + // The queue of QUERY.INDICATION requests waiting to be completed. + // + + LIST_ENTRY QueryIndicationQueue; + + // + // The queue of DATAGRAM.INDICATION requests waiting to be completed. + // + + LIST_ENTRY DatagramIndicationQueue; + + // + // The queue of (currently receive only) IRPs waiting to complete. + // + + LIST_ENTRY IrpCompletionQueue; + + // + // This boolean is TRUE if either of the above two have ever + // had anything on them. + // + + BOOLEAN IndicationQueuesInUse; + + // + // Following are protected by Global Device Context SpinLock + // + + KSPIN_LOCK SpinLock; // lock to manipulate this object. + // (used in KeAcquireSpinLock calls) + + // + // the device context state, among open, closing + // + + UCHAR State; + + // + // Used when processing a STATUS_CLOSING indication. + // + + WORK_QUEUE_ITEM StatusClosingQueueItem; + + // + // The following queue holds free TP_LINK objects available for allocation. + // + + LIST_ENTRY LinkPool; + + // + // These counters keep track of resources uses by TP_LINK objects. + // + + ULONG LinkAllocated; + ULONG LinkInitAllocated; + ULONG LinkMaxAllocated; + ULONG LinkInUse; + ULONG LinkMaxInUse; + ULONG LinkExhausted; + ULONG LinkTotal; + ULONG LinkSamples; + + + // + // The following queue holds free TP_ADDRESS objects available for allocation. + // + + LIST_ENTRY AddressPool; + + // + // These counters keep track of resources uses by TP_ADDRESS objects. + // + + ULONG AddressAllocated; + ULONG AddressInitAllocated; + ULONG AddressMaxAllocated; + ULONG AddressInUse; + ULONG AddressMaxInUse; + ULONG AddressExhausted; + ULONG AddressTotal; + ULONG AddressSamples; + + + // + // The following queue holds free TP_ADDRESS_FILE objects available for allocation. + // + + LIST_ENTRY AddressFilePool; + + // + // These counters keep track of resources uses by TP_ADDRESS_FILE objects. + // + + ULONG AddressFileAllocated; + ULONG AddressFileInitAllocated; + ULONG AddressFileMaxAllocated; + ULONG AddressFileInUse; + ULONG AddressFileMaxInUse; + ULONG AddressFileExhausted; + ULONG AddressFileTotal; + ULONG AddressFileSamples; + + + // + // The following queue holds free TP_CONNECTION objects available for allocation. + // + + LIST_ENTRY ConnectionPool; + + // + // These counters keep track of resources uses by TP_CONNECTION objects. + // + + ULONG ConnectionAllocated; + ULONG ConnectionInitAllocated; + ULONG ConnectionMaxAllocated; + ULONG ConnectionInUse; + ULONG ConnectionMaxInUse; + ULONG ConnectionExhausted; + ULONG ConnectionTotal; + ULONG ConnectionSamples; + + + // + // The following is a free list of TP_REQUEST blocks which have been + // previously allocated and are available for use. + // + + LIST_ENTRY RequestPool; // free request block pool. + + // + // These counters keep track of resources uses by TP_REQUEST objects. + // + + ULONG RequestAllocated; + ULONG RequestInitAllocated; + ULONG RequestMaxAllocated; + ULONG RequestInUse; + ULONG RequestMaxInUse; + ULONG RequestExhausted; + ULONG RequestTotal; + ULONG RequestSamples; + + + // + // The following list comprises a pool of UI NetBIOS frame headers + // that are manipulated by the routines in FRAMESND.C. + // + + LIST_ENTRY UIFramePool; // free UI frames (TP_UI_FRAME objects). + + // + // These counters keep track of resources uses by TP_UI_FRAME objects. + // + + ULONG UIFrameLength; + ULONG UIFrameHeaderLength; + ULONG UIFrameAllocated; + ULONG UIFrameInitAllocated; + ULONG UIFrameExhausted; + + + // + // The following queue holds I-frame Send packets managed by PACKET.C. + // + + SINGLE_LIST_ENTRY PacketPool; + + // + // These counters keep track of resources uses by TP_PACKET objects. + // + + ULONG PacketLength; + ULONG PacketHeaderLength; + ULONG PacketAllocated; + ULONG PacketInitAllocated; + ULONG PacketExhausted; + + + // + // The following queue holds RR-frame Send packets managed by PACKET.C. + // + + SINGLE_LIST_ENTRY RrPacketPool; + + + // + // The following queue contains Receive packets + // + + SINGLE_LIST_ENTRY ReceivePacketPool; + + // + // These counters keep track of resources uses by NDIS_PACKET objects. + // + + ULONG ReceivePacketAllocated; + ULONG ReceivePacketInitAllocated; + ULONG ReceivePacketExhausted; + + + // + // This queue contains pre-allocated receive buffers + // + + SINGLE_LIST_ENTRY ReceiveBufferPool; + + // + // These counters keep track of resources uses by TP_PACKET objects. + // + + ULONG ReceiveBufferLength; + ULONG ReceiveBufferAllocated; + ULONG ReceiveBufferInitAllocated; + ULONG ReceiveBufferExhausted; + + + // + // This holds the total memory allocated for the above structures. + // + + ULONG MemoryUsage; + ULONG MemoryLimit; + + + // + // The following field is a head of a list of TP_ADDRESS objects that + // are defined for this transport provider. To edit the list, you must + // hold the spinlock of the device context object. + // + + LIST_ENTRY AddressDatabase; // list of defined transport addresses. + + // + // The following field is the pointer to the root of the splay tree of + // links that are associated with this Device Context. You must hold the + // LinkSpinLock to modify this list. You must set the LinkTreeSemaphore + // to traverse this list without modifying it. Note that all modify + // operations are deferred to timer(DPC)-time operations. + // + + KSPIN_LOCK LinkSpinLock; // protects these values + PTP_LINK LastLink; // the last link found in the tree. + PRTL_SPLAY_LINKS LinkTreeRoot; // pointer to root of the tree. + ULONG LinkTreeElements; // how many elements in the tree + LIST_ENTRY LinkDeferred; // Deferred operations on links. + ULONG DeferredNotSatisfied; // how many times we've come to the + // deferred well and not gotten it clear. + + // + // The following queue holds connections which are waiting on available + // packets. As each new packet becomes available, a connection is removed + // from this queue and placed on the PacketizeQueue. + // + + LIST_ENTRY PacketWaitQueue; // queue of packet-starved connections. + LIST_ENTRY PacketizeQueue; // queue of ready-to-packetize connections. + + // + // The following queue holds connections which are waiting to send + // a piggyback ack. In that case the CONNECTION_FLAGS_DEFERRED_ACK + // bit in DeferredFlags will be set. + // + + LIST_ENTRY DataAckQueue; + + // + // The following queue holds links which are waiting to send an + // RR frame because the remote they are talking to never polls. + // + + LIST_ENTRY DeferredRrQueue; + + // + // Used to track when the queue has changed. + // + + BOOLEAN DataAckQueueChanged; + + // + // When this hits thirty seconds we checked for stalled connections. + // + + USHORT StalledConnectionCount; + + // + // This queue contains receives that are in progress + // + + LIST_ENTRY ReceiveInProgress; + + // + // NDIS fields + // + + // + // following is used to keep adapter information. + // + + NDIS_HANDLE NdisBindingHandle; + + // + // The following fields are used for talking to NDIS. They keep information + // for the NDIS wrapper to use when determining what pool to use for + // allocating storage. + // + + KSPIN_LOCK SendPoolListLock; // protects these values + PNBF_POOL_LIST_DESC SendPacketPoolDesc; + KSPIN_LOCK RcvPoolListLock; // protects these values + PNBF_POOL_LIST_DESC ReceivePacketPoolDesc; + NDIS_HANDLE NdisBufferPool; + + // + // These are kept around for error logging. + // + + ULONG SendPacketPoolSize; + ULONG ReceivePacketPoolSize; + ULONG MaxRequests; + ULONG MaxLinks; + ULONG MaxConnections; + ULONG MaxAddressFiles; + ULONG MaxAddresses; + PWCHAR DeviceName; + ULONG DeviceNameLength; + + // + // This is the Mac type we must build the packet header for and know the + // offsets for. + // + + NBF_NDIS_IDENTIFICATION MacInfo; // MAC type and other info + ULONG MaxReceivePacketSize; // does not include the MAC header + ULONG MaxSendPacketSize; // includes the MAC header + ULONG CurSendPacketSize; // may be smaller for async + USHORT RecommendedSendWindow; // used for Async lines + BOOLEAN EasilyDisconnected; // TRUE over wireless nets. + + // + // some MAC addresses we use in the transport + // + + HARDWARE_ADDRESS LocalAddress; // our local hardware address. + HARDWARE_ADDRESS NetBIOSAddress; // NetBIOS functional address, used for TR + + // + // The reserved Netbios address; consists of 10 zeroes + // followed by LocalAddress; + // + + UCHAR ReservedNetBIOSAddress[NETBIOS_NAME_LENGTH]; +#ifdef _PNP_POWER + HANDLE TdiDeviceHandle; + HANDLE ReservedAddressHandle; +#endif + + // + // These are used while initializing the MAC driver. + // + + KEVENT NdisRequestEvent; // used for pended requests. + NDIS_STATUS NdisRequestStatus; // records request status. + + // + // This next field maintains a unique number which can next be assigned + // as a connection identifier. It is incremented by one each time a + // value is allocated. + // + + USHORT UniqueIdentifier; // starts at 0, wraps around 2^16-1. + + // + // This contains the next unique indentified to use as + // the FsContext in the file object associated with an + // open of the control channel. + // + + USHORT ControlChannelIdentifier; + + // + // The following fields are used to implement the lightweight timer + // system in the protocol provider. Each TP_LINK object in the device + // context's LinkDatabase contains three lightweight timers that are + // serviced by a DPC routine, which receives control by kernel functions. + // There is one kernel timer for this transport that is set + // to go off at regular intervals. This timer increments the Absolute time, + // which is then used to compare against the timer queues. The timer queues + // are ordered, so whenever the first element is not expired, the rest of + // the queue is not expired. This allows us to have hundreds of timers + // running with very low system overhead. + // A value of -1 indicates that the timer is not active. + // + + LARGE_INTEGER ShortTimerStart; // when the short timer was set. + KDPC ShortTimerSystemDpc; // kernel DPC object, short timer. + KTIMER ShortSystemTimer; // kernel timer object, short timer. + ULONG ShortAbsoluteTime; // up-count timer ticks, short timer. + ULONG AdaptivePurge; // absolute time of next purge (short timer). + KDPC LongTimerSystemDpc; // kernel DPC object, long timer. + KTIMER LongSystemTimer; // kernel timer object, long timer. + ULONG LongAbsoluteTime; // up-count timer ticks, long timer. + union _DC_ACTIVE { + struct _DC_INDIVIDUAL { + BOOLEAN ShortListActive; // ShortList is not empty. + BOOLEAN DataAckQueueActive; // DataAckQueue is not empty. + BOOLEAN LinkDeferredActive; // LinkDeferred is not empty. + } i; + ULONG AnyActive; // used to check all four at once. + } a; + BOOLEAN TimersInitialized; // has the timer system been initialized. + BOOLEAN ProcessingShortTimer; // TRUE if we are in ScanShortTimer. + KSPIN_LOCK TimerSpinLock; // lock for following timer queues + LIST_ENTRY ShortList; // list of links waiting T1 or T2 + LIST_ENTRY LongList; // list of links waiting Ti expire + LIST_ENTRY PurgeList; // list of links waiting LAT expire + + // + // These fields are used on "easily disconnected" adapters. + // Every time the long timer expires, it notes if there has + // been any multicast traffic received. If there has not been, + // it increments LongTimeoutsWithoutMulticast. Activity is + // recorded by incrementing MulticastPacket when MC + // packets are received, and zeroing it when the long timer + // expires. + // + + ULONG LongTimeoutsWithoutMulticast; // LongTimer timeouts since traffic. + ULONG MulticastPacketCount; // How many MC packets rcved, this timeout. + + // + // This information is used to keep track of the speed of + // the underlying medium. + // + + ULONG MediumSpeed; // in units of 100 bytes/sec + BOOLEAN MediumSpeedAccurate; // if FALSE, can't use the link. + + // + // This is TRUE if we are on a UP system. + // + + BOOLEAN UniProcessor; + + // + // Configuration information on how soon we should send + // an unasked for RR with a non-polling remote. + // + + UCHAR MaxConsecutiveIFrames; + + // + // This is configuration information controlling the default + // value of timers and retry counts. + // + + ULONG DefaultT1Timeout; + ULONG MinimumT1Timeout; + ULONG DefaultT2Timeout; + ULONG DefaultTiTimeout; + ULONG LlcRetries; + ULONG LlcMaxWindowSize; + ULONG NameQueryRetries; + ULONG NameQueryTimeout; + ULONG AddNameQueryRetries; + ULONG AddNameQueryTimeout; + ULONG GeneralRetries; + ULONG GeneralTimeout; + ULONG MinimumSendWindowLimit; // how low we can lock a connection's window + + // + // Counters for most of the statistics that NBF maintains; + // some of these are kept elsewhere. Including the structure + // itself wastes a little space but ensures that the alignment + // inside the structure is correct. + // + + TDI_PROVIDER_STATISTICS Statistics; + + // + // These are "temporary" versions of the other counters. + // During normal operations we update these, then during + // the short timer expiration we update the real ones. + // + + ULONG TempIFrameBytesSent; + ULONG TempIFramesSent; + ULONG TempIFrameBytesReceived; + ULONG TempIFramesReceived; + + // + // Some counters needed for Netbios adapter status. + // + + ULONG TiExpirations; + ULONG FrmrReceived; + ULONG FrmrTransmitted; + + // + // These are used to compute AverageSendWindow. + // + + ULONG SendWindowTotal; + ULONG SendWindowSamples; + + // + // Counters for "active" time. + // + + LARGE_INTEGER NbfStartTime; + + // + // This resource guards access to the ShareAccess + // and SecurityDescriptor fields in addresses. + // + + ERESOURCE AddressResource; + + // + // This array is used to keep track of which LSNs are + // available for use by Netbios sessions. LSNs can be + // re-used for sessions to unique names if they are on + // different links, but must be committed beforehand + // for group names. The maximum value that can fit in + // an array element is defined by LSN_TABLE_MAX. + // + + UCHAR LsnTable[NETBIOS_SESSION_LIMIT+1]; + + // + // This is where we start looking in LsnTable for an + // unused LSN. We cycle from 0-63 to prevent quick + // down-and-up connections from getting funny data. + // + + ULONG NextLsnStart; + + // + // This array is used to quickly dismiss UI frames that + // are not destined for us. The count is the number + // of addresses with that first letter that are registered + // on this device. + // + + UCHAR AddressCounts[256]; + + // + // The following structure contains statistics counters for use + // by TdiQueryInformation and TdiSetInformation. They should not + // be used for maintenance of internal data structures. + // + + TDI_PROVIDER_INFO Information; // information about this provider. + + PTP_VARIABLE NetmanVariables; // list of network managable variables. + + // + // The magic bullet is a packet that is sent under certain debugging + // conditions. This allows the transport to signal packet capture devices + // that a particular condiion has been met. This packet has the current + // devicecontext as the source, and 0x04 in every other byte of the packet. + // + + UCHAR MagicBullet[32]; // + +} DEVICE_CONTEXT, *PDEVICE_CONTEXT; + +// +// device context state definitions +// + +#define DEVICECONTEXT_STATE_OPENING 0x00 +#define DEVICECONTEXT_STATE_OPEN 0x01 +#define DEVICECONTEXT_STATE_DOWN 0x02 +#define DEVICECONTEXT_STATE_STOPPING 0x03 + +// +// This is the maximum value that can go in an element +// of LsnTable (should be 0xff if they are UCHARs, +// 0xffff for USHORTs, etc.). +// + +#define LSN_TABLE_MAX 0xff + + +#define MAGIC_BULLET_FOOD 0x04 + + +// +// These are constants for the LoopbackLinks elements. +// The distinctions are arbitrary; the listener link +// is the one established from ProcessNameQuery, and +// the connector link is the one established from +// ProcessNameRecognized. +// + +#define LISTENER_LINK 0 +#define CONNECTOR_LINK 1 + + +// +// This structure defines the packet object, used to represent a DLC I-frame +// in some portion of its lifetime. The PACKET.C module contains routines +// to manage this object. +// + +typedef struct _TP_PACKET { + CSHORT Type; // type of this structure + USHORT Size; // size of this structure + PNDIS_PACKET NdisPacket; // ptr to owning Ndis Packet + ULONG NdisIFrameLength; // Length of NdisPacket + + LIST_ENTRY Linkage; // used to chain packets together. + LONG ReferenceCount; // activity count/this packet. + BOOLEAN PacketSent; // packet completed by NDIS. + BOOLEAN PacketNoNdisBuffer; // chain on this packet was not allocated. + + UCHAR Action; // what to do when we're acked. + BOOLEAN PacketizeConnection; // restart packetizing when completed. + + PVOID Owner; // ptr to owning connection or IrpSp. + PTP_LINK Link; // ptr to link it was sent on. + PDEVICE_CONTEXT Provider; // The owner of this packet. + PKSPIN_LOCK ProviderInterlock; // &Provider->Interlock. + + UCHAR Header[1]; // the MAC, DLC, and NBF headers + +} TP_PACKET, *PTP_PACKET; + + +// +// The following values are placed in the Action field in the TP_PACKET +// object to indicate what action, if any, should be taken when the packet +// is destroyed. +// + +#define PACKET_ACTION_NULL 0 // no special action should be taken. +#define PACKET_ACTION_IRP_SP 1 // Owner is an IRP_SP, deref when done. +#define PACKET_ACTION_CONNECTION 2 // Owner is a TP_CONNECTION, deref when done. +#define PACKET_ACTION_END 3 // shutdown session (sent SESSION_END). +#define PACKET_ACTION_RR 5 // packet is an RR, put back in RR pool. + +// +// Types used to hold information in the send and receive NDIS packets +// + +typedef struct _SEND_PACKET_TAG { + LIST_ENTRY Linkage; // used for threading on loopback queue + BOOLEAN OnLoopbackQueue; // TRUE if the packet is on a loopback queue + UCHAR LoopbackLinkIndex; // index of other link for loopback packets + USHORT Type; // identifier for packet type + PVOID Frame; // backpointer to owning NBF structure + PVOID Owner; // backpointer for owning nbf construct + // (like address, devicecontext, etc) + } SEND_PACKET_TAG, *PSEND_PACKET_TAG; + +// +// Packet types used in send completion +// + +#define TYPE_I_FRAME 1 +#define TYPE_UI_FRAME 2 +#define TYPE_ADDRESS_FRAME 3 + +// +// LoopbackLinkIndex values. +// + +#define LOOPBACK_TO_LISTENER 0 +#define LOOPBACK_TO_CONNECTOR 1 +#define LOOPBACK_UI_FRAME 2 + +// +// receive packet used to hold information about this receive +// + +typedef struct _RECEIVE_PACKET_TAG { + SINGLE_LIST_ENTRY Linkage; // used for threading in pool + PTP_CONNECTION Connection; // connection this receive is occuring on + ULONG BytesToTransfer; // for I-frame, bytes in this transfer + UCHAR PacketType; // the type of packet we're processing + BOOLEAN AllocatedNdisBuffer; // did we allocate our own NDIS_BUFFERs + BOOLEAN EndOfMessage; // does this receive complete the message + BOOLEAN CompleteReceive; // complete the receive after TransferData? + BOOLEAN TransferDataPended; // TRUE if TransferData returned PENDING + } RECEIVE_PACKET_TAG, *PRECEIVE_PACKET_TAG; + +#define TYPE_AT_INDICATE 1 +#define TYPE_AT_COMPLETE 2 +#define TYPE_STATUS_RESPONSE 3 + +// +// receive buffer descriptor (built in memory at the beginning of the buffer) +// + +typedef struct _BUFFER_TAG { + LIST_ENTRY Linkage; // thread in pool and on receive queue + NDIS_STATUS NdisStatus; // completion status for send + PTP_ADDRESS Address; // the address this datagram is for. + PNDIS_BUFFER NdisBuffer; // describes the rest of the buffer + ULONG Length; // the length of the buffer + UCHAR Buffer[1]; // the actual storage (accessed through the NDIS_BUFFER) + } BUFFER_TAG, *PBUFFER_TAG; + +#endif // def _NBFTYPES_ + diff --git a/private/ntos/tdi/nbf/packet.c b/private/ntos/tdi/nbf/packet.c new file mode 100644 index 000000000..2b6de5b25 --- /dev/null +++ b/private/ntos/tdi/nbf/packet.c @@ -0,0 +1,1808 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + packet.c + +Abstract: + + This module contains code that implements the TP_PACKET object, which + describes a DLC I-frame at some point in its lifetime. Routines are + provided to allocate packets for shipment, to ship packets, to reference + packets, to dereference packets, to mark a connection as waiting for a + packet to become available, to satisfy the next waiting connection for + a packet, and to destroy packets (return them to the pool). + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +// +// This is temporary; this is the quota that we charge for a receive +// packet for now, until we fix the problem with token-ring needing +// big packets and using all the memory. The number is the actual +// value for Ethernet. +// + +#if 1 +#define RECEIVE_BUFFER_QUOTA(_DeviceContext) 1533 +#else +#define RECEIVE_BUFFER_QUOTA(_DeviceContext) (_DeviceContext)->ReceiveBufferLength +#endif + +#define PACKET_POOL_GROW_COUNT 32 + +#if DBG +ULONG NbfCreatePacketThreshold = 5; +extern ULONG NbfPacketPanic; +#endif + +NDIS_STATUS +NbfAllocateNdisSendPacket( + IN PDEVICE_CONTEXT DeviceContext, + OUT PNDIS_PACKET *NdisPacket + ) + +/*++ + +Routine Description: + + This routine allocates a recieve packet from the receive packet pool. + It Grows the packet pool if necessary. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + UIFrame - Returns a pointer to the frame, or NULL if no storage + can be allocated. + +Return Value: + + None. + +--*/ + +{ + + PNBF_POOL_LIST_DESC SendPacketPoolDesc; + NDIS_STATUS NdisStatus; + KIRQL oldirql; + + ACQUIRE_SPIN_LOCK (&DeviceContext->SendPoolListLock, &oldirql); + for (SendPacketPoolDesc = DeviceContext->SendPacketPoolDesc; + SendPacketPoolDesc != NULL; + SendPacketPoolDesc = SendPacketPoolDesc->Next) { + + NdisAllocatePacket ( + &NdisStatus, + NdisPacket, + SendPacketPoolDesc->PoolHandle); + + + if (NdisStatus == NDIS_STATUS_SUCCESS) { + + RELEASE_SPIN_LOCK (&DeviceContext->SendPoolListLock, oldirql); + return(NdisStatus); + } + } + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + + PACKET_POOL_GROW_COUNT * + (sizeof(NDIS_PACKET) + sizeof(SEND_PACKET_TAG))) > + DeviceContext->MemoryLimit)) { + + PANIC("NBF: Could not grow packet pool: limit\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_LIMIT, + 106, + DeviceContext->UIFrameLength, + UI_FRAME_RESOURCE_ID); + RELEASE_SPIN_LOCK (&DeviceContext->SendPoolListLock, oldirql); + return(NdisStatus); + } + } + + DeviceContext->MemoryUsage += + (PACKET_POOL_GROW_COUNT * + (sizeof(NDIS_PACKET) + sizeof(SEND_PACKET_TAG))); + + // Allocate Packet pool descriptors for dynamic packet allocation. + + SendPacketPoolDesc = ExAllocatePoolWithTag( + NonPagedPool, + sizeof(NBF_POOL_LIST_DESC), + ' FBN'); + + if (DeviceContext->SendPacketPoolDesc == NULL) { + return(NdisStatus); + } + + RtlZeroMemory(SendPacketPoolDesc, sizeof(NBF_POOL_LIST_DESC)); + + SendPacketPoolDesc->NumElements = + SendPacketPoolDesc->TotalElements = PACKET_POOL_GROW_COUNT; + + NdisAllocatePacketPool ( &NdisStatus, &SendPacketPoolDesc->PoolHandle, + PACKET_POOL_GROW_COUNT, sizeof (SEND_PACKET_TAG)); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { +#if DBG + NbfPrint1 ("NbfGrowSendPacketPool: NdisInitializePacketPool failed, reason: %s.\n", + NbfGetNdisStatus (NdisStatus)); +#endif + RELEASE_SPIN_LOCK (&DeviceContext->SendPoolListLock, oldirql); + ExFreePool (SendPacketPoolDesc); + return(NdisStatus); + } + SendPacketPoolDesc->Next = DeviceContext->SendPacketPoolDesc; + DeviceContext->SendPacketPoolDesc = SendPacketPoolDesc; + RELEASE_SPIN_LOCK (&DeviceContext->SendPoolListLock, oldirql); + NdisAllocatePacket ( &NdisStatus, NdisPacket, + SendPacketPoolDesc->PoolHandle); + + return(NdisStatus); +} + +NDIS_STATUS +NbfAllocateNdisRcvPacket( + IN PDEVICE_CONTEXT DeviceContext, + OUT PNDIS_PACKET *NdisPacket + ) + +/*++ + +Routine Description: + + This routine allocates a recieve packet from the receive packet pool. + It Grows the packet pool if necessary. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + UIFrame - Returns a pointer to the frame, or NULL if no storage + can be allocated. + +Return Value: + + None. + +--*/ + +{ + + PNBF_POOL_LIST_DESC RcvPacketPoolDesc; + NDIS_STATUS NdisStatus; + KIRQL oldirql; + + ACQUIRE_SPIN_LOCK (&DeviceContext->RcvPoolListLock, &oldirql); + for (RcvPacketPoolDesc = DeviceContext->ReceivePacketPoolDesc; + RcvPacketPoolDesc != NULL; + RcvPacketPoolDesc = RcvPacketPoolDesc->Next) { + + NdisAllocatePacket ( + &NdisStatus, + NdisPacket, + RcvPacketPoolDesc->PoolHandle); + + + if (NdisStatus == NDIS_STATUS_SUCCESS) { + RELEASE_SPIN_LOCK (&DeviceContext->RcvPoolListLock, oldirql); + return(NdisStatus); + } + } + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + + PACKET_POOL_GROW_COUNT * + (sizeof(NDIS_PACKET) + sizeof(SEND_PACKET_TAG))) > + DeviceContext->MemoryLimit)) { + + PANIC("NBF: Could not grow packet pool: limit\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_LIMIT, + 106, + DeviceContext->UIFrameLength, + UI_FRAME_RESOURCE_ID); + RELEASE_SPIN_LOCK (&DeviceContext->RcvPoolListLock, oldirql); + return(NdisStatus); + } + } + + DeviceContext->MemoryUsage += + (PACKET_POOL_GROW_COUNT * + (sizeof(NDIS_PACKET) + sizeof(SEND_PACKET_TAG))); + + // Allocate Packet pool descriptors for dynamic packet allocation. + + RcvPacketPoolDesc = ExAllocatePoolWithTag( + NonPagedPool, + sizeof(NBF_POOL_LIST_DESC), + ' FBN'); + + if (RcvPacketPoolDesc == NULL) { + RELEASE_SPIN_LOCK (&DeviceContext->RcvPoolListLock, oldirql); + return(NdisStatus); + } + + RtlZeroMemory(RcvPacketPoolDesc, sizeof(NBF_POOL_LIST_DESC)); + + RcvPacketPoolDesc->NumElements = + RcvPacketPoolDesc->TotalElements = PACKET_POOL_GROW_COUNT; + + NdisAllocatePacketPool ( &NdisStatus, &RcvPacketPoolDesc->PoolHandle, + PACKET_POOL_GROW_COUNT, sizeof (RECEIVE_PACKET_TAG)); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { +#if DBG + NbfPrint1 ("NbfGrowSendPacketPool: NdisInitializePacketPool failed, reason: %s.\n", + NbfGetNdisStatus (NdisStatus)); +#endif + RELEASE_SPIN_LOCK (&DeviceContext->RcvPoolListLock, oldirql); + ExFreePool (RcvPacketPoolDesc); + return(NdisStatus); + } + RcvPacketPoolDesc->Next = DeviceContext->ReceivePacketPoolDesc; + DeviceContext->ReceivePacketPoolDesc = RcvPacketPoolDesc; + RELEASE_SPIN_LOCK (&DeviceContext->RcvPoolListLock, oldirql); + NdisAllocatePacket ( &NdisStatus, NdisPacket, + RcvPacketPoolDesc->PoolHandle); + + return(NdisStatus); +} + + +VOID +NbfAllocateUIFrame( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_UI_FRAME *TransportUIFrame + ) + +/*++ + +Routine Description: + + This routine allocates storage for a UI frame. Some initialization + is done here. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + UIFrame - Returns a pointer to the frame, or NULL if no storage + can be allocated. + +Return Value: + + None. + +--*/ + +{ + + NDIS_STATUS NdisStatus; + PNDIS_PACKET NdisPacket; + PSEND_PACKET_TAG SendTag; + PTP_UI_FRAME UIFrame; + PNDIS_BUFFER NdisBuffer; + PNBF_POOL_LIST_DESC SendPacketPoolDesc; + + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + DeviceContext->UIFrameLength) > + DeviceContext->MemoryLimit)) { + PANIC("NBF: Could not allocate UI frame: limit\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_LIMIT, + 106, + DeviceContext->UIFrameLength, + UI_FRAME_RESOURCE_ID); + *TransportUIFrame = NULL; + return; + } + + UIFrame = (PTP_UI_FRAME) ExAllocatePoolWithTag ( + NonPagedPool, + DeviceContext->UIFrameLength, + 'uFBN'); + if (UIFrame == NULL) { + PANIC("NBF: Could not allocate UI frame: no pool\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 206, + DeviceContext->UIFrameLength, + UI_FRAME_RESOURCE_ID); + *TransportUIFrame = NULL; + return; + } + RtlZeroMemory (UIFrame, DeviceContext->UIFrameLength); + + DeviceContext->MemoryUsage += DeviceContext->UIFrameLength; + NdisStatus = NbfAllocateNdisSendPacket(DeviceContext, &NdisPacket); +#if 0 + for (SendPacketPoolDesc = DeviceContext->SendPacketPoolDesc; + SendPacketPoolDesc != NULL; + SendPacketPoolDesc = SendPacketPoolDesc->Next) { + + NdisAllocatePacket ( + &NdisStatus, + &NdisPacket, + SendPacketPoolDesc->PoolHandle); + + + if (NdisStatus == NDIS_STATUS_SUCCESS) + break; + } +#endif + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + ExFreePool (UIFrame); +#if 0 + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_SPECIFIC, + 306, + 0, + UI_FRAME_RESOURCE_ID); +#endif + *TransportUIFrame = NULL; + return; + } + + + UIFrame->NdisPacket = NdisPacket; + UIFrame->DataBuffer = NULL; + SendTag = (PSEND_PACKET_TAG)NdisPacket->ProtocolReserved; + SendTag->Type = TYPE_UI_FRAME; + SendTag->Frame = UIFrame; + SendTag->Owner = DeviceContext; + + // + // Make the packet header known to the packet descriptor + // + + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + DeviceContext->NdisBufferPool, + UIFrame->Header, + DeviceContext->UIFrameHeaderLength); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_SPECIFIC, + 406, + 0, + UI_FRAME_RESOURCE_ID); + NdisFreePacket (NdisPacket); + ExFreePool (UIFrame); + *TransportUIFrame = NULL; + return; + } + + NdisChainBufferAtFront (NdisPacket, NdisBuffer); + + ++DeviceContext->UIFrameAllocated; + + *TransportUIFrame = UIFrame; + +} /* NbfAllocateUIFrame */ + + +VOID +NbfDeallocateUIFrame( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_UI_FRAME TransportUIFrame + ) + +/*++ + +Routine Description: + + This routine frees storage for a UI frame. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + UIFrame - A pointer to the frame. + +Return Value: + + None. + +--*/ + +{ + PNDIS_PACKET NdisPacket = TransportUIFrame->NdisPacket; + PNDIS_BUFFER NdisBuffer; + + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + if (NdisBuffer != NULL) { + NdisFreeBuffer (NdisBuffer); + } + + NdisFreePacket (NdisPacket); + ExFreePool (TransportUIFrame); + --DeviceContext->UIFrameAllocated; + + DeviceContext->MemoryUsage -= DeviceContext->UIFrameLength; + +} /* NbfDeallocateUIFrame */ + + +VOID +NbfAllocateSendPacket( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_PACKET *TransportSendPacket + ) + +/*++ + +Routine Description: + + This routine allocates storage for a send packet. Some initialization + is done here. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + TransportSendPacket - Returns a pointer to the packet, or NULL if no + storage can be allocated. + +Return Value: + + None. + +--*/ + +{ + + PTP_PACKET Packet; + NDIS_STATUS NdisStatus; + PNDIS_PACKET NdisPacket; + PSEND_PACKET_TAG SendTag; + PNDIS_BUFFER NdisBuffer; + PNBF_POOL_LIST_DESC SendPacketPoolDesc; + + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + DeviceContext->PacketLength) > + DeviceContext->MemoryLimit)) { + PANIC("NBF: Could not allocate send packet: limit\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_LIMIT, + 107, + DeviceContext->PacketLength, + PACKET_RESOURCE_ID); + *TransportSendPacket = NULL; + return; + } + + Packet = (PTP_PACKET)ExAllocatePoolWithTag ( + NonPagedPool, + DeviceContext->PacketLength, + 'pFBN'); + if (Packet == NULL) { + PANIC("NBF: Could not allocate send packet: no pool\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 207, + DeviceContext->PacketLength, + PACKET_RESOURCE_ID); + *TransportSendPacket = NULL; + return; + } + RtlZeroMemory (Packet, DeviceContext->PacketLength); + + DeviceContext->MemoryUsage += DeviceContext->PacketLength; + + NdisStatus = NbfAllocateNdisSendPacket(DeviceContext, &NdisPacket); +#if 0 + for (SendPacketPoolDesc = DeviceContext->SendPacketPoolDesc; + SendPacketPoolDesc != NULL; + SendPacketPoolDesc = SendPacketPoolDesc->Next) { + + NdisAllocatePacket ( + &NdisStatus, + &NdisPacket, + SendPacketPoolDesc->PoolHandle); + + + if (NdisStatus == NDIS_STATUS_SUCCESS) + break; + } +#endif + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + ExFreePool (Packet); +#if 0 + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_SPECIFIC, + 307, + 0, + PACKET_RESOURCE_ID); +#endif + *TransportSendPacket = NULL; + return; + } + + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + DeviceContext->NdisBufferPool, + Packet->Header, + DeviceContext->PacketHeaderLength); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_SPECIFIC, + 407, + 0, + PACKET_RESOURCE_ID); + NdisFreePacket (NdisPacket); + ExFreePool (Packet); + *TransportSendPacket = NULL; + return; + } + + NdisChainBufferAtFront (NdisPacket, NdisBuffer); + + Packet->NdisPacket = NdisPacket; + SendTag = (PSEND_PACKET_TAG)NdisPacket->ProtocolReserved; + SendTag->Type = TYPE_I_FRAME; + SendTag->Frame = Packet; + SendTag->Owner = DeviceContext; + + Packet->Type = NBF_PACKET_SIGNATURE; + Packet->Size = sizeof (TP_PACKET); + Packet->Provider = DeviceContext; + Packet->Owner = NULL; // no connection/irpsp yet. + Packet->Action = PACKET_ACTION_IRP_SP; + Packet->PacketizeConnection = FALSE; + Packet->PacketNoNdisBuffer = FALSE; + Packet->ProviderInterlock = &DeviceContext->Interlock; +// KeInitializeSpinLock (&Packet->Interlock); + + ++DeviceContext->PacketAllocated; + + *TransportSendPacket = Packet; + +} /* NbfAllocateSendPacket */ + + +VOID +NbfDeallocateSendPacket( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_PACKET TransportSendPacket + ) + +/*++ + +Routine Description: + + This routine frees storage for a send packet. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + TransportSendPacket - A pointer to the send packet. + +Return Value: + + None. + +--*/ + +{ + PNDIS_PACKET NdisPacket = TransportSendPacket->NdisPacket; + PNDIS_BUFFER NdisBuffer; + + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + if (NdisBuffer != NULL) { + NdisFreeBuffer (NdisBuffer); + } + + NdisFreePacket (NdisPacket); + ExFreePool (TransportSendPacket); + + --DeviceContext->PacketAllocated; + DeviceContext->MemoryUsage -= DeviceContext->PacketLength; + +} /* NbfDeallocateSendPacket */ + + +VOID +NbfAllocateReceivePacket( + IN PDEVICE_CONTEXT DeviceContext, + OUT PNDIS_PACKET *TransportReceivePacket + ) + +/*++ + +Routine Description: + + This routine allocates storage for a receive packet. Some initialization + is done here. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + TransportReceivePacket - Returns a pointer to the packet, or NULL if no + storage can be allocated. + +Return Value: + + None. + +--*/ + +{ + NDIS_STATUS NdisStatus; + PNDIS_PACKET NdisPacket; + PRECEIVE_PACKET_TAG ReceiveTag; + + // + // This does not count in DeviceContext->MemoryUsage because + // the storage is allocated when we allocate the packet pool. + // + + NdisStatus = NbfAllocateNdisRcvPacket(DeviceContext, &NdisPacket); +#if 0 + NdisAllocatePacket ( + &NdisStatus, + &NdisPacket, + DeviceContext->ReceivePacketPoolDesc->PoolHandle); +#endif + + if (NdisStatus != NDIS_STATUS_SUCCESS) { +#if 0 + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_SPECIFIC, + 309, + 0, + RECEIVE_PACKET_RESOURCE_ID); +#endif + *TransportReceivePacket = NULL; + return; + } + + ReceiveTag = (PRECEIVE_PACKET_TAG)(NdisPacket->ProtocolReserved); + ReceiveTag->PacketType = TYPE_AT_INDICATE; + + ++DeviceContext->ReceivePacketAllocated; + + *TransportReceivePacket = NdisPacket; + +} /* NbfAllocateReceivePacket */ + + +VOID +NbfDeallocateReceivePacket( + IN PDEVICE_CONTEXT DeviceContext, + IN PNDIS_PACKET TransportReceivePacket + ) + +/*++ + +Routine Description: + + This routine frees storage for a receive packet. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + TransportReceivePacket - A pointer to the packet. + +Return Value: + + None. + +--*/ + +{ + + NdisFreePacket (TransportReceivePacket); + + --DeviceContext->ReceivePacketAllocated; + +} /* NbfDeallocateReceivePacket */ + + +VOID +NbfAllocateReceiveBuffer( + IN PDEVICE_CONTEXT DeviceContext, + OUT PBUFFER_TAG *TransportReceiveBuffer + ) + +/*++ + +Routine Description: + + This routine allocates storage for a receive buffer. Some initialization + is done here. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + TransportReceiveBuffer - Returns a pointer to the buffer, or NULL if no + storage can be allocated. + +Return Value: + + None. + +--*/ + +{ + PBUFFER_TAG BufferTag; + NDIS_STATUS NdisStatus; + PNDIS_BUFFER NdisBuffer; + + + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + RECEIVE_BUFFER_QUOTA(DeviceContext)) > + DeviceContext->MemoryLimit)) { + PANIC("NBF: Could not allocate receive buffer: limit\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_LIMIT, + 108, + RECEIVE_BUFFER_QUOTA(DeviceContext), + RECEIVE_BUFFER_RESOURCE_ID); + *TransportReceiveBuffer = NULL; + return; + } + + // + // BUGBUG: The Aligned doesn't help since the header makes it unaligned. + // + + BufferTag = (PBUFFER_TAG)ExAllocatePoolWithTag ( + NonPagedPoolCacheAligned, + DeviceContext->ReceiveBufferLength, + 'tFBN'); + + if (BufferTag == NULL) { + PANIC("NBF: Could not allocate receive buffer: no pool\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 208, + DeviceContext->ReceiveBufferLength, + RECEIVE_BUFFER_RESOURCE_ID); + + *TransportReceiveBuffer = NULL; + return; + } + + DeviceContext->MemoryUsage += RECEIVE_BUFFER_QUOTA(DeviceContext); + + // + // point to the buffer for NDIS + // + + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + DeviceContext->NdisBufferPool, + BufferTag->Buffer, + DeviceContext->MaxReceivePacketSize); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + PANIC("NBF: Could not allocate receive buffer: no buffer\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_SPECIFIC, + 308, + 0, + RECEIVE_BUFFER_RESOURCE_ID); + ExFreePool (BufferTag); + *TransportReceiveBuffer = NULL; + return; + } + + BufferTag->Length = DeviceContext->MaxReceivePacketSize; + BufferTag->NdisBuffer = NdisBuffer; + + ++DeviceContext->ReceiveBufferAllocated; + + *TransportReceiveBuffer = BufferTag; + +} /* NbfAllocateReceiveBuffer */ + + +VOID +NbfDeallocateReceiveBuffer( + IN PDEVICE_CONTEXT DeviceContext, + IN PBUFFER_TAG TransportReceiveBuffer + ) + +/*++ + +Routine Description: + + This routine frees storage for a receive buffer. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + TransportReceiveBuffer - A pointer to the buffer. + +Return Value: + + None. + +--*/ + +{ + + NdisFreeBuffer (TransportReceiveBuffer->NdisBuffer); + ExFreePool (TransportReceiveBuffer); + + --DeviceContext->ReceiveBufferAllocated; + DeviceContext->MemoryUsage -= RECEIVE_BUFFER_QUOTA(DeviceContext); + +} /* NbfDeallocateReceiveBuffer */ + + +NTSTATUS +NbfCreatePacket( + PDEVICE_CONTEXT DeviceContext, + PTP_LINK Link, + PTP_PACKET *Packet + ) + +/*++ + +Routine Description: + + This routine allocates a packet from the device context's pool, + and prepares the MAC and DLC headers for use by the connection. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + Link - The link the packet will be sent over. + + Packet - Pointer to a place where we will return a pointer to the + allocated packet. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PSINGLE_LIST_ENTRY s; + PTP_PACKET ThePacket; + PDLC_I_FRAME DlcHdr; +#if DBG + PNBF_HDR_CONNECTION NbfHdr; +#endif + typedef struct _SIXTEEN_BYTES { + ULONG Data[4]; + } SIXTEEN_BYTES, *PSIXTEEN_BYTES; + + ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint0 ("NbfCreatePacket: Entered.\n"); + } + + // + // Make sure that structure packing hasn't happened. + // + + ASSERT (sizeof(NBF_HDR_CONNECTION) == 14); + +#if defined(NBF_UP) + s = DeviceContext->PacketPool.Next; + if (s != NULL) { + DeviceContext->PacketPool.Next = s->Next; + } +#else + s = ExInterlockedPopEntryList ( + &DeviceContext->PacketPool, + &DeviceContext->Interlock); +#endif + + if (s == NULL) { + NbfGrowSendPacketPool(DeviceContext); + +#if defined(NBF_UP) + s = DeviceContext->PacketPool.Next; + if (s != NULL) { + DeviceContext->PacketPool.Next = s->Next; + } +#else + s = ExInterlockedPopEntryList ( + &DeviceContext->PacketPool, + &DeviceContext->Interlock); +#endif + if (s == NULL) { +#if DBG + ++Link->CreatePacketFailures; + if ((ULONG)Link->CreatePacketFailures >= NbfCreatePacketThreshold) { + if (NbfPacketPanic) { + NbfPrint1 ("NbfCreatePacket: PANIC! no more packets in provider's pool (%d times).\n", + Link->CreatePacketFailures); + } + Link->CreatePacketFailures = 0; + } +#endif + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + ++DeviceContext->PacketExhausted; + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + return STATUS_INSUFFICIENT_RESOURCES; + } + + } +#if DBG + Link->CreatePacketFailures = 0; +#endif + + ThePacket = CONTAINING_RECORD (s, TP_PACKET, Linkage); + + // + // NOTE: ThePacket->Action and ThePacket->Owner are filled + // in by the caller of this function. + // + + ThePacket->ReferenceCount = 1; // automatic ref count of 1. + ThePacket->Link = NULL; // no link yet. + ThePacket->PacketSent = FALSE; + ASSERT (ThePacket->Action == PACKET_ACTION_IRP_SP); + ASSERT (ThePacket->PacketNoNdisBuffer == FALSE); + ASSERT (ThePacket->PacketizeConnection == FALSE); + + // + // Initialize the MAC header for this packet, using the connection's + // link pre-built header. + // + + if (Link->HeaderLength <= 14) { + + *(PSIXTEEN_BYTES)ThePacket->Header = *(PSIXTEEN_BYTES)Link->Header; + + } else { + + RtlCopyMemory( + ThePacket->Header, + Link->Header, + Link->HeaderLength); + + // + // Initialize the TP_FRAME_CONNECTION header for this packet. + // + + DlcHdr = (PDLC_I_FRAME)&(ThePacket->Header[Link->HeaderLength]); + DlcHdr->Dsap = DSAP_NETBIOS_OVER_LLC; + DlcHdr->Ssap = DSAP_NETBIOS_OVER_LLC; +#if DBG + DlcHdr->SendSeq = 0; // known values, will assist debugging. + DlcHdr->RcvSeq = 0; // these are assigned at shipment time. +#endif + + } + + +#if DBG + NbfHdr = (PNBF_HDR_CONNECTION)&(ThePacket->Header[Link->HeaderLength + sizeof(DLC_I_FRAME)]); + NbfHdr->Command = 0xff; // to assist debugging-- assigned later. + NbfHdr->Data1 = 0xff; // to assist debugging-- assigned later. + NbfHdr->Data2Low = 0xff; // to assist debugging-- assigned later. + NbfHdr->Data2High = 0xff; // to assist debugging-- assigned later. + TRANSMIT_CORR(NbfHdr) = 0xffff; // to assist debugging-- assigned later. + RESPONSE_CORR(NbfHdr) = 0xffff; // to assist debugging-- assigned later. + NbfHdr->DestinationSessionNumber = 0xff; // to assist debugging-- assigned later. + NbfHdr->SourceSessionNumber = 0xff; // to assist debugging-- assigned later. +#endif + + *Packet = ThePacket; // return pointer to the packet. + return STATUS_SUCCESS; +} /* NbfCreatePacket */ + + +NTSTATUS +NbfCreateRrPacket( + PDEVICE_CONTEXT DeviceContext, + PTP_LINK Link, + PTP_PACKET *Packet + ) + +/*++ + +Routine Description: + + This routine allocates an RR packet from the device context's pool, + and prepares the MAC and DLC headers for use by the connection. + It first looks in the special RR packet pool, then in the regular + packet pool. + +Arguments: + + DeviceContext - Pointer to our device context to charge the packet to. + + Link - The link the packet will be sent over. + + Packet - Pointer to a place where we will return a pointer to the + allocated packet. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PSINGLE_LIST_ENTRY s; + PTP_PACKET ThePacket; + PDLC_I_FRAME DlcHdr; + NTSTATUS Status; +#if DBG + PNBF_HDR_CONNECTION NbfHdr; +#endif + typedef struct _SIXTEEN_BYTES { + ULONG Data[4]; + } SIXTEEN_BYTES, *PSIXTEEN_BYTES; + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint0 ("NbfCreateRrPacket: Entered.\n"); + } + + // + // Make sure that structure packing hasn't happened. + // + + ASSERT (sizeof(NBF_HDR_CONNECTION) == 14); + +#if defined(NBF_UP) + s = DeviceContext->RrPacketPool.Next; + if (s != NULL) { + DeviceContext->RrPacketPool.Next = s->Next; + } +#else + s = ExInterlockedPopEntryList ( + &DeviceContext->RrPacketPool, + &DeviceContext->Interlock); +#endif + + if (s == NULL) { +#if DBG + ++Link->CreatePacketFailures; + if ((ULONG)Link->CreatePacketFailures >= NbfCreatePacketThreshold) { + if (NbfPacketPanic) { + NbfPrint1 ("NbfCreateRrPacket: PANIC! no more packets in provider's pool (%d times).\n", + Link->CreatePacketFailures); + } + Link->CreatePacketFailures = 0; + } +#endif + // + // Try to get one from the regular pool, and mark it so + // it goes back there. + // + + Status = NbfCreatePacket( + DeviceContext, + Link, + Packet); + + if (Status == STATUS_SUCCESS) { + (*Packet)->Action = PACKET_ACTION_NULL; + } + return Status; + } +#if DBG + Link->CreatePacketFailures = 0; +#endif + + ThePacket = CONTAINING_RECORD (s, TP_PACKET, Linkage); + + // + // NOTE: ThePacket->Owner is filled in by the caller of this + // function. + // + + ThePacket->ReferenceCount = 1; // automatic ref count of 1. + ThePacket->Link = NULL; // no link yet. + ThePacket->PacketSent = FALSE; + ASSERT (ThePacket->Action == PACKET_ACTION_RR); + ASSERT (ThePacket->PacketNoNdisBuffer == FALSE); + + // + // Initialize the MAC header for this packet, using the connection's + // link pre-built header. + // + + if (Link->HeaderLength <= 14) { + + *(PSIXTEEN_BYTES)ThePacket->Header = *(PSIXTEEN_BYTES)Link->Header; + + } else { + + RtlCopyMemory( + ThePacket->Header, + Link->Header, + Link->HeaderLength); + + // + // Initialize the TP_FRAME_CONNECTION header for this packet. + // + + DlcHdr = (PDLC_I_FRAME)&(ThePacket->Header[Link->HeaderLength]); + DlcHdr->Dsap = DSAP_NETBIOS_OVER_LLC; + DlcHdr->Ssap = DSAP_NETBIOS_OVER_LLC; +#if DBG + DlcHdr->SendSeq = 0; // known values, will assist debugging. + DlcHdr->RcvSeq = 0; // these are assigned at shipment time. +#endif + + } + + +#if DBG + NbfHdr = (PNBF_HDR_CONNECTION)&(ThePacket->Header[Link->HeaderLength + sizeof(DLC_I_FRAME)]); + NbfHdr->Command = 0xff; // to assist debugging-- assigned later. + NbfHdr->Data1 = 0xff; // to assist debugging-- assigned later. + NbfHdr->Data2Low = 0xff; // to assist debugging-- assigned later. + NbfHdr->Data2High = 0xff; // to assist debugging-- assigned later. + TRANSMIT_CORR(NbfHdr) = 0xffff; // to assist debugging-- assigned later. + RESPONSE_CORR(NbfHdr) = 0xffff; // to assist debugging-- assigned later. + NbfHdr->DestinationSessionNumber = 0xff; // to assist debugging-- assigned later. + NbfHdr->SourceSessionNumber = 0xff; // to assist debugging-- assigned later. +#endif + + *Packet = ThePacket; // return pointer to the packet. + return STATUS_SUCCESS; +} /* NbfCreateRrPacket */ + + +VOID +NbfDestroyPacket( + PTP_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine destroys a packet, thereby returning it to the pool. If + it is determined that there is at least one connection waiting for a + packet to become available (and it just has), then the connection is + removed from the device context's list and AdvanceSend is called to + prep the connection further. + +Arguments: + + Packet - Pointer to a packet to be returned to the pool. + +Return Value: + + none. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + PTP_CONNECTION Connection; + PLIST_ENTRY p; + PNDIS_BUFFER HeaderBuffer; + PNDIS_BUFFER NdisBuffer; + ULONG Flags; + + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint2 ("NbfDestroyPacket: Entered, Packet: %lx, NdisPacket: %lx\n", + Packet, Packet->NdisPacket); + } + + DeviceContext = Packet->Provider; + + // + // Strip off and unmap the buffers describing data and header. + // + + if (Packet->PacketNoNdisBuffer) { + + // + // If the NDIS_BUFFER chain is not ours, then we can't + // start unchaining since that would mess up the queue; + // instead we just drop the rest of the chain after the + // header. + // + + NdisQueryPacket (Packet->NdisPacket, NULL, NULL, &HeaderBuffer, NULL); + ASSERT (HeaderBuffer != NULL); + + NDIS_BUFFER_LINKAGE(HeaderBuffer) = (PNDIS_BUFFER)NULL; + NdisRecalculatePacketCounts (Packet->NdisPacket); + + Packet->PacketNoNdisBuffer = FALSE; + + } else { + + NdisUnchainBufferAtFront (Packet->NdisPacket, &HeaderBuffer); + ASSERT (HeaderBuffer != NULL); + + // + // Return all the NDIS_BUFFERs to the system. + // + + NdisUnchainBufferAtFront (Packet->NdisPacket, &NdisBuffer); + while (NdisBuffer != NULL) { + NdisFreeBuffer (NdisBuffer); + NdisUnchainBufferAtFront (Packet->NdisPacket, &NdisBuffer); + } + + NDIS_BUFFER_LINKAGE(HeaderBuffer) = (PNDIS_BUFFER)NULL; + NdisChainBufferAtFront (Packet->NdisPacket, HeaderBuffer); + + } + + + // + // invoke the packet deallocate action specified in this packet. + // + + switch (Packet->Action) { + + case PACKET_ACTION_NULL: + // PANIC ("NbfDestroyPacket: no action.\n"); + Packet->Action = PACKET_ACTION_IRP_SP; + break; + + case PACKET_ACTION_IRP_SP: + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint2 ("NbfDestroyPacket: Packet %x deref IrpSp %x.\n", Packet, Packet->Owner); + } + NbfDereferenceSendIrp("Destroy packet", (PIO_STACK_LOCATION)(Packet->Owner), RREF_PACKET); + break; + + case PACKET_ACTION_CONNECTION: + NbfDereferenceConnection ("Destroy packet", (PTP_CONNECTION)(Packet->Owner), CREF_FRAME_SEND); + Packet->Action = PACKET_ACTION_IRP_SP; + break; + + case PACKET_ACTION_END: + NbfDereferenceConnection ("SessionEnd destroyed", (PTP_CONNECTION)(Packet->Owner), CREF_FRAME_SEND); + NbfDereferenceConnection ("SessionEnd destroyed", (PTP_CONNECTION)(Packet->Owner), CREF_LINK); + Packet->Action = PACKET_ACTION_IRP_SP; + break; + + case PACKET_ACTION_RR: +#if defined(NBF_UP) + ((PSINGLE_LIST_ENTRY)&Packet->Linkage)->Next = + DeviceContext->RrPacketPool.Next; + DeviceContext->RrPacketPool.Next = + &((PSINGLE_LIST_ENTRY)&Packet->Linkage)->Next; +#else + ExInterlockedPushEntryList ( + &DeviceContext->RrPacketPool, + (PSINGLE_LIST_ENTRY)&Packet->Linkage, + &DeviceContext->Interlock); +#endif + return; + + default: + IF_NBFDBG (NBF_DEBUG_RESOURCE) { + NbfPrint1 ("NbfDestroyPacket: invalid action (%ld).\n", Packet->Action); + } + ASSERT (FALSE); + } + + + // + // Put the packet back for use again. + // + +#if defined(NBF_UP) + ((PSINGLE_LIST_ENTRY)&Packet->Linkage)->Next = + DeviceContext->PacketPool.Next; + DeviceContext->PacketPool.Next = + &((PSINGLE_LIST_ENTRY)&Packet->Linkage)->Next; +#else + ExInterlockedPushEntryList ( + &DeviceContext->PacketPool, + (PSINGLE_LIST_ENTRY)&Packet->Linkage, + &DeviceContext->Interlock); +#endif + + // + // If there is a connection waiting to ship out more packets, then + // wake it up and start packetizing again. + // + // We do a quick check without the lock; there is a small + // window where we may not take someone off, but this + // window exists anyway and we assume that more packets + // will be freed in the future. + // + + if (IsListEmpty (&DeviceContext->PacketWaitQueue)) { + return; + } + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + if (!(IsListEmpty(&DeviceContext->PacketWaitQueue))) { + + // + // Remove a connection from the "packet starved" queue. + // + + p = RemoveHeadList (&DeviceContext->PacketWaitQueue); + Connection = CONTAINING_RECORD (p, TP_CONNECTION, PacketWaitLinkage); + Connection->OnPacketWaitQueue = FALSE; + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + // + // If this connection is starved because it couldn't send a + // control packet (SI, SC, RO, RC, or DA) then start that + // operation up again. Otherwise, just start packetizing. + // + + if (Connection->Flags & CONNECTION_FLAGS_STARVED) { + + Flags = Connection->Flags & CONNECTION_FLAGS_STARVED; + + if ((Flags & (Flags-1)) != 0) { + + // + // More than one bit is on, use only the low one + // (an arbitrary choice). + // + +#if DBG + DbgPrint ("NBF: Connection %lx has two flag bits on %lx\n", Connection, Connection->Flags); +#endif + Flags &= ~(Flags-1); + + } + + Connection->Flags &= ~Flags; + + if ((Connection->Flags & CONNECTION_FLAGS_W_PACKETIZE) || + (Connection->Flags & CONNECTION_FLAGS_STARVED)) { + + // + // We are waiting for both a specific packet and + // to packetize, or for two specific packets, so + // put ourselves back on the queue. + // + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + if (!Connection->OnPacketWaitQueue) { + Connection->OnPacketWaitQueue = TRUE; + InsertTailList( + &DeviceContext->PacketWaitQueue, + &Connection->PacketWaitLinkage); + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + } + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + if (Flags & CONNECTION_FLAGS_SEND_SI) { + NbfSendSessionInitialize (Connection); + } else if (Flags & CONNECTION_FLAGS_SEND_SC) { + NbfSendSessionConfirm (Connection); + } else if (Flags & CONNECTION_FLAGS_SEND_RO) { + NbfSendReceiveOutstanding (Connection); + } else if (Flags & CONNECTION_FLAGS_SEND_RC) { + NbfSendReceiveContinue (Connection); + } else if (Flags & CONNECTION_FLAGS_SEND_SE) { + NbfSendSessionEnd ( + Connection, + FALSE); + } else if (Flags & CONNECTION_FLAGS_SEND_DA) { + NbfSendDataAck (Connection); + } else { + IF_NBFDBG (NBF_DEBUG_PACKET) { + NbfPrint0 ("NbfDestroyPacket: connection flags mismanaged.\n"); + } + } + + } else { + + // + // Place the connection on the packetize queue and start + // packetizing the next connection to be serviced. If he + // is already on the packetize queue for some reason, then + // don't do this. + // + // We shouldn't be packetizing in this case!! - adb (7/3/91). + // This used to be a check that did nothing if FLAGS_PACKETIZE + // was set, but if that happens something is wrong... + // + + ASSERT (Connection->Flags & CONNECTION_FLAGS_W_PACKETIZE); + Connection->Flags &= ~CONNECTION_FLAGS_W_PACKETIZE; + + Connection->SendState = CONNECTION_SENDSTATE_PACKETIZE; + + if ((Connection->Flags & CONNECTION_FLAGS_READY) && + !(Connection->Flags & CONNECTION_FLAGS_PACKETIZE)) { + + Connection->Flags |= CONNECTION_FLAGS_PACKETIZE; + + NbfReferenceConnection ("Packet available", Connection, CREF_PACKETIZE_QUEUE); + + ExInterlockedInsertTailList( + &DeviceContext->PacketizeQueue, + &Connection->PacketizeLinkage, + &DeviceContext->SpinLock); + } + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + PacketizeConnections (DeviceContext); + + } + + } else { + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + } + +} /* NbfDestroyPacket */ + +VOID NbfGrowSendPacketPool(PDEVICE_CONTEXT DeviceContext) +{ + + NDIS_STATUS NdisStatus; + PNBF_POOL_LIST_DESC SendPacketPoolDesc; + PTP_PACKET TransportSendPacket; + UINT i; + + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + DeviceContext->PacketLength) > + DeviceContext->MemoryLimit)) { + PANIC("NBF: Could not grow send packet pool: limit\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_LIMIT, + 107, + DeviceContext->PacketLength, + PACKET_RESOURCE_ID); + return; + } + + for (i = 0; i < PACKET_POOL_GROW_COUNT; i += 1) { + NbfAllocateSendPacket(DeviceContext, &TransportSendPacket); + + if (TransportSendPacket != NULL) { + ExInterlockedPushEntryList(&(DeviceContext)->PacketPool, + (PSINGLE_LIST_ENTRY)&TransportSendPacket->Linkage, + &(DeviceContext)->Interlock); + } + else { + break; + } + } + + if (i == PACKET_POOL_GROW_COUNT) { + return; + } + +#ifdef DBG + DbgBreakPoint(); +#endif // DBG + +} + +#if DBG +VOID +NbfReferencePacket( + PTP_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine increases the number of reasons why a packet cannot be + discarded. + +Arguments: + + Packet - Pointer to a packet to be referenced. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + IF_NBFDBG (NBF_DEBUG_PACKET) { + NbfPrint3 ("NbfReferencePacket: Entered, NdisPacket: %lx Packet: %lx Ref Count: %lx.\n", + Packet->NdisPacket, Packet, Packet->ReferenceCount); + } + + result = InterlockedIncrement (&Packet->ReferenceCount); + + ASSERT (result >= 0); + +} /* NbfReferencePacket */ + + +VOID +NbfDereferencePacket( + PTP_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine dereferences a transport packet by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + NbfDestroyPacket to remove it from the system. + +Arguments: + + Packet - Pointer to a packet object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&Packet->ReferenceCount); + + // + // If we have deleted all references to this packet, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the packet any longer. + // + + IF_NBFDBG (NBF_DEBUG_PACKET) { + NbfPrint1 ("NbfDereferencePacket: Entered, result: %lx\n", result); + } + + ASSERT (result >= 0); + + if (result == 0) { + NbfDestroyPacket (Packet); + } + +} /* NbfDereferencePacket */ +#endif + + +VOID +NbfWaitPacket( + PTP_CONNECTION Connection, + ULONG Flags + ) + +/*++ + +Routine Description: + + This routine causes the specified connection to be put into a wait + state pending the availability of a packet to send the specified + frame. + +Arguments: + + Connection - Pointer to the connection object to be paused. + + Flags - Bitflag indicating which specific frame should be resent. + +Return Value: + + none. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + + IF_NBFDBG (NBF_DEBUG_PACKET) { + NbfPrint0 ("NbfWaitPacket: Entered.\n"); + } + + DeviceContext = Connection->Provider; + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + // + // Now put this connection on the device context's PacketWaitQueue, + // but only if it isn't already queued there. This state is managed + // with the OnPacketWaitQueue variable. + // + // If the connection is stopping, don't queue him either. + // + + if ((Connection->Flags & CONNECTION_FLAGS_READY) || + (Flags == CONNECTION_FLAGS_SEND_SE)) { + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + // + // Turn on the bitflag that indicates which frame we couldn't send. + // + +#if DBG + if (Flags == CONNECTION_FLAGS_SEND_SE) { + DbgPrint ("NBF: Inserting connection %lx on PacketWait for SESSION_END\n", Connection); + } +#endif + Connection->Flags |= Flags; + + if (!Connection->OnPacketWaitQueue) { + + Connection->OnPacketWaitQueue = TRUE; + InsertTailList ( + &DeviceContext->PacketWaitQueue, + &Connection->PacketWaitLinkage); + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + } + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + +} /* NbfWaitPacket */ + + +#if MAGIC +VOID +NbfSendMagicBullet ( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine sends a magic bullet on the net that can be used to trigger + sniffers or other such things. + +Arguments: + + DeviceContext - pointer to the device context + + Link - This is needed to call NbfCreatePacket + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + NDIS_STATUS NdisStatus; + PTP_UI_FRAME RawFrame; + PUCHAR Header; + PNDIS_BUFFER NdisBuffer; + UINT i; + + UNREFERENCED_PARAMETER (Link); // no longer needed + + Status = NbfCreateConnectionlessFrame (DeviceContext, &RawFrame); + if (!NT_SUCCESS (Status)) { // couldn't make frame. +#if DBG + DbgPrint ("NbfSendMagicBullet: Couldn't allocate frame!\n"); +#endif + return; + } + + + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + DeviceContext->NdisBufferPool, + DeviceContext->MagicBullet, + 32); + + if (NdisStatus == NDIS_STATUS_SUCCESS) { + + Header = (PUCHAR)&RawFrame->Header; + + for (i=0;i<6;i++) { + Header[i] = MAGIC_BULLET_FOOD; + Header[i+6] = DeviceContext->LocalAddress.Address[i]; + } + + Header[12] = 0; + Header[13] = (UCHAR)(DeviceContext->UIFrameHeaderLength + 18); + + for (i=14;i<DeviceContext->UIFrameHeaderLength;i++) { + Header[i] = MAGIC_BULLET_FOOD; + } + + NdisChainBufferAtBack (RawFrame->NdisPacket, NdisBuffer); + + NbfSendUIFrame ( + DeviceContext, + RawFrame, + FALSE); // no loopback + + } + + return; + +} +#endif + diff --git a/private/ntos/tdi/nbf/precomp.h b/private/ntos/tdi/nbf/precomp.h new file mode 100644 index 000000000..4b3c3c642 --- /dev/null +++ b/private/ntos/tdi/nbf/precomp.h @@ -0,0 +1,219 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + nbf.h + +Abstract: + + Private include file for the NBF (NetBIOS Frames Protocol) transport + provider subcomponent of the NTOS project. + +Author: + + Stephen E. Jones (stevej) 25-Oct-1989 + +Revision History: + + David Beaver (dbeaver) 24-Sep-1990 + Remove PDI and PC586-specific support; add NDIS support + +--*/ + +#include <ntddk.h> + +#include <windef.h> +#include <nb30.h> +//#include <ntiologc.h> +//#include <ctype.h> +//#include <assert.h> +//#include <stdio.h> +//#include <stdlib.h> +//#include <memory.h> +//#include <nt.h> +//#include <ntrtl.h> +//#include <nturtl.h> +//#include <string.h> +//#include <windows.h> + +#ifdef BUILD_FOR_511 +#define ExAllocatePoolWithTag(a,b,c) ExAllocatePool(a,b) +#endif + +typedef struct _RTL_SPLAY_LINKS { + struct _RTL_SPLAY_LINKS *Parent; + struct _RTL_SPLAY_LINKS *LeftChild; + struct _RTL_SPLAY_LINKS *RightChild; +} RTL_SPLAY_LINKS; +typedef RTL_SPLAY_LINKS *PRTL_SPLAY_LINKS; + +#define RtlInitializeSplayLinks(Links) { \ + PRTL_SPLAY_LINKS _SplayLinks; \ + _SplayLinks = (PRTL_SPLAY_LINKS)(Links); \ + _SplayLinks->Parent = _SplayLinks; \ + _SplayLinks->LeftChild = NULL; \ + _SplayLinks->RightChild = NULL; \ + } + +#define RtlLeftChild(Links) ( \ + (PRTL_SPLAY_LINKS)(Links)->LeftChild \ + ) + +#define RtlRightChild(Links) ( \ + (PRTL_SPLAY_LINKS)(Links)->RightChild \ + ) + +#define RtlInsertAsLeftChild(ParentLinks,ChildLinks) { \ + PRTL_SPLAY_LINKS _SplayParent; \ + PRTL_SPLAY_LINKS _SplayChild; \ + _SplayParent = (PRTL_SPLAY_LINKS)(ParentLinks); \ + _SplayChild = (PRTL_SPLAY_LINKS)(ChildLinks); \ + _SplayParent->LeftChild = _SplayChild; \ + _SplayChild->Parent = _SplayParent; \ + } + +#define RtlInsertAsRightChild(ParentLinks,ChildLinks) { \ + PRTL_SPLAY_LINKS _SplayParent; \ + PRTL_SPLAY_LINKS _SplayChild; \ + _SplayParent = (PRTL_SPLAY_LINKS)(ParentLinks); \ + _SplayChild = (PRTL_SPLAY_LINKS)(ChildLinks); \ + _SplayParent->RightChild = _SplayChild; \ + _SplayChild->Parent = _SplayParent; \ + } + + +PRTL_SPLAY_LINKS +NTAPI +RtlDelete ( + PRTL_SPLAY_LINKS Links + ); + + +#include <tdikrnl.h> // Transport Driver Interface. + +#include <ndis.h> // Physical Driver Interface. + +#if DEVL +#define STATIC +#else +#define STATIC static +#endif + +#include "nbfconst.h" // private NETBEUI constants. +#include "nbfmac.h" // mac-specific definitions +#include "nbfhdrs.h" // private NETBEUI protocol headers. +#include "nbftypes.h" // private NETBEUI types. +#include "nbfcnfg.h" // configuration information. +#include "nbfprocs.h" // private NETBEUI function prototypes. +#ifdef MEMPRINT +#include "memprint.h" // drt's memory debug print +#endif + +#if defined(NT_UP) && defined(DRIVERS_UP) +#define NBF_UP 1 +#endif + +#ifndef NBF_LOCKS + +#if !defined(NBF_UP) + +#define ACQUIRE_SPIN_LOCK(lock,irql) KeAcquireSpinLock(lock,irql) +#define RELEASE_SPIN_LOCK(lock,irql) KeReleaseSpinLock(lock,irql) +#define ACQUIRE_DPC_SPIN_LOCK(lock) KeAcquireSpinLockAtDpcLevel(lock) +#define RELEASE_DPC_SPIN_LOCK(lock) KeReleaseSpinLockFromDpcLevel(lock) + +#else // NBF_UP + +#define ACQUIRE_SPIN_LOCK(lock,irql) ExAcquireSpinLock(lock,irql) +#define RELEASE_SPIN_LOCK(lock,irql) ExReleaseSpinLock(lock,irql) +#define ACQUIRE_DPC_SPIN_LOCK(lock) +#define RELEASE_DPC_SPIN_LOCK(lock) + +#endif + +#if DBG + +#define ACQUIRE_C_SPIN_LOCK(lock,irql) { \ + PTP_CONNECTION _conn = CONTAINING_RECORD(lock,TP_CONNECTION,SpinLock); \ + KeAcquireSpinLock(lock,irql); \ + _conn->LockAcquired = TRUE; \ + strncpy(_conn->LastAcquireFile, strrchr(__FILE__,'\\')+1, 7); \ + _conn->LastAcquireLine = __LINE__; \ +} +#define RELEASE_C_SPIN_LOCK(lock,irql) { \ + PTP_CONNECTION _conn = CONTAINING_RECORD(lock,TP_CONNECTION,SpinLock); \ + _conn->LockAcquired = FALSE; \ + strncpy(_conn->LastReleaseFile, strrchr(__FILE__,'\\')+1, 7); \ + _conn->LastReleaseLine = __LINE__; \ + KeReleaseSpinLock(lock,irql); \ +} + +#define ACQUIRE_DPC_C_SPIN_LOCK(lock) { \ + PTP_CONNECTION _conn = CONTAINING_RECORD(lock,TP_CONNECTION,SpinLock); \ + KeAcquireSpinLockAtDpcLevel(lock); \ + _conn->LockAcquired = TRUE; \ + strncpy(_conn->LastAcquireFile, strrchr(__FILE__,'\\')+1, 7); \ + _conn->LastAcquireLine = __LINE__; \ +} +#define RELEASE_DPC_C_SPIN_LOCK(lock) { \ + PTP_CONNECTION _conn = CONTAINING_RECORD(lock,TP_CONNECTION,SpinLock); \ + _conn->LockAcquired = FALSE; \ + strncpy(_conn->LastReleaseFile, strrchr(__FILE__,'\\')+1, 7); \ + _conn->LastReleaseLine = __LINE__; \ + KeReleaseSpinLockFromDpcLevel(lock); \ +} + +#else // DBG + +#define ACQUIRE_C_SPIN_LOCK(lock,irql) ACQUIRE_SPIN_LOCK(lock,irql) +#define RELEASE_C_SPIN_LOCK(lock,irql) RELEASE_SPIN_LOCK(lock,irql) +#define ACQUIRE_DPC_C_SPIN_LOCK(lock) ACQUIRE_DPC_SPIN_LOCK(lock) +#define RELEASE_DPC_C_SPIN_LOCK(lock) RELEASE_DPC_SPIN_LOCK(lock) + +#endif // DBG + +#define ENTER_NBF +#define LEAVE_NBF + +#else + +VOID +NbfAcquireSpinLock( + IN PKSPIN_LOCK Lock, + OUT PKIRQL OldIrql, + IN PSZ LockName, + IN PSZ FileName, + IN ULONG LineNumber + ); + +VOID +NbfReleaseSpinLock( + IN PKSPIN_LOCK Lock, + IN KIRQL OldIrql, + IN PSZ LockName, + IN PSZ FileName, + IN ULONG LineNumber + ); + +#define ACQUIRE_SPIN_LOCK(lock,irql) \ + NbfAcquireSpinLock( lock, irql, #lock, __FILE__, __LINE__ ) +#define RELEASE_SPIN_LOCK(lock,irql) \ + NbfReleaseSpinLock( lock, irql, #lock, __FILE__, __LINE__ ) + +#define ACQUIRE_DPC_SPIN_LOCK(lock) \ + { \ + KIRQL OldIrql; \ + NbfAcquireSpinLock( lock, &OldIrql, #lock, __FILE__, __LINE__ ); \ + } +#define RELEASE_DPC_SPIN_LOCK(lock) \ + NbfReleaseSpinLock( lock, DISPATCH_LEVEL, #lock, __FILE__, __LINE__ ) + +#define ENTER_NBF \ + NbfAcquireSpinLock( (PKSPIN_LOCK)NULL, (PKIRQL)NULL, "(Global)", __FILE__, __LINE__ ) +#define LEAVE_NBF \ + NbfReleaseSpinLock( (PKSPIN_LOCK)NULL, (KIRQL)-1, "(Global)", __FILE__, __LINE__ ) + +#endif + diff --git a/private/ntos/tdi/nbf/rcv.c b/private/ntos/tdi/nbf/rcv.c new file mode 100644 index 000000000..8fdc87af9 --- /dev/null +++ b/private/ntos/tdi/nbf/rcv.c @@ -0,0 +1,309 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + rcv.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiReceive + o TdiReceiveDatagram + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +NTSTATUS +NbfTdiReceive( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiReceive request for the transport provider. + +Arguments: + + Irp - I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PTP_CONNECTION connection; + KIRQL oldirql; + PIO_STACK_LOCATION irpSp; + + // + // verify that the operation is taking place on a connection. At the same + // time we do this, we reference the connection. This ensures it does not + // get removed out from under us. Note also that we do the connection + // lookup within a try/except clause, thus protecting ourselves against + // really bogus handles + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + connection = irpSp->FileObject->FsContext; + + // + // Check that this is really a connection. + // + + if ((connection->Size != sizeof (TP_CONNECTION)) || + (connection->Type != NBF_CONNECTION_SIGNATURE)) { +#if DBG + NbfPrint2 ("TdiReceive: Invalid Connection %lx Irp %lx\n", connection, Irp); +#endif + return STATUS_INVALID_CONNECTION; + } + + // + // Initialize bytes transferred here. + // + + Irp->IoStatus.Information = 0; // reset byte transfer count. + + // This reference is removed by NbfDestroyRequest. + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + + ACQUIRE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + if ((connection->Flags & CONNECTION_FLAGS_READY) == 0) { + + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + Irp->IoStatus.Status = connection->Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + status = STATUS_PENDING; + + } else { + KIRQL cancelIrql; + + // + // Once the reference is in, LinkSpinLock will be valid. + // + + NbfReferenceConnection("TdiReceive request", connection, CREF_RECEIVE_IRP); + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + IoAcquireCancelSpinLock(&cancelIrql); + ACQUIRE_DPC_SPIN_LOCK (connection->LinkSpinLock); + + IRP_RECEIVE_IRP(irpSp) = Irp; + IRP_RECEIVE_REFCOUNT(irpSp) = 1; + +#if DBG + NbfReceives[NbfReceivesNext].Irp = Irp; + NbfReceives[NbfReceivesNext].Request = NULL; + NbfReceives[NbfReceivesNext].Connection = (PVOID)connection; + NbfReceivesNext = (NbfReceivesNext++) % TRACK_TDI_LIMIT; +#endif + + // + // If this IRP has been cancelled, complete it now. + // + + if (Irp->Cancel) { + +#if DBG + NbfCompletedReceives[NbfCompletedReceivesNext].Irp = Irp; + NbfCompletedReceives[NbfCompletedReceivesNext].Request = NULL; + NbfCompletedReceives[NbfCompletedReceivesNext].Status = STATUS_CANCELLED; + { + ULONG i,j,k; + PUCHAR va; + PMDL mdl; + + mdl = Irp->MdlAddress; + + NbfCompletedReceives[NbfCompletedReceivesNext].Contents[0] = (UCHAR)0; + + i = 1; + while (i<TRACK_TDI_CAPTURE) { + if (mdl == NULL) break; + va = MmGetSystemAddressForMdl (mdl); + j = MmGetMdlByteCount (mdl); + + for (i=i,k=0;i<TRACK_TDI_CAPTURE&k<j;i++,k++) { + NbfCompletedReceives[NbfCompletedReceivesNext].Contents[i] = *va++; + } + mdl = mdl->Next; + } + } + + NbfCompletedReceivesNext = (NbfCompletedReceivesNext++) % TRACK_TDI_LIMIT; +#endif + + // + // It is safe to do this with locks held. + // + NbfCompleteReceiveIrp (Irp, STATUS_CANCELLED, 0); + + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + IoReleaseCancelSpinLock(cancelIrql); + + } else { + + // + // Insert onto the receive queue, and make the IRP + // cancellable. + // + + InsertTailList (&connection->ReceiveQueue,&Irp->Tail.Overlay.ListEntry); + IoSetCancelRoutine(Irp, NbfCancelReceive); + + // + // Release the cancel spinlock out of order. Since we were + // already at dpc level when it was acquired, we don't + // need to swap irqls. + // + ASSERT(cancelIrql == DISPATCH_LEVEL); + IoReleaseCancelSpinLock(cancelIrql); + + // + // This call releases the link spinlock, and references the + // connection first if it needs to access it after + // releasing the lock. + // + + AwakenReceive (connection); // awaken if sleeping. + + } + + status = STATUS_PENDING; + + } + + KeLowerIrql (oldirql); + + return status; +} /* TdiReceive */ + + +NTSTATUS +NbfTdiReceiveDatagram( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiReceiveDatagram request for the transport + provider. Receive datagrams just get queued up to an address, and are + completed when a DATAGRAM or DATAGRAM_BROADCAST frame is received at + the address. + +Arguments: + + Irp - I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + KIRQL oldirql; + PTP_ADDRESS address; + PTP_ADDRESS_FILE addressFile; + PIO_STACK_LOCATION irpSp; + KIRQL cancelIrql; + + // + // verify that the operation is taking place on an address. At the same + // time we do this, we reference the address. This ensures it does not + // get removed out from under us. Note also that we do the address + // lookup within a try/except clause, thus protecting ourselves against + // really bogus handles + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + addressFile = irpSp->FileObject->FsContext; + + status = NbfVerifyAddressObject (addressFile); + + if (!NT_SUCCESS (status)) { + return status; + } + +#if DBG + if (((PTDI_REQUEST_KERNEL_RECEIVEDG)(&irpSp->Parameters))->ReceiveLength > 0) { + ASSERT (Irp->MdlAddress != NULL); + } +#endif + + address = addressFile->Address; + + IoAcquireCancelSpinLock(&cancelIrql); + ACQUIRE_SPIN_LOCK (&address->SpinLock,&oldirql); + + if ((address->Flags & (ADDRESS_FLAGS_STOPPING | ADDRESS_FLAGS_CONFLICT)) != 0) { + + RELEASE_SPIN_LOCK (&address->SpinLock,oldirql); + IoReleaseCancelSpinLock(cancelIrql); + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = (address->Flags & ADDRESS_FLAGS_STOPPING) ? + STATUS_NETWORK_NAME_DELETED : STATUS_DUPLICATE_NAME; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + } else { + + // + // If this IRP has been cancelled, then call the + // cancel routine. + // + + if (Irp->Cancel) { + + RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); + IoReleaseCancelSpinLock(cancelIrql); + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_CANCELLED; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + } else { + + IoSetCancelRoutine(Irp, NbfCancelReceiveDatagram); + NbfReferenceAddress ("Receive datagram", address, AREF_REQUEST); + InsertTailList (&addressFile->ReceiveDatagramQueue,&Irp->Tail.Overlay.ListEntry); + RELEASE_SPIN_LOCK (&address->SpinLock,oldirql); + IoReleaseCancelSpinLock(cancelIrql); + } + + } + + NbfDereferenceAddress ("Temp rcv datagram", address, AREF_VERIFY); + + return STATUS_PENDING; + +} /* TdiReceiveDatagram */ + diff --git a/private/ntos/tdi/nbf/rcveng.c b/private/ntos/tdi/nbf/rcveng.c new file mode 100644 index 000000000..f3b3eab33 --- /dev/null +++ b/private/ntos/tdi/nbf/rcveng.c @@ -0,0 +1,823 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + rcveng.c + +Abstract: + + This module contains code that implements the receive engine for the + Jetbeui transport provider. This code is responsible for the following + basic activities: + + 1. Transitioning a TdiReceive request from an inactive state on the + connection's ReceiveQueue to the active state on that connection + (ActivateReceive). + + 2. Advancing the status of the active receive request by copying 0 or + more bytes of data from an incoming DATA_FIRST_MIDDLE or DATA_ONLY_LAST + NBF frame. + + 3. Completing receive requests. + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +VOID +ActivateReceive( + PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine activates the next TdiReceive request on the specified + connection object if there is no active request on that connection + already. This allows the request to accept data on the connection. + + NOTE: THIS FUNCTION MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + +Return Value: + + none. + +--*/ + +{ + PIRP Irp; + + ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" ActivateReceive: Entered.\n"); + } + + // + // The ACTIVE_RECEIVE bitflag will be set on the connection if + // the receive-fields in the CONNECTION object are valid. If + // this flag is cleared, then we try to make the next TdiReceive + // request in the ReceiveQueue the active request. + // + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + if (!(Connection->Flags & CONNECTION_FLAGS_ACTIVE_RECEIVE)) { + if (!IsListEmpty (&Connection->ReceiveQueue)) { + + // + // Found a receive, so make it the active one. + // + + Connection->Flags |= CONNECTION_FLAGS_ACTIVE_RECEIVE; + + Irp = CONTAINING_RECORD( + Connection->ReceiveQueue.Flink, + IRP, + Tail.Overlay.ListEntry); + Connection->MessageBytesReceived = 0; + Connection->MessageBytesAcked = 0; + Connection->MessageInitAccepted = 0; + Connection->CurrentReceiveIrp = Irp; + Connection->CurrentReceiveSynchronous = + Connection->Provider->MacInfo.SingleReceive; + Connection->CurrentReceiveMdl = Irp->MdlAddress; + Connection->ReceiveLength = IRP_RECEIVE_LENGTH(IoGetCurrentIrpStackLocation(Irp)); + Connection->ReceiveByteOffset = 0; + } + } + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" ActivateReceive: Exiting.\n"); + } +} /* ActivateReceive */ + + +VOID +AwakenReceive( + PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called to reactivate a sleeping connection with the + RECEIVE_WAKEUP bitflag set because data arrived for which no receive + was available. The caller has made a receive available at the connection, + so here we activate the next receive, and send the appropriate protocol + to restart the message at the first byte offset past the one received + by the last receive. + + NOTE: THIS FUNCTION MUST BE CALLED AT DPC LEVEL. IT IS CALLED + WITH CONNECTION->LINKSPINLOCK HELD. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" AwakenReceive: Entered.\n"); + } + + // + // If the RECEIVE_WAKEUP bitflag is set, then awaken the connection. + // + + if (Connection->Flags & CONNECTION_FLAGS_RECEIVE_WAKEUP) { + if (Connection->ReceiveQueue.Flink != &Connection->ReceiveQueue) { + Connection->Flags &= ~CONNECTION_FLAGS_RECEIVE_WAKEUP; + + // + // Found a receive, so turn off the wakeup flag, activate + // the next receive, and send the protocol. + // + + // + // Quick fix: So there is no window where a receive + // is active but the bit is not on (otherwise we could + // accept whatever data happens to show up in the + // interim). + // + + Connection->Flags |= CONNECTION_FLAGS_W_RESYNCH; + + NbfReferenceConnection ("temp AwakenReceive", Connection, CREF_BY_ID); // release lookup hold. + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + ActivateReceive (Connection); + + // + // BUGBUG: What if this fails? The successful queueing + // of a RCV_O should cause ActivateReceive to be called. + // + // NOTE: Send this after ActivateReceive, since that + // is where the MessageBytesAcked/Received variables + // are initialized. + // + + NbfSendReceiveOutstanding (Connection); + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" AwakenReceive: Returned from NbfSendReceive.\n"); + } + + NbfDereferenceConnection("temp AwakenReceive", Connection, CREF_BY_ID); + return; + } + } + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); +} /* AwakenReceive */ + + +VOID +CompleteReceive( + PTP_CONNECTION Connection, + BOOLEAN EndOfMessage, + IN ULONG BytesTransferred + ) + +/*++ + +Routine Description: + + This routine is called by ProcessIncomingData when the current receive + must be completed. Depending on whether the current frame being + processed is a DATA_FIRST_MIDDLE or DATA_ONLY_LAST, and also whether + all of the data was processed, the EndOfMessage flag will be set accordingly + by the caller to indicate that a message boundary was received. + + NOTE: THIS FUNCTION MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + + EndOfMessage - BOOLEAN set to true if TDI_END_OF_RECORD should be reported. + + BytesTransferred - Number of bytes copied in this receive. + +Return Value: + + none. + +--*/ + +{ + PLIST_ENTRY p; + PIRP Irp; + ULONG BytesReceived; + PIO_STACK_LOCATION IrpSp; + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" CompleteReceive: Entered.\n"); + } + + + if (Connection->SpecialReceiveIrp) { + + PIRP Irp = Connection->SpecialReceiveIrp; + + Irp->IoStatus.Status = STATUS_SUCCESS; + Irp->IoStatus.Information = BytesTransferred; + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + Connection->Flags |= CONNECTION_FLAGS_RC_PENDING; + Connection->Flags &= ~CONNECTION_FLAGS_ACTIVE_RECEIVE; + Connection->SpecialReceiveIrp = FALSE; + + ++Connection->ReceivedTsdus; + + ExInterlockedInsertHeadList( + &Connection->Provider->IrpCompletionQueue, + &Irp->Tail.Overlay.ListEntry, + Connection->ProviderInterlock); + + // + // NOTE: NbfAcknowledgeDataOnlyLast releases + // the connection spinlock. + // + + NbfAcknowledgeDataOnlyLast( + Connection, + Connection->MessageBytesReceived + ); + + } else { + KIRQL cancelIrql; + + if (EndOfMessage) { + + // + // The messages has been completely received, ack it. + // + // We set DEFERRED_ACK and DEFERRED_NOT_Q here, which + // will cause an ack to be piggybacked if any data is + // sent during the call to CompleteReceive. If this + // does not happen, then we will call AcknowledgeDataOnlyLast + // which will will send a DATA ACK or queue a request for + // a piggyback ack. We do this *after* calling CompleteReceive + // so we know that we will complete the receive back to + // the client before we ack the data, to prevent the + // next receive from being sent before this one is + // completed. + // + + IoAcquireCancelSpinLock(&cancelIrql); + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + Connection->DeferredFlags |= + (CONNECTION_FLAGS_DEFERRED_ACK | CONNECTION_FLAGS_DEFERRED_NOT_Q); + Connection->Flags |= CONNECTION_FLAGS_RC_PENDING; + + } else { + + // + // Send a receive outstanding (even though we don't + // know that we have a receive) to get him to + // reframe his send. Pre-2.0 clients require a + // no receive before the receive outstanding. + // + // BUGBUG: what if this fails (due to no send packets)? + // + + if ((Connection->Flags & CONNECTION_FLAGS_VERSION2) == 0) { + NbfSendNoReceive (Connection); + } + NbfSendReceiveOutstanding (Connection); + + // + // If there is a receive posted, make it current and + // send a receive outstanding. + // + // BUGBUG: need general function for this, which sends + // NO_RECEIVE if appropriate. + // + + ActivateReceive (Connection); + + IoAcquireCancelSpinLock(&cancelIrql); + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + } + + // + // If we indicated to the client, adjust this down by the + // amount of data taken, when it hits zero we can reindicate. + // + + if (Connection->ReceiveBytesUnaccepted) { + if (Connection->MessageBytesReceived >= Connection->ReceiveBytesUnaccepted) { + Connection->ReceiveBytesUnaccepted = 0; + } else { + Connection->ReceiveBytesUnaccepted -= Connection->MessageBytesReceived; + } + } + + // + // NOTE: The connection lock is held here. + // + + if (IsListEmpty (&Connection->ReceiveQueue)) { + + ASSERT ((Connection->Flags2 & CONNECTION_FLAGS2_STOPPING) != 0); + + // + // Release the cancel spinlock out of order. Since we were + // already at DPC level when it was acquired, there is no + // need to swap irqls. + // + ASSERT(cancelIrql == DISPATCH_LEVEL); + IoReleaseCancelSpinLock(cancelIrql); + + } else { + + Connection->Flags &= ~CONNECTION_FLAGS_ACTIVE_RECEIVE; + BytesReceived = Connection->MessageBytesReceived; + + + // + // Complete the TdiReceive request at the head of the + // connection's ReceiveQueue. + // + + IF_NBFDBG (NBF_DEBUG_RCVENG) { + NbfPrint0 (" CompleteReceive: Normal IRP is present.\n"); + } + + p = RemoveHeadList (&Connection->ReceiveQueue); + Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + + IoSetCancelRoutine(Irp, NULL); + + // + // Release the cancel spinlock out of order. Since we were + // already at DPC level when it was acquired, there is no + // need to swap irqls. + // + ASSERT(cancelIrql == DISPATCH_LEVEL); + IoReleaseCancelSpinLock(cancelIrql); + + // + // If this request should generate no back traffic, then + // disable piggyback acks for it. + // + + IrpSp = IoGetCurrentIrpStackLocation(Irp); + if (IRP_RECEIVE_FLAGS(IrpSp) & TDI_RECEIVE_NO_RESPONSE_EXP) { + Connection->CurrentReceiveAckQueueable = FALSE; + } + +#if DBG + NbfCompletedReceives[NbfCompletedReceivesNext].Irp = Irp; + NbfCompletedReceives[NbfCompletedReceivesNext].Request = NULL; + NbfCompletedReceives[NbfCompletedReceivesNext].Status = + EndOfMessage ? STATUS_SUCCESS : STATUS_BUFFER_OVERFLOW; + { + ULONG i,j,k; + PUCHAR va; + PMDL mdl; + + mdl = Irp->MdlAddress; + + if (BytesReceived > TRACK_TDI_CAPTURE) { + NbfCompletedReceives[NbfCompletedReceivesNext].Contents[0] = 0xFF; + } else { + NbfCompletedReceives[NbfCompletedReceivesNext].Contents[0] = (UCHAR)BytesReceived; + } + + i = 1; + while (i<TRACK_TDI_CAPTURE) { + if (mdl == NULL) break; + va = MmGetSystemAddressForMdl (mdl); + j = MmGetMdlByteCount (mdl); + + for (i=i,k=0;i<TRACK_TDI_CAPTURE&k<j;i++,k++) { + NbfCompletedReceives[NbfCompletedReceivesNext].Contents[i] = *va++; + } + mdl = mdl->Next; + } + } + + NbfCompletedReceivesNext = (NbfCompletedReceivesNext++) % TRACK_TDI_LIMIT; +#endif + ++Connection->ReceivedTsdus; + + // + // This can be called with locks held. + // + NbfCompleteReceiveIrp( + Irp, + EndOfMessage ? STATUS_SUCCESS : STATUS_BUFFER_OVERFLOW, + BytesReceived); + + } + + + // + // If NOT_Q is still set, that means that the deferred ack was + // not satisfied by anything resulting from the call to + // CompleteReceive, so we need to ack or queue an ack here. + // + + + if ((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_NOT_Q) != 0) { + + Connection->DeferredFlags &= + ~(CONNECTION_FLAGS_DEFERRED_ACK | CONNECTION_FLAGS_DEFERRED_NOT_Q); + + // + // NOTE: NbfAcknowledgeDataOnlyLast releases + // the connection spinlock. + // + + NbfAcknowledgeDataOnlyLast( + Connection, + Connection->MessageBytesReceived + ); + + } else { + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + } + + } + +} /* CompleteReceive */ + + +VOID +NbfCancelReceive( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a receive. + The receive is found on the connection's receive queue; if it + is the current request it is cancelled and the connection + goes into "cancelled receive" mode, otherwise it is cancelled + silently. + + In "cancelled receive" mode the connection makes it appear to + the remote the data is being received, but in fact it is not + indicated to the transport or buffered on our end + + 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; + PIRP ReceiveIrp; + PTP_CONNECTION Connection; + PLIST_ENTRY p; + ULONG BytesReceived; + BOOLEAN Found; + + UNREFERENCED_PARAMETER (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_RECEIVE)); + + Connection = IrpSp->FileObject->FsContext; + + // + // Since this IRP is still in the cancellable state, we know + // that the connection is still around (although it may be in + // the process of being torn down). + // + + // + // See if this is the IRP for the current receive request. + // + + ACQUIRE_SPIN_LOCK (Connection->LinkSpinLock, &oldirql); + + BytesReceived = Connection->MessageBytesReceived; + + p = Connection->ReceiveQueue.Flink; + + // + // If there is a receive active and it is not a special + // IRP, then see if this is it. + // + + if (((Connection->Flags & CONNECTION_FLAGS_ACTIVE_RECEIVE) != 0) && + (!Connection->SpecialReceiveIrp)) { + + ReceiveIrp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + + if (ReceiveIrp == Irp) { + + // + // yes, it is the active receive. Turn on the RCV_CANCELLED + // bit instructing the connection to drop the rest of the + // data received (until the DOL comes in). + // + + Connection->Flags |= CONNECTION_FLAGS_RCV_CANCELLED; + Connection->Flags &= ~CONNECTION_FLAGS_ACTIVE_RECEIVE; + + (VOID)RemoveHeadList (&Connection->ReceiveQueue); + +#if DBG + NbfCompletedReceives[NbfCompletedReceivesNext].Irp = ReceiveIrp; + NbfCompletedReceives[NbfCompletedReceivesNext].Request = NULL; + NbfCompletedReceives[NbfCompletedReceivesNext].Status = STATUS_CANCELLED; + { + ULONG i,j,k; + PUCHAR va; + PMDL mdl; + + mdl = ReceiveIrp->MdlAddress; + + if (BytesReceived > TRACK_TDI_CAPTURE) { + NbfCompletedReceives[NbfCompletedReceivesNext].Contents[0] = 0xFF; + } else { + NbfCompletedReceives[NbfCompletedReceivesNext].Contents[0] = (UCHAR)BytesReceived; + } + + i = 1; + while (i<TRACK_TDI_CAPTURE) { + if (mdl == NULL) break; + va = MmGetSystemAddressForMdl (mdl); + j = MmGetMdlByteCount (mdl); + + for (i=i,k=0;i<TRACK_TDI_CAPTURE&k<j;i++,k++) { + NbfCompletedReceives[NbfCompletedReceivesNext].Contents[i] = *va++; + } + mdl = mdl->Next; + } + } + + NbfCompletedReceivesNext = (NbfCompletedReceivesNext++) % TRACK_TDI_LIMIT; +#endif + + RELEASE_SPIN_LOCK (Connection->LinkSpinLock, oldirql); + IoReleaseCancelSpinLock (Irp->CancelIrql); + +#if DBG + DbgPrint("NBF: Canceled in-progress receive %lx on %lx\n", + Irp, Connection); +#endif + + // + // The following dereference will complete the I/O, provided removes + // the last reference on the request object. The I/O will complete + // with the status and information stored in the Irp. Therefore, + // we set those values here before the dereference. + // + + NbfCompleteReceiveIrp (ReceiveIrp, STATUS_CANCELLED, 0); + return; + + } + + } + + + // + // If we fall through to here, the IRP was not the active receive. + // Scan through the list, looking for this IRP. + // + + Found = FALSE; + + while (p != &Connection->ReceiveQueue) { + + ReceiveIrp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + if (ReceiveIrp == Irp) { + + // + // Found it, remove it from the list here. + // + + RemoveEntryList (p); + + Found = TRUE; + +#if DBG + NbfCompletedReceives[NbfCompletedReceivesNext].Irp = ReceiveIrp; + NbfCompletedReceives[NbfCompletedReceivesNext].Request = NULL; + NbfCompletedReceives[NbfCompletedReceivesNext].Status = STATUS_CANCELLED; + { + ULONG i,j,k; + PUCHAR va; + PMDL mdl; + + mdl = ReceiveIrp->MdlAddress; + + if (BytesReceived > TRACK_TDI_CAPTURE) { + NbfCompletedReceives[NbfCompletedReceivesNext].Contents[0] = 0xFF; + } else { + NbfCompletedReceives[NbfCompletedReceivesNext].Contents[0] = (UCHAR)BytesReceived; + } + + i = 1; + while (i<TRACK_TDI_CAPTURE) { + if (mdl == NULL) break; + va = MmGetSystemAddressForMdl (mdl); + j = MmGetMdlByteCount (mdl); + + for (i=i,k=0;i<TRACK_TDI_CAPTURE&k<j;i++,k++) { + NbfCompletedReceives[NbfCompletedReceivesNext].Contents[i] = *va++; + } + mdl = mdl->Next; + } + } + + NbfCompletedReceivesNext = (NbfCompletedReceivesNext++) % TRACK_TDI_LIMIT; +#endif + + RELEASE_SPIN_LOCK (Connection->LinkSpinLock, oldirql); + IoReleaseCancelSpinLock (Irp->CancelIrql); + +#if DBG + DbgPrint("NBF: Canceled receive %lx on %lx\n", + ReceiveIrp, Connection); +#endif + + // + // The following dereference will complete the I/O, provided removes + // the last reference on the request object. The I/O will complete + // with the status and information stored in the Irp. Therefore, + // we set those values here before the dereference. + // + + NbfCompleteReceiveIrp (ReceiveIrp, STATUS_CANCELLED, 0); + break; + + } + + p = p->Flink; + + } + + if (!Found) { + + // + // We didn't find it! + // + +#if DBG + DbgPrint("NBF: Tried to cancel receive %lx on %lx, not found\n", + Irp, Connection); +#endif + RELEASE_SPIN_LOCK (Connection->LinkSpinLock, oldirql); + IoReleaseCancelSpinLock (Irp->CancelIrql); + } + +} + + +VOID +NbfCancelReceiveDatagram( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a receive + datagram. The receive is looked for on the address file's + receive datagram queue; if it is found it is cancelled. + + 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_ADDRESS_FILE AddressFile; + PTP_ADDRESS Address; + PLIST_ENTRY p; + BOOLEAN Found; + + UNREFERENCED_PARAMETER (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_RECEIVE_DATAGRAM)); + + AddressFile = IrpSp->FileObject->FsContext; + Address = AddressFile->Address; + + // + // Since this IRP is still in the cancellable state, we know + // that the address file is still around (although it may be in + // the process of being torn down). See if the IRP is on the list. + // + + Found = FALSE; + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + for (p = AddressFile->ReceiveDatagramQueue.Flink; + p != &AddressFile->ReceiveDatagramQueue; + p = p->Flink) { + + if (CONTAINING_RECORD(p, IRP, Tail.Overlay.ListEntry) == Irp) { + RemoveEntryList (p); + Found = TRUE; + break; + } + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + IoReleaseCancelSpinLock (Irp->CancelIrql); + + if (Found) { + +#if DBG + DbgPrint("NBF: Canceled receive datagram %lx on %lx\n", + Irp, AddressFile); +#endif + + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = STATUS_CANCELLED; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + NbfDereferenceAddress ("Receive DG cancelled", Address, AREF_REQUEST); + + } else { + +#if DBG + DbgPrint("NBF: Tried to cancel receive datagram %lx on %lx, not found\n", + Irp, AddressFile); +#endif + + } + +} /* NbfCancelReceiveDatagram */ + diff --git a/private/ntos/tdi/nbf/request.c b/private/ntos/tdi/nbf/request.c new file mode 100644 index 000000000..5547e31ee --- /dev/null +++ b/private/ntos/tdi/nbf/request.c @@ -0,0 +1,1376 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + request.c + +Abstract: + + This module contains code which implements the TP_REQUEST object. + Routines are provided to create, destroy, reference, and dereference, + transport request objects. + +Author: + + David Beaver (dbeaver) 1 July 1991 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef RASAUTODIAL +#include <acd.h> +#include <acdapi.h> +#endif // RASAUTODIAL + +// +// External variables +// +#ifdef RASAUTODIAL +extern BOOLEAN fAcdLoadedG; +extern ACD_DRIVER AcdDriverG; + +// +// Imported routines +// +VOID +NbfNoteNewConnection( + PTP_CONNECTION Connection, + PDEVICE_CONTEXT DeviceContext + ); +#endif // RASAUTODIAL + + +VOID +NbfTdiRequestTimeoutHandler( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) + +/*++ + +Routine Description: + + This routine is executed as a DPC at DISPATCH_LEVEL when a request + such as TdiSend, TdiReceive, TdiSendDatagram, TdiReceiveDatagram, etc., + encounters a timeout. This routine cleans up the activity and cancels it. + +Arguments: + + Dpc - Pointer to a system DPC object. + + DeferredContext - Pointer to the TP_REQUEST block representing the + request that has timed out. + + SystemArgument1 - Not used. + + SystemArgument2 - Not used. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql; + PTP_REQUEST Request; + PTP_CONNECTION Connection; +#if DBG + LARGE_INTEGER time, difference; +#endif + PIO_STACK_LOCATION IrpSp; + PTDI_REQUEST_KERNEL_QUERY_INFORMATION query; + PDEVICE_CONTEXT DeviceContext; + + Dpc, SystemArgument1, SystemArgument2; // prevent compiler warnings + + ENTER_NBF; + + Request = (PTP_REQUEST)DeferredContext; + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint1 ("RequestTimeoutHandler: Entered, Request %lx\n", Request); + } + + ACQUIRE_SPIN_LOCK (&Request->SpinLock, &oldirql); + Request->Flags &= ~REQUEST_FLAGS_TIMER; + if ((Request->Flags & REQUEST_FLAGS_STOPPING) == 0) { + +#if DBG + KeQuerySystemTime (&time); + difference.QuadPart = time.QuadPart - (Request->Time).QuadPart; + NbfPrint1 ("RequestTimeoutHandler: Request timed out, queued for %ld seconds\n", + difference.LowPart / SECONDS); +#endif + + // + // find reason for timeout + // + + IrpSp = IoGetCurrentIrpStackLocation (Request->IoRequestPacket); + if (IrpSp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL) { + switch (IrpSp->MinorFunction) { + + // + // none of these should time out. + // + + case TDI_SEND: + case TDI_ACCEPT: + case TDI_SET_INFORMATION: + case TDI_SET_EVENT_HANDLER: + case TDI_SEND_DATAGRAM: + case TDI_RECEIVE_DATAGRAM: + case TDI_RECEIVE: + +#if DBG + NbfPrint1 ("RequestTimeoutHandler: Request: %lx Timed out, and shouldn't have!\n", + Request); +#endif + ASSERT (FALSE); + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + NbfCompleteRequest (Request, STATUS_IO_TIMEOUT, 0); + break; + + + case TDI_LISTEN: + case TDI_CONNECT: + +#if DBG + NbfPrint2 ("RequestTimeoutHandler: %s Failed, Request: %lx\n", + IrpSp->MinorFunction == TDI_LISTEN ? + "Listen" : + IrpSp->MinorFunction == TDI_CONNECT ? + "Connect" : "Disconnect", + Request); +#endif + Connection = (PTP_CONNECTION)(Request->Context); + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + + // + // Since these requests are part of the connection + // itself, we just stop the connection and the + // request will get torn down then. If we get the + // situation where the request times out before + // it is queued to the connection, then the code + // that is about to queue it will check the STOPPING + // flag and complete it then. + // + // Don't stop the connection if an automatic connection + // is in progress. + // + +#if DBG + DbgPrint("RequestTimeoutHandler: AUTOCONNECTING=0x%x\n", Connection->Flags2 & CONNECTION_FLAGS2_AUTOCONNECTING); +#endif + if (!(Connection->Flags2 & CONNECTION_FLAGS2_AUTOCONNECTING)) + NbfStopConnection (Connection, STATUS_IO_TIMEOUT); + break; + + case TDI_DISCONNECT: + + // + // We don't create requests for TDI_DISCONNECT any more. + // + + ASSERT(FALSE); + break; + + case TDI_QUERY_INFORMATION: + + DeviceContext = (PDEVICE_CONTEXT)IrpSp->FileObject->DeviceObject; + query = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION)&IrpSp->Parameters; + + IF_NBFDBG (NBF_DEBUG_DEVCTX) { + NbfPrint1 ("RequestTimeout: %lx:\n", DeviceContext); + } + + // + // Determine if the request is done, or if we should + // requeue it. + // + + --Request->Retries; + + if (Request->Retries > 0) { + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + + // + // Send another packet out, and restart the timer. + // + + if (query->QueryType == TDI_QUERY_FIND_NAME) { + + NbfSendQueryFindName ( + DeviceContext, + Request); + + } else if (query->QueryType == TDI_QUERY_ADAPTER_STATUS) { + + PUCHAR SingleSR; + UINT SingleSRLength; + + // + // Send the STATUS_QUERY frames out as + // single-route source routing. + // + // BUGBUG: On a second status query this should + // really be sent directed, but currently we + // don't record the address anywhere. + // + + MacReturnSingleRouteSR( + &DeviceContext->MacInfo, + &SingleSR, + &SingleSRLength); + + NbfSendStatusQuery ( + DeviceContext, + Request, + &DeviceContext->NetBIOSAddress, + SingleSR, + SingleSRLength); + + } else { + + ASSERT (FALSE); + + } + + } else { + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + + // + // That's it, we retried enough, complete it. + // + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock,&oldirql); + RemoveEntryList (&Request->Linkage); + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + if (Request->BytesWritten > 0) { + + NbfCompleteRequest (Request, STATUS_SUCCESS, Request->BytesWritten); + + } else { + + NbfCompleteRequest (Request, STATUS_IO_TIMEOUT, Request->BytesWritten); + + } + + + } + + break; + + default: +#if DBG + NbfPrint2 ("RequestTimeoutHandler: Unknown Request Timed out, Request: %lx Type: %x\n", + Request, IrpSp->MinorFunction); +#endif + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + break; + + } // end of switch + + } else { + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + + } + + NbfDereferenceRequest ("Timeout", Request, RREF_TIMER); // for the timeout + + } else { + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + NbfDereferenceRequest ("Timeout: stopping", Request, RREF_TIMER); // for the timeout + + } + + LEAVE_NBF; + return; + +} /* RequestTimeoutHandler */ + + +VOID +NbfAllocateRequest( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_REQUEST *TransportRequest + ) + +/*++ + +Routine Description: + + This routine allocates a request packet from nonpaged pool and initializes + it to a known state. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + TransportRequest - Pointer to a place where this routine will return + a pointer to a transport request structure. It returns NULL if no + storage can be allocated. + +Return Value: + + None. + +--*/ + +{ + PTP_REQUEST Request; + + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + sizeof(TP_REQUEST)) > + DeviceContext->MemoryLimit)) { + PANIC("NBF: Could not allocate request: limit\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_LIMIT, + 104, + sizeof(TP_REQUEST), + REQUEST_RESOURCE_ID); + *TransportRequest = NULL; + return; + } + + Request = (PTP_REQUEST)ExAllocatePoolWithTag ( + NonPagedPool, + sizeof (TP_REQUEST), + 'rFBN'); + if (Request == NULL) { + PANIC("NBF: Could not allocate request: no pool\n"); + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_POOL, + 204, + sizeof(TP_REQUEST), + REQUEST_RESOURCE_ID); + *TransportRequest = NULL; + return; + } + RtlZeroMemory (Request, sizeof(TP_REQUEST)); + + DeviceContext->MemoryUsage += sizeof(TP_REQUEST); + ++DeviceContext->RequestAllocated; + + Request->Type = NBF_REQUEST_SIGNATURE; + Request->Size = sizeof (TP_REQUEST); + + Request->ResponseBuffer = NULL; + + Request->Provider = DeviceContext; + Request->ProviderInterlock = &DeviceContext->Interlock; + KeInitializeSpinLock (&Request->SpinLock); + KeInitializeDpc (&Request->Dpc, NbfTdiRequestTimeoutHandler, (PVOID)Request); + KeInitializeTimer (&Request->Timer); // set to not-signaled state. + + *TransportRequest = Request; + +} /* NbfAllocateRequest */ + + +VOID +NbfDeallocateRequest( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_REQUEST TransportRequest + ) + +/*++ + +Routine Description: + + This routine frees a request packet. + + NOTE: This routine is called with the device context spinlock + held, or at such a time as synchronization is unnecessary. + +Arguments: + + DeviceContext - Pointer to the device context (which is really just + the device object with its extension) to be associated with the + address. + + TransportRequest - Pointer to a transport request structure. + +Return Value: + + None. + +--*/ + +{ + + ExFreePool (TransportRequest); + --DeviceContext->RequestAllocated; + DeviceContext->MemoryUsage -= sizeof(TP_REQUEST); + +} /* NbfDeallocateRequest */ + + +NTSTATUS +NbfCreateRequest( + IN PIRP Irp, + IN PVOID Context, + IN ULONG Flags, + IN PMDL Buffer2, + IN ULONG Buffer2Length, + IN LARGE_INTEGER Timeout, + OUT PTP_REQUEST * TpRequest + ) + +/*++ + +Routine Description: + + This routine creates a transport request and associates it with the + specified IRP, context, and queue. All major requests, including + TdiSend, TdiSendDatagram, TdiReceive, and TdiReceiveDatagram requests, + are composed in this manner. + +Arguments: + + Irp - Pointer to an IRP which was received by the transport for this + request. + + Context - Pointer to anything to associate this request with. This + value is not interpreted except at request cancelation time. + + Flags - A set of bitflags indicating the disposition of this request. + + Timeout - Timeout value (if non-zero) to start a timer for this request. + If zero, then no timer is activated for the request. + + TpRequest - If the function returns STATUS_SUCCESS, this will return + pointer to the TP_REQUEST structure allocated. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PDEVICE_CONTEXT DeviceContext; + PTP_REQUEST Request; + PLIST_ENTRY p; + PIO_STACK_LOCATION irpSp; +#if DBG + LARGE_INTEGER Time; +#endif + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint0 ("NbfCreateRequest: Entered.\n"); + } + + irpSp = IoGetCurrentIrpStackLocation(Irp); + DeviceContext = (PDEVICE_CONTEXT)irpSp->FileObject->DeviceObject; + +#if DBG + if (!MmIsNonPagedSystemAddressValid (DeviceContext->RequestPool.Flink)) { + NbfPrint2 ("NbfCreateRequest: RequestList hosed: %lx DeviceContext: %lx\n", + &DeviceContext->RequestPool, DeviceContext); + } +#endif + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + p = RemoveHeadList (&DeviceContext->RequestPool); + if (p == &DeviceContext->RequestPool) { + + if ((DeviceContext->RequestMaxAllocated == 0) || + (DeviceContext->RequestAllocated < DeviceContext->RequestMaxAllocated)) { + + NbfAllocateRequest (DeviceContext, &Request); + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint1 ("NBF: Allocated request at %lx\n", Request); + } + + } else { + + NbfWriteResourceErrorLog( + DeviceContext, + EVENT_TRANSPORT_RESOURCE_SPECIFIC, + 404, + sizeof(TP_REQUEST), + REQUEST_RESOURCE_ID); + Request = NULL; + + } + + if (Request == NULL) { + ++DeviceContext->RequestExhausted; + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + PANIC ("NbfCreateConnection: Could not allocate request object!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + } else { + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + + } + + ++DeviceContext->RequestInUse; + if (DeviceContext->RequestInUse > DeviceContext->RequestMaxInUse) { + ++DeviceContext->RequestMaxInUse; + } + + DeviceContext->RequestTotal += DeviceContext->RequestInUse; + ++DeviceContext->RequestSamples; + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + + // + // fill out the request. + // + + // Request->Provider = DeviceContext; + Request->IoRequestPacket = Irp; + Request->Buffer2 = Buffer2; + Request->Buffer2Length = Buffer2Length; + Request->Flags = Flags; + Request->Context = Context; + Request->ReferenceCount = 1; // initialize reference count. + +#if DBG + { + UINT Counter; + for (Counter = 0; Counter < NUMBER_OF_RREFS; Counter++) { + Request->RefTypes[Counter] = 0; + } + + // This reference is removed by NbfCompleteRequest + + Request->RefTypes[RREF_CREATION] = 1; + } +#endif + +#if DBG + Request->Completed = FALSE; + Request->Destroyed = FALSE; + Request->TotalReferences = 0; + Request->TotalDereferences = 0; + Request->NextRefLoc = 0; + ExInterlockedInsertHeadList (&NbfGlobalRequestList, &Request->GlobalLinkage, &NbfGlobalInterlock); + StoreRequestHistory (Request, TRUE); +#endif + +#if DBG + KeQuerySystemTime (&Time); // ugly, but effective + Request->Time.LowPart = Time.LowPart; + Request->Time.HighPart = Time.HighPart; +#endif + + IF_NBFDBG (NBF_DEBUG_IRP) { + if (Irp->MdlAddress != NULL) { + PMDL mdl; + NbfPrint2 ("NbfCreateRequest: Map request %lx Irp %lx MdlChain \n", + Request, Request->IoRequestPacket); + mdl = Request->Buffer2; + while (mdl != NULL) { + NbfPrint4 ("Mdl %lx Va %lx StartVa %lx Flags %x\n", + mdl, MmGetSystemAddressForMdl(mdl), MmGetMdlVirtualAddress(mdl), + mdl->MdlFlags); + mdl = mdl->Next; + } + } + } + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint3 ("NbfCreateRequest: Request %lx Buffer2: %lx Irp: %lx\n", Request, + Buffer2, Irp); + } + + if ((Timeout.LowPart == 0) && (Timeout.HighPart == 0)) { + + // no timeout + } else { + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint3 ("NbfCreateRequest: Starting timer %lx%lx Flags %lx\n", + Timeout.HighPart, Timeout.LowPart, Request->Flags); + } + Request->Flags |= REQUEST_FLAGS_TIMER; // there is a timeout on this request. + KeInitializeTimer (&Request->Timer); // set to not-signaled state. + NbfReferenceRequest ("Create: timer", Request, RREF_TIMER); // one for the timer + KeSetTimer (&Request->Timer, Timeout, &Request->Dpc); + } + + *TpRequest = Request; + + return STATUS_SUCCESS; +} /* NbfCreateRequest */ + + +VOID +NbfDestroyRequest( + IN PTP_REQUEST Request + ) + +/*++ + +Routine Description: + + This routine returns a request block to the free pool. + +Arguments: + + Request - Pointer to a TP_REQUEST block to return to the free pool. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PIO_STACK_LOCATION irpSp; + PDEVICE_CONTEXT DeviceContext; + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint0 ("NbfDestroyRequest: Entered.\n"); + } + +#if DBG + if (Request->Destroyed) { + NbfPrint1 ("attempt to destroy already-destroyed request 0x%lx\n", Request); + DbgBreakPoint (); + } + Request->Destroyed = TRUE; +#if 1 + ACQUIRE_SPIN_LOCK (&NbfGlobalInterlock, &oldirql); + RemoveEntryList (&Request->GlobalLinkage); + RELEASE_SPIN_LOCK (&NbfGlobalInterlock, oldirql); +#else + ExInterlockedRemoveHeadList (Request->GlobalLinkage.Blink, &NbfGlobalInterlock); +#endif +#endif + ASSERT(Request->Completed); + + // + // Return the request to the caller with whatever status is in the IRP. + // + + IF_NBFDBG (NBF_DEBUG_IRP) { + NbfPrint1 ("NbfCompleteRequest: Completing IRP: %lx\n", + Request->IoRequestPacket); + } + + // + // Now dereference the owner of this request so that we are safe when + // we finally tear down the {connection, address}. The problem we're + // facing here is that we can't allow the user to assume semantics; + // the end of life for a connection must truly be the real end of life. + // for that to occur, we reference the owning object when the request is + // created and we dereference it just before we return it to the pool. + // + + switch (Request->Owner) { + case ConnectionType: + NbfDereferenceConnection ("Removing Connection",((PTP_CONNECTION)Request->Context), CREF_REQUEST); + break; + +#if DBG + case AddressType: + ASSERT (FALSE); + NbfDereferenceAddress ("Removing Address", ((PTP_ADDRESS)Request->Context), AREF_REQUEST); + break; +#endif + + case DeviceContextType: + NbfDereferenceDeviceContext ("Removing Address", ((PDEVICE_CONTEXT)Request->Context), DCREF_REQUEST); + break; + } + + // + // Unmap a possibly mapped buffer. We've only mapped the buffer if the + // Irp Major function is not method 0. (of 0, 1, 2, and 3.) + // + + IF_NBFDBG (NBF_DEBUG_IRP) { + { + PMDL mdl; + NbfPrint2 ("NbfDestroyRequest: Unmap request %lx Irp %lx MdlChain \n", + Request, Request->IoRequestPacket); + mdl = Request->Buffer2; + while (mdl != NULL) { + NbfPrint4 ("Mdl %lx Va %lx StartVa %lx Flags %x\n", + mdl, MmGetSystemAddressForMdl(mdl), MmGetMdlVirtualAddress(mdl), + mdl->MdlFlags); + mdl = mdl->Next; + } + } + } + + irpSp = IoGetCurrentIrpStackLocation (Request->IoRequestPacket); + DeviceContext = Request->Provider; + + LEAVE_NBF; + IoCompleteRequest (Request->IoRequestPacket, IO_NETWORK_INCREMENT); + ENTER_NBF; + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + // + // Put the request back on the free list. NOTE: we have the + // lock held here. + // + + + DeviceContext->RequestTotal += DeviceContext->RequestInUse; + ++DeviceContext->RequestSamples; + --DeviceContext->RequestInUse; + + if ((DeviceContext->RequestAllocated - DeviceContext->RequestInUse) > + DeviceContext->RequestInitAllocated) { + NbfDeallocateRequest (DeviceContext, Request); + IF_NBFDBG (NBF_DEBUG_DYNAMIC) { + NbfPrint1 ("NBF: Deallocated request at %lx\n", Request); + } + } else { + InsertTailList (&DeviceContext->RequestPool, &Request->Linkage); + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + +} /* NbfDestroyRequest */ + + +#if DBG +VOID +NbfRefRequest( + IN PTP_REQUEST Request + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a transport request. + +Arguments: + + Request - Pointer to a TP_REQUEST block. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint1 ("NbfRefRequest: Entered, ReferenceCount: %x\n", + Request->ReferenceCount); + } + +#if DBG + StoreRequestHistory( Request, TRUE ); +#endif + + ASSERT (Request->ReferenceCount > 0); + + result = InterlockedIncrement (&Request->ReferenceCount); + +} /* NbfRefRequest */ +#endif + + +VOID +NbfDerefRequest( + IN PTP_REQUEST Request + ) + +/*++ + +Routine Description: + + This routine dereferences a transport request by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + NbfDestroyRequest to remove it from the system. + +Arguments: + + Request - Pointer to a transport request object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint1 ("NbfDerefRequest: Entered, ReferenceCount: %x\n", + Request->ReferenceCount); + } + +#if DBG + StoreRequestHistory( Request, FALSE ); +#endif + + result = InterlockedDecrement (&Request->ReferenceCount); + + ASSERT (result >= 0); + + // + // If we have deleted all references to this request, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the request any longer. + // + + if (result == 0) { + NbfDestroyRequest (Request); + } + +} /* NbfDerefRequest */ + + +VOID +NbfCompleteRequest( + IN PTP_REQUEST Request, + IN NTSTATUS Status, + IN ULONG Information + ) + +/*++ + +Routine Description: + + This routine completes a transport request object, completing the I/O, + stopping the timeout, and freeing up the request object itself. + +Arguments: + + Request - Pointer to a transport request object. + + Status - Actual return status to be assigned to the request. This + value may be overridden if the timed-out bitflag is set in the request. + + Information - the information field for the I/O Status Block. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql; + PIRP Irp; + PIO_STACK_LOCATION IrpSp; + NTSTATUS FinalStatus = Status; + NTSTATUS CopyStatus; + BOOLEAN TimerWasSet; + + ASSERT (Status != STATUS_PENDING); + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint2 ("NbfCompleteRequest: Entered Request %lx, Request->Flags %lx\n", + Request, Request->Flags); + } + +#if DBG + if (Request->Completed) { + NbfPrint1 ("attempt to completed already-completed request 0x%lx\n", Request); + DbgBreakPoint (); + } + Request->Completed = TRUE; +#endif + + ACQUIRE_SPIN_LOCK (&Request->SpinLock, &oldirql); + + if ((Request->Flags & REQUEST_FLAGS_STOPPING) == 0) { + Request->Flags |= REQUEST_FLAGS_STOPPING; + + // + // Cancel the pending timeout on this request. Not all requests + // have their timer set. If this request has the TIMER bit set, + // then the timer needs to be cancelled. If it cannot be cancelled, + // then the timer routine will be run, so we just return and let + // the timer routine worry about cleaning up this request. + // + + if ((Request->Flags & REQUEST_FLAGS_TIMER) != 0) { + Request->Flags &= ~REQUEST_FLAGS_TIMER; + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + TimerWasSet = KeCancelTimer (&Request->Timer); + + if (TimerWasSet) { + NbfDereferenceRequest ("Complete: stop timer", Request, RREF_TIMER); + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint1 ("NbfCompleteRequest: Canceled timer: %lx.\n", &Request->Timer); + } + } + + } else { + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + } + + Irp = Request->IoRequestPacket; + +#ifdef RASAUTODIAL + // + // If this is a connect operation that has + // returned with either STATUS_SUCCESS or + // STATUS_BAD_NETWORK_PATH, then + // inform the automatic connection driver. + // + if (fAcdLoadedG) { + IrpSp = IoGetCurrentIrpStackLocation(Irp); + if (IrpSp->MinorFunction == TDI_CONNECT && + FinalStatus == STATUS_SUCCESS) + { + KIRQL adirql; + BOOLEAN fEnabled; + + ACQUIRE_SPIN_LOCK(&AcdDriverG.SpinLock, &adirql); + fEnabled = AcdDriverG.fEnabled; + RELEASE_SPIN_LOCK(&AcdDriverG.SpinLock, adirql); + if (fEnabled) { + NbfNoteNewConnection( + IrpSp->FileObject->FsContext, + (PDEVICE_CONTEXT)IrpSp->FileObject->DeviceObject); + } + } + } +#endif // RASAUTODIAL + + // + // For requests associated with a device context, we need + // to copy the data from the temp buffer to the MDL and + // free the temp buffer. + // + + if (Request->ResponseBuffer != NULL) { + + if ((FinalStatus == STATUS_SUCCESS) || + (FinalStatus == STATUS_BUFFER_OVERFLOW)) { + + CopyStatus = TdiCopyBufferToMdl ( + Request->ResponseBuffer, + 0L, + Information, + Irp->MdlAddress, + 0, + &Information); + + if (CopyStatus != STATUS_SUCCESS) { + FinalStatus = CopyStatus; + } + + } + + ExFreePool (Request->ResponseBuffer); + Request->ResponseBuffer = NULL; + + } + + // + // Install the return code in the IRP so that when we call NbfDestroyRequest, + // it will get completed with the proper return status. + // + + Irp->IoStatus.Status = FinalStatus; + Irp->IoStatus.Information = Information; + + // + // The entire transport is done with this request. + // + + NbfDereferenceRequest ("Complete", Request, RREF_CREATION); // remove creation reference. + + } else { + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + + } + +} /* NbfCompleteRequest */ + + +#if DBG +VOID +NbfRefSendIrp( + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a send IRP. + +Arguments: + + IrpSp - Pointer to the IRP's stack location. + +Return Value: + + none. + +--*/ + +{ + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint1 ("NbfRefSendIrp: Entered, ReferenceCount: %x\n", + IRP_SEND_REFCOUNT(IrpSp)); + } + + ASSERT (IRP_SEND_REFCOUNT(IrpSp) > 0); + + InterlockedIncrement (&IRP_SEND_REFCOUNT(IrpSp)); + +} /* NbfRefSendIrp */ + + +VOID +NbfDerefSendIrp( + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine dereferences a transport send IRP by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + IoCompleteRequest to actually complete the IRP. + +Arguments: + + Request - Pointer to a transport send IRP's stack location. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint1 ("NbfDerefSendIrp: Entered, ReferenceCount: %x\n", + IRP_SEND_REFCOUNT(IrpSp)); + } + + result = InterlockedDecrement (&IRP_SEND_REFCOUNT(IrpSp)); + + ASSERT (result >= 0); + + // + // If we have deleted all references to this request, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the request any longer. + // + + if (result == 0) { + + PIRP Irp = IRP_SEND_IRP(IrpSp); + + IRP_SEND_REFCOUNT(IrpSp) = 0; + IRP_SEND_IRP (IrpSp) = NULL; + + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + } + +} /* NbfDerefSendIrp */ +#endif + + +VOID +NbfCompleteSendIrp( + IN PIRP Irp, + IN NTSTATUS Status, + IN ULONG Information + ) + +/*++ + +Routine Description: + + This routine completes a transport send IRP. + +Arguments: + + Irp - Pointer to a send IRP. + + Status - Actual return status to be assigned to the request. This + value may be overridden if the timed-out bitflag is set in the request. + + Information - the information field for the I/O Status Block. + +Return Value: + + none. + +--*/ + +{ + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + PTP_CONNECTION Connection; + + ASSERT (Status != STATUS_PENDING); + + Connection = IRP_SEND_CONNECTION(IrpSp); + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint2 ("NbfCompleteSendIrp: Entered IRP %lx, connection %lx\n", + Irp, Connection); + } + + Irp->IoStatus.Status = Status; + Irp->IoStatus.Information = Information; + + NbfDereferenceSendIrp ("Complete", IrpSp, RREF_CREATION); // remove creation reference. + + NbfDereferenceConnectionMacro ("Removing Connection", Connection, CREF_SEND_IRP); + +} /* NbfCompleteSendIrp */ + + +#if DBG +VOID +NbfRefReceiveIrpLocked( + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine increments the reference count on a receive IRP. + +Arguments: + + IrpSp - Pointer to the IRP's stack location. + +Return Value: + + none. + +--*/ + +{ + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint1 ("NbfRefReceiveIrpLocked: Entered, ReferenceCount: %x\n", + IRP_RECEIVE_REFCOUNT(IrpSp)); + } + + ASSERT (IRP_RECEIVE_REFCOUNT(IrpSp) > 0); + + IRP_RECEIVE_REFCOUNT(IrpSp)++; + +} /* NbfRefReceiveIrpLocked */ +#endif + + +VOID +NbfDerefReceiveIrp( + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine dereferences a transport receive IRP by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + IoCompleteRequest to actually complete the IRP. + +Arguments: + + Request - Pointer to a transport receive IRP's stack location. + +Return Value: + + none. + +--*/ + +{ + ULONG result; + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint1 ("NbfDerefReceiveIrp: Entered, ReferenceCount: %x\n", + IRP_RECEIVE_REFCOUNT(IrpSp)); + } + + result = ExInterlockedAddUlong ( + (PULONG)&IRP_RECEIVE_REFCOUNT(IrpSp), + (ULONG)-1, + (IRP_RECEIVE_CONNECTION(IrpSp)->LinkSpinLock)); + + ASSERT (result > 0); + + // + // If we have deleted all references to this request, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the request any longer. + // + + if (result == 1) { + + PIRP Irp = IRP_RECEIVE_IRP(IrpSp); + + ExInterlockedInsertTailList( + &(IRP_DEVICE_CONTEXT(IrpSp)->IrpCompletionQueue), + &Irp->Tail.Overlay.ListEntry, + &(IRP_DEVICE_CONTEXT(IrpSp)->Interlock)); + + } + +} /* NbfDerefReceiveIrp */ + + +#if DBG +VOID +NbfDerefReceiveIrpLocked( + IN PIO_STACK_LOCATION IrpSp + ) + +/*++ + +Routine Description: + + This routine dereferences a transport receive IRP by decrementing the + reference count contained in the structure. If, after being + decremented, the reference count is zero, then this routine calls + IoCompleteRequest to actually complete the IRP. + +Arguments: + + Request - Pointer to a transport receive IRP's stack location. + +Return Value: + + none. + +--*/ + +{ + ULONG result; + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint1 ("NbfDerefReceiveIrpLocked: Entered, ReferenceCount: %x\n", + IRP_RECEIVE_REFCOUNT(IrpSp)); + } + + result = IRP_RECEIVE_REFCOUNT(IrpSp)--; + + ASSERT (result > 0); + + // + // If we have deleted all references to this request, then we can + // destroy the object. It is okay to have already released the spin + // lock at this point because there is no possible way that another + // stream of execution has access to the request any longer. + // + + if (result == 1) { + + PIRP Irp = IRP_RECEIVE_IRP(IrpSp); + + ExInterlockedInsertTailList( + &(IRP_DEVICE_CONTEXT(IrpSp)->IrpCompletionQueue), + &Irp->Tail.Overlay.ListEntry, + &(IRP_DEVICE_CONTEXT(IrpSp)->Interlock)); + + } + +} /* NbfDerefReceiveIrpLocked */ +#endif + + +VOID +NbfCompleteReceiveIrp( + IN PIRP Irp, + IN NTSTATUS Status, + IN ULONG Information + ) + +/*++ + +Routine Description: + + This routine completes a transport receive IRP. + + NOTE: THIS ROUTINE MUST BE CALLED WITH THE CONNECTION SPINLOCK + HELD. + +Arguments: + + Irp - Pointer to a receive IRP. + + Status - Actual return status to be assigned to the request. This + value may be overridden if the timed-out bitflag is set in the request. + + Information - the information field for the I/O Status Block. + +Return Value: + + none. + +--*/ + +{ + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + PTP_CONNECTION Connection; + + ASSERT (Status != STATUS_PENDING); + + Connection = IRP_RECEIVE_CONNECTION(IrpSp); + + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint2 ("NbfCompleteReceiveIrp: Entered IRP %lx, connection %lx\n", + Irp, Connection); + } + + Irp->IoStatus.Status = Status; + Irp->IoStatus.Information = Information; + + NbfDereferenceReceiveIrpLocked ("Complete", IrpSp, RREF_CREATION); // remove creation reference. + +} /* NbfCompleteReceiveIrp */ diff --git a/private/ntos/tdi/nbf/send.c b/private/ntos/tdi/nbf/send.c new file mode 100644 index 000000000..a8a6cc7f3 --- /dev/null +++ b/private/ntos/tdi/nbf/send.c @@ -0,0 +1,503 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + send.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiSend + o TdiSendDatagram + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + +NTSTATUS +NbfTdiSend( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiSend request for the transport provider. + + NOTE: THIS FUNCTION MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql, cancelIrql; + PTP_CONNECTION connection; + PIO_STACK_LOCATION irpSp; + PTDI_REQUEST_KERNEL_SEND parameters; + PIRP TempIrp; + + // + // Determine which connection this send belongs on. + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + connection = irpSp->FileObject->FsContext; + + // + // Check that this is really a connection. + // + + if ((connection->Size != sizeof (TP_CONNECTION)) || + (connection->Type != NBF_CONNECTION_SIGNATURE)) { +#if DBG + NbfPrint2 ("TdiSend: Invalid Connection %lx Irp %lx\n", connection, Irp); +#endif + return STATUS_INVALID_CONNECTION; + } + +#if DBG + Irp->IoStatus.Information = 0; // initialize it. + Irp->IoStatus.Status = 0x01010101; // initialize it. +#endif + + // + // Interpret send options. + // + +#if DBG + parameters = (PTDI_REQUEST_KERNEL_SEND)(&irpSp->Parameters); + if ((parameters->SendFlags & TDI_SEND_PARTIAL) != 0) { + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint0 ("NbfTdiSend: TDI_END_OF_RECORD not found.\n"); + } + } +#endif + + // + // Now we have a reference on the connection object. Queue up this + // send to the connection object. + // + + // + // We would normally add a connection reference of type + // CREF_SEND_IRP, however we delay doing this until we + // know we are not going to call PacketizeSend with the + // second parameter TRUE. If we do call that it assumes + // we have not added the reference. + // + + IRP_SEND_IRP(irpSp) = Irp; + IRP_SEND_REFCOUNT(irpSp) = 1; + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + + ACQUIRE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + if ((connection->Flags & CONNECTION_FLAGS_READY) == 0) { + + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + Irp->IoStatus.Status = connection->Status; + Irp->IoStatus.Information = 0; + + NbfDereferenceSendIrp ("Complete", irpSp, RREF_CREATION); // remove creation reference. + + } else { + + // + // Once the reference is in, LinkSpinLock will stay valid. + // + + NbfReferenceConnection ("Verify Temp Use", connection, CREF_BY_ID); + RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock); + + IoAcquireCancelSpinLock(&cancelIrql); + ACQUIRE_DPC_SPIN_LOCK (connection->LinkSpinLock); + +#if DBG + NbfSends[NbfSendsNext].Irp = Irp; + NbfSends[NbfSendsNext].Request = NULL; + NbfSends[NbfSendsNext].Connection = (PVOID)connection; + { + ULONG i,j; + PUCHAR va; + PMDL mdl; + + mdl = Irp->MdlAddress; + if (parameters->SendLength > TRACK_TDI_CAPTURE) { + NbfSends[NbfSendsNext].Contents[0] = 0xFF; + } else { + NbfSends[NbfSendsNext].Contents[0] = (UCHAR)parameters->SendLength; + } + + i = 1; + while (i < TRACK_TDI_CAPTURE) { + if (mdl == NULL) break; + for ( va = MmGetSystemAddressForMdl (mdl), + j = MmGetMdlByteCount (mdl); + (i < TRACK_TDI_CAPTURE) && (j > 0); + i++, j-- ) { + NbfSends[NbfSendsNext].Contents[i] = *va++; + } + mdl = mdl->Next; + } + } + + NbfSendsNext++; + if (NbfSendsNext >= TRACK_TDI_LIMIT) NbfSendsNext = 0; +#endif + + // + // If this IRP has been cancelled already, complete it now. + // + + if (Irp->Cancel) { + +#if DBG + NbfCompletedSends[NbfCompletedSendsNext].Irp = Irp; + NbfCompletedSends[NbfCompletedSendsNext].Status = STATUS_CANCELLED; + NbfCompletedSendsNext = (NbfCompletedSendsNext++) % TRACK_TDI_LIMIT; +#endif + + NbfReferenceConnection("TdiSend cancelled", connection, CREF_SEND_IRP); + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + IoReleaseCancelSpinLock(cancelIrql); + + NbfCompleteSendIrp (Irp, STATUS_CANCELLED, 0); + KeLowerIrql (oldirql); + + NbfDereferenceConnection ("IRP cancelled", connection, CREF_BY_ID); // release lookup hold. + return STATUS_PENDING; + } + + // + // Insert onto the send queue, and make the IRP + // cancellable. + // + + InsertTailList (&connection->SendQueue,&Irp->Tail.Overlay.ListEntry); + IoSetCancelRoutine(Irp, NbfCancelSend); + + // + // Release the cancel spinlock out of order. We were at DPC level + // when we acquired both the cancel and link spinlocks, so the irqls + // don't need to be swapped. + // + ASSERT(cancelIrql == DISPATCH_LEVEL); + IoReleaseCancelSpinLock(cancelIrql); + + // + // If this connection is waiting for an EOR to appear because a non-EOR + // send failed at some point in the past, fail this send. Clear the + // flag that causes this if this request has the EOR set. + // + // BUGBUG: Should the FailSend status be clearer here? + // + + if ((connection->Flags & CONNECTION_FLAGS_FAILING_TO_EOR) != 0) { + + NbfReferenceConnection("TdiSend failing to EOR", connection, CREF_SEND_IRP); + + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + + // + // BUGBUG: Should we save status from real failure? + // + + FailSend (connection, STATUS_LINK_FAILED, TRUE); + + parameters = (PTDI_REQUEST_KERNEL_SEND)(&irpSp->Parameters); + if ( (parameters->SendFlags & TDI_SEND_PARTIAL) == 0) { + connection->Flags &= ~CONNECTION_FLAGS_FAILING_TO_EOR; + } + + KeLowerIrql (oldirql); + + NbfDereferenceConnection ("Failing to EOR", connection, CREF_BY_ID); // release lookup hold. + return STATUS_PENDING; + } + + + // + // If the send state is either IDLE or W_EOR, then we should + // begin packetizing this send. Otherwise, some other event + // will cause it to be packetized. + // + + // + // NOTE: If we call StartPacketizingConnection, we make + // sure that it is the last operation we do on this + // connection. This allows us to "hand off" the reference + // we have to that function, which converts it into + // a reference for being on the packetize queue. + // + +// NbfPrint2 ("TdiSend: Sending, connection %lx send state %lx\n", +// connection, connection->SendState); + + switch (connection->SendState) { + + case CONNECTION_SENDSTATE_IDLE: + + InitializeSend (connection); // sets state to PACKETIZE + + // + // If we can, packetize right now. + // + + if (!(connection->Flags & CONNECTION_FLAGS_PACKETIZE)) { + + ASSERT (!(connection->Flags2 & CONNECTION_FLAGS2_STOPPING)); + connection->Flags |= CONNECTION_FLAGS_PACKETIZE; + +#if DBG + NbfReferenceConnection ("Packetize", connection, CREF_PACKETIZE_QUEUE); + NbfDereferenceConnection("temp TdiSend", connection, CREF_BY_ID); +#endif + + // + // This releases the spinlock. Note that PacketizeSend + // assumes that the current SendIrp has a reference + // of type RREF_PACKET; + // + +#if DBG + NbfReferenceSendIrp ("Packetize", irpSp, RREF_PACKET); +#else + ++IRP_SEND_REFCOUNT(irpSp); // OK since it was just queued. +#endif + PacketizeSend (connection, TRUE); + + } else { + +#if DBG + NbfReferenceConnection("TdiSend packetizing", connection, CREF_SEND_IRP); + NbfDereferenceConnection ("Stopping or already packetizing", connection, CREF_BY_ID); // release lookup hold. +#endif + + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + + } + + break; + + case CONNECTION_SENDSTATE_W_EOR: + connection->SendState = CONNECTION_SENDSTATE_PACKETIZE; + + // + // Adjust the send variables on the connection so that + // they correctly point to this new send. We can't call + // InitializeSend to do that, because we need to keep + // track of the other outstanding sends on this connection + // which have been sent but are a part of this message. + // + + TempIrp = CONTAINING_RECORD( + connection->SendQueue.Flink, + IRP, + Tail.Overlay.ListEntry); + + connection->sp.CurrentSendIrp = TempIrp; + connection->sp.CurrentSendMdl = TempIrp->MdlAddress; + connection->sp.SendByteOffset = 0; + connection->CurrentSendLength += + IRP_SEND_LENGTH(IoGetCurrentIrpStackLocation(TempIrp)); + + // + // StartPacketizingConnection removes the CREF_BY_ID + // reference. + // + + NbfReferenceConnection("TdiSend W_EOR", connection, CREF_SEND_IRP); + + StartPacketizingConnection (connection, TRUE); + break; + + default: +// NbfPrint2 ("TdiSend: Sending, unknown state! connection %lx send state %lx\n", +// connection, connection->SendState); + // + // The connection is in another state (such as + // W_ACK or W_LINK), we just need to make sure + // to call InitializeSend if the new one is + // the first one on the list. + // + + // + // BUGBUG: Currently InitializeSend sets SendState, + // we should fix this. + // + + if (connection->SendQueue.Flink == &Irp->Tail.Overlay.ListEntry) { + ULONG SavedSendState; + SavedSendState = connection->SendState; + InitializeSend (connection); + connection->SendState = SavedSendState; + } + +#if DBG + NbfReferenceConnection("TdiSend other", connection, CREF_SEND_IRP); + NbfDereferenceConnection("temp TdiSend", connection, CREF_BY_ID); +#endif + + RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); + + } + + } + + KeLowerIrql (oldirql); + return STATUS_PENDING; + +} /* TdiSend */ + + +NTSTATUS +NbfTdiSendDatagram( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiSendDatagram request for the transport + provider. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + KIRQL oldirql; + PTP_ADDRESS_FILE addressFile; + PTP_ADDRESS address; + PIO_STACK_LOCATION irpSp; + PTDI_REQUEST_KERNEL_SENDDG parameters; + UINT MaxUserData; + + irpSp = IoGetCurrentIrpStackLocation (Irp); + addressFile = irpSp->FileObject->FsContext; + + status = NbfVerifyAddressObject (addressFile); + if (!NT_SUCCESS (status)) { + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint2 ("TdiSendDG: Invalid address %lx Irp %lx\n", + addressFile, Irp); + } + return status; + } + + address = addressFile->Address; + parameters = (PTDI_REQUEST_KERNEL_SENDDG)(&irpSp->Parameters); + + // + // Check that the length is short enough. + // + + MacReturnMaxDataSize( + &address->Provider->MacInfo, + NULL, + 0, + address->Provider->MaxSendPacketSize, + FALSE, + &MaxUserData); + + if (parameters->SendLength > + (MaxUserData - sizeof(DLC_FRAME) - sizeof(NBF_HDR_CONNECTIONLESS))) { + + NbfDereferenceAddress("tmp send datagram", address, AREF_VERIFY); + return STATUS_INVALID_PARAMETER; + + } + + // + // If we are on a disconnected RAS link, then fail the datagram + // immediately. + // + + if ((address->Provider->MacInfo.MediumAsync) && + (!address->Provider->MediumSpeedAccurate)) { + + NbfDereferenceAddress("tmp send datagram", address, AREF_VERIFY); + return STATUS_DEVICE_NOT_READY; + } + + // + // Check that the target address includes a Netbios component. + // + + if (!(NbfValidateTdiAddress( + parameters->SendDatagramInformation->RemoteAddress, + parameters->SendDatagramInformation->RemoteAddressLength)) || + (NbfParseTdiAddress(parameters->SendDatagramInformation->RemoteAddress, TRUE) == NULL)) { + + NbfDereferenceAddress("tmp send datagram", address, AREF_VERIFY); + return STATUS_BAD_NETWORK_PATH; + } + + ACQUIRE_SPIN_LOCK (&address->SpinLock,&oldirql); + + if ((address->Flags & (ADDRESS_FLAGS_STOPPING | ADDRESS_FLAGS_CONFLICT)) != 0) { + + RELEASE_SPIN_LOCK (&address->SpinLock,oldirql); + Irp->IoStatus.Information = 0; + Irp->IoStatus.Status = (address->Flags & ADDRESS_FLAGS_STOPPING) ? + STATUS_NETWORK_NAME_DELETED : STATUS_DUPLICATE_NAME; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + } else { + + NbfReferenceAddress ("Send datagram", address, AREF_REQUEST); + Irp->IoStatus.Information = parameters->SendLength; + InsertTailList ( + &address->SendDatagramQueue, + &Irp->Tail.Overlay.ListEntry); + RELEASE_SPIN_LOCK (&address->SpinLock,oldirql); + + // + // The request is queued. Ship the next request at the head of the queue, + // provided the completion handler is not active. We serialize this so + // that only one MDL and NBF datagram header needs to be statically + // allocated for reuse by all send datagram requests. + // + + (VOID)NbfSendDatagramsOnAddress (address); + + } + + NbfDereferenceAddress("tmp send datagram", address, AREF_VERIFY); + + return STATUS_PENDING; + +} /* NbfTdiSendDatagram */ diff --git a/private/ntos/tdi/nbf/sendeng.c b/private/ntos/tdi/nbf/sendeng.c new file mode 100644 index 000000000..1b6556581 --- /dev/null +++ b/private/ntos/tdi/nbf/sendeng.c @@ -0,0 +1,3657 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + sendeng.c + +Abstract: + + This module contains code that implements the send engine for the + Jetbeui transport provider. This code is responsible for the following + basic activities, including some subordinate glue. + + 1. Packetizing TdiSend requests already queued up on a TP_CONNECTION + object, using I-frame packets acquired from the PACKET.C module, + and turning them into shippable packets and placing them on the + TP_LINK's WackQ. In the process of doing this, the packets are + actually submitted as I/O requests to the Physical Provider, in + the form of PdiSend requests. + + 2. Retiring packets queued to a TP_LINK's WackQ and returning them to + the device context's pool for use by other links. In the process + of retiring acked packets, step 1 may be reactivated. + + 3. Resending packets queued to a TP_LINK's WackQ because of a reject + condition on the link. This involves no state update in the + TP_CONNECTION object. + + 4. Handling of Send completion events from the Physical Provider, + to allow proper synchronization of the reuse of packets. + + 5. Completion of TdiSend requests. This is triggered by the receipt + (in IFRAMES.C) of a DataAck frame, or by a combination of other + frames when the proper protocol has been negotiated. One routine + in this routine is responsible for the actual mechanics of TdiSend + request completion. + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#if DBG +extern ULONG NbfSendsIssued; +extern ULONG NbfSendsCompletedInline; +extern ULONG NbfSendsCompletedOk; +extern ULONG NbfSendsCompletedFail; +extern ULONG NbfSendsPended; +extern ULONG NbfSendsCompletedAfterPendOk; +extern ULONG NbfSendsCompletedAfterPendFail; +#endif + + +// +// Temporary variables to control piggyback ack usage. +// +#define NbfUsePiggybackAcks 1 +#if DBG +ULONG NbfDebugPiggybackAcks = 0; +#endif + + +#if DBG +// +// *** This is the original version of StartPacketizingConnection, which +// is now a macro on the free build. It has been left here as the +// fully-commented version of the code. +// + +VOID +StartPacketizingConnection( + PTP_CONNECTION Connection, + IN BOOLEAN Immediate + ) + +/*++ + +Routine Description: + + This routine is called to place a connection on the PacketizeQueue + of its device context object. Then this routine starts packetizing + the first connection on that queue. + + *** The Connection LinkSpinLock must be held on entry to this routine. + + *** THIS FUNCTION MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + + Immediate - TRUE if the connection should be packetized + immediately; FALSE if the connection should be queued + up for later packetizing (implies that ReceiveComplete + will be called in the future, which packetizes always). + + NOTE: If this is TRUE, it also implies that we have + a connection reference of type CREF_BY_ID which we + will "convert" into the CREF_PACKETIZE_QUEUE one. + +Return Value: + + none. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint1 ("StartPacketizingConnection: Entered for connection %lx.\n", + Connection); + } + + DeviceContext = Connection->Provider; + + // + // If this connection's SendState is set to PACKETIZE and if + // we are not already on the PacketizeQueue, then go ahead and + // append us to the end of that queue, and remember that we're + // on it by setting the CONNECTION_FLAGS_PACKETIZE bitflag. + // + // Also don't queue it if the connection is stopping. + // + + if ((Connection->SendState == CONNECTION_SENDSTATE_PACKETIZE) && + !(Connection->Flags & CONNECTION_FLAGS_PACKETIZE) && + (Connection->Flags & CONNECTION_FLAGS_READY)) { + + ASSERT (!(Connection->Flags2 & CONNECTION_FLAGS2_STOPPING)); + + Connection->Flags |= CONNECTION_FLAGS_PACKETIZE; + + if (!Immediate) { + NbfReferenceConnection ("Packetize", Connection, CREF_PACKETIZE_QUEUE); + } else { +#if DBG + NbfReferenceConnection ("Packetize", Connection, CREF_PACKETIZE_QUEUE); + NbfDereferenceConnection("temp TdiSend", Connection, CREF_BY_ID); +#endif + } + + ExInterlockedInsertTailList( + &DeviceContext->PacketizeQueue, + &Connection->PacketizeLinkage, + &DeviceContext->SpinLock); + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + } else { + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + if (Immediate) { + NbfDereferenceConnection("temp TdiSend", Connection, CREF_BY_ID); + } + } + + if (Immediate) { + PacketizeConnections (DeviceContext); + } + +} /* StartPacketizingConnection */ +#endif + + +VOID +PacketizeConnections( + PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + + This routine attempts to packetize all connections waiting on the + PacketizeQueue of the DeviceContext. + + +Arguments: + + DeviceContext - Pointer to a DEVICE_CONTEXT object. + +Return Value: + + none. + +--*/ + +{ + PLIST_ENTRY p; + PTP_CONNECTION Connection; + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint1 ("PacketizeConnections: Entered for device context %lx.\n", + DeviceContext); + } + + // + // Pick connections off of the device context's packetization queue + // until there are no more left to pick off. For each one, we call + // PacketizeSend. Note this routine can be executed concurrently + // on multiple processors and it doesn't matter; multiple connections + // may be packetized concurrently. + // + + while (TRUE) { + + p = ExInterlockedRemoveHeadList( + &DeviceContext->PacketizeQueue, + &DeviceContext->SpinLock); + + if (p == NULL) { + break; + } + Connection = CONTAINING_RECORD (p, TP_CONNECTION, PacketizeLinkage); + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + if (Connection->SendState != CONNECTION_SENDSTATE_PACKETIZE) { + Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE; + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + NbfDereferenceConnection ("No longer packetizing", Connection, CREF_PACKETIZE_QUEUE); + } else { + NbfReferenceSendIrp ("Packetize", IoGetCurrentIrpStackLocation(Connection->sp.CurrentSendIrp), RREF_PACKET); + PacketizeSend (Connection, FALSE); // releases the lock. + } + } + +} /* PacketizeConnections */ + + +VOID +PacketizeSend( + IN PTP_CONNECTION Connection, + IN BOOLEAN Direct + ) + +/*++ + +Routine Description: + + This routine packetizes the current TdiSend request on the specified + connection as much as limits will permit. A given here is that there + is an active send on the connection that needs further packetization. + + NOTE: This routine is called with the connection spinlock held and + returns with it released. THIS FUNCTION MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + + Direct - TRUE if we are called from TdiSend. This implies that + the connection does not have a reference of type CREF_SEND_IRP, + which we need to add before we leave. + +Return Value: + + none. + +--*/ + +{ + ULONG MaxFrameSize, FrameSize; + ULONG PacketBytes; + PNDIS_BUFFER PacketDescriptor; + PDEVICE_CONTEXT DeviceContext; + PTP_PACKET Packet; + NTSTATUS Status; + PNBF_HDR_CONNECTION NbfHeader; + BOOLEAN LinkCheckpoint; + BOOLEAN SentPacket = FALSE; + BOOLEAN ExitAfterSendOnePacket = FALSE; + PIO_STACK_LOCATION IrpSp; + ULONG LastPacketLength; + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint1 ("PacketizeSend: Entered for connection %lx.\n", Connection); + } + + DeviceContext = Connection->Provider; + + ASSERT (Connection->SendState == CONNECTION_SENDSTATE_PACKETIZE); + + // + // Just loop until one of three events happens: (1) we run out of + // packets from NbfCreatePacket, (2) we completely packetize the send, + // or (3) we can't send any more packets because SendOnePacket failed. + // + +#if DBG + + // + // Convert the queue reference into a packetize one. It is OK + // to do this with the lock held because we know that the refcount + // must already be at least one, so we don't drop to zero. + // + + NbfReferenceConnection ("PacketizeSend", Connection, CREF_PACKETIZE); + NbfDereferenceConnection ("Off packetize queue", Connection, CREF_PACKETIZE_QUEUE); +#endif + + MaxFrameSize = Connection->MaximumDataSize; + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint1 ("PacketizeSend: MaxFrameSize for user data=%ld.\n", MaxFrameSize); + } + + + // + // It is possible for a frame to arrive during the middle of this loop + // (such as a NO_RECEIVE) that will put us into a new state (such as + // W_RCVCONT). For this reason, we have to check the state every time + // (at the end of the loop). + // + + do { + + if (!NT_SUCCESS (NbfCreatePacket (DeviceContext, Connection->Link, &Packet))) { + + // + // We need a packet to finish packetizing the current send, but + // there are no more packets available in the pool right now. + // Set our send state to W_PACKET, and put this connection on + // the PacketWaitQueue of the device context object. Then, + // when NbfDestroyPacket frees up a packet, it will check this + // queue for starved connections, and if it finds one, it will + // take a connection off the list and set its send state to + // SENDSTATE_PACKETIZE and put it on the PacketizeQueue. + // + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint0 ("PacketizeSend: NbfCreatePacket failed.\n"); + } + Connection->SendState = CONNECTION_SENDSTATE_W_PACKET; + + // + // Clear the PACKETIZE flag, indicating that we're no longer + // on the PacketizeQueue or actively packetizing. The flag + // was set by StartPacketizingConnection to indicate that + // the connection was already on the PacketizeQueue. + // + // Don't queue him if the connection is stopping. + // + + Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE; + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); +#if DBG + if (Connection->Flags2 & CONNECTION_FLAGS2_STOPPING) { + DbgPrint ("NBF: BUGBUG! Trying to PacketWait stopping connection %lx\n", Connection); + DbgBreakPoint(); + } +#endif + Connection->Flags |= CONNECTION_FLAGS_W_PACKETIZE; + if (!Connection->OnPacketWaitQueue) { + Connection->OnPacketWaitQueue = TRUE; + InsertTailList( + &DeviceContext->PacketWaitQueue, + &Connection->PacketWaitLinkage); + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + if (!SentPacket) { + NbfDereferenceSendIrp ("No packet", IoGetCurrentIrpStackLocation(Connection->sp.CurrentSendIrp), RREF_PACKET); + } + if (Direct) { + NbfReferenceConnection ("Delayed request ref", Connection, CREF_SEND_IRP); + } + + NbfDereferenceConnection ("No packet", Connection, CREF_PACKETIZE); + return; + + } + + // + // Set the length of the packet now, while only the + // header is attached. + // + + NbfSetNdisPacketLength( + Packet->NdisPacket, + Connection->Link->HeaderLength + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION)); + + // Add a reference count to the request, and keep track of + // which request it is. We rely on NbfDestroyPacket to + // remove the reference. + + IrpSp = IoGetCurrentIrpStackLocation(Connection->sp.CurrentSendIrp); + + Packet->Owner = IrpSp; + // Packet->Action = PACKET_ACTION_IRP_SP; + IF_NBFDBG (NBF_DEBUG_REQUEST) { + NbfPrint2 ("PacketizeSend: Packet %x ref IrpSp %x.\n", Packet, Packet->Owner); + } + + // + // For performance reasons, the first time through here on + // a direct call, we have a IrpSp reference already. + // + + if (SentPacket) { + NbfReferenceSendIrp ("Packetize", IrpSp, RREF_PACKET); + } + + // + // Now build a DATA_ONLY_LAST header in this frame. If it + // turns out we need a DFM, we change it. The header we copy + // already has ResponseCorrelator set to our current correlator + // and TransmitCorrelator set to the last one we received from + // him (if we do not piggyback an ack, then we zero out + // TransmitCorrelator). + // + + NbfHeader = (PNBF_HDR_CONNECTION)&(Packet->Header[Connection->Link->HeaderLength + sizeof(DLC_I_FRAME)]); + *(NBF_HDR_CONNECTION UNALIGNED *)NbfHeader = Connection->NetbiosHeader; + + ASSERT (RESPONSE_CORR(NbfHeader) != 0); + + // + // Determine if we need the resynch bit here. + // + + if (Connection->Flags & CONNECTION_FLAGS_RESYNCHING) { + + NbfHeader->Data2Low = 1; + Connection->Flags &= ~CONNECTION_FLAGS_RESYNCHING; + + } else { + + NbfHeader->Data2Low = 0; + + } + + + // + // build an NDIS_BUFFER chain that describes the buffer we're using, and + // thread it off the NdisBuffer. This chain may not complete the + // packet, as the remaining part of the MDL chain may be shorter than + // the packet. + // + + FrameSize = MaxFrameSize; + + // + // Check if we have less than FrameSize left to send. + // + + if (Connection->sp.MessageBytesSent + FrameSize > Connection->CurrentSendLength) { + + FrameSize = Connection->CurrentSendLength - Connection->sp.MessageBytesSent; + + } + + + // + // Make a copy of the MDL chain for this send, unless + // there are zero bytes left. + // + + if (FrameSize != 0) { + + // + // If the whole send will fit inside one packet, + // then there is no need to duplicate the MDL + // (note that this may include multi-MDL sends). + // + + if ((Connection->sp.SendByteOffset == 0) && + (Connection->CurrentSendLength == FrameSize)) { + + PacketDescriptor = (PNDIS_BUFFER)Connection->sp.CurrentSendMdl; + PacketBytes = FrameSize; + Connection->sp.CurrentSendMdl = NULL; + Connection->sp.SendByteOffset = FrameSize; + Packet->PacketNoNdisBuffer = TRUE; + + } else { + + Status = BuildBufferChainFromMdlChain ( + DeviceContext, + Connection->sp.CurrentSendMdl, + Connection->sp.SendByteOffset, + FrameSize, + &PacketDescriptor, + &Connection->sp.CurrentSendMdl, + &Connection->sp.SendByteOffset, + &PacketBytes); + + if (!NT_SUCCESS(Status)) { + + if (NbfHeader->Data2Low) { + Connection->Flags |= CONNECTION_FLAGS_RESYNCHING; + } + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + NbfDereferencePacket (Packet); // remove creation hold. + goto BufferChainFailure; + } + + } + + // + // Chain the buffers to the packet, unless there + // are zero bytes of data. + // + + Connection->sp.MessageBytesSent += PacketBytes; + NdisChainBufferAtBack (Packet->NdisPacket, PacketDescriptor); + + } else { + + PacketBytes = 0; + Connection->sp.CurrentSendMdl = NULL; + + } + + { + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + {PNDIS_BUFFER NdisBuffer; + NdisQueryPacket(Packet->NdisPacket, NULL, NULL, &NdisBuffer, NULL); + NbfPrint1 ("PacketizeSend: NDIS_BUFFER Built, chain is: %lx is Packet->Head\n", NdisBuffer); + NdisGetNextBuffer (NdisBuffer, &NdisBuffer); + while (NdisBuffer != NULL) { + NbfPrint1 (" %lx is Next\n", + NdisBuffer); + NdisGetNextBuffer (NdisBuffer, &NdisBuffer); + }} + } + + // + // Have we run out of Mdl Chain in this request? + // + +#if DBG + if (PacketBytes < FrameSize) { + ASSERT (Connection->sp.CurrentSendMdl == NULL); + } +#endif + + if ((Connection->sp.CurrentSendMdl == NULL) || + (Connection->CurrentSendLength <= Connection->sp.MessageBytesSent)) { + + // + // Yep. We know that we've exhausted the current request's buffer + // here, so see if there's another request without EOF set that we + // can build start throwing into this packet. + // + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint0 ("PacketizeSend: Used up entire request.\n"); + } + + if (!(IRP_SEND_FLAGS(IrpSp) & TDI_SEND_PARTIAL)) { + + // + // We are sending the last packet in a message. Change + // the packet type and indicate in the connection object's + // send state that we are waiting for a DATA_ACK NetBIOS- + // level acknowlegement. + // + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint0 ("PacketizeSend: Request has EOR, making pkt a DOL.\n"); + } + + // + // Keep track of how many consecutive sends we have done. + // + + Connection->ConsecutiveSends++; + Connection->ConsecutiveReceives = 0; + + // + // Change it to a DOL with piggyback ack allowed if wanted. + // + + ASSERT (NbfHeader->Command == NBF_CMD_DATA_ONLY_LAST); + if (!(IRP_SEND_FLAGS(IrpSp) & + TDI_SEND_NO_RESPONSE_EXPECTED) && + (Connection->ConsecutiveSends < 2)) { + if (NbfUsePiggybackAcks) { + NbfHeader->Data1 |= DOL_OPTIONS_ACK_W_DATA_ALLOWED; + } + } + + Connection->SendState = CONNECTION_SENDSTATE_W_ACK; + Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE; + ExitAfterSendOnePacket = TRUE; + + } else { + + // + // We are sending the last packet in this request. If there + // are more requests in the connection's SendQueue, then + // advance complex send pointer to point to the next one + // in line. Otherwise, if there aren't any more requests + // ready to packetize, then we enter the W_EOR state and + // stop packetizing. Note that we're waiting here for the TDI + // client to come up with data to send; we're just hanging out + // until then. + // + // DGB: Note that this will allow the last packet in the + // request to be smaller than the max packet length. This + // is not addressed anywhere that I can find in the NBF + // spec, and will be interesting to test against a non-NT + // NBF protocol. + // + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint0 ("PacketizeSend: Request doesn't have EOR.\n"); + } + + NbfHeader->Command = NBF_CMD_DATA_FIRST_MIDDLE; + + if (Connection->sp.CurrentSendIrp->Tail.Overlay.ListEntry.Flink == &Connection->SendQueue) { + + Connection->SendState = CONNECTION_SENDSTATE_W_EOR; + Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE; + ExitAfterSendOnePacket = TRUE; + + } else { + + Connection->sp.CurrentSendIrp = + CONTAINING_RECORD ( + Connection->sp.CurrentSendIrp->Tail.Overlay.ListEntry.Flink, + IRP, + Tail.Overlay.ListEntry); + Connection->sp.CurrentSendMdl = + Connection->sp.CurrentSendIrp->MdlAddress; + Connection->sp.SendByteOffset = 0; + Connection->CurrentSendLength += + IRP_SEND_LENGTH(IoGetCurrentIrpStackLocation(Connection->sp.CurrentSendIrp)); + } + } + + } else { + + NbfHeader->Command = NBF_CMD_DATA_FIRST_MIDDLE; + + } + + // + // Before we release the spinlock, see if we want to + // piggyback an ack on here. + // + + if ((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_ACK) != 0) { + + // + // Turn off the flags. We don't take it off the queue, + // that will be handled by the timer function. + // + + Connection->DeferredFlags &= + ~(CONNECTION_FLAGS_DEFERRED_ACK | CONNECTION_FLAGS_DEFERRED_NOT_Q); + + ASSERT (DOL_OPTIONS_ACK_INCLUDED == DFM_OPTIONS_ACK_INCLUDED); + +#if DBG + if (NbfDebugPiggybackAcks) { + NbfPrint0("A"); + } +#endif + + // + // TRANSMIT_CORR(NbfHeader) is already set correctly. + // + + NbfHeader->Data1 |= DOL_OPTIONS_ACK_INCLUDED; + + } else { + + TRANSMIT_CORR(NbfHeader) = (USHORT)0; + + } + + // + // To prevent a send "crossing" the receive and + // causing a bogus piggyback ack timeout (this + // only matters if a receive indication is in + // progress). + // + + Connection->CurrentReceiveAckQueueable = FALSE; + + SentPacket = TRUE; + LastPacketLength = + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION) + PacketBytes; + + MacModifyHeader( + &DeviceContext->MacInfo, + Packet->Header, + LastPacketLength); + + Packet->NdisIFrameLength = LastPacketLength; + + ASSERT (Connection->LinkSpinLock == &Connection->Link->SpinLock); + + Status = SendOnePacket (Connection, Packet, FALSE, &LinkCheckpoint); + + if (Status == STATUS_LINK_FAILED) { + + // + // If SendOnePacket failed due to the link being + // dead, then we tear down the link. + // + + FailSend (Connection, STATUS_LINK_FAILED, TRUE); // fail the send + NbfDereferencePacket (Packet); // remove creation hold. + if (Direct) { + NbfReferenceConnection ("Delayed request ref", Connection, CREF_SEND_IRP); + } + NbfDereferenceConnection ("Send failed", Connection, CREF_PACKETIZE); + + return; + + } else { + + // + // SendOnePacket returned success, so update our counters; + // + + DeviceContext->TempIFrameBytesSent += PacketBytes; + ++DeviceContext->TempIFramesSent; + + if ((Status == STATUS_SUCCESS) && LinkCheckpoint) { + + // + // We are checkpointing; this means that SendOnePacket + // will already have set the state to W_LINK and turned + // off the PACKETIZE flag, so we should leave. When + // the checkpoint response is received, we will + // resume packetizing. We don't have to worry about + // doing all the other recovery stuff (resetting + // the piggyback ack flag, complex send pointer, etc.) + // because the send did in fact succeed. + // + + if (Direct) { +#if DBG + NbfReferenceConnection ("Delayed request ref", Connection, CREF_SEND_IRP); + NbfDereferenceConnection ("Link checkpoint", Connection, CREF_PACKETIZE); +#endif + } else { + NbfDereferenceConnection ("Link checkpoint", Connection, CREF_PACKETIZE); + } + return; + + } else if (ExitAfterSendOnePacket || + (Status == STATUS_MORE_PROCESSING_REQUIRED)) { + + if (Direct) { +#if DBG + NbfReferenceConnection ("Delayed request ref", Connection, CREF_SEND_IRP); + NbfDereferenceConnection ("Packetize done", Connection, CREF_PACKETIZE); +#endif + } else { + NbfDereferenceConnection ("Packetize done", Connection, CREF_PACKETIZE); + } + return; + + } + } + } + +BufferChainFailure:; + + // + // Note that we may have fallen out of the BuildBuffer... if above with + // Status set to STATUS_INSUFFICIENT_RESOURCES. if we have, we'll just + // stick this connection back onto the packetize queue and hope the + // system gets more resources later. + // + + + if (!NT_SUCCESS (Status)) { + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint0 ("PacketizeSend: SendOnePacket failed.\n"); + } + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + // + // Indicate we're waiting on favorable link conditions. + // + + Connection->SendState = CONNECTION_SENDSTATE_W_LINK; + + // + // Clear the PACKETIZE flag, indicating that we're no longer + // on the PacketizeQueue or actively packetizing. The flag + // was set by StartPacketizingConnection to indicate that + // the connection was already on the PacketizeQueue. + // + + Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE; + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + // + // If we are exiting and we sent a packet without + // polling, we need to start T1. + // + + if (Direct) { + + // + // We have to do the CREF_SEND_IRP reference that is missing. + // + +#if DBG + NbfReferenceConnection("TdiSend", Connection, CREF_SEND_IRP); + NbfDereferenceConnection ("Send failed", Connection, CREF_PACKETIZE); +#endif + } else { + NbfDereferenceConnection ("Send failed", Connection, CREF_PACKETIZE); + } + + return; + } + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + // + // It is probable that a NetBIOS frame arrived while we released + // the connection's spin lock, so our state has probably changed. + // When we cycle around this loop again, we will have the lock + // again, so we can test the connection's send state. + // + + } while (Connection->SendState == CONNECTION_SENDSTATE_PACKETIZE); + + // + // Clear the PACKETIZE flag, indicating that we're no longer on the + // PacketizeQueue or actively packetizing. The flag was set by + // StartPacketizingConnection to indicate that the connection was + // already on the PacketizeQueue. + // + + Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE; + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + + if (Direct) { +#if DBG + NbfReferenceConnection ("Delayed request ref", Connection, CREF_SEND_IRP); + NbfDereferenceConnection ("PacketizeSend done", Connection, CREF_PACKETIZE); +#endif + } else { + NbfDereferenceConnection ("PacketizeSend done", Connection, CREF_PACKETIZE); + } + +} /* PacketizeSend */ + + +VOID +CompleteSend( + PTP_CONNECTION Connection, + IN USHORT Correlator + ) + +/*++ + +Routine Description: + + This routine is called because the connection partner acknowleged + an entire message at the NetBIOS Frames Protocol level, either through + a DATA_ACK response, or a RECEIVE_OUTSTANDING, or RECEIVE_CONTINUE, + or NO_RECEIVE response where the number of bytes specified exactly + matches the number of bytes sent in the message. Here we retire all + of the TdiSends on the connection's SendQueue up to and including the + one with the TDI_END_OF_RECORD bitflag set. For each request, we + complete the I/O. + + NOTE: This function is called with the connection spinlock + held and returns with it held, but it may release it in the + middle. THIS FUNCTION MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + + Correlator - The correlator in the DATA_ACK or piggybacked ack. + + OldIrqlP - Returns the IRQL at which the connection spinlock + was acquired. + +Return Value: + + none. + +--*/ + +{ + PIRP Irp; + PIO_STACK_LOCATION IrpSp; + PLIST_ENTRY p; + BOOLEAN EndOfRecord; + KIRQL cancelIrql; + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint1 ("CompleteSend: Entered for connection %lx.\n", Connection); + } + + + // + // Make sure that the correlator is the expect one, and + // that we are in a good state (don't worry about locking + // since this is an unusual case anyway). + // + + if (Correlator != Connection->NetbiosHeader.ResponseCorrelator) { + NbfPrint0 ("NbfCompleteSend: ack ignored, wrong correlator\n"); + return; + } + + if (Connection->SendState != CONNECTION_SENDSTATE_W_ACK) { + NbfPrint0 ("NbfCompleteSend: ack not expected\n"); + return; + } + + // + // Pick off TP_REQUEST objects from the connection's SendQueue until + // we find one with an END_OF_RECORD mark embedded in it. + // + + while (!(IsListEmpty(&Connection->SendQueue))) { + + // + // We know for a fact that we wouldn't be calling this routine if + // we hadn't received an acknowlegement for an entire message, + // since NBF doesn't provide stream mode sends. Therefore, we + // know that we will run into a request with the END_OF_RECORD + // mark set BEFORE we will run out of requests on that queue, + // so there is no reason to check to see if we ran off the end. + // Note that it's possible that the send has been failed and the + // connection not yet torn down; if this has happened, we could be + // removing from an empty queue here. Make sure that doesn't happen. + // + + p = RemoveHeadList(&Connection->SendQueue); + + Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + IrpSp = IoGetCurrentIrpStackLocation (Irp); + + EndOfRecord = !(IRP_SEND_FLAGS(IrpSp) & TDI_SEND_PARTIAL); + +#if DBG + NbfCompletedSends[NbfCompletedSendsNext].Irp = Irp; + NbfCompletedSends[NbfCompletedSendsNext].Request = NULL; + NbfCompletedSends[NbfCompletedSendsNext].Status = STATUS_SUCCESS; + NbfCompletedSendsNext = (NbfCompletedSendsNext++) % TRACK_TDI_LIMIT; +#endif +#if DBG + IF_NBFDBG (NBF_DEBUG_TRACKTDI) { + if ((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_SENDS) != 0){ + NbfPrint1 ("CompleteSend: Completing send request %lx\n", Irp); + if (++Connection->DeferredPasses >= 4) { + Connection->DeferredFlags &= ~CONNECTION_FLAGS_DEFERRED_SENDS; + Connection->DeferredPasses = 0; + } + + } + + } +#endif + + + // + // Complete the send. Note that this may not actually call + // IoCompleteRequest for the Irp until sometime later, if the + // in-progress LLC resending going on below us needs to complete. + // + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + // + // Since the irp is no longer on the send list, the cancel routine + // cannot find it and will just return. We must grab the cancel + // spinlock to lock out the cancel function while we null out + // the Irp->CancelRoutine field. + // + + IoAcquireCancelSpinLock(&cancelIrql); + IoSetCancelRoutine(Irp, NULL); + IoReleaseCancelSpinLock(cancelIrql); + + NbfCompleteSendIrp ( + Irp, + STATUS_SUCCESS, + IRP_SEND_LENGTH(IrpSp)); + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + ++Connection->TransmittedTsdus; + + if (EndOfRecord) { + break; + } + + } + + // + // We've finished processing the current send. Update our state. + // + // Note: The connection spinlock is held here. + // + + Connection->SendState = CONNECTION_SENDSTATE_IDLE; + + // + // If there is another send pending on the connection, then initialize + // it and start packetizing it. + // + + if (!(IsListEmpty (&Connection->SendQueue))) { + + InitializeSend (Connection); + + // + // This code is similar to calling StartPacketizingConnection + // with the second parameter FALSE. + // + + if ((!(Connection->Flags & CONNECTION_FLAGS_PACKETIZE)) && + (Connection->Flags & CONNECTION_FLAGS_READY)) { + + Connection->Flags |= CONNECTION_FLAGS_PACKETIZE; + + NbfReferenceConnection ("Packetize", Connection, CREF_PACKETIZE_QUEUE); + + ExInterlockedInsertTailList( + &Connection->Provider->PacketizeQueue, + &Connection->PacketizeLinkage, + &Connection->Provider->SpinLock); + + } + + } + + // + // NOTE: We return with the lock held. + // + +} /* CompleteSend */ + + +VOID +FailSend( + IN PTP_CONNECTION Connection, + IN NTSTATUS RequestStatus, + IN BOOLEAN StopConnection + ) + +/*++ + +Routine Description: + + This routine is called because something on the link caused this send to be + unable to complete. There are a number of possible reasons for this to have + happened, but all will fail with the common error STATUS_LINK_FAILED. + or NO_RECEIVE response where the number of bytes specified exactly + Here we retire all of the TdiSends on the connection's SendQueue up to + and including the current one, which is the one that failed. + + Later - Actually, a send failing is cause for the entire circuit to wave + goodbye to this life. We now simply tear down the connection completly. + Any future sends on this connection will be blown away. + + NOTE: THIS FUNCTION MUST BE CALLED WITH THE SPINLOCK HELD. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + +Return Value: + + none. + +--*/ + +{ + PIRP Irp; + PIO_STACK_LOCATION IrpSp; + PLIST_ENTRY p; + BOOLEAN EndOfRecord; + BOOLEAN GotCurrent = FALSE; + KIRQL cancelIrql; + + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint1 ("FailSend: Entered for connection %lx.\n", Connection); + } + + + // + // Pick off IRP objects from the connection's SendQueue until + // we get to this one. If this one does NOT have an EOF mark set, we'll + // need to keep going until we hit one that does have EOF set. Note that + // this may cause us to continue failing sends that have not yet been + // queued. (We do all this because NBF does not provide stream mode sends.) + // + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + NbfReferenceConnection ("Failing Send", Connection, CREF_COMPLETE_SEND); + + do { + if (IsListEmpty (&Connection->SendQueue)) { + + // + // got an empty list, so we've run out of send requests to fail + // without running into an EOR. Set the connection flag that will + // cause all further sends to be failed up to an EOR and get out + // of here. + // + + Connection->Flags |= CONNECTION_FLAGS_FAILING_TO_EOR; + break; + } + p = RemoveHeadList (&Connection->SendQueue); + Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + IrpSp = IoGetCurrentIrpStackLocation (Irp); + + if (Irp == Connection->sp.CurrentSendIrp) { + GotCurrent = TRUE; + } + EndOfRecord = !(IRP_SEND_FLAGS(IrpSp) & TDI_SEND_PARTIAL); + +#if DBG + NbfCompletedSends[NbfCompletedSendsNext].Irp = Irp; + NbfCompletedSends[NbfCompletedSendsNext].Status = RequestStatus; + NbfCompletedSendsNext = (NbfCompletedSendsNext++) % TRACK_TDI_LIMIT; +#endif + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + IoAcquireCancelSpinLock(&cancelIrql); + IoSetCancelRoutine(Irp, NULL); + IoReleaseCancelSpinLock(cancelIrql); + + // + // The following dereference will complete the I/O, provided removes + // the last reference on the request object. The I/O will complete + // with the status and information stored in the Irp. Therefore, + // we set those values here before the dereference. + // + + NbfCompleteSendIrp (Irp, RequestStatus, 0); + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + ++Connection->TransmissionErrors; + + } while (!EndOfRecord & !GotCurrent); + + // + // We've finished processing the current send. Update our state. + // + + Connection->SendState = CONNECTION_SENDSTATE_IDLE; + Connection->sp.CurrentSendIrp = NULL; + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + // + // Blow away this connection; a failed send is a terrible thing to waste. + // Note that we are not on any packetizing queues or similar things at this + // point; we'll just disappear into the night. + // + +#if MAGIC + if (NbfEnableMagic) { + extern VOID NbfSendMagicBullet (PDEVICE_CONTEXT, PTP_LINK); + NbfSendMagicBullet (Connection->Provider, Connection->Link); + } +#endif + + if (StopConnection) { +#if DBG + if (NbfDisconnectDebug) { + STRING remoteName, localName; + remoteName.Length = NETBIOS_NAME_LENGTH - 1; + remoteName.Buffer = Connection->RemoteName; + localName.Length = NETBIOS_NAME_LENGTH - 1; + localName.Buffer = Connection->AddressFile->Address->NetworkName->NetbiosName; + NbfPrint2( "FailSend stopping connection to %S from %S\n", + &remoteName, &localName ); + } +#endif + NbfStopConnection (Connection, STATUS_LINK_FAILED); + } + +#if DBG + //DbgBreakPoint (); +#endif + + NbfDereferenceConnection ("FailSend", Connection, CREF_COMPLETE_SEND); + +} /* FailSend */ + +#if DBG +// +// *** This is the original version of InitializeSend, which is now a macro. +// It has been left here as the fully-commented version of the code. +// + + +VOID +InitializeSend( + PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called whenever the next send on a connection should + be initialized; that is, all of the fields associated with the state + of the current send are set to refer to the first send on the SendQueue. + + WARNING: This routine is executed with the Connection lock acquired + since it must be atomically executed with the caller's setup. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint1 ("InitializeSend: Entered for connection %lx.\n", Connection); + } + + ASSERT (!IsListEmpty (&Connection->SendQueue)); + + Connection->SendState = CONNECTION_SENDSTATE_PACKETIZE; + Connection->FirstSendIrp = + CONTAINING_RECORD (Connection->SendQueue.Flink, IRP, Tail.Overlay.ListEntry); + Connection->FirstSendMdl = Connection->FirstSendIrp->MdlAddress; + Connection->FirstSendByteOffset = 0; + Connection->sp.MessageBytesSent = 0; + Connection->sp.CurrentSendIrp = Connection->FirstSendIrp; + Connection->sp.CurrentSendMdl = Connection->FirstSendMdl; + Connection->sp.SendByteOffset = Connection->FirstSendByteOffset; + Connection->CurrentSendLength = + IRP_SEND_LENGTH(IoGetCurrentIrpStackLocation(Connection->sp.CurrentSendIrp)); + Connection->StallCount = 0; + Connection->StallBytesSent = 0; + + // + // The send correlator isn't used for much; it is used so we + // can distinguish which send a piggyback ack is acking. + // + + if (Connection->NetbiosHeader.ResponseCorrelator == 0xffff) { + Connection->NetbiosHeader.ResponseCorrelator = 1; + } else { + ++Connection->NetbiosHeader.ResponseCorrelator; + } + +} /* InitializeSend */ +#endif + + +VOID +ReframeSend( + PTP_CONNECTION Connection, + ULONG BytesReceived + ) + +/*++ + +Routine Description: + + This routine is called to reset the send state variables in the connection + object to correctly point to the first byte of data to be transmitted. + In essence, this is the byte-level acknowlegement processor at the NetBIOS + level for this transport. + + This is not straightforward because potentially multiple send requests + may be posted to the connection to comprise a single message. When a + send request has its TDI_END_OF_RECORD option bitflag set, then that + send is the last one to be sent in a logical message. Therefore, we + assume that the multiple-send scenario is the general case. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + + BytesReceived - Number of bytes received thus far. + +Return Value: + + none. + +--*/ + +{ + PIRP Irp; + PMDL Mdl; + ULONG Offset; + ULONG BytesLeft; + ULONG MdlBytes; + PLIST_ENTRY p; + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint3 ("ReframeSend: Entered for connection %lx, Flags: %lx Current Mdl: %lx\n", + Connection, Connection->Flags, Connection->sp.CurrentSendMdl); + } + + // + // The caller is responsible for restarting the packetization process + // on this connection. In some cases (i.e., NO_RECEIVE handler) we + // don't want to start packetizing, so that's why we do it elsewhere. + // + + // + // Examine all of the send requests and associated MDL chains starting + // with the first one at the head of the connection's SendQueue, advancing + // our complex current send pointer through the requests and MDL chains + // until we reach the byte count he's specified. + // + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + // + // In the case where a local disconnect has been issued, and we get a frame + // that causes us to reframe the send our FirstSendIrp and FirstMdl + // pointers are stale. Catch this condition and prevent faults caused by + // this. A better fix would be to change the logic that switches the + // connection sendstate from idle to W_LINK to not do that. However, this + // is a broader change than fixing it right here. + // + + if (IsListEmpty(&Connection->SendQueue)) { + RELEASE_DPC_SPIN_LOCK(Connection->LinkSpinLock); + return; + } + + BytesLeft = BytesReceived; + Irp = Connection->FirstSendIrp; + Mdl = Connection->FirstSendMdl; + if (Mdl) { + MdlBytes = MmGetMdlByteCount (Mdl); + } else { + MdlBytes = 0; // zero-length send + } + Offset = Connection->FirstSendByteOffset; + +#if DBG + IF_NBFDBG (NBF_DEBUG_TRACKTDI) { + NbfPrint3 ("ReFrameSend: Called with Connection %lx FirstSend %lx CurrentSend %lx\n", + Connection, Connection->FirstSendIrp, Connection->sp.CurrentSendIrp); + Connection->DeferredFlags |= CONNECTION_FLAGS_DEFERRED_SENDS; + Connection->DeferredPasses = 0; + } +#endif + + // + // We loop through while we have acked bytes left to account for, + // advancing our pointers and completing any sends that have been + // completely acked. + // + + while (BytesLeft != 0) { + + if (Mdl == NULL) { + KIRQL cancelIrql; + + // + // We have exhausted the MDL chain on this request, so it has + // been implicitly acked. That means we must complete the I/O + // by dereferencing the request before we reframe further. + // + + p = RemoveHeadList (&Connection->SendQueue); + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + + // + // Since the irp is no longer on the list, the cancel routine + // won't find it. Grab the cancel spinlock to synchronize + // and complete the irp. + // + + IoAcquireCancelSpinLock(&cancelIrql); + IoSetCancelRoutine(Irp, NULL); + IoReleaseCancelSpinLock(cancelIrql); + + NbfCompleteSendIrp (Irp, STATUS_SUCCESS, Offset); + + // + // Now continue with the next request in the list. + // + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + p = Connection->SendQueue.Flink; + if (p == &Connection->SendQueue) { + + ULONG DumpData[2]; + + // + // The byte acknowledgement was for more than the + // total length of sends we have outstanding; to + // avoid problems we tear down the connection. + // +#if DBG + NbfPrint2 ("NbfReframeSend: Got %d extra bytes acked on %lx\n", + BytesLeft, Connection); + ASSERT (FALSE); +#endif + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + DumpData[0] = Offset; + DumpData[1] = BytesLeft; + + NbfWriteGeneralErrorLog( + Connection->Provider, + EVENT_TRANSPORT_BAD_PROTOCOL, + 1, + STATUS_INVALID_NETWORK_RESPONSE, + L"REFRAME", + 2, + DumpData); + + NbfStopConnection (Connection, STATUS_INVALID_NETWORK_RESPONSE); + + return; + + } + + Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + Mdl = Irp->MdlAddress; + MdlBytes = MmGetMdlByteCount (Mdl); + Offset = 0; + + } else if (MdlBytes > (Offset + BytesLeft)) { + + // + // This MDL has more data than we really need. Just use + // part of it. Then get out, because we're done. + // + + Offset += BytesLeft; + BytesLeft = 0; + break; + + } else { + + // + // This MDL does not have enough data to satisfy the ACK, so + // use as much data as it has, and cycle around again. + // + + Offset = 0; + BytesLeft -= MdlBytes; + Mdl = Mdl->Next; + + if (Mdl != NULL) { + MdlBytes = MmGetMdlByteCount (Mdl); + } + + } + } + + // + // Tmp debugging; we want to see if we got byte acked + // for the entire send. This will break if we have + // non-EOR sends. + // + +#if DBG + if (BytesReceived != 0) { + ASSERTMSG ("NbfReframeSend: Byte ack for entire send\n", + Mdl != NULL); + } +#endif + + // + // We've acked some data, possibly on a byte or message boundary. + // We must pretend we're sending a new message all over again, + // starting with the byte immediately after the last one he acked. + // + + Connection->FirstSendIrp = Irp; + Connection->FirstSendMdl = Mdl; + Connection->FirstSendByteOffset = Offset; + + // + // Since we haven't started sending this new reframed message yet, + // we set our idea of the current complex send pointer to the first + // complex send pointer. + // + + Connection->sp.MessageBytesSent = 0; + Connection->sp.CurrentSendIrp = Irp; + Connection->sp.CurrentSendMdl = Mdl; + Connection->sp.SendByteOffset = Offset; + Connection->CurrentSendLength -= BytesReceived; + Connection->StallCount = 0; + Connection->StallBytesSent = 0; + +#if DBG + IF_NBFDBG (NBF_DEBUG_TRACKTDI) { + + { + PLIST_ENTRY p; + NbfPrint0 ("ReFrameSend: Walking Send List:\n"); + + for ( + p = Connection->SendQueue.Flink; + p != &Connection->SendQueue; + p=p->Flink ) { + + Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + NbfPrint1 (" Irp %lx\n", Irp); + } + }} +#endif + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + +} /* ReframeSend */ + + +VOID +NbfCancelSend( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to cancel a send. + The send is found on the connection's send queue; if it is the + current request it is cancelled and the connection is torn down, + otherwise it is silently cancelled. + + 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, oldirql1; + PIO_STACK_LOCATION IrpSp; + PTP_CONNECTION Connection; + PIRP SendIrp; + PLIST_ENTRY p; + BOOLEAN Found; + + UNREFERENCED_PARAMETER (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_SEND)); + + Connection = IrpSp->FileObject->FsContext; + + // + // Since this IRP is still in the cancellable state, we know + // that the connection is still around (although it may be in + // the process of being torn down). + // + + + // + // See if this is the IRP for the current send request. + // + + ACQUIRE_SPIN_LOCK (Connection->LinkSpinLock, &oldirql); + NbfReferenceConnection ("Cancelling Send", Connection, CREF_COMPLETE_SEND); + + p = Connection->SendQueue.Flink; + SendIrp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + + if (SendIrp == Irp) { + + // + // yes, it is the first one on the send queue, so + // trash the send/connection. The first send is a special case + // there are multiple pointers to the send request. Just stop the + // connection. + // + + // p = RemoveHeadList (&Connection->SendQueue); + +#if DBG + NbfCompletedSends[NbfCompletedSendsNext].Irp = SendIrp; + NbfCompletedSends[NbfCompletedSendsNext].Status = STATUS_CANCELLED; + NbfCompletedSendsNext = (NbfCompletedSendsNext++) % TRACK_TDI_LIMIT; +#endif + + // + // Prevent anyone from getting in to packetize before we + // call NbfStopConnection. + // + + Connection->SendState = CONNECTION_SENDSTATE_IDLE; + + RELEASE_SPIN_LOCK (Connection->LinkSpinLock, oldirql); + IoReleaseCancelSpinLock (Irp->CancelIrql); + +#if DBG + DbgPrint("NBF: Canceled in-progress send %lx on %lxn", + SendIrp, Connection); +#endif + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql1); + + // + // The following dereference will complete the I/O, provided removes + // the last reference on the request object. The I/O will complete + // with the status and information stored in the Irp. Therefore, + // we set those values here before the dereference. + // + + // NbfCompleteSendIrp (SendIrp, STATUS_CANCELLED, 0); + + // + // Since we are cancelling the current send, blow away + // the connection. + // + + NbfStopConnection (Connection, STATUS_CANCELLED); + + KeLowerIrql (oldirql1); + + } else { + + // + // Scan through the list, looking for this IRP. If we + // cancel anything up to the first EOR on the list + // we still tear down the connection since this would + // mess up our packetizing otherwise. We set CancelledFirstEor + // to FALSE when we pass an IRP without SEND_PARTIAL. + // + // NO MATTER WHAT WE MUST SHUT DOWN THE CONNECTION!!!! + +#if 0 + if (!(IRP_SEND_FLAGS(IoGetCurrentIrpStackLocation(SendIrp)) & TDI_SEND_PARTIAL)) { + CancelledFirstEor = FALSE; + } else { + CancelledFirstEor = TRUE; + } +#endif + + Found = FALSE; + p = p->Flink; + while (p != &Connection->SendQueue) { + + SendIrp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + if (SendIrp == Irp) { + + // + // Found it, remove it from the list here. + // + + RemoveEntryList (p); + + Found = TRUE; + +#if DBG + NbfCompletedSends[NbfCompletedSendsNext].Irp = SendIrp; + NbfCompletedSends[NbfCompletedSendsNext].Status = STATUS_CANCELLED; + NbfCompletedSendsNext = (NbfCompletedSendsNext++) % TRACK_TDI_LIMIT; +#endif + + RELEASE_SPIN_LOCK (Connection->LinkSpinLock, oldirql); + IoReleaseCancelSpinLock (Irp->CancelIrql); + +#if DBG + DbgPrint("NBF: Canceled queued send %lx on %lx\n", + SendIrp, Connection); +#endif + + // + // The following dereference will complete the I/O, provided removes + // the last reference on the request object. The I/O will complete + // with the status and information stored in the Irp. Therefore, + // we set those values here before the dereference. + // + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql1); + + NbfCompleteSendIrp (SendIrp, STATUS_CANCELLED, 0); + // + // STOP THE CONNECTION NO MATTER WHAT!!! + // + NbfStopConnection (Connection, STATUS_CANCELLED); + + KeLowerIrql (oldirql1); + break; + + } +#if 0 + else { + + if (CancelledFirstEor && (!(IRP_SEND_FLAGS(IoGetCurrentIrpStackLocation(SendIrp)) & TDI_SEND_PARTIAL))) { + CancelledFirstEor = FALSE; + } + } +#endif + + p = p->Flink; + + } + + if (!Found) { + + // + // We didn't find it! + // + +#if DBG + DbgPrint("NBF: Tried to cancel send %lx on %lx, not found\n", + Irp, Connection); +#endif + RELEASE_SPIN_LOCK (Connection->LinkSpinLock, oldirql); + IoReleaseCancelSpinLock (Irp->CancelIrql); + } + + } + + NbfDereferenceConnection ("Cancelling Send", Connection, CREF_COMPLETE_SEND); + +} + + +BOOLEAN +ResendPacket ( + PTP_LINK Link, + PTP_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine resends a packet on the link. Since this is a resend, we + are careful to not reset the state unless all resends have completed. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Link - Pointer to a TP_LINK object. + + Packet - pointer to packet to be resent. + +Return Value: + + True if resending should continue; FALSE otherwise. + +--*/ + +{ + BOOLEAN PollFinal; + PDLC_I_FRAME DlcHeader; + UINT DataLength; + + + // + + DlcHeader = (PDLC_I_FRAME)&(Packet->Header[Link->HeaderLength]); + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint3 ("ReSendPacket: %lx NdisPacket: %lx # %x\n", + Packet, Packet->NdisPacket, + DlcHeader->RcvSeq >>1); + IF_NBFDBG (NBF_DEBUG_PKTCONTENTS) { + {PUCHAR q; + USHORT i; + q = Packet->Header; + for (i=0;i<20;i++) { + NbfPrint1 (" %2x",q[i]); + } + NbfPrint0 ("\n");} + } + } + + DataLength = Packet->NdisIFrameLength; + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + Link->WindowErrors++; + + PollFinal = (BOOLEAN)((DlcHeader->RcvSeq & DLC_I_PF) != 0); + + StopT2 (Link); // since this is potentially acking some frames + + if (Link->Provider->MacInfo.MediumAsync) { + if (PollFinal) { + ASSERT (Packet->Link != NULL); + NbfReferenceLink ("ResendPacket", Link, LREF_START_T1); + } else { + StartT1 (Link, 0); + } + } else { + StartT1 (Link, PollFinal ? DataLength : 0); // restart transmission timer + } + + // + // Update the expected next receive in case it's changed + // + + if (PollFinal) { + + DlcHeader->RcvSeq = DLC_I_PF; // set the poll bit. + Link->SendState = SEND_STATE_CHECKPOINTING; + + Link->ResendingPackets = FALSE; + + } else { + + DlcHeader->RcvSeq = 0; + + } + + // + // DlcHeader->RcvSeq has Link->NextReceive inserted by NbfNdisSend. + // + + NbfReferencePacket (Packet); // so we don't remove it in send completion + + NbfReferenceLink ("ResendPacket", Link, LREF_NDIS_SEND); + + ASSERT (Packet->PacketSent == TRUE); + Packet->PacketSent = FALSE; + + // + // Update our "bytes resent" counters. + // + + DataLength -= + Link->HeaderLength + sizeof(DLC_I_FRAME) + sizeof(NBF_HDR_CONNECTION); + + + ADD_TO_LARGE_INTEGER( + &Link->Provider->Statistics.DataFrameBytesResent, + DataLength); + ++Link->Provider->Statistics.DataFramesResent; + + + // + // Send the packet (this release the link spinlock). + // + + NbfNdisSend (Link, Packet); + + ++Link->PacketsResent; + + NbfDereferenceLink ("ResendPacket", Link, LREF_NDIS_SEND); + + // + // if this packet has POLL set, stop the resending so the + // link doesn't get all twisted up. + // + + if (PollFinal) { + + // + // so we're in the state of having sent a poll and not + // sending anything else until we get a final. This avoids + // overrunning the remote. Note that we leave the routine + // with state LINK_SENDSTATE_REJECTING, which guarentees + // we won't start any new sends until we traverse through + // this routine again. + // + // + + return FALSE; + } + + return TRUE; +} + +BOOLEAN +ResendLlcPackets ( + PTP_LINK Link, + UCHAR AckSequenceNumber, + BOOLEAN Resend + ) + +/*++ + +Routine Description: + + This routine advances the state of a data link connection by retiring + all of the packets on the link's WackQ that have send sequence numbers + logically less than that number specified as the AckSequenceNumber, and + resending those above that number. The packets are disposed of by + dereferencing them. We cannot simply destroy them because this + acknowlegement might arrive even before the Physical Provider has had a + chance to issue a completion event for the associated I/O. + + NOTE: This function is called with the link spinlock held and + returns with it held, but it may release it in between. THIS + ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Link - Pointer to a TP_LINK object. + + AckSequenceNumber - An unsigned number specifing the sequence number of + the first packet within the window that is NOT acknowleged. + + Resend - if TRUE, resend packets. If FALSE, just remove them from the + wackq and get out. + +Return Value: + + none. + +--*/ + +{ + PTP_PACKET packet; + PLIST_ENTRY p, p1; + UCHAR packetSeq; + BOOLEAN passedAck = FALSE; + PDLC_I_FRAME DlcHeader; + SCHAR Difference; + BOOLEAN ReturnValue = FALSE; +// NDIS_STATUS ndisStatus; + + // + // Move through the queue, releasing those we've been acked for and resending + // others above that. + // + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint3 ("ResendLlcPackets: Link %lx, Ack: %x, LinkLastAck: %x.\n", + Link, AckSequenceNumber, Link->LastAckReceived); + NbfPrint0 ("RLP: Walking WackQ, Packets:\n"); + p = Link->WackQ.Flink; // p = ptr, 1st pkt's linkage. + while (p != &Link->WackQ) { + packet = CONTAINING_RECORD (p, TP_PACKET, Linkage); + DlcHeader = (PDLC_I_FRAME)&(packet->Header[Link->HeaderLength]); + NbfPrint4 ("RLP: Pkt: %lx # %x Flags: %d %d\n", packet, + (UCHAR)(DlcHeader->SendSeq >> 1), packet->PacketSent, packet->PacketNoNdisBuffer); + p = packet->Linkage.Flink; + } + } + + // + // If somebody else is resending LLC packets (which means they + // are in this function with Resend == TRUE), then ignore + // this frame. This is because it may ack a frame that he + // is in the middle of resending, which will cause problems. + // + // BUGBUG: This isn't a great solution, we should keep track + // of where the other guy is and avoid stepping on him. This + // might mess up his walking of the queue however. + // + + if (Link->ResendingPackets) { + NbfPrint1("ResendLlcPackets: Someone else resending on %lx\n", Link); + return TRUE; + } + + // + // We have already checked that AckSequenceNumber is reasonable. + // + + Link->LastAckReceived = AckSequenceNumber; + + if (Resend) { + + // + // Only one person can be resending or potentially resending + // at one time. + // + + Link->ResendingPackets = TRUE; + } + + // + // Resend as many packets as we have window to send. We spin through the + // queue and remove those packets that have been acked or that are + // sequence numbered logically below the current ack number. The flags + // PACKET_FLAGS_RESEND and PACKET_FLAGS_SENT correspond to the three states + // a packet on this queue can be in: + // + // 1) if _RESEND is set, the packet has not been acked + // + // 2) if _SENT is set, the packet send has completed (conversely, if NOT + // set, the packet has not yet been completely sent, thus it is + // unnecessary to resend it). + // 3) if _RESEND and _SENT are both set, the packet has been sent and not + // acked and is grist for our mills. + // 4) if neither is set, the world is coming to an end next Thursday. + // + + p=Link->WackQ.Flink; + while (p != &Link->WackQ) { + packet = CONTAINING_RECORD (p, TP_PACKET, Linkage); + DlcHeader = (PDLC_I_FRAME)&(packet->Header[Link->HeaderLength]); + + // + // if both bits aren't set we can't do a thing with this packet, or, + // for that matter, with the rest of the packet list. We can't + // have reached the ack number yet, as these packets haven't even + // completed sending. + // (Later) actually, we can have reached passedAck, and if we did + // we're in a world of hurt. We can't send more regular packets, + // but we can't send any resend packets either. Force the link to + // checkpoint and things will clear themselves up later. + // + + if (!(packet->PacketSent)) { + if (passedAck) { + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint2 ("ResendLLCPacket: Can't send WACKQ Packet RcvSeq %x %x \n", + DlcHeader->RcvSeq, DlcHeader->SendSeq); + } + + if (Link->SendState != SEND_STATE_CHECKPOINTING) { + + // + // Don't start checkpointing if we already are. + // + + Link->SendState = SEND_STATE_CHECKPOINTING; + StopTi (Link); + StartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); // start checkpoint timeout. + Link->ResendingPackets = FALSE; + + // + // Try this...in this case don't actually send + // an RR, since his response might put us right + // back here. When T1 expires we will recover. + // + // NbfSendRr (Link, TRUE, TRUE); + + } else { + + Link->ResendingPackets = FALSE; + + } + + return TRUE; + } + + // + // Don't break, since passedAck is FALSE all we will + // do in the next section is TpDereferencePacket, which + // is correct. + // + // break; + } + + // + // This loop is somewhat schizo; at this point, if we've not yet reached + // the ack number, we'll be ditching the packet. If we've gone through + // the ack number, we'll be re-transmitting. Note that in the first + // personality, we are always looking at the beginning of the list. + // + + // + // NOTE: Link spinlock is held here. + // + + packetSeq = (UCHAR)(DlcHeader->SendSeq >> 1); + if (!passedAck){ + + // + // Compute the signed difference here; see if + // packetSeq is equal to or "greater than" + // LastAckReceived. + // + + Difference = packetSeq - Link->LastAckReceived; + + if (((Difference >= 0) && (Difference < 0x40)) || + (Difference < -0x40)) { + + // + // We have found a packet on the queue that was + // not acknowledged by LastAckReceived. + // + + if (Link->SendState == SEND_STATE_CHECKPOINTING) { + + // + // If we are checkpointing, we should not do any of + // the passedAck things (i.e. any of the things which + // potentially involve sending packets) - adb 7/30/91. + // + + if (Resend) { + Link->ResendingPackets = FALSE; + } + return TRUE; + } + + if (!Resend) { + + // + // If we are not supposed to resend, then exit. + // Since there are still packets on the queue + // we restart T1. + // + + StopTi (Link); + StartT1 (Link, 0); // start checkpoint timeout. + return TRUE; + } + + // + // Lock out senders, so we maintain packet sequences properly + // + + Link->SendState = SEND_STATE_REJECTING; // we're resending. + + passedAck = TRUE; + + // + // Note that we don't advance the pointer to the next packet; + // thus, we will resend this packet on the next pass through + // the while loop (taking the passedAck branch). + // + + } else { + p1 = RemoveHeadList (&Link->WackQ); + ASSERTMSG (" ResendLLCPacket: Packet not at queue head!\n", (p == p1)); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + ReturnValue = TRUE; + NbfDereferencePacket (packet); + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + p = Link->WackQ.Flink; + } + + } else { +// NbfPrint1 ("RLP: # %x\n",packetSeq); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + // + // If this call returns FALSE (because we checkpoint) + // it clears ResendingPacket before it returns. + // + + if (!ResendPacket (Link, packet)) { + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + return ReturnValue; + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + p = p->Flink; + } + } + + // + // NOTE: Link spinlock is held here. + // + + if (passedAck) { + + // + // If we exit through here with passedAck TRUE, it means that we + // successfully called ResendPacket on every packet in the + // WackQ, which means we did not resend a poll packet, so we + // can start sending normally again. We have to clear + // ResendingPackets here. + // + + Link->SendState = SEND_STATE_READY; + Link->ResendingPackets = FALSE; + StartTi (Link); + + } else if (!Resend) { + + // + // If Resend is FALSE (in which case passedAck will also be FALSE, + // by the way), and the WackQ is empty, that means that we + // successfully acknowledged all the packets on a non-final + // frame. In this case T1 may be running, but in fact is not + // needed since there are no sends outstanding. + // + + if (Link->WackQ.Flink == &Link->WackQ) { + StopT1 (Link); // BUGBUG: StartTi?? + } + Link->SendState = SEND_STATE_READY; + StartTi (Link); + + } else { + + // + // Resend is TRUE, but passedAck is FALSE; we came in + // expecting to resend, but didn't. This means that + // we have emptied the queue after receiving an + // RR/f, i.e. this send window is done and we can + // update our send window size, etc. + // + + Link->ResendingPackets = FALSE; + + if (Link->Provider->MacInfo.MediumAsync) { + return ReturnValue; + } + + if (Link->WindowErrors > 0) { + + // + // We had transmit errors on this window. + // + + Link->PrevWindowSize = Link->SendWindowSize; + + // + // We use 100 ms delay as the cutoff for a LAN. + // + + if (Link->Delay < (100*MILLISECONDS)) { + + // + // On a LAN, if we have a special case + // if one packet was lost; this means the + // final packet was retransmitted once. In + // that case, we keep track of Consecutive + // LastPacketLost, and if it reaches 2, then + // we lock the send window at its current + // value minus one. + // + + if (Link->WindowErrors == 1) { + + ++Link->ConsecutiveLastPacketLost; + + if (Link->ConsecutiveLastPacketLost >= 2) { + + // + // Freeze the window wherever it was. + // + + if (Link->SendWindowSize > Link->Provider->MinimumSendWindowLimit) { + Link->MaxWindowSize = Link->SendWindowSize - 1; + Link->SendWindowSize = (UCHAR)Link->MaxWindowSize; + } + + } + + // + // Otherwise, we leave the window where it is. + // + + } else { + + Link->ConsecutiveLastPacketLost = 0; + Link->SendWindowSize -= (UCHAR)Link->WindowErrors; + + } + + } else { + + // + // On a WAN we cut the send window in half, + // regardless of how many frames were retransmitted. + // + + Link->SendWindowSize /= 2; + Link->WindowsUntilIncrease = 1; // in case Prev is also 1. + Link->ConsecutiveLastPacketLost = 0; + + } + + if ((SCHAR)Link->SendWindowSize < 1) { + Link->SendWindowSize = 1; + } + + // + // Reset our counters for the next window. + // + + Link->WindowErrors = 0; + + } else { + + // + // We have successfully sent a window of data, increase + // the send window size unless we are at the limit. + // We use 100 ms delay as the WAN/LAN cutoff. + // + + if ((ULONG)Link->SendWindowSize < Link->MaxWindowSize) { + + if (Link->Delay < (100*MILLISECONDS)) { + + // + // On a LAN, increase the send window by 1. + // + // BUGBUG: Need to determine optimal window + // size. + // + + Link->SendWindowSize++; + + } else { + + // + // On a WAN, increase the send window by 1 until + // we hit PrevWindowSize, then do it more slowly. + // + + if (Link->SendWindowSize < Link->PrevWindowSize) { + + Link->SendWindowSize++; + + // + // If we just increased it to the previous window + // size, prepare for the next time through here. + // + + if (Link->SendWindowSize == Link->PrevWindowSize) { + Link->WindowsUntilIncrease = Link->SendWindowSize; + } + + } else { + + // + // We passed the previous size, so only update every + // WindowsUntilIncrease times. + // + + if (--Link->WindowsUntilIncrease == 0) { + + Link->SendWindowSize++; + Link->WindowsUntilIncrease = Link->SendWindowSize; + + } + } + } + + if ((ULONG)Link->SendWindowSize > Link->Provider->Statistics.MaximumSendWindow) { + Link->Provider->Statistics.MaximumSendWindow = Link->SendWindowSize; + } + + } + + // + // Clear this since we had no errors. + // + + Link->ConsecutiveLastPacketLost = 0; + + } + + } + + return ReturnValue; + +} /* ResendLlcPackets */ + + +VOID +NbfSendCompletionHandler( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus + ) + +/*++ + +Routine Description: + + This routine is called by the I/O system to indicate that a connection- + oriented packet has been shipped and is no longer needed by the Physical + Provider. + +Arguments: + + NdisContext - the value associated with the adapter binding at adapter + open time (which adapter we're talking on). + + NdisPacket/RequestHandle - A pointer to the NDIS_PACKET that we sent. + + NdisStatus - the completion status of the send. + +Return Value: + + none. + +--*/ + +{ + PSEND_PACKET_TAG SendContext; + PTP_PACKET Packet; + KIRQL oldirql1; + ProtocolBindingContext; // avoid compiler warnings + +#if DBG + if (NdisStatus != NDIS_STATUS_SUCCESS) { + NbfSendsCompletedAfterPendFail++; + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint2 ("NbfSendComplete: Entered for packet %lx, Status %s\n", + NdisPacket, NbfGetNdisStatus (NdisStatus)); + } + } else { + NbfSendsCompletedAfterPendOk++; + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint2 ("NbfSendComplete: Entered for packet %lx, Status %s\n", + NdisPacket, NbfGetNdisStatus (NdisStatus)); + } + } +#endif + + SendContext = (PSEND_PACKET_TAG)&NdisPacket->ProtocolReserved[0]; + + switch (SendContext->Type) { + case TYPE_I_FRAME: + + // + // Just dereference the packet. There are a couple possibilities here. + // First, the I/O completion might happen before an ACK is received, + // in which case this will remove one of the references, but not both. + // Second, the LLC ACK for this packet may have already been processed, + // in which case this will destroy the packet. Third, this packet may + // be resent, either before or after this call, in which case the deref + // won't destroy the packet. + // + // NbfDereferencePacket will call PacketizeSend if it determines that + // there is at least one connection waiting to be packetized because + // of out-of-resource conditions or because its window has been opened. + // + + Packet = ((PTP_PACKET)SendContext->Frame); + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql1); + + if (Packet->Provider->MacInfo.MediumAsync) { + + if (Packet->Link) { + + ASSERT (Packet->NdisIFrameLength > 0); + + ACQUIRE_DPC_SPIN_LOCK (&Packet->Link->SpinLock); + StartT1 (Packet->Link, Packet->NdisIFrameLength); + RELEASE_DPC_SPIN_LOCK (&Packet->Link->SpinLock); + + NbfDereferenceLink ("Send completed", Packet->Link, LREF_START_T1); + } + + if (Packet->PacketizeConnection) { + + PTP_CONNECTION Connection = IRP_SEND_CONNECTION((PIO_STACK_LOCATION)(Packet->Owner)); + PDEVICE_CONTEXT DeviceContext = Packet->Provider; + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + if ((Connection->SendState == CONNECTION_SENDSTATE_PACKETIZE) && + (Connection->Flags & CONNECTION_FLAGS_READY)) { + + ASSERT (Connection->Flags & CONNECTION_FLAGS_PACKETIZE); + + ACQUIRE_DPC_SPIN_LOCK(&DeviceContext->SpinLock); + + NbfReferenceConnection ("Delayed packetizing", Connection, CREF_PACKETIZE_QUEUE); + InsertTailList(&DeviceContext->PacketizeQueue, &Connection->PacketizeLinkage); + + if (!DeviceContext->WanThreadQueued) { + + DeviceContext->WanThreadQueued = TRUE; + ExQueueWorkItem(&DeviceContext->WanDelayedQueueItem, DelayedWorkQueue); + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + } else { + + Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE; + + } + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + NbfDereferenceConnection ("PacketizeConnection FALSE", Connection, CREF_TEMP); + + Packet->PacketizeConnection = FALSE; + + } + } +#if DBG + if (Packet->PacketSent) { + DbgPrint ("NbfSendCompletionHandler: Packet %lx already completed\n", Packet); + DbgBreakPoint(); + } +#endif + Packet->PacketSent = TRUE; + + NbfDereferencePacket (Packet); + + KeLowerIrql (oldirql1); + break; + + case TYPE_UI_FRAME: + + // + // just destroy the frame; name stuff doesn't depend on having any + // of the sent message left around after the send completed. + // + + NbfDestroyConnectionlessFrame ((PDEVICE_CONTEXT)SendContext->Owner, + (PTP_UI_FRAME)SendContext->Frame); + break; + + case TYPE_ADDRESS_FRAME: + + // + // Addresses get their own frames; let the address know it's ok to + // use the frame again. + // + + NbfSendDatagramCompletion ((PTP_ADDRESS)SendContext->Owner, + NdisPacket, + NdisStatus ); + break; + } + + return; + +} /* NbfSendCompletionHandler */ + + +NTSTATUS +SendOnePacket( + IN PTP_CONNECTION Connection, + IN PTP_PACKET Packet, + IN BOOLEAN ForceAck, + OUT PBOOLEAN LinkCheckpoint OPTIONAL + ) + +/*++ + +Routine Description: + + This routine sends a connection-oriented packet by calling the NDIS + Send service. At least one event will occur following + (or during) the Send request's processing. (1) The Send request + will complete through the I/O system, calling IoCompleteRequest. + (2) The sequenced packet will be acknowleged at the LLC level, or it + will be rejected and reset at the LLC level. If the packet is resent, + then it remains queued at the TP_LINK object. If the packet is ACKed, + then is removed from the link's WackQ and the Action field in the + TP_PACKET structure dictates what operation to perform next. + + NOTE: This routine is called with the link spinlock held. THIS + ROUTINE MUST BE CALLED AT DPC LEVEL. + + NOTE: This routine will now accept all frames unless the link + is down. If the link cannot send, the packet will be queued and + sent when possible. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + + Packet - Pointer to a TP_PACKET object. + + ForceAck - Boolean that, if true, indicates this packet should always have + the Poll bit set; this force the other side to ack immediately, + which is necessary for correct session teardown. + + LinkCheckpoint - If specified, will return TRUE if the link has + just entered a checkpoint state. In this case the status + will be STATUS_SUCCESS, but the connection should stop + packetizing now (in fact, to close a window, the connection + is put into the W_LINK state if this status will be + returned, so he must stop because somebody else may + already be doing it). + +Return Value: + + STATUS_LINK_FAILED - the link is dead or not ready. + STATUS_SUCCESS - the packet has been sent. + STATUS_INSUFFICIENT_RESOURCES - the packet has been queued. + +--*/ + +{ + PTP_LINK Link; + PDLC_I_FRAME DlcHeader; + PNDIS_BUFFER ndisBuffer; + ULONG SendsOutstanding; + BOOLEAN Poll = FALSE; + NTSTATUS Status; + + IF_NBFDBG (NBF_DEBUG_PACKET) { + NbfPrint3 ("SendOnePacket: Entered, connection %lx, packet %lx DnisPacket %lx.\n", + Connection, Packet, Packet->NdisPacket); + } + + Link = Connection->Link; + + IF_NBFDBG (NBF_DEBUG_PACKET) { + UINT PLength, PCount; + UINT BLength; + PVOID BAddr; + NdisQueryPacket(Packet->NdisPacket, &PCount, NULL, &ndisBuffer, &PLength); + NbfPrint3 ("Sending Data Packet: %lx, Length: %lx Pages: %lx\n", + Packet->NdisPacket, PLength, PCount); + while (ndisBuffer != NULL) { + NdisQueryBuffer(ndisBuffer, &BAddr, &BLength); + NbfPrint3 ("Sending Data Packet: Buffer %08lx Length %08lx Va %08lx\n", + ndisBuffer, BLength, BAddr); + NdisGetNextBuffer (ndisBuffer, &ndisBuffer); + } + } + + // + // If the general state of the link is not READY, then we can't ship. + // This failure can be expected under some conditions, and may not cause + // failure of the send. + // + + if (Link->State != LINK_STATE_READY) { + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint1 ("SendOnePacket: Link state is not READY (%ld).\n", Link->State); + } + + // + // determine what to do with this problem. If we shouldn't be sending + // here, percolate an error upward. + // + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint3 ("SendOnePacket: Link Bad state, link: %lx Link Flags %lx Link State %lx\n", + Link, Link->Flags, Link->State); + } + return STATUS_LINK_FAILED; + } + + + SendsOutstanding = (((ULONG)Link->NextSend+128L-(ULONG)Link->LastAckReceived)%128L); + + // + // Format LLC header while we've got the spinlock to atomically update + // the link's state information. + // + + DlcHeader = (PDLC_I_FRAME)&(Packet->Header[Link->HeaderLength]); + DlcHeader->SendSeq = (UCHAR)(Link->NextSend << 1); + Link->NextSend = (UCHAR)((Link->NextSend + 1) & 0x7f); + DlcHeader->RcvSeq = 0; // Link->NextReceive is inserted by NbfNdisSend + + // + // Before we release the spinlock, we append the packet to the + // end of the link's WackQ, so that if an ACK arrives before the NdisSend + // completes, it will be on the queue already. Also, mark the packet as + // needing resend, which is canceled by AckLLCPackets, and used by + // ResendLLCPackets. Thus, all packets will need to be resent until they + // are acked. + // + + ASSERT (Packet->PacketSent == FALSE); + + InsertTailList (&Link->WackQ, &Packet->Linkage); + //SrvCheckListIntegrity( &Link->WackQ, 200 ); + + + // + // If the send state is not READY, we can't ship. + // This failure is mostly caused by flow control or retransmit in progress, + // and is never cause for failure of the send. + // + + if ((Link->SendState != SEND_STATE_READY) || + (Link->LinkBusy) || + (SendsOutstanding >= (ULONG)Link->SendWindowSize)) { + + if ((Link->SendWindowSize == 1) || ForceAck) { + DlcHeader->RcvSeq |= DLC_I_PF; // set the poll bit. + if (Link->Provider->MacInfo.MediumAsync) { + Packet->Link = Link; + } + } + + Packet->PacketSent = TRUE; // allows it to be resent. + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + +#if DBG + if (Link->SendState != SEND_STATE_READY) { + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint1 ("SendOnePacket: Link send state not READY (%ld).\n", Link->SendState); + } + } else if (Link->LinkBusy) { + IF_NBFDBG (NBF_DEBUG_SENDENG) { + PANIC ("SendOnePacket: Link is busy.\n"); + } + } else if (SendsOutstanding >= (ULONG)Link->SendWindowSize) { + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint3 ("SendOnePacket: No link send window; N(S)=%ld,LAR=%ld,SW=%ld.\n", + Link->NextSend, Link->LastAckReceived, Link->SendWindowSize); + } + } +#endif + + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Reference the packet since it is given to the NDIS driver. + // + +#if DBG + NbfReferencePacket (Packet); +#else + ++Packet->ReferenceCount; // OK since it is not queued anywhere. +#endif + + // + // If this is the last I-frame in the window, then indicate that we + // should checkpoint. Also checkpoint if the sender is requesting + // acknowledgement (currently on SendSessionEnd does this). + // By default, this will also be a command frame. + // + + if (((SendsOutstanding+1) >= (ULONG)Link->SendWindowSize) || + ForceAck) { + Link->SendState = SEND_STATE_CHECKPOINTING; + StopTi (Link); + DlcHeader->RcvSeq |= DLC_I_PF; // set the poll bit. + Poll = TRUE; + + } + + + // + // If we are polling, and the caller cares about it, then + // we set LinkCheckpoint, and also set up the connection to + // be waiting for resources. We do this now, before the send, + // so that even if the ack is receive right away, we will + // be in a good state. When we return LinkCheckpoint TRUE + // the caller realizes that he no longer owns the right + // to "packetize" and exits immediately. + // + // We also want to start our retransmission timer so, if this + // packet gets dropped, we will know to retransmit it. The + // exception is if LinkCheckpoint was specified, then we + // only StartT1 of we are not polling (the caller will + // ensure it is started if he exits before we poll). + // + + if (ARGUMENT_PRESENT(LinkCheckpoint)) { + + if (Poll) { + + // + // If the connection still has send state PACKETIZE, + // then change it to W_LINK. If it is something else + // (such as W_PACKET or W_ACK) then don't worry, when + // that condition clears he will repacketize and the + // link conditions will be re-examined. In all + // case we turn off the PACKETIZE flag, because when + // we return with LinkCheckpoint TRUE he will stop + // packetizing, and to close the window we turn it + // off now (before the NdisSend) rather than then. + // + + ASSERT (Connection->LinkSpinLock == &Link->SpinLock); + if (Connection->SendState == CONNECTION_SENDSTATE_PACKETIZE) { + Connection->SendState = CONNECTION_SENDSTATE_W_LINK; + } + Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE; + + if (Link->Provider->MacInfo.MediumAsync) { + Packet->Link = Link; + NbfReferenceLink ("Send I-frame", Link, LREF_START_T1); + } else { + StartT1 (Link, Packet->NdisIFrameLength); + } + *LinkCheckpoint = TRUE; + + } else { + + StartT1 (Link, 0); + *LinkCheckpoint = FALSE; + + } + + } else { + + // + // If LinkCheckpoint is not true, then we are sending + // an I-frame other than DFM/DOL. In this case, as + // an optimization, we'll set W_LINK if a) we are + // polling b) we are IDLE (to avoid messing up other + // states such as W_ACK). This will avoid a window + // where we don't go W_LINK until after the next + // send tries to packetize and fails. + // + + if (Poll) { + + ASSERT (Connection->LinkSpinLock == &Link->SpinLock); + if (Connection->SendState == CONNECTION_SENDSTATE_IDLE) { + Connection->SendState = CONNECTION_SENDSTATE_W_LINK; + } + + } + + // + // This is an optimization; we know that if LinkCheckpoint + // is present than we are being called from PacketizeSend; + // in this case the Link will have the LREF_CONNECTION + // reference and the connection will have the CREF_PACKETIZE + // reference, so we don't have to reference the link + // again. + // + + NbfReferenceLink ("SendOnePacket", Link, LREF_NDIS_SEND); + + + // + // Start the retransmission timer. + // + + if (Link->Provider->MacInfo.MediumAsync) { + if (Poll) { + Packet->Link = Link; + NbfReferenceLink ("ResendPacket", Link, LREF_START_T1); + } else { + StartT1 (Link, 0); + } + } else { + StartT1 (Link, Poll ? Packet->NdisIFrameLength : 0); + } + + } + + // + // Since this I-frame contains an N(R), it is potentially ACKing some + // previously received I-frames as reverse traffic. So we stop our + // delayed acknowlegement timer. + // + + StopT2 (Link); + + if ((Link->Provider->MacInfo.MediumAsync) && + (ARGUMENT_PRESENT(LinkCheckpoint)) && + (Link->SendWindowSize >= 3) && + (!Poll) && (SendsOutstanding == (ULONG)(Link->SendWindowSize-2))) { + + Status = STATUS_MORE_PROCESSING_REQUIRED; + + Connection->Flags |= CONNECTION_FLAGS_PACKETIZE; + NbfReferenceConnection ("PacketizeConnection TRUE", Connection, CREF_TEMP); + Packet->PacketizeConnection = TRUE; + + } else { + + Status = STATUS_SUCCESS; + } + + // + // Send the packet; no locks held. Note that if the send fails, we will + // NOT fail upward; we allow things to continue onward. This lets us retry + // the send multiple times before we crap out; additionally, it keeps us + // from failing obscurely when sending control Iframes. + // + // NOTE: NbfNdisSend releases the link spinlock. + // + + NbfNdisSend (Link, Packet); + + Link->PacketsSent++; + + // + // Remove the reference made above if needed. + // + + if (!ARGUMENT_PRESENT(LinkCheckpoint)) { + NbfDereferenceLink ("SendOnePacket", Link, LREF_NDIS_SEND); + } + + return Status; + +} /* SendOnePacket */ + + +VOID +SendControlPacket( + IN PTP_LINK Link, + IN PTP_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine sends a connection-oriented packet by calling the Physical + Provider's Send service. While SendOnePacket is used to send an I- + frame, this routine is used to send one of the following: RR, RNR, REJ, + SABME, UA, DISC, DM, FRMR, TEST, and XID. + + NOTE: This function is called with the link spinlock held, + and returns with it released. IT MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Link - Pointer to a TP_LINK object. + + Packet - Pointer to a TP_PACKET object. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + USHORT i; + PUCHAR p; + PNDIS_BUFFER ndisBuffer; + + IF_NBFDBG (NBF_DEBUG_PACKET) { + NbfPrint3 ("SendControlPacket: Entered for link %lx, packet %lx, NdisPacket %lx\n 00:", + Link, Packet, Packet->NdisPacket); + IF_NBFDBG (NBF_DEBUG_PKTCONTENTS) { + UINT PLength, PCount; + UINT BLength; + PVOID BAddr; + p = Packet->Header; + for (i=0;i<20;i++) { + NbfPrint1 (" %2x",p[i]); + } + NbfPrint0 ("\n"); + NdisQueryPacket(Packet->NdisPacket, &PCount, NULL, &ndisBuffer, &PLength); + NbfPrint3 ("Sending Control Packet: %lx, Length: %lx Pages: %lx\n", + Packet->NdisPacket, PLength, PCount); + while (ndisBuffer != NULL) { + NdisQueryBuffer (ndisBuffer, &BAddr, &BLength); + NbfPrint3 ("Sending Control Packet: Buffer %08lx Length %08lx Va %08lx\n", + ndisBuffer, BLength, BAddr); + NdisGetNextBuffer (ndisBuffer, &ndisBuffer); + } + } + } + + ASSERT (Packet->PacketSent == FALSE); + + NbfReferenceLink ("SendControlPacket", Link, LREF_NDIS_SEND); + + // + // Send the packet (we have the lock, NbfNdisSend released + // it. + // + + NbfNdisSend (Link, Packet); + + NbfDereferenceLink ("SendControlPacket", Link, LREF_NDIS_SEND); + +} /* SendControlPacket */ + + +VOID +NbfNdisSend( + IN PTP_LINK Link, + IN PTP_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine is used to ensure that receive sequence numbers on + packets are numbered correctly. It is called in place of NdisSend + and after assigning the receive sequence number it locks out other + sends until the NdisSend call has returned (not necessarily completed), + insuring that the packets with increasing receive sequence numbers + are queue in the right order by the MAC. + + NOTE: This routine is called with the link spinlock held, + and it returns with it released. THIS ROUTINE MUST BE CALLED + AT DPC LEVEL. + +Arguments: + + Link - Pointer to a TP_LINK object. + + Packet - Pointer to a TP_PACKET object. + +Return Value: + + None. + +--*/ + +{ + + NDIS_STATUS NdisStatus; + PLIST_ENTRY p; + PDLC_S_FRAME DlcHeader; + PNDIS_PACKET TmpNdisPacket; + ULONG result; + + ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); + + if (Link->Provider->UniProcessor) { + + // + // On a uni-processor, we can send without fear of + // being interrupted by an incoming packet. + // + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + DlcHeader = (PDLC_S_FRAME)&(Packet->Header[Link->HeaderLength]); + + if ((DlcHeader->Command & DLC_U_INDICATOR) != DLC_U_INDICATOR) { + + // + // It's not a U-frame, so we assign RcvSeq. + // + + DlcHeader->RcvSeq |= (UCHAR)(Link->NextReceive << 1); + + } + +#if DBG + NbfSendsIssued++; +#endif + + INCREMENT_COUNTER (Link->Provider, PacketsSent); + + if (Link->Loopback) { + + // + // This packet is sent to ourselves; we should loop it + // back. + // + + NbfInsertInLoopbackQueue( + Link->Provider, + Packet->NdisPacket, + Link->LoopbackDestinationIndex + ); + + NdisStatus = NDIS_STATUS_PENDING; + + } else { + + NdisSend ( + &NdisStatus, + Link->Provider->NdisBindingHandle, + Packet->NdisPacket); + + } + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint1 ("NbfNdisSend: NdisSend completed Status: %s.\n", + NbfGetNdisStatus(NdisStatus)); + } + + switch (NdisStatus) { + + case NDIS_STATUS_PENDING: +#if DBG + NbfSendsPended++; +#endif + break; + + case NDIS_STATUS_SUCCESS: +#if DBG + NbfSendsCompletedInline++; + NbfSendsCompletedOk++; +#endif + NbfSendCompletionHandler (Link->Provider->NdisBindingHandle, + Packet->NdisPacket, + NDIS_STATUS_SUCCESS); + break; + + default: +#if DBG + NbfSendsCompletedInline++; + NbfSendsCompletedFail++; +#endif + NbfSendCompletionHandler (Link->Provider->NdisBindingHandle, + Packet->NdisPacket, + NDIS_STATUS_SUCCESS); + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint1 ("NbfNdisSend failed, status not Pending or Complete: %lx.\n", + NbfGetNdisStatus (NdisStatus)); + } + break; + + } + + } else { + + // + // If there is a send in progress, then queue this packet + // and return. + // + + if (Link->NdisSendsInProgress > 0) { + + p = (PLIST_ENTRY)(Packet->NdisPacket->MacReserved); + InsertTailList (&Link->NdisSendQueue, p); + ++Link->NdisSendsInProgress; + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + return; + + } + + // + // No send in progress. Set the flag to true, and fill in the + // receive sequence field in the packet (note that the RcvSeq + // field is in the same place for I- and S-frames. + // + + Link->NdisSendsInProgress = 1; + + while (TRUE) { + + DlcHeader = (PDLC_S_FRAME)&(Packet->Header[Link->HeaderLength]); + + if ((DlcHeader->Command & DLC_U_INDICATOR) != DLC_U_INDICATOR) { + + // + // It's not a U-frame, so we assign RcvSeq. + // + + DlcHeader->RcvSeq |= (UCHAR)(Link->NextReceive << 1); + + } + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + +#if DBG + NbfSendsIssued++; +#endif + + INCREMENT_COUNTER (Link->Provider, PacketsSent); + + if (Link->Loopback) { + + // + // This packet is sent to ourselves; we should loop it + // back. + // + + NbfInsertInLoopbackQueue( + Link->Provider, + Packet->NdisPacket, + Link->LoopbackDestinationIndex + ); + + NdisStatus = NDIS_STATUS_PENDING; + + } else { + + NdisSend ( + &NdisStatus, + Link->Provider->NdisBindingHandle, + Packet->NdisPacket); + + } + + // + // Take the ref count down, which may allow others + // to come through. + // + + result = ExInterlockedAddUlong( + &Link->NdisSendsInProgress, + (ULONG)-1, + &Link->SpinLock); + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint1 ("NbfNdisSend: NdisSend completed Status: %s.\n", + NbfGetNdisStatus(NdisStatus)); + } + + switch (NdisStatus) { + + case NDIS_STATUS_PENDING: +#if DBG + NbfSendsPended++; +#endif + break; + + case NDIS_STATUS_SUCCESS: +#if DBG + NbfSendsCompletedInline++; + NbfSendsCompletedOk++; +#endif + NbfSendCompletionHandler (Link->Provider->NdisBindingHandle, + Packet->NdisPacket, + NDIS_STATUS_SUCCESS); + break; + + default: +#if DBG + NbfSendsCompletedInline++; + NbfSendsCompletedFail++; +#endif + NbfSendCompletionHandler (Link->Provider->NdisBindingHandle, + Packet->NdisPacket, + NDIS_STATUS_SUCCESS); + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint1 ("NbfNdisSend failed, status not Pending or Complete: %lx.\n", + NbfGetNdisStatus (NdisStatus)); + } + break; + + } + + // + // We have now sent a packet, see if any queued up while we + // were doing it. If the count was zero after removing ours, + // then anything else queued is being processed, so we can + // exit. + // + + if (result == 1) { + return; + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + p = RemoveHeadList(&Link->NdisSendQueue); + + // + // If the refcount was not zero, then nobody else should + // have taken packets off since they would have been + // blocked by us. So, the queue should not be empty. + // + + ASSERT (p != &Link->NdisSendQueue); + + // + // Get back the TP_PACKET by using the Frame pointer in the + // ProtocolReserved field of the NDIS_PACKET. + // + + TmpNdisPacket = CONTAINING_RECORD (p, NDIS_PACKET, MacReserved[0]); + Packet = (PTP_PACKET)(((PSEND_PACKET_TAG)(&TmpNdisPacket->ProtocolReserved[0]))->Frame); + + } // while loop + + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + } + +} /* NbfNdisSend */ + + +VOID +RestartLinkTraffic( + PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine continues the activities of the connections on a link. + + NOTE: This function is called with the link spinlock held and + it returns with it released. THIS FUNCTION MUST BE CALLED AT + DPC LEVEL. + +Arguments: + + Link - Pointer to a TP_LINK object. + +Return Value: + + none. + +--*/ + +{ + PTP_CONNECTION connection; + PLIST_ENTRY p; + + IF_NBFDBG (NBF_DEBUG_SENDENG) { + NbfPrint1 ("RestartLinkTraffic: Entered for link %lx.\n", Link); + } + + // + // Link conditions may have cleared up. Make all connections on this + // link eligible for more packetization if they are in W_LINK state. + // + + for (p = Link->ConnectionDatabase.Flink; + p != &Link->ConnectionDatabase; + p = p->Flink) { + + connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList); + + ASSERT (connection->LinkSpinLock == &Link->SpinLock); + + // + // If we tried to send a plain-ole data frame DFM/DOL, but + // link conditions were not satisfactory, then we changed + // send state to W_LINK. Check for that now, and possibly + // start repacketizing. + // + + if (connection->SendState == CONNECTION_SENDSTATE_W_LINK) { + if (!(IsListEmpty (&connection->SendQueue))) { + + connection->SendState = CONNECTION_SENDSTATE_PACKETIZE; + + // + // This is similar to calling StartPacketizingConnection + // with the Immediate set to FALSE. + // + + if (!(connection->Flags & CONNECTION_FLAGS_PACKETIZE) && + (connection->Flags & CONNECTION_FLAGS_READY)) { + + ASSERT (!(connection->Flags2 & CONNECTION_FLAGS2_STOPPING)); + connection->Flags |= CONNECTION_FLAGS_PACKETIZE; + + NbfReferenceConnection ("Packetize", connection, CREF_PACKETIZE_QUEUE); + + ExInterlockedInsertTailList( + &connection->Provider->PacketizeQueue, + &connection->PacketizeLinkage, + &connection->Provider->SpinLock); + + } + + } else { + connection->SendState = CONNECTION_SENDSTATE_IDLE; + } + } + + } + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + +} /* RestartLinkTraffic */ + + +VOID +NbfProcessWanDelayedQueue( + IN PVOID Parameter + ) + +/*++ + +Routine Description: + + This is the thread routine which restarts packetizing + that has been delayed on WAN to allow RRs to come in. + This is very similar to PacketizeConnections. + +Arguments: + + Parameter - A pointer to the device context. + +Return Value: + + None. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + PLIST_ENTRY p; + PTP_CONNECTION Connection; + KIRQL oldirql; + + DeviceContext = (PDEVICE_CONTEXT)Parameter; + + // + // Packetize all waiting connections + // + + KeRaiseIrql (DISPATCH_LEVEL, &oldirql); + ASSERT (DeviceContext->WanThreadQueued); + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + while (!IsListEmpty(&DeviceContext->PacketizeQueue)) { + + p = RemoveHeadList(&DeviceContext->PacketizeQueue); + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + Connection = CONTAINING_RECORD (p, TP_CONNECTION, PacketizeLinkage); + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + if (Connection->SendState != CONNECTION_SENDSTATE_PACKETIZE) { + Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE; + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + NbfDereferenceConnection ("No longer packetizing", Connection, CREF_PACKETIZE_QUEUE); + } else { + NbfReferenceSendIrp ("Packetize", IoGetCurrentIrpStackLocation(Connection->sp.CurrentSendIrp), RREF_PACKET); + PacketizeSend (Connection, FALSE); // releases the lock. + } + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + } + + DeviceContext->WanThreadQueued = FALSE; + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + KeLowerIrql (oldirql); + +} /* NbfProcessWanDelayedQueue */ + + +NTSTATUS +BuildBufferChainFromMdlChain ( + IN PDEVICE_CONTEXT DeviceContext, + IN PMDL CurrentMdl, + IN ULONG ByteOffset, + IN ULONG DesiredLength, + OUT PNDIS_BUFFER *Destination, + OUT PMDL *NewCurrentMdl, + OUT ULONG *NewByteOffset, + OUT ULONG *TrueLength + ) + +/*++ + +Routine Description: + + This routine is called to build an NDIS_BUFFER chain from a source Mdl chain and + offset into it. We assume we don't know the length of the source Mdl chain, + and we must allocate the NDIS_BUFFERs for the destination chain, which + we do from the NDIS buffer pool. + + The NDIS_BUFFERs that are returned are mapped and locked. (Actually, the pages in + them are in the same state as those in the source MDLs.) + + If the system runs out of memory while we are building the destination + NDIS_BUFFER chain, we completely clean up the built chain and return with + NewCurrentMdl and NewByteOffset set to the current values of CurrentMdl + and ByteOffset. TrueLength is set to 0. + +Environment: + + Kernel Mode, Source Mdls locked. It is recommended, although not required, + that the source Mdls be mapped and locked prior to calling this routine. + +Arguments: + + BufferPoolHandle - The buffer pool to allocate buffers from. + + CurrentMdl - Points to the start of the Mdl chain from which to draw the + packet. + + ByteOffset - Offset within this MDL to start the packet at. + + DesiredLength - The number of bytes to insert into the packet. + + Destination - returned pointer to the NDIS_BUFFER chain describing the packet. + + NewCurrentMdl - returned pointer to the Mdl that would be used for the next + byte of packet. NULL if the source Mdl chain was exhausted. + + NewByteOffset - returned offset into the NewCurrentMdl for the next byte of + packet. NULL if the source Mdl chain was exhausted. + + TrueLength - The actual length of the returned NDIS_BUFFER Chain. If less than + DesiredLength, the source Mdl chain was exhausted. + +Return Value: + + STATUS_SUCCESS if the build of the returned NDIS_BUFFER chain succeeded (even if + shorter than the desired chain). + + STATUS_INSUFFICIENT_RESOURCES if we ran out of NDIS_BUFFERs while building the + destination chain. + +--*/ +{ + ULONG AvailableBytes; + PMDL OldMdl; + PNDIS_BUFFER NewNdisBuffer; + NDIS_STATUS NdisStatus; + + // + + IF_NBFDBG (NBF_DEBUG_NDIS) { + NbfPrint3 ("BuildBufferChain: Mdl: %lx Offset: %ld Length: %ld\n", + CurrentMdl, ByteOffset, DesiredLength); + } + + AvailableBytes = MmGetMdlByteCount (CurrentMdl) - ByteOffset; + if (AvailableBytes > DesiredLength) { + AvailableBytes = DesiredLength; + } + + OldMdl = CurrentMdl; + *NewCurrentMdl = OldMdl; + *NewByteOffset = ByteOffset + AvailableBytes; + *TrueLength = AvailableBytes; + + + // + // Build the first NDIS_BUFFER, which could conceivably be the only one... + // + + NdisCopyBuffer( + &NdisStatus, + &NewNdisBuffer, + DeviceContext->NdisBufferPool, + OldMdl, + ByteOffset, + AvailableBytes); + + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + *NewByteOffset = ByteOffset; + *TrueLength = 0; + *Destination = NULL; + return STATUS_INSUFFICIENT_RESOURCES; + } + + *Destination = NewNdisBuffer; + + +// IF_NBFDBG (NBF_DEBUG_SENDENG) { +// PVOID PAddr, UINT PLen; +// NdisQueryBuffer (NewNdisBuffer, &PAddr, &PLen); +// NbfPrint4 ("BuildBufferChain: (start)Built Mdl: %lx Length: %lx, Next: %lx Va: %lx\n", +// NewNdisBuffer, PLen, NDIS_BUFFER_LINKAGE(NewNdisBuffer), PAddr); +// } + + // + // Was the first NDIS_BUFFER enough data, or are we out of Mdls? + // + + if ((AvailableBytes == DesiredLength) || (OldMdl->Next == NULL)) { + if (*NewByteOffset >= MmGetMdlByteCount (OldMdl)) { + *NewCurrentMdl = OldMdl->Next; + *NewByteOffset = 0; + } + return STATUS_SUCCESS; + } + + // + // Need more data, so follow the in Mdl chain to create a packet. + // + + OldMdl = OldMdl->Next; + *NewCurrentMdl = OldMdl; + + while (OldMdl != NULL) { + AvailableBytes = DesiredLength - *TrueLength; + if (AvailableBytes > MmGetMdlByteCount (OldMdl)) { + AvailableBytes = MmGetMdlByteCount (OldMdl); + } + + NdisCopyBuffer( + &NdisStatus, + &(NDIS_BUFFER_LINKAGE(NewNdisBuffer)), + DeviceContext->NdisBufferPool, + OldMdl, + 0, + AvailableBytes); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + + // + // ran out of resources. put back what we've used in this call and + // return the error. + // + + while (*Destination != NULL) { + NewNdisBuffer = NDIS_BUFFER_LINKAGE(*Destination); + NdisFreeBuffer (*Destination); + *Destination = NewNdisBuffer; + } + + *NewByteOffset = ByteOffset; + *TrueLength = 0; + *NewCurrentMdl = CurrentMdl; + + return STATUS_INSUFFICIENT_RESOURCES; + } + + NewNdisBuffer = NDIS_BUFFER_LINKAGE(NewNdisBuffer); + + *TrueLength += AvailableBytes; + *NewByteOffset = AvailableBytes; + +// IF_NBFDBG (NBF_DEBUG_SENDENG) { +// PVOID PAddr, UINT PLen; +// NdisQueryBuffer (NewNdisBuffer, &PAddr, &PLen); +// NbfPrint4 ("BuildBufferChain: (continue) Built Mdl: %lx Length: %lx, Next: %lx Va: %lx\n", +// NewNdisBuffer, PLen, NDIS_BUFFER_LINKAGE(NewNdisBuffer), PAddr); +// } + + if (*TrueLength == DesiredLength) { + if (*NewByteOffset == MmGetMdlByteCount (OldMdl)) { + *NewCurrentMdl = OldMdl->Next; + *NewByteOffset = 0; + } + return STATUS_SUCCESS; + } + OldMdl = OldMdl->Next; + *NewCurrentMdl = OldMdl; + + } // while (mdl chain exists) + + *NewCurrentMdl = NULL; + *NewByteOffset = 0; + return STATUS_SUCCESS; + +} // BuildBufferChainFromMdlChain + diff --git a/private/ntos/tdi/nbf/sources b/private/ntos/tdi/nbf/sources new file mode 100644 index 000000000..6d6ab7f87 --- /dev/null +++ b/private/ntos/tdi/nbf/sources @@ -0,0 +1,79 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=nbf + +TARGETNAME=nbf +TARGETPATH=\nt\public\sdk\lib +TARGETTYPE=DRIVER + +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\tdi.lib \ + $(BASEDIR)\public\sdk\lib\*\ndis.lib + +INCLUDES=..\..\inc;..\..\..\inc + +#C_DEFINES = -DRASAUTODIAL +C_DEFINES = -DRASAUTODIAL -D_PNP_POWER + +MSC_WARNING_LEVEL=/W3 /WX + +SOURCES=action.c \ + address.c \ + autodial.c \ + connect.c \ + connobj.c \ + devctx.c \ + dlc.c \ + event.c \ + framecon.c \ + framesnd.c \ + iframes.c \ + info.c \ + link.c \ + linktree.c \ + nbf.rc \ + nbfcnfg.c \ + nbfdrvr.c \ + nbfdebug.c \ + nbfmac.c \ + nbfndis.c \ + nbfpnp.c \ + packet.c \ + rcv.c \ + rcveng.c \ + request.c \ + send.c \ + sendeng.c \ + spnlckdb.c \ + timer.c \ + uframes.c + +!IFNDEF 386_WARNING_LEVEL +386_WARNING_LEVEL=/W3 +!ENDIF + +PRECOMPILED_INCLUDE=precomp.h +PRECOMPILED_PCH=precomp.pch +PRECOMPILED_OBJ=precomp.obj diff --git a/private/ntos/tdi/nbf/spnlckdb.c b/private/ntos/tdi/nbf/spnlckdb.c new file mode 100644 index 000000000..485431ad5 --- /dev/null +++ b/private/ntos/tdi/nbf/spnlckdb.c @@ -0,0 +1,156 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + spnlckdb.c + +Abstract: + + This module contains code which allows debugging of spinlock related NBF + problems. Most of this code is conditional on the manifest constant + NBF_LOCKS. + +Author: + + David Beaver 13-Feb-1991 + (From Chuck Lenzmeier, Jan 1991) + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + +#ifdef NBF_LOCKS + +KSPIN_LOCK NbfGlobalLock = NULL; +PKTHREAD NbfGlobalLockOwner = NULL; +ULONG NbfGlobalLockRecursionCount = 0; +ULONG NbfGlobalLockMaxRecursionCount = 0; +KIRQL NbfGlobalLockPreviousIrql = (KIRQL)-1; +BOOLEAN NbfGlobalLockPrint = 1; + +#define PRINT_ERR if ( (NbfGlobalLockPrint & 1) != 0 ) DbgPrint +#define PRINT_INFO if ( (NbfGlobalLockPrint & 2) != 0 ) DbgPrint + +VOID +NbfAcquireSpinLock( + IN PKSPIN_LOCK Lock, + OUT PKIRQL OldIrql, + IN PSZ LockName, + IN PSZ FileName, + IN ULONG LineNumber + ) +{ + KIRQL previousIrql; + + PKTHREAD currentThread = KeGetCurrentThread( ); + + if ( NbfGlobalLockOwner == currentThread ) { + + ASSERT( Lock != NULL ); // else entering NBF with lock held + + ASSERT( NbfGlobalLockRecursionCount != 0 ); + NbfGlobalLockRecursionCount++; + if ( NbfGlobalLockRecursionCount > NbfGlobalLockMaxRecursionCount ) { + NbfGlobalLockMaxRecursionCount = NbfGlobalLockRecursionCount; + } + + PRINT_INFO( "NBF reentered from %s/%ld, new count %ld\n", + FileName, LineNumber, NbfGlobalLockRecursionCount ); + + } else { + + ASSERT( Lock == NULL ); // else missing an ENTER_NBF call + + KeAcquireSpinLock( &NbfGlobalLock, &previousIrql ); + + ASSERT( NbfGlobalLockRecursionCount == 0 ); + NbfGlobalLockOwner = currentThread; + NbfGlobalLockPreviousIrql = previousIrql; + NbfGlobalLockRecursionCount = 1; + + PRINT_INFO( "NBF entered from %s/%ld\n", FileName, LineNumber ); + + } + + ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL ); + + return; + +} // NbfAcquireSpinLock + +VOID +NbfReleaseSpinLock( + IN PKSPIN_LOCK Lock, + IN KIRQL OldIrql, + IN PSZ LockName, + IN PSZ FileName, + IN ULONG LineNumber + ) +{ + PKTHREAD currentThread = KeGetCurrentThread( ); + KIRQL previousIrql; + + ASSERT( NbfGlobalLockOwner == currentThread ); + ASSERT( NbfGlobalLockRecursionCount != 0 ); + ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL ); + + if ( --NbfGlobalLockRecursionCount == 0 ) { + + ASSERT( Lock == NULL ); // else not exiting NBF, but releasing lock + + NbfGlobalLockOwner = NULL; + previousIrql = NbfGlobalLockPreviousIrql; + NbfGlobalLockPreviousIrql = (KIRQL)-1; + + PRINT_INFO( "NBF exited from %s/%ld\n", FileName, LineNumber ); + + KeReleaseSpinLock( &NbfGlobalLock, previousIrql ); + + } else { + + ASSERT( Lock != NULL ); // else exiting NBF with lock held + + PRINT_INFO( "NBF semiexited from %s/%ld, new count %ld\n", + FileName, LineNumber, NbfGlobalLockRecursionCount ); + + } + + return; + +} // NbfReleaseSpinLock + +VOID +NbfFakeSendCompletionHandler( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus + ) +{ + ENTER_NBF; + NbfSendCompletionHandler (ProtocolBindingContext, NdisPacket, NdisStatus); + LEAVE_NBF; +} + +VOID +NbfFakeTransferDataComplete ( + IN NDIS_HANDLE BindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus, + IN UINT BytesTransferred + ) +{ + ENTER_NBF; + NbfTransferDataComplete (BindingContext, NdisPacket, NdisStatus, BytesTransferred); + LEAVE_NBF; +} + +#endif // def NBF_LOCKS diff --git a/private/ntos/tdi/nbf/testnbf.c b/private/ntos/tdi/nbf/testnbf.c new file mode 100644 index 000000000..91a3ca645 --- /dev/null +++ b/private/ntos/tdi/nbf/testnbf.c @@ -0,0 +1,1466 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + testtdi.c + +Abstract: + + Kernel Mode test program for any Tdi network provider. This routine is an + example of how to use the TDI interface at the kernel level. + +Author: + + Dave Beaver (DBeaver) 5 June 1991 + +Revision History: + +--*/ + +#include "nbf.h" +#include <ctype.h> + +#define TRANSPORT_NAME L"\\Device\\Nbf" + +PSZ ServerName = "DCTDISERVER "; +PSZ ClientName = "DCTDICLIENT "; +PSZ AnyName = "* "; + +static PUCHAR TextBuffer; // dynamically allocated non-paged buffer. +ULONG c9_Xmt = 0xff; +ULONG c9_Rcv = 0xff; +ULONG c9_Iteration = 0xffffffff; + +static ULONG TextBufferLength; // size of the above in bytes. +#define BUFFER_SIZE 0xffff +PUCHAR RBuff; +PUCHAR XBuff; +UCHAR c9_ListBlock[512]; +UCHAR c9_ConnBlock[512]; + +extern KEVENT TdiSendEvent; +extern KEVENT TdiReceiveEvent; +extern KEVENT TdiServerEvent; + +ULONG ApcContext; + +NTSTATUS +TSTRCVCompletion( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PVOID Context + ) +{ + DbgPrint ("TSTRCVCompletion event: %lx\n" , Context); +// KeSetEvent ((PKEVENT)Context, 0, TRUE); + return STATUS_MORE_PROCESSING_REQUIRED; + UNREFERENCED_PARAMETER( DeviceObject ); + UNREFERENCED_PARAMETER( Irp ); +} + +#define InitWaitObject(_event)\ + KeInitializeEvent (\ + _event,\ + SynchronizationEvent,\ + FALSE) + +VOID +NbfTestTimer( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); + +NTSTATUS +TtdiOpenAddress ( + IN PHANDLE FileHandle, + IN PSZ Name + ); + +NTSTATUS +TtdiOpenConnection ( + IN PHANDLE FileHandle, + IN ULONG ConnectionContext + ); + + +NTSTATUS +TtdiOpenAddress ( + IN PHANDLE FileHandle, + IN PSZ Name) +{ + IO_STATUS_BLOCK IoStatusBlock; + NTSTATUS Status; + UNICODE_STRING NameString; + OBJECT_ATTRIBUTES ObjectAttributes; + PFILE_FULL_EA_INFORMATION EaBuffer; + PTDI_ADDRESS_NETBIOS AddressName; + PTRANSPORT_ADDRESS Address; + PTA_ADDRESS AddressType; + int i; + + DbgPrint ("TtdiOpenAddress: Opening "); + DbgPrint (Name); + DbgPrint (".\n"); + RtlInitUnicodeString (&NameString, TRANSPORT_NAME); + InitializeObjectAttributes ( + &ObjectAttributes, + &NameString, + 0, + NULL, + NULL); + + EaBuffer = (PFILE_FULL_EA_INFORMATION)ExAllocatePool (NonPagedPool, 100); + if (EaBuffer == NULL) { + DbgBreakPoint (); + } + + EaBuffer->NextEntryOffset =0; + EaBuffer->Flags = 0; + EaBuffer->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH; + EaBuffer->EaValueLength = sizeof (TDI_ADDRESS_NETBIOS); + + for (i=0;i<(int)EaBuffer->EaNameLength;i++) { + EaBuffer->EaName[i] = TdiTransportAddress[i]; + } + + Address = (PTRANSPORT_ADDRESS)&EaBuffer->EaName[EaBuffer->EaNameLength+1]; + Address->TAAddressCount = 1; + + AddressType = (PTA_ADDRESS)((PUCHAR)Address + sizeof (Address->TAAddressCount)); + + AddressType->AddressType = TDI_ADDRESS_TYPE_NETBIOS; + AddressType->AddressLength = TDI_ADDRESS_LENGTH_NETBIOS; + + AddressName = (PTDI_ADDRESS_NETBIOS)((PUCHAR)AddressType + + sizeof (AddressType->AddressType) + sizeof (AddressType->AddressLength)); + AddressName->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + AddressName->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + + for (i=0;i<16;i++) { + AddressName->NetbiosName[i] = Name[i]; + } + + Status = IoCreateFile ( + FileHandle, + 0, // desired access. + &ObjectAttributes, // object attributes. + &IoStatusBlock, // returned status information. + 0, // block size (unused). + FO_SYNCHRONOUS_IO, // file attributes. + 0, + 0, + 0, // create options. + EaBuffer, // EA buffer. + (PUCHAR)&AddressName->NetbiosName[i] - (PUCHAR)EaBuffer + 1, // ea length + CreateFileTypeNone, + (PVOID)NULL, + 0 ); // EA length. + + if (!NT_SUCCESS( Status )) { + DbgPrint ("TtdiOpenAddress: FAILURE, NtCreateFile returned status code=%lC.\n", Status); + return Status; + } + + Status = IoStatusBlock.Status; + + if (!(NT_SUCCESS( Status ))) { + DbgPrint ("TtdiOpenAddress: FAILURE, IoStatusBlock.Status contains status code=%lC.\n", Status); + } + + DbgPrint ("TtdiOpenAddress: returning\n"); + + return Status; +} /* TtdiOpenAddress */ + + +NTSTATUS +TtdiOpenConnection (IN PHANDLE FileHandle, IN ULONG ConnectionContext) +{ + IO_STATUS_BLOCK IoStatusBlock; + NTSTATUS Status; + UNICODE_STRING NameString; + OBJECT_ATTRIBUTES ObjectAttributes; + PFILE_FULL_EA_INFORMATION EaBuffer; + int i; + + DbgPrint ("TtdiOpenConnection: Opening Context %lx...\n ", + ConnectionContext); + RtlInitUnicodeString (&NameString, TRANSPORT_NAME); + InitializeObjectAttributes ( + &ObjectAttributes, + &NameString, + 0, + NULL, + NULL); + + EaBuffer = (PFILE_FULL_EA_INFORMATION)ExAllocatePool (NonPagedPool, 100); + if (EaBuffer == NULL) { + DbgBreakPoint (); + } + + EaBuffer->NextEntryOffset =0; + EaBuffer->Flags = 0; + EaBuffer->EaNameLength = TDI_CONNECTION_CONTEXT_LENGTH; + EaBuffer->EaValueLength = sizeof (ULONG); + for (i=0;i<(int)EaBuffer->EaNameLength;i++) { + EaBuffer->EaName[i] = TdiConnectionContext[i]; + } + + RtlMoveMemory ( + &EaBuffer->EaName[EaBuffer->EaValueLength + 1], + &ConnectionContext, + sizeof (PVOID)); + + Status = NtCreateFile ( + FileHandle, + 0, + &ObjectAttributes, // object attributes. + &IoStatusBlock, // returned status information. + 0, // block size (unused). + FO_SYNCHRONOUS_IO, // file attributes. + 0, + 0, + 0, // create options. + EaBuffer, // EA buffer. + 100); // EA length. + + if (!NT_SUCCESS( Status )) { + DbgPrint ("TtdiOpenConnection: FAILURE, NtCreateFile returned status code=%lC.\n", Status); + return Status; + } + + Status = IoStatusBlock.Status; + + if (!(NT_SUCCESS( Status ))) { + DbgPrint ("TtdiOpenConnection: FAILURE, IoStatusBlock.Status contains status code=%lC.\n", Status); + } + + DbgPrint ("TtdiOpenConnection: returning\n"); + + return Status; +} /* TtdiOpenEndpoint */ + +NTSTATUS +CloseAddress (IN HANDLE FileHandle) +{ + NTSTATUS Status; + + Status = NtClose (FileHandle); + + if (!(NT_SUCCESS( Status ))) { + DbgPrint ("CloseAddress: FAILURE, NtClose returned status code=%lC.\n", Status); + } else { + DbgPrint ("CloseAddress: NT_SUCCESS.\n"); + } + + return Status; +} /* CloseAddress */ + + +BOOLEAN +TtdiSend() +{ + USHORT i, Iteration, Increment; + HANDLE RdrHandle, RdrConnectionHandle; + KEVENT Event1; + PFILE_OBJECT AddressObject, ConnectionObject; + PDEVICE_OBJECT DeviceObject; + NTSTATUS Status; + PMDL SendMdl, ReceiveMdl; + IO_STATUS_BLOCK Iosb1; + TDI_CONNECTION_INFORMATION RequestInformation; + TDI_CONNECTION_INFORMATION ReturnInformation; + PTRANSPORT_ADDRESS ListenBlock; + PTRANSPORT_ADDRESS ConnectBlock; + PTDI_ADDRESS_NETBIOS temp; + PUCHAR MessageBuffer; + ULONG MessageBufferLength; + ULONG CurrentBufferLength; + PUCHAR SendBuffer; + ULONG SendBufferLength; + PIRP Irp; + + Status = KeWaitForSingleObject (&TdiSendEvent, Suspended, KernelMode, FALSE, NULL); + + SendBufferLength = (ULONG)BUFFER_SIZE; + MessageBufferLength = (ULONG)BUFFER_SIZE; + + + DbgPrint( "\n****** Start of Send Test ******\n" ); + + XBuff = ExAllocatePool (NonPagedPool, BUFFER_SIZE); + if (XBuff == (PVOID)NULL) { + DbgPrint ("Unable to allocate nonpaged pool for send buffer exiting\n"); + return FALSE; + } + RBuff = ExAllocatePool (NonPagedPool, BUFFER_SIZE); + if (RBuff == (PVOID)NULL) { + DbgPrint ("Unable to allocate nonpaged pool for receive buffer exiting\n"); + return FALSE; + } + + ListenBlock = ExAllocatePool (NonPagedPool, sizeof (TRANSPORT_ADDRESS) + + sizeof (TDI_ADDRESS_NETBIOS)); + ConnectBlock = ExAllocatePool (NonPagedPool, sizeof (TRANSPORT_ADDRESS) + + sizeof (TDI_ADDRESS_NETBIOS)); + + ListenBlock->TAAddressCount = 1; + ListenBlock->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; + ListenBlock->Address[0].AddressLength = sizeof (TDI_ADDRESS_NETBIOS); + temp = (PTDI_ADDRESS_NETBIOS)ListenBlock->Address[0].Address; + + temp->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + for (i=0;i<16;i++) { + temp->NetbiosName[i] = ClientName[i]; + } + + ConnectBlock->TAAddressCount = 1; + ConnectBlock->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; + ConnectBlock->Address[0].AddressLength = sizeof (TDI_ADDRESS_NETBIOS); + temp = (PTDI_ADDRESS_NETBIOS)ConnectBlock->Address[0].Address; + + temp->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + for (i=0;i<16;i++) { + temp->NetbiosName[i] = ServerName[i]; + } + + // + // Create an event for the synchronous I/O requests that we'll be issuing. + // + + KeInitializeEvent ( + &Event1, + SynchronizationEvent, + FALSE); + + Status = TtdiOpenAddress (&RdrHandle, AnyName); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Send Test: FAILED on open of client: %lC ******\n", Status ); + return FALSE; + } + + Status = ObReferenceObjectByHandle ( + RdrHandle, + 0L, + NULL, + KernelMode, + (PVOID *) &AddressObject, + NULL); + + // + // Open the connection on the transport. + // + + Status = TtdiOpenConnection (&RdrConnectionHandle, 1); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Send Test: FAILED on open of server Connection: %lC ******\n", Status ); + return FALSE; + } + + Status = ObReferenceObjectByHandle ( + RdrConnectionHandle, + 0L, + NULL, + KernelMode, + (PVOID *) &ConnectionObject, + NULL); + + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Send Test: FAILED on open of server Connection: %lC ******\n", Status ); + return FALSE; + } + + // + // Get a pointer to the stack location for the first driver. This will be + // used to pass the original function codes and parameters. + // + + DeviceObject = IoGetRelatedDeviceObject( ConnectionObject ); + + Irp = TdiBuildInternalDeviceControlIrp ( + TDI_ASSOCIATE_ADDRESS, + DeviceObject, + ConnectionObject, + &Event1, + &Iosb1); + + + // + // Get a pointer to the stack location for the first driver. This will be + // used to pass the original function codes and parameters. + // + + TdiBuildAssociateAddress (Irp, + DeviceObject, + ConnectionObject, + TSTRCVCompletion, + &Event1, + RdrHandle); + + Status = IoCallDriver (DeviceObject, Irp); + +// IoFreeIrp (Irp); + + if (Status == STATUS_PENDING) { + Status = KeWaitForSingleObject (&Event1, Suspended, KernelMode, TRUE, NULL); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Send Test: FAILED Event1 Wait Associate: %lC ******\n", Status ); + return FALSE; + } + if (!NT_SUCCESS(Iosb1.Status)) { + DbgPrint( "\n****** Send Test: FAILED Associate Iosb status: %lC ******\n", Status ); + return FALSE; + } + + } else { + if (!NT_SUCCESS (Status)) { + DbgPrint( "\n****** Send Test: AssociateAddress FAILED Status: %lC ******\n", Status ); + return FALSE; + } else { + DbgPrint ("********** Send Test: Success AssociateAddress\n"); + } + } + + // + // Post a TdiConnect to the client endpoint. + // + + RequestInformation.RemoteAddress = ConnectBlock; + RequestInformation.RemoteAddressLength = sizeof (TRANSPORT_ADDRESS) + + sizeof (TDI_ADDRESS_NETBIOS); + + KeInitializeEvent ( + &Event1, + SynchronizationEvent, + FALSE); + + Irp = TdiBuildInternalDeviceControlIrp ( + TDI_CONNECT, + DeviceObject, + ConnectionObject, + &Event1, + &Iosb1); + + TdiBuildConnect ( + Irp, + DeviceObject, + ConnectionObject, + TSTRCVCompletion, + &Event1, + 0, + &RequestInformation, + &ReturnInformation); + + InitWaitObject (&Event1); + + Status = IoCallDriver (DeviceObject, Irp); + +// IoFreeIrp (Irp); + + if (Status == STATUS_PENDING) { + Status = KeWaitForSingleObject (&Event1, Suspended, KernelMode, TRUE, NULL); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Send Test: FAILED Event1 Wait Connect: %lC ******\n", Status ); + return FALSE; + } + if (!NT_SUCCESS(Iosb1.Status)) { + DbgPrint( "\n****** Send Test: FAILED Iosb status Connect: %lC ******\n", Status ); + return FALSE; + } else { + DbgPrint ("********** Send Test: Success Connect Iosb\n"); + } + + } else { + if (!NT_SUCCESS (Status)) { + DbgPrint( "\n****** Send Test: Connect FAILED Status: %lC ******\n", Status ); + return FALSE; + } else { + DbgPrint ("********** Send Test: Success Connect Immediate\n"); + } + } + + DbgPrint( "\n****** Send Test: SUCCESSFUL TdiConnect: ******\n"); + + // + // Send/receive 1 or 10 messages. + // + + SendBuffer = (PUCHAR)ExAllocatePool (NonPagedPool, SendBufferLength); + if (SendBuffer == NULL) { + DbgPrint ("\n****** Send Test: ExAllocatePool failed! ******\n"); + } + SendMdl = IoAllocateMdl (SendBuffer, SendBufferLength, FALSE, FALSE, NULL); + MmBuildMdlForNonPagedPool (SendMdl); + + MessageBuffer=(PUCHAR)ExAllocatePool (NonPagedPool, MessageBufferLength); + if (MessageBuffer == NULL) { + DbgPrint ("\n****** Send Test: ExAllocatePool failed! ******\n"); + } + ReceiveMdl = IoAllocateMdl (MessageBuffer, MessageBufferLength, FALSE, FALSE, NULL); + MmBuildMdlForNonPagedPool (ReceiveMdl); + + // + // Cycle the buffer length from 0 up through the maximum for Tdi. after a + // couple of shots at the full range in one byte steps, increment by ever + // increasing amounts to get to the max. + // + + CurrentBufferLength = 0; + Increment = 1; + for (Iteration=1; Iteration<(USHORT)c9_Iteration; Iteration++) { + CurrentBufferLength += Increment; + if (CurrentBufferLength > MessageBufferLength) { + CurrentBufferLength = 0; + Increment = 1; + } + if (CurrentBufferLength > 7500) { + Increment++; + } + if ((USHORT)((Iteration / 100) * 100) == Iteration) { + DbgPrint ("Iteration #%d Buffer Length: %lx Buffer Start: %x\n", + Iteration, CurrentBufferLength,Iteration % 256); + } + for (i=0; i<(USHORT)CurrentBufferLength; i++) { + SendBuffer [i] = (UCHAR)(i + Iteration % 256 ); + MessageBuffer [i] = 0; // zap this sucker with something. + } + + // + // Now issue a send on the client side. + // + + KeInitializeEvent ( + &Event1, + SynchronizationEvent, + FALSE); + + Irp = TdiBuildInternalDeviceControlIrp ( + TDI_SEND, + DeviceObject, + ConnectionObject, + &Event1, + &Iosb1); + + TdiBuildSend (Irp, + DeviceObject, + ConnectionObject, + TSTRCVCompletion, + &Event1, + ReceiveMdl, + 0, + CurrentBufferLength); + + InitWaitObject (&Event1); + + Status = IoCallDriver (DeviceObject, Irp); + +// IoFreeIrp (Irp); + + if (Status == STATUS_PENDING) { + Status = KeWaitForSingleObject (&Event1, Suspended, KernelMode, TRUE, NULL); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Send Test: FAILED Event1 Wait Send: %lC %d ******\n", + Status, Iteration ); + return FALSE; + } + if (!NT_SUCCESS(Iosb1.Status)) { + DbgPrint( "\n****** Send Test: FAILED Iosb status Send: %lC %d ******\n", + Status, Iteration ); + return FALSE; + } else { + DbgPrint ("********** Send Test: Success SendIosb\n"); + } + + } else { + if (!NT_SUCCESS (Status)) { + DbgPrint( "\n****** Send Test: Send FAILED Status: %lC %d ******\n", + Status, Iteration ); + return FALSE; + } else { + DbgPrint ("********** Send Test: Success Send Immediate\n"); + } + } + + if (Iosb1.Information != CurrentBufferLength) { + DbgPrint ("SendTest: Bytes sent <> Send buffer size.\n"); + DbgPrint ("SendTest: BytesToSend=%ld. BytesSent=%ld.\n", + CurrentBufferLength, Iosb1.Information); + } + + } + + // + // We're done with this endpoint. Close it and get out. + // + + Status = CloseAddress (RdrHandle); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Send Test: FAILED on 2nd Close: %lC ******\n", Status ); + return FALSE; + } + + DbgPrint( "\n****** End of Send Test ******\n" ); + return TRUE; +} /* Send */ + + +BOOLEAN +TtdiReceive() +{ + USHORT i, Iteration, Increment; + SHORT j,k; + HANDLE SvrHandle, SvrConnectionHandle; + PFILE_OBJECT AddressObject, ConnectionObject; + PDEVICE_OBJECT DeviceObject; + NTSTATUS Status; + PMDL SendMdl, ReceiveMdl; + IO_STATUS_BLOCK Iosb1; + TDI_CONNECTION_INFORMATION RequestInformation; + TDI_CONNECTION_INFORMATION ReturnInformation; + PTRANSPORT_ADDRESS ListenBlock; + PTRANSPORT_ADDRESS ConnectBlock; + PTDI_ADDRESS_NETBIOS temp; + PUCHAR MessageBuffer; + ULONG MessageBufferLength; + ULONG CurrentBufferLength; + PUCHAR SendBuffer; + ULONG SendBufferLength; + PIRP Irp; + KEVENT Event1; + + Status = KeWaitForSingleObject (&TdiReceiveEvent, Suspended, KernelMode, FALSE, NULL); + + SendBufferLength = (ULONG)BUFFER_SIZE; + MessageBufferLength = (ULONG)BUFFER_SIZE; + + + DbgPrint( "\n****** Start of Receive Test ******\n" ); + + XBuff = ExAllocatePool (NonPagedPool, BUFFER_SIZE); + if (XBuff == (PVOID)NULL) { + DbgPrint ("Unable to allocate nonpaged pool for send buffer exiting\n"); + return FALSE; + } + RBuff = ExAllocatePool (NonPagedPool, BUFFER_SIZE); + if (RBuff == (PVOID)NULL) { + DbgPrint ("Unable to allocate nonpaged pool for receive buffer exiting\n"); + return FALSE; + } + + ListenBlock = ExAllocatePool (NonPagedPool, sizeof (TRANSPORT_ADDRESS) + + sizeof (TDI_ADDRESS_NETBIOS)); + ConnectBlock = ExAllocatePool (NonPagedPool, sizeof (TRANSPORT_ADDRESS) + + sizeof (TDI_ADDRESS_NETBIOS)); + + ListenBlock->TAAddressCount = 1; + ListenBlock->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; + ListenBlock->Address[0].AddressLength = sizeof (TDI_ADDRESS_NETBIOS); + temp = (PTDI_ADDRESS_NETBIOS)ListenBlock->Address[0].Address; + + temp->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + for (i=0;i<16;i++) { + temp->NetbiosName[i] = ClientName[i]; + } + + ConnectBlock->TAAddressCount = 1; + ConnectBlock->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; + ConnectBlock->Address[0].AddressLength = sizeof (TDI_ADDRESS_NETBIOS); + temp = (PTDI_ADDRESS_NETBIOS)ConnectBlock->Address[0].Address; + + temp->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + for (i=0;i<16;i++) { + temp->NetbiosName[i] = ServerName[i]; + } + + // + // Create an event for the synchronous I/O requests that we'll be issuing. + // + + KeInitializeEvent ( + &Event1, + SynchronizationEvent, + FALSE); + + Status = TtdiOpenAddress (&SvrHandle, ServerName); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Receive Test: FAILED on open of server Address: %lC ******\n", Status ); + return FALSE; + } + + Status = ObReferenceObjectByHandle ( + SvrHandle, + 0L, + NULL, + KernelMode, + (PVOID *) &AddressObject, + NULL); + + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Receive Test: FAILED on open of server Address: %lC ******\n", Status ); + return FALSE; + } + + Status = TtdiOpenConnection (&SvrConnectionHandle, 2); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Receive Test: FAILED on open of server Connection: %lC ******\n", Status ); + return FALSE; + } + + Status = ObReferenceObjectByHandle ( + SvrConnectionHandle, + 0L, + NULL, + KernelMode, + (PVOID *) &ConnectionObject, + NULL); + + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Receive Test: FAILED on open of server Connection: %lC ******\n", Status ); + return FALSE; + } + + // + // Get a pointer to the stack location for the first driver. This will be + // used to pass the original function codes and parameters. + // + + DeviceObject = IoGetRelatedDeviceObject( ConnectionObject ); + + + Irp = TdiBuildInternalDeviceControlIrp ( + TDI_ASSOCIATE_ADDRESS, + DeviceObject, + ConnectionObject, + &Event1, + &Iosb1); + + DbgPrint ("Build Irp %lx, Handle %lx \n", + Irp, SvrHandle); + + TdiBuildAssociateAddress ( + Irp, + DeviceObject, + ConnectionObject, + TSTRCVCompletion, + &Event1, + SvrHandle); + InitWaitObject (&Event1); + + { + PULONG Temp=(PULONG)IoGetNextIrpStackLocation (Irp); + DbgPrint ("Built IrpSp %lx %lx %lx %lx %lx \n", *(Temp++), *(Temp++), + *(Temp++), *(Temp++), *(Temp++)); + } + + Status = IoCallDriver (DeviceObject, Irp); + +// IoFreeIrp (Irp); + + if (Status == STATUS_PENDING) { + Status = KeWaitForSingleObject (&Event1, Suspended, KernelMode, TRUE, NULL); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Receive Test: FAILED Event1 Wait Associate: %lC ******\n", Status ); + return FALSE; + } + if (!NT_SUCCESS(Iosb1.Status)) { + DbgPrint( "\n****** Receive Test: FAILED Associate Iosb status: %lC ******\n", Status ); + return FALSE; + } + + } else { + if (!NT_SUCCESS (Status)) { + DbgPrint( "\n****** Receive Test: AssociateAddress FAILED Status: %lC ******\n", Status ); + return FALSE; + } else { + DbgPrint ("********** Receive Test: Success AssociateAddress\n"); + } + } + + RequestInformation.RemoteAddress = ConnectBlock; + RequestInformation.RemoteAddressLength = sizeof (TRANSPORT_ADDRESS) + + sizeof (TDI_ADDRESS_NETBIOS); + + // + // Post a TdiListen to the server endpoint. + // + + KeInitializeEvent ( + &Event1, + SynchronizationEvent, + FALSE); + + Irp = TdiBuildInternalDeviceControlIrp ( + TDI_LISTEN, + DeviceObject, + ConnectionObject, + &Event1, + &Iosb1); + + TdiBuildListen ( + Irp, + DeviceObject, + ConnectionObject, + TSTRCVCompletion, + &Event1, + 0, + &RequestInformation, + &ReturnInformation); + InitWaitObject (&Event1); + + Status = IoCallDriver (DeviceObject, Irp); + +// IoFreeIrp (Irp); + + if (Status == STATUS_PENDING) { + Status = KeWaitForSingleObject (&Event1, Suspended, KernelMode, TRUE, NULL); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Receive Test: FAILED Event1 Wait Listen: %lC ******\n", Status ); + return FALSE; + } + if (!NT_SUCCESS(Iosb1.Status)) { + DbgPrint( "\n****** Receive Test: FAILED Listen Iosb status: %lC ******\n", Status ); + return FALSE; + } else { + DbgPrint ("********** Receive Test: Success Listen IOSB\n"); + } + + } else { + if (!NT_SUCCESS (Status)) { + DbgPrint( "\n****** Receive Test: Listen FAILED Status: %lC ******\n", Status ); + return FALSE; + } else { + DbgPrint ("********** Receive Test: Success Listen Immediate\n"); + } + } + + + DbgPrint ("\n****** Receive Test: LISTEN just completed! ******\n"); + + KeInitializeEvent ( + &Event1, + SynchronizationEvent, + FALSE); + + Irp = TdiBuildInternalDeviceControlIrp ( + TDI_ACCEPT, + DeviceObject, + ConnectionObject, + &Event1, + &Iosb1); + + TdiBuildAccept (Irp, DeviceObject, ConnectionObject, NULL, NULL, &RequestInformation, NULL, 0); + InitWaitObject (&Event1); + + Status = IoCallDriver (DeviceObject, Irp); + +// IoFreeIrp (Irp); + + if (Status == STATUS_PENDING) { + Status = KeWaitForSingleObject (&Event1, Suspended, KernelMode, TRUE, NULL); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Receive Test: FAILED Event1 Wait Accept: %lC ******\n", Status ); + return FALSE; + } + if (!NT_SUCCESS(Iosb1.Status)) { + DbgPrint( "\n****** Receive Test: FAILED Accept Iosb status: %lC ******\n", Status ); + return FALSE; + } + + } else { + if (!NT_SUCCESS (Status)) { + DbgPrint( "\n****** Receive Test: Accept FAILED Status: %lC ******\n", Status ); + return FALSE; + } + } + + // + // We have the connection data now. Sanity check it. + // + + DbgPrint ("\n****** Receive Test: LISTEN completed successfully! ******\n"); + + // + // Receive/receive 1 or 10 messages. + // + + SendBuffer = (PUCHAR)ExAllocatePool (NonPagedPool, SendBufferLength); + if (SendBuffer == NULL) { + DbgPrint ("\n****** Send Test: ExAllocatePool failed! ******\n"); + } + SendMdl = IoAllocateMdl (SendBuffer, SendBufferLength, FALSE, FALSE, NULL); + MmBuildMdlForNonPagedPool (SendMdl); + + MessageBuffer=(PUCHAR)ExAllocatePool (NonPagedPool, MessageBufferLength); + if (MessageBuffer == NULL) { + DbgPrint ("\n****** Send Test: ExAllocatePool failed! ******\n"); + } + ReceiveMdl = IoAllocateMdl (MessageBuffer, MessageBufferLength, FALSE, FALSE, NULL); + MmBuildMdlForNonPagedPool (ReceiveMdl); + + // + // Cycle the buffer length from 0 up through the maximum for Tdi. after a + // couple of shots at the full range in one byte steps, increment by ever + // increasing amounts to get to the max. + // + + CurrentBufferLength = 0; + Increment = 1; + for (Iteration=1; Iteration<(USHORT)c9_Iteration; Iteration++) { + CurrentBufferLength += Increment; + if (CurrentBufferLength > MessageBufferLength) { + CurrentBufferLength = 0; + Increment = 1; + } + if (CurrentBufferLength > 7500) { + Increment++; + } + if ((USHORT)((Iteration / 100) * 100) == Iteration) { + DbgPrint ("Iteration #%d Buffer Length: %lx Buffer Start: %x\n", + Iteration, CurrentBufferLength,Iteration % 256); + } + for (i=0; i<(USHORT)CurrentBufferLength; i++) { + SendBuffer [i] = (UCHAR)(i + Iteration % 256 ); + MessageBuffer [i] = 0; // zap this sucker with something. + } + + KeInitializeEvent ( + &Event1, + SynchronizationEvent, + FALSE); + + Irp = TdiBuildInternalDeviceControlIrp ( + TDI_RECEIVE, + DeviceObject, + ConnectionObject, + &Event1, + &Iosb1); + + TdiBuildReceive (Irp, + DeviceObject, + ConnectionObject, + TSTRCVCompletion, + &Event1, + ReceiveMdl, + MessageBufferLength); + + InitWaitObject (&Event1); + + Status = IoCallDriver (DeviceObject, Irp); + +// IoFreeIrp (Irp); + + if (Status == STATUS_PENDING) { + Status = KeWaitForSingleObject (&Event1, Suspended, KernelMode, TRUE, NULL); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Receive Test: FAILED Event1 Wait Receive: %lC ******\n", Status ); + return FALSE; + } + if (!NT_SUCCESS(Iosb1.Status)) { + DbgPrint( "\n****** Receive Test: FAILED Receive Iosb status: %lC ******\n", Status ); + return FALSE; + } + + } else { + if (!NT_SUCCESS (Status)) { + DbgPrint( "\n****** Receive Test: Listen FAILED Status: %lC ******\n", Status ); + return FALSE; + } + } + + // + // The receive completed. Make sure the data is correct. + // + + if (Iosb1.Information != CurrentBufferLength) { + DbgPrint ("Iteration #%d Buffer Length: %lx Buffer Start: %x\n", + Iteration, CurrentBufferLength,Iteration % 256); + DbgPrint ("ReceiveTest: Bytes received <> bytes sent.\n"); + DbgPrint ("ReceiveTest: BytesToSend=%ld. BytesReceived=%ld.\n", + CurrentBufferLength, Iosb1.Information); + } + + if (i == (USHORT)CurrentBufferLength) { +// DbgPrint ("ReceiveTest: Message contains correct data.\n"); + } else { + DbgPrint ("ReceiveTest: Message data corrupted at offset %lx of %lx.\n", (ULONG)i, (ULONG)SendBufferLength); + DbgPrint ("ReceiveTest: Data around corrupted location:\n"); + for (j=-4;j<=3;j++) { + DbgPrint ("%08lx ", (ULONG) i+j*16); + for (k=(SHORT)i+(j*(SHORT)16);k<(SHORT)i+((j+(SHORT)1)*(SHORT)16);k++) { + DbgPrint ("%02x",MessageBuffer [k]); + } + for (k=(SHORT)i+(j*(SHORT)16);k<(SHORT)i+((j+(SHORT)1)*(SHORT)16);k++) { + DbgPrint ("%c",MessageBuffer [k]); + } + DbgPrint ("\n"); + } + DbgPrint ("ReceiveTest: End of Corrupt Data.\n"); + } + } + + // + // We're done with this endpoint. Close it and get out. + // + + Status = CloseAddress (SvrHandle); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Receive Test: FAILED on 1st Close: %lC ******\n", Status ); + return FALSE; + } + + DbgPrint( "\n****** End of Receive Test ******\n" ); + return TRUE; +} /* Receive */ + +BOOLEAN +TtdiServer() +{ + USHORT i; + HANDLE RdrHandle, SrvConnectionHandle; + KEVENT Event1; + PFILE_OBJECT AddressObject, ConnectionObject; + PDEVICE_OBJECT DeviceObject; + NTSTATUS Status; + PMDL ReceiveMdl; + IO_STATUS_BLOCK Iosb1; + TDI_CONNECTION_INFORMATION RequestInformation; + TDI_CONNECTION_INFORMATION ReturnInformation; + PTRANSPORT_ADDRESS ListenBlock; + PTRANSPORT_ADDRESS ConnectBlock; + PTDI_ADDRESS_NETBIOS temp; + PUCHAR MessageBuffer; + ULONG MessageBufferLength; + ULONG CurrentBufferLength; + PIRP Irp; + + Status = KeWaitForSingleObject (&TdiServerEvent, Suspended, KernelMode, FALSE, NULL); + + MessageBufferLength = (ULONG)BUFFER_SIZE; + + + DbgPrint( "\n****** Start of Server Test ******\n" ); + + RBuff = ExAllocatePool (NonPagedPool, BUFFER_SIZE); + if (RBuff == (PVOID)NULL) { + DbgPrint ("Unable to allocate nonpaged pool for receive buffer exiting\n"); + return FALSE; + } + + ListenBlock = ExAllocatePool (NonPagedPool, sizeof (TRANSPORT_ADDRESS) + + sizeof (TDI_ADDRESS_NETBIOS)); + ConnectBlock = ExAllocatePool (NonPagedPool, sizeof (TRANSPORT_ADDRESS) + + sizeof (TDI_ADDRESS_NETBIOS)); + + ListenBlock->TAAddressCount = 1; + ListenBlock->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; + ListenBlock->Address[0].AddressLength = sizeof (TDI_ADDRESS_NETBIOS); + temp = (PTDI_ADDRESS_NETBIOS)ListenBlock->Address[0].Address; + + temp->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + for (i=0;i<16;i++) { + temp->NetbiosName[i] = AnyName[i]; + } + + ConnectBlock->TAAddressCount = 1; + ConnectBlock->Address[0].AddressType = TDI_ADDRESS_TYPE_NETBIOS; + ConnectBlock->Address[0].AddressLength = sizeof (TDI_ADDRESS_NETBIOS); + temp = (PTDI_ADDRESS_NETBIOS)ConnectBlock->Address[0].Address; + + temp->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + for (i=0;i<16;i++) { + temp->NetbiosName[i] = ServerName[i]; + } + + // + // Create an event for the synchronous I/O requests that we'll be issuing. + // + + KeInitializeEvent ( + &Event1, + SynchronizationEvent, + FALSE); + + Status = TtdiOpenAddress (&RdrHandle, ServerName); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Server Test: FAILED on open of client: %lC ******\n", Status ); + return FALSE; + } + + Status = ObReferenceObjectByHandle ( + RdrHandle, + 0L, + NULL, + KernelMode, + (PVOID *) &AddressObject, + NULL); + + // + // Now loop forever trying to get a connection from a remote client to + // this server. We will create connections until we run out of resources, + // and we will echo the data we are sent back along the same connection. + // Sends and Receives are always asynchronous, while listens are + // synchronous. + // + + while (TRUE) { + + // + // Open the connection on the transport. + // + + Status = TtdiOpenConnection (&SrvConnectionHandle, 1); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Server Test: FAILED on open of server Connection: %lC ******\n", Status ); + return FALSE; + } + + Status = ObReferenceObjectByHandle ( + SrvConnectionHandle, + 0L, + NULL, + KernelMode, + (PVOID *) &ConnectionObject, + NULL); + + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Server Test: FAILED on open of server Connection: %lC ******\n", Status ); + return FALSE; + } + + // + // Get a pointer to the stack location for the first driver. This will be + // used to pass the original function codes and parameters. + // + + DeviceObject = IoGetRelatedDeviceObject( ConnectionObject ); + + // + // Now register the device handler for receives + // + +// Irp = TdiBuildInternalDeviceControlIrp ( +// TDI_SET_EVENT_HANDLER, +// DeviceObject, +// ConnectionObject, +// &Event1, +// &Iosb1); + +// TdiBuildSetEventHandler (Irp, +// DeviceObject, +// ConnectionObject, +// TSTRCVCompletion, +// &Event1, +// TDI_RECEIVE_HANDLER, +// TdiTestReceiveHandler, +// ConnectionObject); + +// Status = IoCallDriver (DeviceObject, Irp); + +// if (Status == STATUS_PENDING) { +// Status = KeWaitForSingleObject (&Event1, Suspended, KernelMode, TRUE, NULL); +// if (!NT_SUCCESS(Status)) { +// DbgPrint( "\n****** Server Test: FAILED Event1 Wait Register: %lC ******\n", Status ); +// return FALSE; +// } +// if (!NT_SUCCESS(Iosb1.Status)) { +// DbgPrint( "\n****** Server Test: FAILED Register Iosb status: %lC ******\n", Status ); +// return FALSE; +// } +// +// } else { +// if (!NT_SUCCESS (Status)) { +// DbgPrint( "\n****** Server Test: RegisterHandler FAILED Status: %lC ******\n", Status ); +// return FALSE; +// } +// } + + Irp = TdiBuildInternalDeviceControlIrp ( + TDI_ASSOCIATE_ADDRESS, + DeviceObject, + ConnectionObject, + &Event1, + &Iosb1); + + TdiBuildAssociateAddress (Irp, + DeviceObject, + ConnectionObject, + TSTRCVCompletion, + &Event1, + RdrHandle); + + Status = IoCallDriver (DeviceObject, Irp); + + // IoFreeIrp (Irp); + + if (Status == STATUS_PENDING) { + Status = KeWaitForSingleObject (&Event1, Suspended, KernelMode, TRUE, NULL); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Server Test: FAILED Event1 Wait Associate: %lC ******\n", Status ); + return FALSE; + } + if (!NT_SUCCESS(Iosb1.Status)) { + DbgPrint( "\n****** Server Test: FAILED Associate Iosb status: %lC ******\n", Status ); + return FALSE; + } + + } else { + if (!NT_SUCCESS (Status)) { + DbgPrint( "\n****** Server Test: AssociateAddress FAILED Status: %lC ******\n", Status ); + return FALSE; + } + } + + // + // Post a TdiListen to the server endpoint. + // + + RequestInformation.RemoteAddress = ListenBlock; + RequestInformation.RemoteAddressLength = sizeof (TRANSPORT_ADDRESS) + + sizeof (TDI_ADDRESS_NETBIOS); + + KeInitializeEvent ( + &Event1, + SynchronizationEvent, + FALSE); + + Irp = TdiBuildInternalDeviceControlIrp ( + TDI_LISTEN, + DeviceObject, + ConnectionObject, + &Event1, + &Iosb1); + + TdiBuildListen ( + Irp, + DeviceObject, + ConnectionObject, + TSTRCVCompletion, + &Event1, + 0, + &RequestInformation, + NULL); + + Status = IoCallDriver (DeviceObject, Irp); + + if (Status == STATUS_PENDING) { + Status = KeWaitForSingleObject (&Event1, Suspended, KernelMode, TRUE, NULL); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Server Test: FAILED Event1 Wait Listen: %lC ******\n", Status ); + return FALSE; + } + if (!NT_SUCCESS(Iosb1.Status)) { + DbgPrint( "\n****** Server Test: FAILED Listen Iosb status: %lC ******\n", Status ); + return FALSE; + } + + } else { + if (!NT_SUCCESS (Status)) { + DbgPrint( "\n****** Server Test: Listen FAILED Status: %lC ******\n", Status ); + return FALSE; + } + } + + DbgPrint ("\n****** Server Test: LISTEN just completed! ******\n"); + + // + // accept the connection from the remote + // + + KeInitializeEvent ( + &Event1, + SynchronizationEvent, + FALSE); + + Irp = TdiBuildInternalDeviceControlIrp ( + TDI_ACCEPT, + DeviceObject, + ConnectionObject, + &Event1, + &Iosb1); + + TdiBuildAccept ( + Irp, + DeviceObject, + ConnectionObject, + NULL, + NULL, + &RequestInformation, + NULL, + 0); + + Status = IoCallDriver (DeviceObject, Irp); + + // IoFreeIrp (Irp); + + if (Status == STATUS_PENDING) { + Status = KeWaitForSingleObject (&Event1, Suspended, KernelMode, TRUE, NULL); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Receive Test: FAILED Event1 Wait Accept: %lC ******\n", Status ); + return FALSE; + } + if (!NT_SUCCESS(Iosb1.Status)) { + DbgPrint( "\n****** Receive Test: FAILED Accept Iosb status: %lC ******\n", Status ); + return FALSE; + } + + } else { + if (!NT_SUCCESS (Status)) { + DbgPrint( "\n****** Accept Test: Listen FAILED Status: %lC ******\n", Status ); + return FALSE; + } + } + + // + // Get a buffer for the continued read/write loop. + // + + MessageBuffer=(PUCHAR)ExAllocatePool (NonPagedPool, MessageBufferLength); + if (MessageBuffer == NULL) { + DbgPrint ("\n****** Send Test: ExAllocatePool failed! ******\n"); + } + ReceiveMdl = IoAllocateMdl (MessageBuffer, MessageBufferLength, FALSE, FALSE, NULL); + MmBuildMdlForNonPagedPool (ReceiveMdl); + + // + // have a receive buffer, and a connection; go ahead and read and write + // until the remote disconnects. + // + + while (TRUE) { + + KeInitializeEvent ( + &Event1, + SynchronizationEvent, + FALSE); + + Irp = TdiBuildInternalDeviceControlIrp ( + TDI_RECEIVE, + DeviceObject, + ConnectionObject, + &Event1, + &Iosb1); + + TdiBuildReceive (Irp, + DeviceObject, + ConnectionObject, + TSTRCVCompletion, + &Event1, + ReceiveMdl, + MessageBufferLength); + + InitWaitObject (&Event1); + + Status = IoCallDriver (DeviceObject, Irp); + + if (Status == STATUS_PENDING) { + Status = KeWaitForSingleObject (&Event1, Suspended, KernelMode, TRUE, NULL); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Receive Test: FAILED Event1 Wait Receive: %lC ******\n", Status ); + return FALSE; + } + if (!NT_SUCCESS(Iosb1.Status)) { + DbgPrint( "\n****** Receive Test: FAILED Receive Iosb status: %lC ******\n", Status ); + return FALSE; + } + + } else { + if (!NT_SUCCESS (Status)) { + + // + // Check to see if the remote has disconnected, which is + // the only reason for us shutting down/ + // + + if (Status == STATUS_REMOTE_DISCONNECT) { + + // + // We've been disconnected from; get out + // + + NtClose (SrvConnectionHandle); + break; + } + + DbgPrint( "\n****** Receive Test: Listen FAILED Status: %lC ******\n", Status ); + return FALSE; + } else { + + // + // successful return, what length is the data? + // + + CurrentBufferLength = Iosb1.Information; + } + } + + // + // send the data back + // + + KeInitializeEvent ( + &Event1, + SynchronizationEvent, + FALSE); + + Irp = TdiBuildInternalDeviceControlIrp ( + TDI_SEND, + DeviceObject, + ConnectionObject, + &Event1, + &Iosb1); + + TdiBuildSend (Irp, + DeviceObject, + ConnectionObject, + TSTRCVCompletion, + &Event1, + ReceiveMdl, + 0, + CurrentBufferLength); + + Status = IoCallDriver (DeviceObject, Irp); + + if (Status == STATUS_PENDING) { + Status = KeWaitForSingleObject (&Event1, Suspended, KernelMode, TRUE, NULL); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Receive Test: FAILED Event1 Wait Send: %lC ******\n", Status ); + return FALSE; + } + if (!NT_SUCCESS(Iosb1.Status)) { + DbgPrint( "\n****** Receive Test: FAILED Send Iosb status: %lC ******\n", Status ); + return FALSE; + } + + } else { + if (!NT_SUCCESS (Status)) { + + DbgPrint( "\n****** Receive Test: Send FAILED Status: %lC ******\n", Status ); + NtClose (SrvConnectionHandle); + break; + + } + } + } // end of receive/send while + + IoFreeMdl (ReceiveMdl); + ExFreePool (MessageBuffer); + + } + + // + // We're done with this address. Close it and get out. + // + + Status = CloseAddress (RdrHandle); + if (!NT_SUCCESS(Status)) { + DbgPrint( "\n****** Send Test: FAILED on 2nd Close: %lC ******\n", Status ); + return FALSE; + } + + DbgPrint( "\n****** End of Send Test ******\n" ); + return TRUE; +} /* Server */ diff --git a/private/ntos/tdi/nbf/testtdi.c b/private/ntos/tdi/nbf/testtdi.c new file mode 100644 index 000000000..6cf1b335f --- /dev/null +++ b/private/ntos/tdi/nbf/testtdi.c @@ -0,0 +1,291 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + tstrcv.c + +Abstract: + + start receive side tests utility + +Author: + + Dave Beaver (dbeaver) 24-Mar-1991 + +Revision History: + +--*/ + +// +// download a ub board +// + +typedef unsigned char uchar_t; + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <memory.h> +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> +#include <string.h> +//#include <windows.h> +#include <nbf.h> + +#define TDIDEV "\\Device\\Nbf" +char Tdidevice[] = TDIDEV; /* default device */ +char *Tdidev = Tdidevice; + +HANDLE FileHandle; + +VOID +usage( + VOID + ); + + +NTSTATUS +main ( + IN SHORT argc, + IN PSZ argv[] + ) +{ + IO_STATUS_BLOCK IoStatusBlock; + NTSTATUS Status; + STRING NameString; + UNICODE_STRING unicodeString; + PUCHAR buffer; + ULONG IoControlCode; + int n; + CHAR c; + + for( n = 1; n < argc && argv[n][0] == '-' ; ++n ) { + c = argv[n][1]; + + switch( c ) { + + case 's': // send test + IoControlCode = IOCTL_TDI_SEND_TEST; + break; + + case 'r': // receive test + IoControlCode = IOCTL_TDI_RECEIVE_TEST; + + break; + + case 'b': /* both test */ + IoControlCode = IOCTL_TDI_SERVER_TEST; + + break; + + default: + usage (); + break; + + } + } + + printf ("Opening TDI device: %s \n", Tdidev); + RtlInitString (&NameString, Tdidev); + Status = RtlAnsiStringToUnicodeString( + &unicodeString, + &NameString, + TRUE); + + buffer = (PUCHAR)malloc (100); + + Status = TdiOpenNetbiosAddress (&FileHandle, buffer, (PVOID)&NameString, NULL); + + RtlFreeUnicodeString(&unicodeString); + free (buffer); + + if (!NT_SUCCESS( Status )) { + printf ("FAILURE, Unable to open TDI driver %s, status: %lx.\n", + Tdidev,Status); + return (Status); + } + + if (!(NT_SUCCESS( IoStatusBlock.Status ))) { + printf ("FAILURE, Unable to open TDI driver %s, IoStatusBlock.Status: %lx.\n", + Tdidev, IoStatusBlock.Status); + return (IoStatusBlock.Status); + } + + // + // start the test + // + + printf("Starting test.... "); + Status = NtDeviceIoControlFile( + FileHandle, + NULL, + NULL, + NULL, + &IoStatusBlock, + IoControlCode, + NULL, + 0, + NULL, + 0); + + if (!NT_SUCCESS( Status )) { + printf ("FAILURE, Unable to start test: %lx.\n", Status); + return (Status); + } + + if (!(NT_SUCCESS( IoStatusBlock.Status ))) { + printf ("FAILURE, Unable to start test: %lx.\n", IoStatusBlock.Status); + return (IoStatusBlock.Status); + } + + NtClose (FileHandle); + + return STATUS_SUCCESS; + +} + + +NTSTATUS +TdiOpenNetbiosAddress ( + IN OUT PHANDLE FileHandle, + IN PUCHAR Buffer, + IN PVOID DeviceName, + IN PVOID Address) + +/*++ + +Routine Description: + + Opens an address on the given file handle and device. + +Arguments: + + FileHandle - the returned handle to the file object that is opened. + + Buffer - pointer to a buffer that the ea is to be built in. This buffer + must be at least 40 bytes long. + + DeviceName - the Unicode string that points to the device object. + + Name - the address to be registered. If this pointer is NULL, the routine + will attempt to open a "control channel" to the device; that is, it + will attempt to open the file object with a null ea pointer, and if the + transport provider allows for that, will return that handle. + +Return Value: + + An informative error code if something goes wrong. STATUS_SUCCESS if the + returned file handle is valid. + +--*/ +{ + IO_STATUS_BLOCK IoStatusBlock; + NTSTATUS Status; + OBJECT_ATTRIBUTES ObjectAttributes; + PFILE_FULL_EA_INFORMATION EaBuffer; + PTRANSPORT_ADDRESS TAAddress; + PTA_ADDRESS AddressType; + PTDI_ADDRESS_NETBIOS AddressName; + PSZ Name; + ULONG Length; + int i; + + if (Address != NULL) { + Name = (PSZ)Address; + try { + Length = sizeof (FILE_FULL_EA_INFORMATION) + + sizeof (TRANSPORT_ADDRESS) + + sizeof (TDI_ADDRESS_NETBIOS); + EaBuffer = (PFILE_FULL_EA_INFORMATION)Buffer; + + if (EaBuffer == NULL) { + return STATUS_UNSUCCESSFUL; + } + + EaBuffer->NextEntryOffset =0; + EaBuffer->Flags = 0; + EaBuffer->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH; + EaBuffer->EaValueLength = sizeof (TDI_ADDRESS_NETBIOS) + + sizeof (TRANSPORT_ADDRESS); + + for (i=0;i<(int)EaBuffer->EaNameLength;i++) { + EaBuffer->EaName[i] = TdiTransportAddress[i]; + } + + TAAddress = (PTRANSPORT_ADDRESS)&EaBuffer->EaName[EaBuffer->EaNameLength+1]; + TAAddress->TAAddressCount = 1; + + AddressType = (PTA_ADDRESS)((PUCHAR)TAAddress + sizeof (TAAddress->TAAddressCount)); + + AddressType->AddressType = TDI_ADDRESS_TYPE_NETBIOS; + AddressType->AddressLength = TDI_ADDRESS_LENGTH_NETBIOS; + + AddressName = (PTDI_ADDRESS_NETBIOS)((PUCHAR)AddressType + + sizeof (AddressType->AddressType) + sizeof (AddressType->AddressLength)); + AddressName->NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + + for (i=0;i<16;i++) { + AddressName->NetbiosName[i] = Name[i]; + } + } except(EXCEPTION_EXECUTE_HANDLER) { + + // + // Couldn't touch the passed parameters; just return an error + // status. + // + + return GetExceptionCode(); + } + } else { + EaBuffer = NULL; + Length = 0; + } + + InitializeObjectAttributes ( + &ObjectAttributes, + DeviceName, + 0, + NULL, + NULL); + + Status = NtCreateFile ( + FileHandle, + FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, // desired access. + &ObjectAttributes, // object attributes. + &IoStatusBlock, // returned status information. + 0, // block size (unused). + 0, // file attributes. + FILE_SHARE_READ | FILE_SHARE_WRITE, // share access. + FILE_CREATE, // create disposition. + 0, // create options. + EaBuffer, // EA buffer. + Length); // EA length. + + if (!NT_SUCCESS( Status )) { + return Status; + } + + Status = IoStatusBlock.Status; + + if (!(NT_SUCCESS( Status ))) { + } + + return Status; +} /* TdiOpenNetbiosAddress */ + +VOID +usage( + VOID + ) +{ + printf( "usage: tsttdi [-r] [-s] -[b]\n"); + printf( "usage: -r run receive test.\n" ); + printf( "usage: -b run server test.\n" ); + printf( "usage: -s run send test.\n" ); + printf( "\n" ); + exit( 1 ); +} + diff --git a/private/ntos/tdi/nbf/timer.c b/private/ntos/tdi/nbf/timer.c new file mode 100644 index 000000000..0664f0aa6 --- /dev/null +++ b/private/ntos/tdi/nbf/timer.c @@ -0,0 +1,2762 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + timer.c + +Abstract: + + This module contains code that implements the lightweight timer system + for the NBF protocol provider. This is not a general-purpose timer system; + rather, it is specific to servicing LLC (802.2) links with three timers + each. + + Services are provided in macro form (see NBFPROCS.H) to start and stop + timers. This module contains the code that gets control when the timer + in the device context expires as a result of calling kernel services. + The routine scans the device context's link database, looking for timers + that have expired, and for those that have expired, their expiration + routines are executed. + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "precomp.h" +#pragma hdrstop + +ULONG StartTimer = 0; +ULONG StartTimerSet = 0; +ULONG StartTimerT1 = 0; +ULONG StartTimerT2 = 0; +ULONG StartTimerDelayedAck = 0; +ULONG StartTimerLinkDeferredAdd = 0; +ULONG StartTimerLinkDeferredDelete = 0; + + +#if DBG +extern ULONG NbfDebugPiggybackAcks; +ULONG NbfDebugShortTimer = 0; +#endif + +#if DBG +// +// These are temp, to track how the timers are working +// +ULONG TimerInsertsAtEnd = 0; +ULONG TimerInsertsEmpty = 0; +ULONG TimerInsertsInMiddle = 0; +#endif + +// +// These are constants calculated by InitializeTimerSystem +// to be the indicated amound divided by the tick increment. +// + +ULONG NbfTickIncrement = 0; +ULONG NbfTwentyMillisecondsTicks = 0; +ULONG NbfShortTimerDeltaTicks = 0; +ULONG NbfMaximumIntervalTicks = 0; // usually 60 seconds in ticks + +LARGE_INTEGER DueTimeDelta = { (ULONG)(-SHORT_TIMER_DELTA), -1 }; + +VOID +ExpireT2Timer( + PTP_LINK Link + ); + +VOID +StopStalledConnections( + IN PDEVICE_CONTEXT DeviceContext + ); + + + +ULONG +GetTimerInterval( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + GetTimerInterval returns the difference in time between the + current time and Link->CurrentTimerStart (in ticks). + We limit the interval to 60 seconds. A value of 0 may + be returned which should be interpreted as 1/2. + + NOTE: This routine should be called with the link spinlock + held. + +Arguments: + + Link - Pointer to a transport link object. + +Return Value: + + The interval. + +--*/ + +{ + + LARGE_INTEGER CurrentTick; + LARGE_INTEGER Interval; + + + // + // Determine the current tick; the start tick has been saved + // in Link->CurrentTimerStart. + // + + KeQueryTickCount (&CurrentTick); + + // + // Determine the difference between now and then. + // + + Interval.QuadPart = CurrentTick.QuadPart - + (Link->CurrentTimerStart).QuadPart; + + // + // If the gap is too big, return 1 minute. + // + + if (Interval.HighPart != 0 || (Interval.LowPart > NbfMaximumIntervalTicks)) { + return NbfMaximumIntervalTicks; + } + + return Interval.LowPart; + +} /* GetTimerInterval */ + + +VOID +BackoffCurrentT1Timeout( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine is called if T1 expires and we are about to + retransmit a poll frame. It backs off CurrentT1Timeout, + up to a limit of 10 seconds. + + NOTE: This routine should be called with the link spinlock + held. + +Arguments: + + Link - Pointer to a transport link object. + +Return Value: + + None. + +--*/ + +{ + + // + // We must have previously sent a poll frame if we are + // calling this. + // + // BUGBUG: we need spinlock guarding for MP. + // + + if (!Link->CurrentPollOutstanding) { + return; + } + + ++Link->CurrentPollRetransmits; + + // + // T1 backs off 1.5 times each time. + // + + Link->CurrentT1Timeout += (Link->CurrentT1Timeout >> 1); + + // + // Limit T1 to 10 seconds. + // + + if (Link->CurrentT1Timeout > ((10 * SECONDS) / SHORT_TIMER_DELTA)) { + Link->CurrentT1Timeout = (10 * SECONDS) / SHORT_TIMER_DELTA; + } + +} /* BackoffCurrentT1Timeout */ + + +VOID +UpdateBaseT1Timeout( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine is called when a response to a poll frame is + received. StartT1 will have been called when the frame is + sent. The routine updates the link's T1 timeout as well + as delay and throughput. + + NOTE: This routine should be called with the link spinlock + held. + +Arguments: + + Link - Pointer to a transport link object. + +Return Value: + + None. + +--*/ + +{ + ULONG Delay; + ULONG ShiftedTicksDelay; + + // + // We must have previously sent a poll frame if we are + // calling this. + // + + if (!Link->CurrentPollOutstanding) { + return; + } + + Delay = GetTimerInterval (Link); + + if (Link->CurrentPollRetransmits == 0) { + + // + // Convert the delay into NBF ticks, shifted by + // DLC_TIMER_ACCURACY and also multiplied by 4. + // We want to divide by SHORT_TIMER_DELTA, then + // shift left by DLC_TIMER_ACCURACY+2. We divide + // by NbfShortTimerDeltaTicks because the Delay + // is returned in ticks. + // + // We treat a delay of 0 as 1/2, so we use 1 + // shifted left by (DLC_TIMER_ACCURACY+1). + // + + if (Delay == 0) { + + ShiftedTicksDelay = (1 << (DLC_TIMER_ACCURACY + 1)) / + NbfShortTimerDeltaTicks; + + } else { + + ShiftedTicksDelay = (Delay << (DLC_TIMER_ACCURACY + 2)) / + NbfShortTimerDeltaTicks; + + } + + + // + // Use the timing information to update BaseT1Timeout, + // if the last frame sent was large enough to matter + // (we use half of the max frame size here). This is + // so we don't shrink the timeout too much after sending + // a short frame. However, we update even for small frames + // if the last time we sent a poll we had to retransmit + // it, since that means T1 is much too small and we should + // increase it as much as we can. We also update for any + // size frame if the new delay is bigger than the current + // value, so we can ramp up quickly if needed. + // + + if (ShiftedTicksDelay > Link->BaseT1Timeout) { + + // + // If our new delay is more, than we weight it evenly + // with the previous value. + // + + Link->BaseT1Timeout = (Link->BaseT1Timeout + + ShiftedTicksDelay) / 2; + + } else if (Link->CurrentT1Backoff) { + + // + // If we got a retransmit last time, then weight + // the new timer more heavily than usual. + // + + Link->BaseT1Timeout = ((Link->BaseT1Timeout * 3) + + ShiftedTicksDelay) / 4; + + } else if (Link->CurrentPollSize >= Link->BaseT1RecalcThreshhold) { + + // + // Normally, the new timeout is 7/8 the previous value and + // 1/8 the newly observed delay. + // + + Link->BaseT1Timeout = ((Link->BaseT1Timeout * 7) + + ShiftedTicksDelay) / 8; + + } + + // + // Restrict the real timeout to a minimum based on + // the link speed (always >= 400 ms). + // + + if (Link->BaseT1Timeout < Link->MinimumBaseT1Timeout) { + + Link->BaseT1Timeout = Link->MinimumBaseT1Timeout; + + } + + + // + // Update link delay and throughput also. Remember + // that a delay of 0 should be interpreted as 1/2. + // + + UpdateDelayAndThroughput( + Link, + (Delay == 0) ? + (NbfTickIncrement / 2) : + (Delay * NbfTickIncrement)); + + + // + // We had no retransmits last time, so go back to current base. + // + + Link->CurrentT1Timeout = Link->BaseT1Timeout >> DLC_TIMER_ACCURACY; + + Link->CurrentT1Backoff = FALSE; + + } else { + + Link->CurrentT1Backoff = TRUE; + + if (!(Link->ThroughputAccurate)) { + + // + // If we are just starting up, we have to update the + // throughput even on a retransmit, so we get *some* + // value there. + // + + UpdateDelayAndThroughput( + Link, + (Delay == 0) ? + (NbfTickIncrement / 2) : + (Delay * NbfTickIncrement)); + + } + + } + +} /* UpdateBaseT1Timeout */ + + +VOID +CancelT1Timeout( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine is called when we have not received any + responses to a poll frame and are giving up rather + than retransmitting. + + NOTE: This routine should be called with the link spinlock + held. + +Arguments: + + Link - Pointer to a transport link object. + +Return Value: + + None. + +--*/ + +{ + + // + // We must have previously sent a poll frame if we are + // calling this. + // + // BUGBUG: We need spinlock guarding for MP. + // + + if (!Link->CurrentPollOutstanding) { + return; + } + + // + // We are stopping a polling condition, so reset T1. + // + + Link->CurrentT1Timeout = Link->BaseT1Timeout >> DLC_TIMER_ACCURACY; + + Link->CurrentT1Backoff = FALSE; + + // + // Again, this isn't safe on MP (or UP, maybe). + // + + Link->CurrentPollOutstanding = FALSE; + +} /* CancelT1Timeout */ + + +VOID +UpdateDelayAndThroughput( + IN PTP_LINK Link, + IN ULONG TimerInterval + ) + +/*++ + +Routine Description: + + This routine is called when a response packet used to time + link delay has been received. It is assumed that StartT1 + or FakeStartT1 was called when the initial packet was sent. + + NOTE: For now, we also calculate throughput based on this. + + NOTE: This routine should be called with the link spinlock + held. + +Arguments: + + Link - Pointer to a transport link object. + + TimerInterval - The link delay measured. + +Return Value: + + None. + +--*/ + +{ + + ULONG PacketSize; + + + if (Link->Delay == 0xffffffff) { + + // + // If delay is unknown, use this. + // + + Link->Delay = TimerInterval; + + } else if (Link->CurrentPollSize <= 64) { + + // + // Otherwise, for small frames calculate the new + // delay by averaging with the old one. + // + + Link->Delay = (Link->Delay + TimerInterval) / 2; + + } + + + // + // Calculate the packet size times the number of time units + // in 10 milliseconds, which will allow us to calculate + // throughput in bytes/10ms (we later multiply by 100 + // to obtain the real throughput in bytes/s). + // + // Given the size of MILLISECONDS, this allows packets of up + // to ~20K, so for bigger packets we just assume that (since + // throughput won't be an issue there). + // + + if (Link->CurrentPollSize > 20000) { + PacketSize = 20000 * (10 * MILLISECONDS); + } else { + PacketSize = Link->CurrentPollSize * (10*MILLISECONDS); + } + + // + // If throughput is not accurate, then we will use this + // packet only to calculate it. To avoid being confused + // by very small packets, assume a minimum size of 64. + // + + if ((!Link->ThroughputAccurate) && (PacketSize < (64*(10*MILLISECONDS)))) { + PacketSize = 64 * (10*MILLISECONDS); + } + + // + // PacketSize is going to be divided by TimerInterval; + // to prevent a zero throughput, we boost it up if needed. + // + + if (PacketSize < TimerInterval) { + PacketSize = TimerInterval; + } + + + if (Link->CurrentPollSize >= 512) { + + // + // Calculate throughput here by removing the established delay + // from the time. + // + + if ((Link->Delay + (2*MILLISECONDS)) < TimerInterval) { + + // + // If the current delay is less than the new timer + // interval (plus 2 ms), then subtract it off for a + // more accurate throughput calculation. + // + + TimerInterval -= Link->Delay; + + } + + // + // We assume by this point (sending a > 512-byte frame) we + // already have something established as Link->Throughput. + // + + if (!(Link->ThroughputAccurate)) { + + Link->Throughput.QuadPart = + UInt32x32To64((PacketSize / TimerInterval), 100); + + Link->ThroughputAccurate = TRUE; + +#if 0 + NbfPrint2 ("INT: %ld.%1.1d us\n", + TimerInterval / 10, TimerInterval % 10); + NbfPrint4 ("D: %ld.%1.1d us T: %ld (%d)/s\n", + Link->Delay / 10, Link->Delay % 10, + Link->Throughput.LowPart, Link->CurrentPollSize); +#endif + + } else { + + LARGE_INTEGER TwiceThroughput; + + // + // New throughput is the average of the old throughput, and + // the current packet size divided by the delay just observed. + // First we calculate the sum, then we shift right by one. + // + + TwiceThroughput.QuadPart = Link->Throughput.QuadPart + + UInt32x32To64((PacketSize / TimerInterval), 100); + + Link->Throughput.QuadPart = TwiceThroughput.QuadPart >> 1; + } + + } else if (!(Link->ThroughputAccurate)) { + + // + // We don't have accurate throughput, so just get an estimate + // by ignoring the delay on this small frame. + // + + Link->Throughput.QuadPart = + UInt32x32To64((PacketSize / TimerInterval), 100); + + } + +} /* UpdateDelayAndThroughput */ + + +VOID +FakeStartT1( + IN PTP_LINK Link, + IN ULONG PacketSize + ) + +/*++ + +Routine Description: + + This routine is called before sending a packet that will be used + to time link delay, but where StartT1 will not be started. + It is assumed that FakeUpdateBaseT1Timeout will be called + when the response is received. This is used for timing + frames that have a known immediate response, but are not + poll frames. + + NOTE: This routine should be called with the link spinlock + held. + +Arguments: + + Link - Pointer to a transport link object. + + PacketSize - The size of the packet that was just sent. + +Return Value: + + None. + +--*/ + +{ + + Link->CurrentPollSize = PacketSize; + KeQueryTickCount(&Link->CurrentTimerStart); + +} /* FakeStartT1 */ + + +VOID +FakeUpdateBaseT1Timeout( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine is called when a response to a frame is + received, and we called FakeStartT1 when the initial + frame was sent. This is used for timing frames that have + a known immediate response, but are not poll frames. + + NOTE: This routine should be called with the link spinlock + held. + +Arguments: + + Link - Pointer to a transport link object. + +Return Value: + + None. + +--*/ + + +{ + ULONG Delay; + + Delay = GetTimerInterval (Link); + + // + // Convert the delay into NBF ticks, shifted by + // DLC_TIMER_ACCURACY and also multiplied by 4. + // We want to divide by SHORT_TIMER_DELTA, then + // shift left by DLC_TIMER_ACCURACY+2. We divide + // by NbfShortTimerDeltaTicks because the Delay + // is returned in ticks. We treat a Delay of 0 + // as 1/2 and calculate ((1/2) << x) as (1 << (x-1)). + // + // This timeout is treated as the correct value. + // + + if (Delay == 0) { + + Link->BaseT1Timeout = (1 << (DLC_TIMER_ACCURACY + 1)) / + NbfShortTimerDeltaTicks; + + } else { + + Link->BaseT1Timeout = (Delay << (DLC_TIMER_ACCURACY + 2)) / + NbfShortTimerDeltaTicks; + + } + + // + // Restrict the real timeout to a minimum based on + // the link speed (always >= 400 ms). + // + + if (Link->BaseT1Timeout < Link->MinimumBaseT1Timeout) { + Link->BaseT1Timeout = Link->MinimumBaseT1Timeout; + } + + Link->CurrentT1Timeout = Link->BaseT1Timeout >> DLC_TIMER_ACCURACY; + + // + // Update link delay and throughput also. + // + + UpdateDelayAndThroughput( + Link, + (Delay == 0) ? + (NbfTickIncrement / 2) : + (Delay * NbfTickIncrement)); + +} /* FakeUpdateBaseT1Timeout */ + + +VOID +StartT1( + IN PTP_LINK Link, + IN ULONG PacketSize + ) + +/*++ + +Routine Description: + + This routine starts the T1 timer for the given link. If the link was + already on the list, it is moved to the tail. If not, it is inserted at + tail. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Link - pointer to the link of interest. + + PollPacketSize - If a poll packet was just sent it is its size; + otherwise this will be 0 (when non-poll I-frames are sent). + +Return Value: + + None. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext = Link->Provider; + + if (PacketSize > 0) { + + // + // If we are sending an initial poll frame, then do timing stuff. + // + + Link->CurrentPollRetransmits = 0; + Link->CurrentPollSize = PacketSize; + Link->CurrentPollOutstanding = TRUE; + KeQueryTickCount(&Link->CurrentTimerStart); + + } else { + + Link->CurrentPollOutstanding = FALSE; + + } + + + // + // Insert us in the queue if we aren't in it. + // + + Link->T1 = DeviceContext->ShortAbsoluteTime+Link->CurrentT1Timeout; + + if (!Link->OnShortList) { + + ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + if (!Link->OnShortList) { + Link->OnShortList = TRUE; + InsertTailList (&DeviceContext->ShortList, &Link->ShortList); + } + + if (!DeviceContext->a.i.ShortListActive) { + + StartTimerT1++; + NbfStartShortTimer (DeviceContext); + DeviceContext->a.i.ShortListActive = TRUE; + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + } + +} + + +VOID +StartT2( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine adds the given link to the T2 queue and starts the timer. + If the link is already on the queue, it is moved to the queue end. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Link - pointer to the link of interest. + +Return Value: + + None. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext = Link->Provider; + + + if (DeviceContext->MacInfo.MediumAsync) { + + // + // On an async line, expire it as soon as possible. + // + + Link->T2 = DeviceContext->ShortAbsoluteTime; + + } else { + + Link->T2 = DeviceContext->ShortAbsoluteTime+Link->T2Timeout; + + } + + + if (!Link->OnShortList) { + + ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + if (!Link->OnShortList) { + Link->OnShortList = TRUE; + InsertTailList (&DeviceContext->ShortList, &Link->ShortList); + } + + if (!DeviceContext->a.i.ShortListActive) { + + StartTimerT2++; + NbfStartShortTimer (DeviceContext); + DeviceContext->a.i.ShortListActive = TRUE; + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + } + +} + + +VOID +StartTi( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine adds the given link to the Ti queue and starts the timer. + As above, if the link is already on the queue it is moved to the queue end. + + NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL. + +Arguments: + + Link - pointer to the link of interest. + +Return Value: + + None. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext = Link->Provider; + + + // + // On an easily disconnected link, with only server connections + // on this link, we set a long Ti timeout, and when it + // expires with no activity we start checkpointing, otherwise + // we assume things are OK. + // + + if (DeviceContext->EasilyDisconnected && Link->NumberOfConnectors == 0) { + Link->Ti = DeviceContext->LongAbsoluteTime + (2 * Link->TiTimeout); + Link->TiStartPacketsReceived = Link->PacketsReceived; + } else { + Link->Ti = DeviceContext->LongAbsoluteTime+Link->TiTimeout; + } + + + if (!Link->OnLongList) { + + ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL); + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + if (!Link->OnLongList) { + Link->OnLongList = TRUE; + InsertTailList (&DeviceContext->LongList, &Link->LongList); + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + } + + +} + +#if DBG + +VOID +StopT1( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine + +Arguments: + + Link - pointer to the link of interest. + +Return Value: + + None. + +--*/ + +{ + // + // Again, this isn't safe on MP (or UP, maybe). + // + + Link->CurrentPollOutstanding = FALSE; + Link->T1 = 0; + +} + + +VOID +StopT2( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine + +Arguments: + + Link - pointer to the link of interest. + +Return Value: + + None. + +--*/ + +{ + Link->ConsecutiveIFrames = 0; + Link->T2 = 0; + +} + + +VOID +StopTi( + IN PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine + +Arguments: + + Link - pointer to the link of interest. + +Return Value: + + None. + +--*/ + +{ + Link->Ti = 0; +} +#endif + + +VOID +ExpireT1Timer( + PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine is called when a link's T1 timer expires. T1 is the + retransmission timer, and is used to remember that a response is + expected to any of the following: (1) a checkpoint, (2) a transmitted + I-frame, (3) a SABME, or (4) a DISC. Cases 3 and 4 are actually + special forms of a checkpoint, since they are sent by this protocol + implementation with the poll bit set, effectively making them a + checkpoint sequence. + +Arguments: + + Link - Pointer to the TP_LINK object whose T1 timer has expired. + +Return Value: + + none. + +--*/ + +{ + PDLC_I_FRAME DlcHeader; + + IF_NBFDBG (NBF_DEBUG_TIMER) { + NbfPrint0 ("ExpireT1Timer: Entered.\n"); + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + switch (Link->State) { + + case LINK_STATE_ADM: + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + IF_NBFDBG (NBF_DEBUG_TIMER) { + NbfPrint0 ("ExpireT1Timer: State=ADM, timeout not expected.\n"); + } + break; + + case LINK_STATE_READY: + + // + // We've sent an I-frame and haven't received an acknowlegement + // yet, or we are checkpointing, and must retry the checkpoint. + // Another possibility is that we're rejecting, and he hasn't + // sent anything yet. + // + + switch (Link->SendState) { + + case SEND_STATE_DOWN: + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + IF_NBFDBG (NBF_DEBUG_TIMER) { + NbfPrint0 ("ExpireT1Timer: Link READY but SendState=DOWN.\n"); + } + break; + + case SEND_STATE_READY: + + // + // We sent an I-frame and didn't get an acknowlegement. + // Initiate a checkpoint sequence. + // + + IF_NBFDBG (NBF_DEBUG_TIMER) { + {PTP_PACKET packet; + PLIST_ENTRY p; + NbfPrint0 ("ExpireT1Timer: Link State=READY, SendState=READY .\n"); + NbfDumpLinkInfo (Link); + p=Link->WackQ.Flink; + NbfPrint0 ("ExpireT1Timer: Link WackQ entries:\n"); + while (p != &Link->WackQ) { + packet = CONTAINING_RECORD (p, TP_PACKET, Linkage); + DlcHeader = (PDLC_I_FRAME)&(packet->Header[Link->HeaderLength]); + NbfPrint2 (" %08lx %03d\n", p, + (DlcHeader->SendSeq >> 1)); + p = p->Flink; + }} + } + + Link->SendRetries = (UCHAR)Link->LlcRetries; + Link->SendState = SEND_STATE_CHECKPOINTING; + // Don't BackoffT1Timeout yet. + NbfSendRr (Link, TRUE, TRUE);// send RR-c/p, StartT1, release lock + break; + + case SEND_STATE_REJECTING: + + IF_NBFDBG (NBF_DEBUG_TIMER) { + NbfPrint0 ("ExpireT1Timer: Link State=READY, SendState=REJECTING.\n"); + NbfPrint0 ("so what do we do here? consult the manual...\n"); + } + Link->SendState = SEND_STATE_CHECKPOINTING; +// Link->SendRetries = Link->LlcRetries; +// break; // DGB: doing nothing is obviously wrong, we've +// // gotten a T1 expiration during resend. Try +// // an RR to say hey. + + case SEND_STATE_CHECKPOINTING: + + IF_NBFDBG (NBF_DEBUG_TIMER) { + NbfPrint0 ("ExpireT1Timer: Link State=READY, SendState=CHECKPOINTING.\n"); + NbfDumpLinkInfo (Link); + } + if (--Link->SendRetries == 0) { + + // + // We have not gotten any response to RR-p packets, + // initiate orderly link teardown. + // + + CancelT1Timeout (Link); // we are stopping a polling state + + Link->State = LINK_STATE_W_DISC_RSP; // we are awaiting a DISC/f. + Link->SendState = SEND_STATE_DOWN; + Link->ReceiveState = RECEIVE_STATE_DOWN; + Link->SendRetries = (UCHAR)Link->LlcRetries; + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + NbfStopLink (Link); + + StartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); // retransmit timer. + NbfSendDisc (Link, TRUE); // send DISC-c/p. + +#if DBG + if (NbfDisconnectDebug) { + NbfPrint0( "ExpireT1Timer sending DISC (checkpoint failed)\n" ); + } +#endif + } else { + + BackoffCurrentT1Timeout (Link); + NbfSendRr (Link, TRUE, TRUE); // send RR-c/p, StartT1, release lock. + + } + break; + + default: + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + IF_NBFDBG (NBF_DEBUG_TIMER) { + NbfPrint1 ("ExpireT1Timer: Link State=READY, SendState=%ld (UNKNOWN).\n", + Link->SendState); + } + } + break; + + case LINK_STATE_CONNECTING: + + // + // We sent a SABME-c/p and have not yet received UA-r/f. This + // means we must decrement the retry count and if it is not yet + // zero, we issue another SABME command, because he has probably + // dropped our first one. + // + + if (--Link->SendRetries == 0) { + + CancelT1Timeout (Link); // we are stopping a polling state + + Link->State = LINK_STATE_ADM; + NbfSendDm (Link, FALSE); // send DM/0, release lock +#if DBG + if (NbfDisconnectDebug) { + NbfPrint0( "ExpireT1Timer calling NbfStopLink (no response to SABME)\n" ); + } +#endif + NbfStopLink (Link); + + // moving to ADM, remove reference + NbfDereferenceLinkSpecial("Expire T1 in CONNECTING mode", Link, LREF_NOT_ADM); + + return; // skip extra spinlock release. + } else { + BackoffCurrentT1Timeout (Link); + NbfSendSabme (Link, TRUE); // send SABME/p, StartT1, release lock + } + break; + + case LINK_STATE_W_POLL: + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + IF_NBFDBG (NBF_DEBUG_TIMER) { + NbfPrint0 ("ExpireT1Timer: State=W_POLL, timeout not expected.\n"); + } + break; + + case LINK_STATE_W_FINAL: + + // + // We sent our initial RR-c/p and have not received his RR-r/f. + // We have to restart the checkpoint, unless our retries have + // run out, in which case we just abort the link. + // + + IF_NBFDBG (NBF_DEBUG_TIMER) { + NbfPrint0 ("ExpireT1Timer: Link State=W_FINAL.\n"); + NbfDumpLinkInfo (Link); + } + + if (--Link->SendRetries == 0) { + + CancelT1Timeout (Link); // we are stopping a polling state + + Link->State = LINK_STATE_ADM; + NbfSendDm (Link, FALSE); // send DM/0, release lock +#if DBG + if (NbfDisconnectDebug) { + NbfPrint0( "ExpireT1Timer calling NbfStopLink (no final received)\n" ); + } +#endif + NbfStopLink (Link); + + // moving to ADM, remove reference + NbfDereferenceLinkSpecial("Expire T1 in W_FINAL mode", Link, LREF_NOT_ADM); + + return; // skip extra spinlock release. + + } else { + + BackoffCurrentT1Timeout (Link); + NbfSendRr (Link, TRUE, TRUE); // send RR-c/p, StartT1, release lock + + } + break; + + case LINK_STATE_W_DISC_RSP: + + // + // We sent a DISC-c/p to disconnect this link and are awaiting + // his response, either a UA-r/f or DM-r/f. We have to issue + // the DISC again, unless we've tried a few times, in which case + // we just shut the link down. + // + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint0 ("ExpireT1Timer: Link State=W_DISC_RESP.\n"); + NbfDumpLinkInfo (Link); + } + + if (--Link->SendRetries == 0) { + + CancelT1Timeout (Link); // we are stopping a polling state + + Link->State = LINK_STATE_ADM; + NbfSendDm (Link, FALSE); // send DM/0, release lock +#if DBG + if (NbfDisconnectDebug) { + NbfPrint0( "ExpireT1Timer calling NbfStopLink (no response to DISC)\n" ); + } +#endif + NbfStopLink (Link); + + // moving to ADM, remove reference + NbfDereferenceLinkSpecial("Expire T1 in W_DISC_RSP mode", Link, LREF_NOT_ADM); + + return; // skip extra spinlock release. + + } else { + + // we don't bother calling BackoffCurrentT1Timeout for DISCs. + ++Link->CurrentPollRetransmits; + StartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); // startup timer again. + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + NbfSendDisc (Link, TRUE); // send DISC/p. + + } + break; + + default: + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + IF_NBFDBG (NBF_DEBUG_TIMER) { + NbfPrint1 ("ExpireT1Timer: State=%ld (UNKNOWN), timeout not expected.\n", + Link->State); + } + } + + +} /* ExpireT1Timer */ + + +VOID +ExpireT2Timer( + PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine is called when a link's T2 timer expires. T2 is the + delayed acknowlegement timer in the LLC connection-oriented procedures. + The T2 timer is started when a valid I-frame is received but not + immediately acknowleged. Then, if reverse I-frame traffic is sent, + the timer is stopped, since the reverse traffic will acknowlege the + received I-frames. If no reverse I-frame traffic becomes available + to send, then this timer fires, causing a RR-r/0 to be sent so as + to acknowlege the received but as yet unacked I-frames. + +Arguments: + + Link - Pointer to the TP_LINK object whose T2 timer has expired. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_TIMER) { + NbfPrint0 ("ExpireT2Timer: Entered.\n"); + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + NbfSendRr (Link, FALSE, FALSE); // send RR-r/f, release lock. + +} /* ExpireT2Timer */ + + +VOID +ExpireTiTimer( + PTP_LINK Link + ) + +/*++ + +Routine Description: + + This routine is called when a link's Ti timer expires. Ti is the + inactivity timer, and serves as a keep-alive on a link basis, to + periodically perform some protocol exchange with the remote connection + partner that will implicitly reveal whether the link is still active + or not. This implementation simply uses a checkpoint sequence, but + some other protocols may choose to add protocol, including sending + a NetBIOS SESSION_ALIVE frame. If a checkpoint sequence is already + in progress, then we do nothing. + + This timer expiration routine is self-perpetuating; that is, it starts + itself after finishing its tasks every time. + +Arguments: + + Link - Pointer to the TP_LINK object whose Ti timer has expired. + +Return Value: + + none. + +--*/ + +{ + IF_NBFDBG (NBF_DEBUG_TIMER) { + NbfPrint0 ("ExpireTiTimer: Entered.\n"); + } + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + if ((Link->State != LINK_STATE_ADM) && + (Link->State != LINK_STATE_W_DISC_RSP) && + (Link->SendState != SEND_STATE_CHECKPOINTING)) { + + IF_NBFDBG (NBF_DEBUG_TIMER) { + NbfPrint0 ("ExpireTiTimer: Entered.\n"); + NbfDumpLinkInfo (Link); + } + + if (Link->Provider->EasilyDisconnected && Link->NumberOfConnectors == 0) { + + // + // On an easily disconnected network with only server connections, + // if there has been no activity in this timeout period then + // we trash the connection. + // + + if (Link->PacketsReceived == Link->TiStartPacketsReceived) { + + Link->State = LINK_STATE_ADM; + NbfSendDm (Link, FALSE); // send DM/0, release lock +#if DBG + if (NbfDisconnectDebug) { + NbfPrint0( "ExpireT1Timer calling NbfStopLink (no final received)\n" ); + } +#endif + NbfStopLink (Link); + + // moving to ADM, remove reference + NbfDereferenceLinkSpecial("Expire T1 in W_FINAL mode", Link, LREF_NOT_ADM); + + } else { + + // + // There was traffic, restart the timer. + // + + StartTi (Link); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + } + + } else { + +#if 0 + if ((Link->SendState == SEND_STATE_READY) && + (Link->T1 == 0) && + (!IsListEmpty (&Link->WackQ))) { + + // + // If we think the link is idle but there are packets + // on the WackQ, the link is messed up, disconnect it. + // + + NbfPrint1 ("NBF: Link %d hung at Ti expiration, recovering\n", Link); + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + NbfStopLink (Link); + + } else { +#endif + + Link->SendState = SEND_STATE_CHECKPOINTING; + Link->PacketsSent = 0; + Link->PacketsResent = 0; + Link->PacketsReceived = 0; + NbfSendRr (Link, TRUE, TRUE); // send RR-c/p, StartT1, release lock. + +#if 0 + } +#endif + + } + + } else { + + Link->PacketsSent = 0; + Link->PacketsResent = 0; + Link->PacketsReceived = 0; + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); +#if DBG + if (Link->SendState == SEND_STATE_REJECTING) { + NbfPrint0 ("ExpireTiTimer: link state == rejecting, shouldn't be\n"); + } +#endif + + } + +#if 0 + // + // Startup the inactivity timer again. + // + + if (Link->State != LINK_STATE_ADM) { + StartTi (Link); + } +#endif + +} /* ExpireTiTimer */ + +#if 0 + +VOID +ExpirePurgeTimer( + PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + + This routine is called when the device context's periodic adaptive + window algorithm timer expires. The timer perpetuates itself on a + regular basis. + +Arguments: + + DeviceContext - Pointer to the device context whose purge timer has expired. + +Return Value: + + none. + +--*/ + +{ + PTP_LINK Link; + PLIST_ENTRY p; + + IF_NBFDBG (NBF_DEBUG_TIMER) { + NbfPrint0 ("ExpirePurgeTimer: Entered.\n"); + } + + // + // Scan through the link database on this device context and clear + // their worst window size limit. This will allow stuck links to + // grow their window again even though they encountered temporary + // congestion at the remote link station's adapter. + // + + while (!IsListEmpty (&DeviceContext->PurgeList)) { + p = RemoveHeadList (&DeviceContext->PurgeList); + Link = CONTAINING_RECORD (p, TP_LINK, PurgeList); + Link->WorstWindowSize = Link->MaxWindowSize; // maximum window possible. + + } + + // + // Restart purge timer. + // + + DeviceContext->AdaptivePurge = DeviceContext->ShortAbsoluteTime + TIMER_PURGE_TICKS; + + +} /* ExpirePurgeTimer */ +#endif + + +VOID +ScanShortTimersDpc( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) + +/*++ + +Routine Description: + + This routine is called at DISPATCH_LEVEL by the system at regular + intervals to determine if any link-level timers have expired, and + if they have, to execute their expiration routines. + +Arguments: + + DeferredContext - Pointer to our DEVICE_CONTEXT object. + +Return Value: + + none. + +--*/ + +{ + PLIST_ENTRY p, nextp; + PDEVICE_CONTEXT DeviceContext; + PTP_LINK Link; + PTP_CONNECTION Connection; + BOOLEAN RestartTimer = FALSE; + LARGE_INTEGER CurrentTick; + LARGE_INTEGER TickDifference; + ULONG TickDelta; + + + Dpc, SystemArgument1, SystemArgument2; // prevent compiler warnings + + ENTER_NBF; + + IF_NBFDBG (NBF_DEBUG_TIMERDPC) { + NbfPrint0 ("ScanShortTimersDpc: Entered.\n"); + } + + DeviceContext = DeferredContext; + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + // + // This prevents anybody from starting the timer while we + // are in this routine (the main reason for this is that it + // makes it easier to determine whether we should restart + // it at the end of this routine). + // + + DeviceContext->ProcessingShortTimer = TRUE; + + // + // Advance the up-counter used to mark time in SHORT_TIMER_DELTA units. If we + // advance it all the way to 0xf0000000, then reset it to 0x10000000. + // We also run all the lists, decreasing all counters by 0xe0000000. + // + + + KeQueryTickCount (&CurrentTick); + + TickDifference.QuadPart = CurrentTick.QuadPart - + (DeviceContext->ShortTimerStart).QuadPart; + + TickDelta = TickDifference.LowPart / NbfShortTimerDeltaTicks; + if (TickDelta == 0) { + TickDelta = 1; + } + + DeviceContext->ShortAbsoluteTime += TickDelta; + + if (DeviceContext->ShortAbsoluteTime >= 0xf0000000) { + + ULONG Timeout; + + DeviceContext->ShortAbsoluteTime -= 0xe0000000; + + p = DeviceContext->ShortList.Flink; + while (p != &DeviceContext->ShortList) { + + Link = CONTAINING_RECORD (p, TP_LINK, ShortList); + + Timeout = Link->T1; + if (Timeout) { + Link->T1 = Timeout - 0xe0000000; + } + + Timeout = Link->T2; + if (Timeout) { + Link->T2 = Timeout - 0xe0000000; + } + + p = p->Flink; + } + + } + + // + // now, as the timers are started, links are added to the end of the + // respective queue for that timer. since we know the additions are + // done in an orderly fashion and are sequential, we must only traverse + // a particular timer list to the first entry that is greater than our + // timer. That entry and all further entries will not need service. + // When a timer is cancelled, we remove the link from the list. With all + // of this fooling around, we wind up only visiting those links that are + // actually in danger of timing out, minimizing time in this routine. + // + + // T1 timers first; this is the link-level response expected timer, and is + // the shortest one. + // T2 timers. This is the iframe response expected timer, and is typically + // about 300 ms. + + p = DeviceContext->ShortList.Flink; + while (p != &DeviceContext->ShortList) { + + Link = CONTAINING_RECORD (p, TP_LINK, ShortList); + + ASSERT (Link->OnShortList); + + // + // To avoid problems with the refcount being 0, don't + // do this if we are in ADM. + // + + if (Link->State != LINK_STATE_ADM) { + + if (Link->T1 && (DeviceContext->ShortAbsoluteTime > Link->T1)) { + + Link->T1 = 0; + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + ExpireT1Timer (Link); // no spinlocks held + INCREMENT_COUNTER (DeviceContext, ResponseTimerExpirations); + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + } + + if (Link->T2 && (DeviceContext->ShortAbsoluteTime > Link->T2)) { + + Link->T2 = 0; + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + ExpireT2Timer (Link); // no spinlocks held + INCREMENT_COUNTER (DeviceContext, AckTimerExpirations); + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + } + + } + + if (!Link->OnShortList) { + + // + // The link has been taken out of the list while + // we were processing it. In this (rare) case we + // stop processing the whole list, we'll get it + // next time. + // + +#if DBG + DbgPrint ("NBF: Stop processing ShortList, %lx removed\n", Link); +#endif + break; + + } + + nextp = p->Flink; + + if ((Link->T1 == 0) && (Link->T2 == 0)) { + Link->OnShortList = FALSE; + RemoveEntryList(p); + + // + // Do another check; that way if someone slipped in between + // the check of Link->Tx and the OnShortList = FALSE and + // therefore exited without inserting, we'll catch that here. + // + + if ((Link->T1 != 0) || (Link->T2 != 0)) { + InsertTailList(&DeviceContext->ShortList, &Link->ShortList); + Link->OnShortList = TRUE; + } + + } + + p = nextp; + + } + + // + // If the list is empty note that, otherwise ShortListActive + // remains TRUE. + // + + if (IsListEmpty (&DeviceContext->ShortList)) { + DeviceContext->a.i.ShortListActive = FALSE; + } + + // + // NOTE: DeviceContext->TimerSpinLock is held here. + // + + + // + // Connection Data Ack timers. This queue is used to indicate + // that a piggyback ack is pending for this connection. We walk + // the queue, for each element we check if the connection has + // been on the queue for NbfDeferredPasses times through + // here. If so, we take it off and send an ack. Note that + // we have to be very careful how we walk the queue, since + // it may be changing while this is running. + // + // NOTE: There is no expiration time for connections on this + // queue; it "expires" every time ScanShortTimersDpc runs. + // + + + for (p = DeviceContext->DataAckQueue.Flink; + p != &DeviceContext->DataAckQueue; + p = p->Flink) { + + Connection = CONTAINING_RECORD (p, TP_CONNECTION, DataAckLinkage); + + // + // Skip this connection if it is not queued or it is + // too recent to matter. We may skip incorrectly if + // the connection is just being queued, but that is + // OK, we will get it next time. + // + + if (((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_ACK) == 0) && + ((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_NOT_Q) == 0)) { + continue; + } + + TickDifference.QuadPart = CurrentTick.QuadPart - + (Connection->ConnectStartTime).QuadPart; + + if ((TickDifference.HighPart == 0) && + (TickDifference.LowPart <= NbfTwentyMillisecondsTicks)) { + continue; + } + + NbfReferenceConnection ("ScanShortTimersDpc", Connection, CREF_DATA_ACK_QUEUE); + + DeviceContext->DataAckQueueChanged = FALSE; + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + // + // Check the correct connection flag, to ensure that a + // send has not just taken him off the queue. + // + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + if (((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_ACK) != 0) && + ((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_NOT_Q) == 0)) { + + // + // Yes, we were waiting to piggyback an ack, but no send + // has come along. Turn off the flags and send an ack. + // + // We have to ensure we nest the spin lock acquisition + // correctly. + // + + Connection->DeferredFlags &= ~CONNECTION_FLAGS_DEFERRED_ACK; + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + INCREMENT_COUNTER (DeviceContext, PiggybackAckTimeouts); + +#if DBG + if (NbfDebugPiggybackAcks) { + NbfPrint0("T"); + } +#endif + + NbfSendDataAck (Connection); + + } else { + + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + } + + NbfDereferenceConnection ("ScanShortTimersDpc", Connection, CREF_DATA_ACK_QUEUE); + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + // + // If the list has changed, then we need to stop processing + // since p->Flink is not valid. + // + + if (DeviceContext->DataAckQueueChanged) { + break; + } + + } + + if (IsListEmpty (&DeviceContext->DataAckQueue)) { + DeviceContext->a.i.DataAckQueueActive = FALSE; + } + +#if 0 + + // + // NOTE: This is currently disabled, it may be reenabled + // at some point - adamba 9/1/92 + // + // If the adaptive purge timer has expired, then run the purge + // algorithm on all affected links. + // + + if (DeviceContext->ShortAbsoluteTime > DeviceContext->AdaptivePurge) { + DeviceContext->AdaptivePurge = DeviceContext->ShortAbsoluteTime + + TIMER_PURGE_TICKS; + ExpirePurgeTimer (DeviceContext); + } +#endif + + // + // deferred processing. We will handle all link structure additions and + // deletions here; we must be the exclusive user of the link tree to do + // this. We verify that we are by examining the semaphore that tells us + // how many readers of the tree are curretly processing it. If there are + // any readers, we simply increment our "deferred processing locked out" + // counter and do something else. If we defer too many times, we simply + // bugcheck, as something is wrong somewhere in the system. + // + + if (!IsListEmpty (&DeviceContext->LinkDeferred)) { + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + // + // now do additions or deletions if we can. + // + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + while (!IsListEmpty (&DeviceContext->LinkDeferred)) { + p = RemoveHeadList (&DeviceContext->LinkDeferred); + DeviceContext->DeferredNotSatisfied = 0; + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + // + // now do an addition or deletion if we can. + // + + Link = CONTAINING_RECORD (p, TP_LINK, DeferredList); + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint4 ("ScanShortTimersDPC: link off deferred queue %lx %lx %lx Flags: %lx \n", + Link, DeviceContext->LinkDeferred.Flink, + DeviceContext->LinkDeferred.Blink, Link->DeferredFlags); + } + Link->DeferredList.Flink = Link->DeferredList.Blink = + &Link->DeferredList; + + if ((Link->DeferredFlags & LINK_FLAGS_DEFERRED_MASK) == 0) { + // Tried to do an operation we don't understand; whine. + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint2 ("ScanTimerDPC: Attempting deferred operation on nothing! \nScanTimerDPC: Link: %lx, DeviceContext->DeferredQueue: %lx\n", + Link, &DeviceContext->LinkDeferred); + DbgBreakPoint (); + } + InitializeListHead (&DeviceContext->LinkDeferred); + // We could have a hosed deferred operations queue here; + // BUGBUG: take some time to figure out if it is ok. + + } + + if ((Link->DeferredFlags & LINK_FLAGS_DEFERRED_ADD) != 0) { + + Link->DeferredFlags &= ~LINK_FLAGS_DEFERRED_ADD; + + if ((Link->DeferredFlags & LINK_FLAGS_DEFERRED_DELETE) != 0) { + + // + // It is being added and deleted; just destroy it. + // + Link->DeferredFlags &= ~LINK_FLAGS_DEFERRED_DELETE; + NbfDestroyLink (Link); + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint1 ("ScanTimerDPC: deferred processing: Add AND Delete link: %lx\n",Link); + } + + } else { + + NbfAddLinkToTree (DeviceContext, Link); + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint1 ("ScanTimerDPC: deferred processing: Added link to tree: %lx\n",Link); + } + + } + + } else if ((Link->DeferredFlags & LINK_FLAGS_DEFERRED_DELETE) != 0) { + Link->DeferredFlags &= ~LINK_FLAGS_DEFERRED_DELETE; + NbfRemoveLinkFromTree (DeviceContext, Link); + NbfDestroyLink (Link); + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint1 ("ScanTimerDPC: deferred processing: returning link %lx to LinkPool.\n", Link); + } + + } + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + } + + InitializeListHead (&DeviceContext->LinkDeferred); + + DeviceContext->a.i.LinkDeferredActive = FALSE; + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + RELEASE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock); + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + } + + + // + // Update the real counters from the temp ones. + // + + ADD_TO_LARGE_INTEGER( + &DeviceContext->Statistics.DataFrameBytesSent, + DeviceContext->TempIFrameBytesSent); + DeviceContext->Statistics.DataFramesSent += DeviceContext->TempIFramesSent; + + DeviceContext->TempIFrameBytesSent = 0; + DeviceContext->TempIFramesSent = 0; + + ADD_TO_LARGE_INTEGER( + &DeviceContext->Statistics.DataFrameBytesReceived, + DeviceContext->TempIFrameBytesReceived); + DeviceContext->Statistics.DataFramesReceived += DeviceContext->TempIFramesReceived; + + DeviceContext->TempIFrameBytesReceived = 0; + DeviceContext->TempIFramesReceived = 0; + + + // + // Determine if we have to restart the timer. + // + + DeviceContext->ProcessingShortTimer = FALSE; + + if (DeviceContext->a.AnyActive && + (DeviceContext->State != DEVICECONTEXT_STATE_STOPPING)) { + + RestartTimer = TRUE; + + } + + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + if (RestartTimer) { + + // + // Start up the timer again. Note that because we start the timer + // after doing work (above), the timer values will slip somewhat, + // depending on the load on the protocol. This is entirely acceptable + // and will prevent us from using the timer DPC in two different + // threads of execution. + // + + KeQueryTickCount(&DeviceContext->ShortTimerStart); + KeSetTimer ( + &DeviceContext->ShortSystemTimer, + DueTimeDelta, + &DeviceContext->ShortTimerSystemDpc); + + } else { + +#if DBG + if (NbfDebugShortTimer) { + DbgPrint("x"); + } +#endif + NbfDereferenceDeviceContext ("Don't restart short timer", DeviceContext, DCREF_SCAN_TIMER); + + } + + + LEAVE_NBF; + return; + +} /* ScanShortTimersDpc */ + + +VOID +ScanLongTimersDpc( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) + +/*++ + +Routine Description: + + This routine is called at DISPATCH_LEVEL by the system at regular + intervals to determine if any long timers have expired, and + if they have, to execute their expiration routines. + +Arguments: + + DeferredContext - Pointer to our DEVICE_CONTEXT object. + +Return Value: + + none. + +--*/ + +{ + LARGE_INTEGER DueTime; + PLIST_ENTRY p, nextp; + PDEVICE_CONTEXT DeviceContext; + PTP_LINK Link; + PTP_CONNECTION Connection; + + Dpc, SystemArgument1, SystemArgument2; // prevent compiler warnings + + ENTER_NBF; + + IF_NBFDBG (NBF_DEBUG_TIMERDPC) { + NbfPrint0 ("ScanLongTimersDpc: Entered.\n"); + } + + DeviceContext = DeferredContext; + + // + // Advance the up-counter used to mark time in LONG_TIMER_DELTA units. If we + // advance it all the way to 0xf0000000, then reset it to 0x10000000. + // We also run all the lists, decreasing all counters by 0xe0000000. + // + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + if (++DeviceContext->LongAbsoluteTime == 0xf0000000) { + + ULONG Timeout; + + DeviceContext->LongAbsoluteTime = 0x10000000; + + p = DeviceContext->LongList.Flink; + while (p != &DeviceContext->LongList) { + + Link = CONTAINING_RECORD (p, TP_LINK, LongList); + + Timeout = Link->Ti; + if (Timeout) { + Link->Ti = Timeout - 0xe0000000; + } + + p = p->Flink; + } + + } + + // + // now, as the timers are started, links are added to the end of the + // respective queue for that timer. since we know the additions are + // done in an orderly fashion and are sequential, we must only traverse + // a particular timer list to the first entry that is greater than our + // timer. That entry and all further entries will not need service. + // When a timer is cancelled, we remove the link from the list. With all + // of this fooling around, we wind up only visiting those links that are + // actually in danger of timing out, minimizing time in this routine. + // + + + // + // Ti timers. This is the inactivity timer for the link, used when no + // activity has occurred on the link in some time. We only check this + // every four expirations of the timer since the granularity is usually + // in the 30 second range. + // NOTE: DeviceContext->TimerSpinLock is held here. + // + + if ((DeviceContext->LongAbsoluteTime % 4) == 0) { + + p = DeviceContext->LongList.Flink; + while (p != &DeviceContext->LongList) { + + Link = CONTAINING_RECORD (p, TP_LINK, LongList); + + ASSERT (Link->OnLongList); + + // + // To avoid problems with the refcount being 0, don't + // do this if we are in ADM. + // + +#if DBG + if (Link->SendState == SEND_STATE_REJECTING) { + NbfPrint0 ("Timer: link state == rejecting, shouldn't be\n"); + } +#endif + + if (Link->State != LINK_STATE_ADM) { + + if (Link->Ti && (DeviceContext->LongAbsoluteTime > Link->Ti)) { + + Link->Ti = 0; + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + ExpireTiTimer (Link); // no spinlocks held + ++DeviceContext->TiExpirations; + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + } + + } + + if (!Link->OnLongList) { + + // + // The link has been taken out of the list while + // we were processing it. In this (rare) case we + // stop processing the whole list, we'll get it + // next time. + // + +#if DBG + DbgPrint ("NBF: Stop processing LongList, %lx removed\n", Link); +#endif + break; + + } + + nextp = p->Flink; + + if (Link->Ti == 0) { + + Link->OnLongList = FALSE; + RemoveEntryList(p); + + if (Link->Ti != 0) { + InsertTailList(&DeviceContext->LongList, &Link->LongList); + Link->OnLongList = TRUE; + } + + } + + p = nextp; + + } + + } + + + // + // Now scan the data ack queue, looking for connections with + // no acks queued that we can get rid of. + // + // Note: The timer spinlock is held here. + // + + p = DeviceContext->DataAckQueue.Flink; + + while (p != &DeviceContext->DataAckQueue && + !DeviceContext->DataAckQueueChanged) { + + Connection = CONTAINING_RECORD (DeviceContext->DataAckQueue.Flink, TP_CONNECTION, DataAckLinkage); + + if ((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_ACK) != 0) { + p = p->Flink; + continue; + } + + NbfReferenceConnection ("ScanShortTimersDpc", Connection, CREF_DATA_ACK_QUEUE); + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + // + // Have to check again, because the connection might + // just have been stopped. + // + + if (Connection->OnDataAckQueue) { + Connection->OnDataAckQueue = FALSE; + + RemoveEntryList (&Connection->DataAckLinkage); + + if ((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_ACK) != 0) { + InsertTailList (&DeviceContext->DataAckQueue, &Connection->DataAckLinkage); + Connection->OnDataAckQueue = TRUE; + } + + DeviceContext->DataAckQueueChanged = TRUE; + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock); + + NbfDereferenceConnection ("ScanShortTimersDpc", Connection, CREF_DATA_ACK_QUEUE); + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + // + // Since we have changed the list, we can't tell if p->Flink + // is valid, so break. The effect is that we gradually peel + // connections off the queue. + // + + break; + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock); + + + // + // See if we got any multicast traffic last time. + // + + if (DeviceContext->MulticastPacketCount == 0) { + + ++DeviceContext->LongTimeoutsWithoutMulticast; + + if (DeviceContext->EasilyDisconnected && + (DeviceContext->LongTimeoutsWithoutMulticast > 5)) { + + PLIST_ENTRY p; + PTP_ADDRESS address; + + // + // We have had five timeouts in a row with no + // traffic, mark all the addresses as needing + // reregistration next time a connect is + // done on them. + // + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + for (p = DeviceContext->AddressDatabase.Flink; + p != &DeviceContext->AddressDatabase; + p = p->Flink) { + + address = CONTAINING_RECORD (p, TP_ADDRESS, Linkage); + address->Flags |= ADDRESS_FLAGS_NEED_REREGISTER; + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + DeviceContext->LongTimeoutsWithoutMulticast = 0; + + } + + } else { + + DeviceContext->LongTimeoutsWithoutMulticast = 0; + + } + + DeviceContext->MulticastPacketCount = 0; + + + // + // Every thirty seconds, check for stalled connections + // + + ++DeviceContext->StalledConnectionCount; + + if (DeviceContext->StalledConnectionCount == + (USHORT)((30 * SECONDS) / LONG_TIMER_DELTA)) { + + DeviceContext->StalledConnectionCount = 0; + StopStalledConnections (DeviceContext); + + } + + + // + // Scan for any uncompleted receive IRPs, this may happen if + // the cable is pulled and we don't get any more ReceiveComplete + // indications. + + NbfReceiveComplete((NDIS_HANDLE)DeviceContext); + + + // + // Start up the timer again. Note that because we start the timer + // after doing work (above), the timer values will slip somewhat, + // depending on the load on the protocol. This is entirely acceptable + // and will prevent us from using the timer DPC in two different + // threads of execution. + // + + if (DeviceContext->State != DEVICECONTEXT_STATE_STOPPING) { + DueTime.HighPart = -1; + DueTime.LowPart = (ULONG)-(LONG_TIMER_DELTA); // delta time to next click. + KeSetTimer ( + &DeviceContext->LongSystemTimer, + DueTime, + &DeviceContext->LongTimerSystemDpc); + } else { + NbfDereferenceDeviceContext ("Don't restart long timer", DeviceContext, DCREF_SCAN_TIMER); + } + + LEAVE_NBF; + return; + +} /* ScanLongTimersDpc */ + + +VOID +StopStalledConnections( + IN PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + + This routine is called from ScanLongTimersDpc every 30 seconds. + It checks for connections that have not made any progress in + their sends in the last two minutes, and stops them. + +Arguments: + + DeviceContext - The device context to check. + +Return Value: + + none. + +--*/ + +{ + + PTP_ADDRESS Address, PrevAddress; + PTP_CONNECTION Connection, StalledConnection; + PLIST_ENTRY p, q; + + + // + // If we have crossed a thirty-second interval, then + // check each address for connections that have not + // made any progress in two minutes. + // + + PrevAddress = NULL; + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + for (p = DeviceContext->AddressDatabase.Flink; + p != &DeviceContext->AddressDatabase; + p = p->Flink) { + + Address = CONTAINING_RECORD ( + p, + TP_ADDRESS, + Linkage); + + if ((Address->Flags & ADDRESS_FLAGS_STOPPING) != 0) { + continue; + } + + // + // By referencing the address, we ensure that it will stay + // in the AddressDatabase, this its Flink will stay valid. + // + + NbfReferenceAddress("checking for dead connections", Address, AREF_TIMER_SCAN); + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + if (PrevAddress) { + NbfDereferenceAddress ("done checking", PrevAddress, AREF_TIMER_SCAN); + } + + // + // Scan this addresses connection database for connections + // that have not made progress in the last two minutes; we + // kill the first one we find. + // + + StalledConnection = NULL; + + ACQUIRE_DPC_SPIN_LOCK (&Address->SpinLock); + + for (q = Address->ConnectionDatabase.Flink; + q != &Address->ConnectionDatabase; + q = q->Flink) { + + Connection = CONTAINING_RECORD (q, TP_CONNECTION, AddressList); + + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + if (!IsListEmpty (&Connection->SendQueue)) { + + // + // If there is a connection on the queue... + // + + if (Connection->StallBytesSent == Connection->sp.MessageBytesSent) { + + // + // ...and it has not made any progress... + // + + if (Connection->StallCount >= 4) { + + // + // .. four times in a row, the connection is dead. + // + + if (!StalledConnection) { + StalledConnection = Connection; + NbfReferenceConnection ("stalled", Connection, CREF_STALLED); + } +#if DBG + DbgPrint ("NBF: Found connection %lx [%d for %d] stalled on %lx\n", + Connection, Connection->StallBytesSent, Connection->StallCount, Address); +#endif + + } else { + + // + // If it is stuck, increment the count. + // + + ++Connection->StallCount; + + } + + } else { + + Connection->StallBytesSent = Connection->sp.MessageBytesSent; + + } + + } + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + + } + + RELEASE_DPC_SPIN_LOCK (&Address->SpinLock); + + if (StalledConnection) { + + PTP_LINK Link = StalledConnection->Link; + +#if DBG + DbgPrint("NBF: Stopping stalled connection %lx, link %lx\n", StalledConnection, Link); +#endif + + FailSend (StalledConnection, STATUS_IO_TIMEOUT, TRUE); // fail the send + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + if (Link->State == LINK_STATE_READY) { + CancelT1Timeout (Link); + Link->State = LINK_STATE_W_DISC_RSP; + Link->SendState = SEND_STATE_DOWN; + Link->ReceiveState = RECEIVE_STATE_DOWN; + Link->SendRetries = (UCHAR)Link->LlcRetries; + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + NbfStopLink (Link); + StartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); // retransmit timer. + NbfSendDisc (Link, TRUE); // send DISC-c/p. + } else { + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + NbfStopLink (Link); + } + + NbfDereferenceConnection ("stalled", StalledConnection, CREF_STALLED); + + } + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + PrevAddress = Address; + + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + if (PrevAddress) { + NbfDereferenceAddress ("done checking", PrevAddress, AREF_TIMER_SCAN); + } + +} /* StopStalledConnections */ + + +VOID +NbfStartShortTimer( + IN PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + + This routine starts the short timer, if it is not already running. + +Arguments: + + DeviceContext - Pointer to our device context. + +Return Value: + + none. + +--*/ + +{ + + // + // Start the timer unless it the DPC is already running (in + // which case it will restart the timer itself if needed), + // or some list is active (meaning the timer is already + // queued up). + // + // We use a trick to check all four active lists at the + // same time, but this depends on some alignment and + // size assumptions. + // + + ASSERT (sizeof(ULONG) >= 3 * sizeof(BOOLEAN)); + ASSERT ((PVOID)&DeviceContext->a.AnyActive == + (PVOID)&DeviceContext->a.i.ShortListActive); + + StartTimer++; + + if ((!DeviceContext->ProcessingShortTimer) && + (!(DeviceContext->a.AnyActive))) { + +#if DBG + if (NbfDebugShortTimer) { + DbgPrint("X"); + } +#endif + + NbfReferenceDeviceContext ("Start short timer", DeviceContext, DCREF_SCAN_TIMER); + + KeQueryTickCount(&DeviceContext->ShortTimerStart); + StartTimerSet++; + KeSetTimer ( + &DeviceContext->ShortSystemTimer, + DueTimeDelta, + &DeviceContext->ShortTimerSystemDpc); + + } + +} /* NbfStartShortTimer */ + + +VOID +NbfInitializeTimerSystem( + IN PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + + This routine initializes the lightweight timer system for the transport + provider. + +Arguments: + + DeviceContext - Pointer to our device context. + +Return Value: + + none. + +--*/ + +{ + LARGE_INTEGER DueTime; + + IF_NBFDBG (NBF_DEBUG_TIMER) { + NbfPrint0 ("NbfInitializeTimerSystem: Entered.\n"); + } + + // + // Set these up. + // + + NbfTickIncrement = KeQueryTimeIncrement(); + + if (NbfTickIncrement > (20 * MILLISECONDS)) { + NbfTwentyMillisecondsTicks = 1; + } else { + NbfTwentyMillisecondsTicks = (20 * MILLISECONDS) / NbfTickIncrement; + } + + if (NbfTickIncrement > (SHORT_TIMER_DELTA)) { + NbfShortTimerDeltaTicks = 1; + } else { + NbfShortTimerDeltaTicks = (SHORT_TIMER_DELTA) / NbfTickIncrement; + } + + // + // MaximumIntervalTicks represents 60 seconds, unless the value + // when shifted out by the accuracy required is too big. + // + + if ((((ULONG)0xffffffff) >> (DLC_TIMER_ACCURACY+2)) > ((60 * SECONDS) / NbfTickIncrement)) { + NbfMaximumIntervalTicks = (60 * SECONDS) / NbfTickIncrement; + } else { + NbfMaximumIntervalTicks = ((ULONG)0xffffffff) >> (DLC_TIMER_ACCURACY + 2); + } + + // + // The AbsoluteTime cycles between 0x10000000 and 0xf0000000. + // + + DeviceContext->ShortAbsoluteTime = 0x10000000; // initialize our timer click up-counter. + DeviceContext->LongAbsoluteTime = 0x10000000; // initialize our timer click up-counter. + + DeviceContext->AdaptivePurge = TIMER_PURGE_TICKS; + + DeviceContext->MulticastPacketCount = 0; + DeviceContext->LongTimeoutsWithoutMulticast = 0; + + KeInitializeDpc( + &DeviceContext->ShortTimerSystemDpc, + ScanShortTimersDpc, + DeviceContext); + + KeInitializeDpc( + &DeviceContext->LongTimerSystemDpc, + ScanLongTimersDpc, + DeviceContext); + + KeInitializeTimer (&DeviceContext->ShortSystemTimer); + + KeInitializeTimer (&DeviceContext->LongSystemTimer); + + DueTime.HighPart = -1; + DueTime.LowPart = (ULONG)-(LONG_TIMER_DELTA); + + // + // One reference for the long timer. + // + + NbfReferenceDeviceContext ("Long timer active", DeviceContext, DCREF_SCAN_TIMER); + + KeSetTimer ( + &DeviceContext->LongSystemTimer, + DueTime, + &DeviceContext->LongTimerSystemDpc); + + DeviceContext->TimersInitialized = TRUE; + +} /* NbfInitializeTimerSystem */ + + +VOID +NbfStopTimerSystem( + IN PDEVICE_CONTEXT DeviceContext + ) + +/*++ + +Routine Description: + + This routine stops the lightweight timer system for the transport + provider. + +Arguments: + + DeviceContext - Pointer to our device context. + +Return Value: + + none. + +--*/ + +{ + + // + // For now we ignore what happens if the timer is currently + // running when we do this. + // + + if (DeviceContext->TimersInitialized) { + + if (KeCancelTimer( + &DeviceContext->LongSystemTimer)) { + NbfDereferenceDeviceContext ("Long timer cancelled", DeviceContext, DCREF_SCAN_TIMER); + } + + if (KeCancelTimer( + &DeviceContext->ShortSystemTimer)) { + NbfDereferenceDeviceContext ("Short timer cancelled", DeviceContext, DCREF_SCAN_TIMER); + } + + } + +} diff --git a/private/ntos/tdi/nbf/uframes.c b/private/ntos/tdi/nbf/uframes.c new file mode 100644 index 000000000..654f92ff6 --- /dev/null +++ b/private/ntos/tdi/nbf/uframes.c @@ -0,0 +1,2974 @@ +/*++ + +Copyright (c) 1989, 1990, 1991 Microsoft Corporation + +Module Name: + + uframes.c + +Abstract: + + This module contains a routine called NbfProcessUi, that gets control + from routines in DLC.C when a DLC UI frame is received. Here we + decode the encapsulated connectionless NetBIOS frame and dispatch + to the correct NetBIOS frame handler. + + The following frame types are cracked by routines in this module: + + o NBF_CMD_ADD_GROUP_NAME_QUERY + o NBF_CMD_ADD_NAME_QUERY + o NBF_CMD_NAME_IN_CONFLICT + o NBF_CMD_STATUS_QUERY + o NBF_CMD_TERMINATE_TRACE + o NBF_CMD_DATAGRAM + o NBF_CMD_DATAGRAM_BROADCAST + o NBF_CMD_NAME_QUERY + o NBF_CMD_ADD_NAME_RESPONSE + o NBF_CMD_NAME_RECOGNIZED + o NBF_CMD_STATUS_RESPONSE + o NBF_CMD_TERMINATE_TRACE2 + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Environment: + + Kernel mode, DISPATCH_LEVEL. + +Revision History: + +--*/ + +#include "precomp.h" +#pragma hdrstop + + + +VOID +NbfListenTimeout( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ) + +/*++ + +Routine Description: + + This routine is executed as a DPC at DISPATCH_LEVEL when the timeout + period for the session setup after listening a connection occurs. This + will occur if the remote has discovered our name and we do not get a + connection started within some reasonable period of time. In this + routine we simply tear down the connection (and, most likely, the link + associated with it). + +Arguments: + + Dpc - Pointer to a system DPC object. + + DeferredContext - Pointer to the TP_CONNECTION block representing the + request that has timed out. + + SystemArgument1 - Not used. + + SystemArgument2 - Not used. + +Return Value: + + none. + +--*/ + +{ + PTP_CONNECTION Connection; + + Dpc, SystemArgument1, SystemArgument2; // prevent compiler warnings + + ENTER_NBF; + + Connection = (PTP_CONNECTION)DeferredContext; + + // + // If this connection is being run down, then we can't do anything. + // + + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + if ((Connection->Flags2 & CONNECTION_FLAGS2_STOPPING) || + ((Connection->Flags & CONNECTION_FLAGS_WAIT_SI) == 0)) { + + // + // The connection is stopping, or the SESSION_INITIALIZE + // has already been processed. + // + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint1 ("ListenTimeout: connection %lx stopping.\n", + Connection); + } + + NbfDereferenceConnection ("Listen timeout, ignored", Connection, CREF_TIMER); + LEAVE_NBF; + return; + } + + // + // We connected to the link before sending the NAME_RECOGNIZED, + // so we disconnect from it now. + // + +#if DBG + if (NbfDisconnectDebug) { + STRING remoteName, localName; + remoteName.Length = NETBIOS_NAME_LENGTH - 1; + remoteName.Buffer = Connection->RemoteName; + localName.Length = NETBIOS_NAME_LENGTH - 1; + localName.Buffer = Connection->AddressFile->Address->NetworkName->NetbiosName; + NbfPrint2( "NbfListenTimeout disconnecting connection to %S from %S\n", + &remoteName, &localName ); + } +#endif + + // + // BUBGUG: This is really ugly and I doubt it is correct. + // + + if ((Connection->Flags2 & CONNECTION_FLAGS2_ACCEPTED) != 0) { + + // + // This connection is up, we stop it. + // + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint1 ("ListenTimeout: connection %lx, accepted.\n", + Connection); + } + + // + // Set this so that the client will get a disconnect + // indication. + // + + Connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED; + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + NbfStopConnection (Connection, STATUS_IO_TIMEOUT); + + } else if (Connection->Link != (PTP_LINK)NULL) { + + // + // This connection is from a listen...we want to + // silently reset the listen. + // + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint1 ("ListenTimeout: connection %lx, listen restarted.\n", + Connection); + } + + Connection->Flags &= ~CONNECTION_FLAGS_WAIT_SI; + Connection->Flags2 &= ~CONNECTION_FLAGS2_REMOTE_VALID; + Connection->Flags2 |= CONNECTION_FLAGS2_WAIT_NQ; + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + NbfDereferenceConnection ("Timeout", Connection, CREF_LINK); + (VOID)NbfDisconnectFromLink (Connection, FALSE); + + } else { + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint1 ("ListenTimeout: connection %lx, link down.\n", + Connection); + } + + } + + + NbfDereferenceConnection("Listen Timeout", Connection, CREF_TIMER); + + LEAVE_NBF; + return; + +} /* ListenTimeout */ + + +NTSTATUS +ProcessAddGroupNameQuery( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS Address, + IN PNBF_HDR_CONNECTIONLESS Header, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ) + +/*++ + +Routine Description: + + This routine processes an incoming ADD_GROUP_NAME_QUERY frame. Because + our caller has already verified that the destination name in the frame + matches the transport address passed to us, we must simply transmit an + ADD_NAME_RESPONSE frame and exit with STATUS_ABANDONED. + + When we return STATUS_MORE_PROCESSING_REQUIRED, the caller of + this routine will continue to call us for each address for the device + context. When we return STATUS_SUCCESS, the caller will switch to the + next address. + When we return any other status code, including STATUS_ABANDONED, the + caller will stop distributing the frame. + +Arguments: + + DeviceContext - Pointer to our device context. + + Address - Pointer to the transport address object. + + Header - Pointer to the connectionless NetBIOS header of the frame. + + SourceAddress - Pointer to the source hardware address in the received + frame. + + SourceRouting - Pointer to the source routing information in + the frame. + + SourceRoutingLength - Length of the source routing information. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_UI_FRAME RawFrame; // ptr to allocated connectionless frame. + UINT HeaderLength; + UCHAR TempSR[MAX_SOURCE_ROUTING]; + PUCHAR ResponseSR; + + UNREFERENCED_PARAMETER (SourceAddress); + UNREFERENCED_PARAMETER (Address); + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint2 ("ProcessAddGroupNameQuery %lx: [%.16s].\n", Address, Header->DestinationName); + } + + // + // Allocate a UI frame from the pool. + // + + if (NbfCreateConnectionlessFrame (DeviceContext, &RawFrame) != STATUS_SUCCESS) { + return STATUS_ABANDONED; // no resources to do this. + } + + + // + // Build the MAC header. ADD_NAME_RESPONSE frames go out as + // non-broadcast source routing. + // + + if (SourceRouting != NULL) { + + RtlCopyMemory( + TempSR, + SourceRouting, + SourceRoutingLength); + + MacCreateNonBroadcastReplySR( + &DeviceContext->MacInfo, + TempSR, + SourceRoutingLength, + &ResponseSR); + + } else { + + ResponseSR = NULL; + + } + + MacConstructHeader ( + &DeviceContext->MacInfo, + RawFrame->Header, + SourceAddress->Address, + DeviceContext->LocalAddress.Address, + sizeof (DLC_FRAME) + sizeof (NBF_HDR_CONNECTIONLESS), + ResponseSR, + SourceRoutingLength, + &HeaderLength); + + + // + // Build the DLC UI frame header. + // + + NbfBuildUIFrameHeader(&RawFrame->Header[HeaderLength]); + HeaderLength += sizeof(DLC_FRAME); + + + // + // Build the Netbios header. + // + + ConstructAddNameResponse ( + (PNBF_HDR_CONNECTIONLESS)&(RawFrame->Header[HeaderLength]), + NETBIOS_NAME_TYPE_UNIQUE, // type of name is UNIQUE. + RESPONSE_CORR(Header), // correlator from rec'd frame. + (PUCHAR)Header->SourceName); // NetBIOS name being responded to. + + HeaderLength += sizeof(NBF_HDR_CONNECTIONLESS); + + + // + // Munge the packet length and send it. + // + + NbfSetNdisPacketLength(RawFrame->NdisPacket, HeaderLength); + + NbfSendUIFrame ( + DeviceContext, + RawFrame, + FALSE); // no loopback. + + return STATUS_ABANDONED; // don't forward frame to other addr's. +} /* ProcessAddGroupNameQuery */ + + +NTSTATUS +ProcessAddNameQuery( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS Address, + IN PNBF_HDR_CONNECTIONLESS Header, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ) + +/*++ + +Routine Description: + + This routine processes an incoming ADD_NAME_QUERY frame. Because + our caller has already verified that the destination name in the frame + matches the transport address passed to us, we must simply transmit an + ADD_NAME_RESPONSE frame and exit with STATUS_ABANDONED. + + When we return STATUS_MORE_PROCESSING_REQUIRED, the caller of + this routine will continue to call us for each address for the device + context. When we return STATUS_SUCCESS, the caller will switch to the + next address. When we return any other status code, including + STATUS_ABANDONED, the caller will stop distributing the frame. + +Arguments: + + DeviceContext - Pointer to our device context. + + Address - Pointer to the transport address object. + + Header - Pointer to the connectionless NetBIOS header of the frame. + + SourceAddress - Pointer to the source hardware address in the received + frame. + + SourceRouting - Pointer to the source routing information in + the frame. + + SourceRoutingLength - Length of the source routing information. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_UI_FRAME RawFrame; // ptr to allocated connectionless frame. + UINT HeaderLength; + UCHAR TempSR[MAX_SOURCE_ROUTING]; + PUCHAR ResponseSR; + + Address, SourceAddress; // prevent compiler warnings + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint2 ("ProcessAddNameQuery %lx: [%.16s].\n", Address, Header->DestinationName); + } + + // + // Allocate a UI frame from the pool. + // + + if (NbfCreateConnectionlessFrame (DeviceContext, &RawFrame) != STATUS_SUCCESS) { + return STATUS_ABANDONED; // no resources to do this. + } + + + // + // Build the MAC header. ADD_NAME_RESPONSE frames go out as + // non-broadcast source routing. + // + + if (SourceRouting != NULL) { + + RtlCopyMemory( + TempSR, + SourceRouting, + SourceRoutingLength); + + MacCreateNonBroadcastReplySR( + &DeviceContext->MacInfo, + TempSR, + SourceRoutingLength, + &ResponseSR); + + } else { + + ResponseSR = NULL; + + } + + MacConstructHeader ( + &DeviceContext->MacInfo, + RawFrame->Header, + SourceAddress->Address, + DeviceContext->LocalAddress.Address, + sizeof (DLC_FRAME) + sizeof (NBF_HDR_CONNECTIONLESS), + ResponseSR, + SourceRoutingLength, + &HeaderLength); + + + // + // Build the DLC UI frame header. + // + + NbfBuildUIFrameHeader(&RawFrame->Header[HeaderLength]); + HeaderLength += sizeof(DLC_FRAME); + + + // + // Build the Netbios header. + // + + ConstructAddNameResponse ( + (PNBF_HDR_CONNECTIONLESS)&(RawFrame->Header[HeaderLength]), + NETBIOS_NAME_TYPE_UNIQUE, // type of name is UNIQUE. + RESPONSE_CORR(Header), // correlator from received frame. + (PUCHAR)Header->SourceName); // NetBIOS name being responded to + + HeaderLength += sizeof(NBF_HDR_CONNECTIONLESS); + + + // + // Munge the packet length and send it. + // + + NbfSetNdisPacketLength(RawFrame->NdisPacket, HeaderLength); + + NbfSendUIFrame ( + DeviceContext, + RawFrame, + FALSE); // no loopback. + + return STATUS_ABANDONED; // don't forward frame to other addr's. +} /* ProcessAddNameQuery */ + + +NTSTATUS +ProcessNameInConflict( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS Address, + IN PNBF_HDR_CONNECTIONLESS Header, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ) + +/*++ + +Routine Description: + + This routine processes an incoming NAME_IN_CONFLICT frame. + Although we can't disrupt any traffic on this address, it is considered + invalid and cannot be used for any new address files or new connections. + Therefore, we just mark the address as invalid. + + When we return STATUS_MORE_PROCESSING_REQUIRED, the caller of + this routine will continue to call us for each address for the device + context. When we return STATUS_SUCCESS, the caller will switch to the + next address. When we return any other status code, including + STATUS_ABANDONED, the caller will stop distributing the frame. + +Arguments: + + DeviceContext - Pointer to our device context. + + Address - Pointer to the transport address object. + + Header - Pointer to the connectionless NetBIOS header of the frame. + + SourceAddress - Pointer to the source hardware address in the received + frame. + + SourceRouting - Pointer to the source routing information in + the frame. + + SourceRoutingLength - Length of the source routing information. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + DeviceContext, Header, SourceAddress; // prevent compiler warnings + + + // + // Ignore this if we are registering/deregistering (the name will + // go away anyway) or if we have already marked this name as + // in conflict and logged an error. + // + + if (Address->Flags & (ADDRESS_FLAGS_REGISTERING | ADDRESS_FLAGS_DEREGISTERING | ADDRESS_FLAGS_CONFLICT)) { + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint2 ("ProcessNameInConflict %lx: address marked [%.16s].\n", Address, Header->SourceName); + } + return STATUS_ABANDONED; + } + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint2 ("ProcessNameInConflict %lx: [%.16s].\n", Address, Header->SourceName); + } + +#if 0 + ACQUIRE_DPC_SPIN_LOCK (&Address->SpinLock); + + Address->Flags |= ADDRESS_FLAGS_CONFLICT; + + RELEASE_DPC_SPIN_LOCK (&Address->SpinLock); + + DbgPrint ("NBF: Name-in-conflict on <%.16s> from ", Header->DestinationName); + DbgPrint ("%2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x\n", + SourceAddress->Address[0], + SourceAddress->Address[1], + SourceAddress->Address[2], + SourceAddress->Address[3], + SourceAddress->Address[4], + SourceAddress->Address[5]); +#endif + + NbfWriteGeneralErrorLog( + Address->Provider, + EVENT_TRANSPORT_BAD_PROTOCOL, + 2, + STATUS_DUPLICATE_NAME, + L"NAME_IN_CONFLICT", + 16/sizeof(ULONG), + (PULONG)(Header->DestinationName)); + + return STATUS_ABANDONED; + +} /* ProcessNameInConflict */ + + +NTSTATUS +NbfIndicateDatagram( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS Address, + IN PUCHAR Dsdu, + IN ULONG Length + ) + +/*++ + +Routine Description: + + This routine processes an incoming DATAGRAM or DATAGRAM_BROADCAST frame. + BROADCAST and normal datagrams have the same receive logic, except + for broadcast datagrams Address will be the broadcast address. + + When we return STATUS_MORE_PROCESSING_REQUIRED, the caller of + this routine will continue to call us for each address for the device + context. When we return STATUS_SUCCESS, the caller will switch to the + next address. When we return any other status code, including + STATUS_ABANDONED, the caller will stop distributing the frame. + +Arguments: + + DeviceContext - Pointer to our device context. + + Address - Pointer to the transport address object. + + Dsdu - Pointer to a Mdl buffer that contains the received datagram. + The first byte of information in the buffer is the first byte in + the NetBIOS connectionless header, and it is already negotiated that + the data link layer will provide at least the entire NetBIOS header + as contiguous data. + + Length - The length of the MDL pointed to by Dsdu. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PLIST_ENTRY p, q; + PIRP irp; + PIO_STACK_LOCATION irpSp; + ULONG IndicateBytesCopied, MdlBytesCopied, BytesToCopy; + TA_NETBIOS_ADDRESS SourceName; + TA_NETBIOS_ADDRESS DestinationName; + PTDI_CONNECTION_INFORMATION remoteInformation; + ULONG returnLength; + PTP_ADDRESS_FILE addressFile, prevaddressFile; + PTDI_CONNECTION_INFORMATION DatagramInformation; + TDI_ADDRESS_NETBIOS UNALIGNED * DatagramAddress; + PNBF_HDR_CONNECTIONLESS Header = (PNBF_HDR_CONNECTIONLESS)Dsdu; + + IF_NBFDBG (NBF_DEBUG_DATAGRAMS) { + NbfPrint0 ("NbfIndicateDatagram: Entered.\n"); + } + + // + // If this datagram wasn't big enough for a transport header, then don't + // let the caller look at any data. + // + + if (Length < sizeof(NBF_HDR_CONNECTIONLESS)) { + IF_NBFDBG (NBF_DEBUG_DATAGRAMS) { + NbfPrint0 ("NbfIndicateDatagram: Short datagram abandoned.\n"); + } + return STATUS_ABANDONED; + } + + // + // Update our statistics. + // + + ++DeviceContext->Statistics.DatagramsReceived; + ADD_TO_LARGE_INTEGER( + &DeviceContext->Statistics.DatagramBytesReceived, + Length - sizeof(NBF_HDR_CONNECTIONLESS)); + + + // + // Call the client's ReceiveDatagram indication handler. He may + // want to accept the datagram that way. + // + + TdiBuildNetbiosAddress ((PUCHAR)Header->SourceName, FALSE, &SourceName); + TdiBuildNetbiosAddress ((PUCHAR)Header->DestinationName, FALSE, &DestinationName); + + + ACQUIRE_DPC_SPIN_LOCK (&Address->SpinLock); + + // + // Find the first open address file in the list. + // + + p = Address->AddressFileDatabase.Flink; + while (p != &Address->AddressFileDatabase) { + addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); + if (addressFile->State != ADDRESSFILE_STATE_OPEN) { + p = p->Flink; + continue; + } + NbfReferenceAddressFile(addressFile); + break; + } + + while (p != &Address->AddressFileDatabase) { + + // + // do we have a datagram receive request outstanding? If so, we will + // satisfy it first. We run through the receive datagram queue + // until we find a datagram with no remote address or with + // this sender's address as its remote address. + // + + for (q = addressFile->ReceiveDatagramQueue.Flink; + q != &addressFile->ReceiveDatagramQueue; + q = q->Flink) { + + irp = CONTAINING_RECORD (q, IRP, Tail.Overlay.ListEntry); + DatagramInformation = ((PTDI_REQUEST_KERNEL_RECEIVEDG) + &((IoGetCurrentIrpStackLocation(irp))-> + Parameters))->ReceiveDatagramInformation; + + if (DatagramInformation && + (DatagramInformation->RemoteAddress) && + (DatagramAddress = NbfParseTdiAddress(DatagramInformation->RemoteAddress, FALSE)) && + (!RtlEqualMemory( + Header->SourceName, + DatagramAddress->NetbiosName, + NETBIOS_NAME_LENGTH))) { + continue; + } + break; + } + + if (q != &addressFile->ReceiveDatagramQueue) { + KIRQL cancelIrql; + + + RemoveEntryList (q); + RELEASE_DPC_SPIN_LOCK (&Address->SpinLock); + + IF_NBFDBG (NBF_DEBUG_DATAGRAMS) { + NbfPrint0 ("NbfIndicateDatagram: Receive datagram request found, copying.\n"); + } + + // + // Copy the actual user data. + // + + MdlBytesCopied = 0; + + BytesToCopy = Length - sizeof(NBF_HDR_CONNECTIONLESS); + + if (BytesToCopy > 0) { + status = TdiCopyBufferToMdl ( + Dsdu, + sizeof(NBF_HDR_CONNECTIONLESS), // offset + BytesToCopy, // length + irp->MdlAddress, + 0, + &MdlBytesCopied); + } else { + status = STATUS_SUCCESS; + } + + // + // Copy the addressing information. + // + + irpSp = IoGetCurrentIrpStackLocation (irp); + remoteInformation = + ((PTDI_REQUEST_KERNEL_RECEIVEDG)(&irpSp->Parameters))-> + ReturnDatagramInformation; + if (remoteInformation != NULL) { + try { + if (remoteInformation->RemoteAddressLength != 0) { + if (remoteInformation->RemoteAddressLength >= + sizeof (TA_NETBIOS_ADDRESS)) { + + RtlCopyMemory ( + (PTA_NETBIOS_ADDRESS)remoteInformation->RemoteAddress, + &SourceName, + sizeof (TA_NETBIOS_ADDRESS)); + + returnLength = sizeof(TA_NETBIOS_ADDRESS); + remoteInformation->RemoteAddressLength = returnLength; + + } else { + + RtlCopyMemory ( + (PTA_NETBIOS_ADDRESS)remoteInformation->RemoteAddress, + &SourceName, + remoteInformation->RemoteAddressLength); + + returnLength = remoteInformation->RemoteAddressLength; + remoteInformation->RemoteAddressLength = returnLength; + + } + + } else { + + returnLength = 0; + } + + status = STATUS_SUCCESS; + + } except (EXCEPTION_EXECUTE_HANDLER) { + + returnLength = 0; + status = GetExceptionCode (); + + } + + } + + IoAcquireCancelSpinLock(&cancelIrql); + IoSetCancelRoutine(irp, NULL); + IoReleaseCancelSpinLock(cancelIrql); + irp->IoStatus.Information = MdlBytesCopied; + irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest (irp, IO_NETWORK_INCREMENT); + + NbfDereferenceAddress ("Receive DG done", Address, AREF_REQUEST); + + } else { + + RELEASE_DPC_SPIN_LOCK (&Address->SpinLock); + + // + // no receive datagram requests; is there a kernel client? + // + + if (addressFile->RegisteredReceiveDatagramHandler) { + + IndicateBytesCopied = 0; + + status = (*addressFile->ReceiveDatagramHandler)( + addressFile->ReceiveDatagramHandlerContext, + sizeof (TA_NETBIOS_ADDRESS), + &SourceName, + 0, + NULL, + TDI_RECEIVE_COPY_LOOKAHEAD, + Length - sizeof(NBF_HDR_CONNECTIONLESS), // indicated + Length - sizeof(NBF_HDR_CONNECTIONLESS), // available + &IndicateBytesCopied, + Dsdu + sizeof(NBF_HDR_CONNECTIONLESS), + &irp); + + if (status == STATUS_SUCCESS) { + + // + // The client accepted the datagram and so we're done. + // + + } else if (status == STATUS_DATA_NOT_ACCEPTED) { + + // + // The client did not accept the datagram and we need to satisfy + // a TdiReceiveDatagram, if possible. + // + + IF_NBFDBG (NBF_DEBUG_DATAGRAMS) { + NbfPrint0 ("NbfIndicateDatagram: Picking off a rcv datagram request from this address.\n"); + } + status = STATUS_MORE_PROCESSING_REQUIRED; + + } else if (status == STATUS_MORE_PROCESSING_REQUIRED) { + + // + // The client returned an IRP that we should queue up to the + // address to satisfy the request. + // + + irp->IoStatus.Status = STATUS_PENDING; // init status information. + irp->IoStatus.Information = 0; + irpSp = IoGetCurrentIrpStackLocation (irp); // get current stack loctn. + if ((irpSp->MajorFunction != IRP_MJ_INTERNAL_DEVICE_CONTROL) || + (irpSp->MinorFunction != TDI_RECEIVE_DATAGRAM)) { + irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; + return status; + } + + // + // Now copy the actual user data. + // + + MdlBytesCopied = 0; + + BytesToCopy = Length - sizeof(NBF_HDR_CONNECTIONLESS) - IndicateBytesCopied; + + if (BytesToCopy > 0) { + status = TdiCopyBufferToMdl ( + Dsdu, + sizeof(NBF_HDR_CONNECTIONLESS) + IndicateBytesCopied, + BytesToCopy, + irp->MdlAddress, + 0, + &MdlBytesCopied); + } else { + status = STATUS_SUCCESS; + } + + irp->IoStatus.Information = MdlBytesCopied; + irp->IoStatus.Status = status; + LEAVE_NBF; + IoCompleteRequest (irp, IO_NETWORK_INCREMENT); + ENTER_NBF; + } + } + } + + // + // Save this to dereference it later. + // + + prevaddressFile = addressFile; + + // + // Reference the next address file on the list, so it + // stays around. + // + + ACQUIRE_DPC_SPIN_LOCK (&Address->SpinLock); + + p = p->Flink; + while (p != &Address->AddressFileDatabase) { + addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); + if (addressFile->State != ADDRESSFILE_STATE_OPEN) { + p = p->Flink; + continue; + } + NbfReferenceAddressFile(addressFile); + break; + } + + RELEASE_DPC_SPIN_LOCK (&Address->SpinLock); + + // + // Now dereference the previous address file with + // the lock released. + // + + NbfDereferenceAddressFile (prevaddressFile); + + ACQUIRE_DPC_SPIN_LOCK (&Address->SpinLock); + + } // end of while loop + + RELEASE_DPC_SPIN_LOCK (&Address->SpinLock); + + return status; // to dispatcher. +} /* NbfIndicateDatagram */ + + +NTSTATUS +ProcessNameQuery( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS Address, + IN PNBF_HDR_CONNECTIONLESS Header, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ) + +/*++ + +Routine Description: + + This routine processes an incoming NAME_QUERY frame. There are two + types of NAME_QUERY frames, with basically the same layout. If the + session number in the frame is 0, then the frame is really a request + for information about the name, and not a request to establish a + session. If the session number is non-zero, then the frame is a + connection request that we use to satisfy a listen. + + With the new version of TDI, we now indicate the user that a request + for connection has been received, iff there is no outstanding listen. + If this does occur, the user can return a connection that is to be used + to accept the connection on. + + When we return STATUS_MORE_PROCESSING_REQUIRED, the caller of + this routine will continue to call us for each address for the device + context. When we return STATUS_SUCCESS, the caller will switch to the + next address. When we return any other status code, including + STATUS_ABANDONED, the caller will stop distributing the frame. + +Arguments: + + DeviceContext - Pointer to our device context. + + Address - Pointer to the transport address object. + + Header - Pointer to the connectionless NetBIOS header of the frame. + + SourceAddress - Pointer to the source hardware address in the received + frame. + + SourceRouting - Pointer to the source routing information in + the frame. + + SourceRoutingLength - Length of the source routing information. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PTP_UI_FRAME RawFrame; + PTP_CONNECTION Connection; + PTP_LINK Link; + UCHAR NameType; + BOOLEAN ConnectIndicationBlocked = FALSE; + PLIST_ENTRY p; + UINT HeaderLength; + PUCHAR GeneralSR; + UINT GeneralSRLength; + BOOLEAN UsedListeningConnection = FALSE; + PTP_ADDRESS_FILE addressFile, prevaddressFile; + PIRP acceptIrp; + + CONNECTION_CONTEXT connectionContext; + TA_NETBIOS_ADDRESS RemoteAddress; + + // + // If we are just registering or deregistering this address, then don't + // allow state changes. Just throw the packet away, and let the frame + // distributor try the next address. + // + // Also drop it if the address is in conflict. + // + + if (Address->Flags & (ADDRESS_FLAGS_REGISTERING | ADDRESS_FLAGS_DEREGISTERING | ADDRESS_FLAGS_CONFLICT)) { + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint2 ("ProcessNameQuery %lx: address not stable [%.16s].\n", Address, Header->SourceName); + } + return STATUS_SUCCESS; + } + + // + // Process this differently depending on whether it is a find name + // request or an incoming connection. + // + + if (Header->Data2Low == 0) { + + // + // This is a find-name request. Respond with a NAME_RECOGNIZED frame. + // + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint2 ("ProcessNameQuery %lx: find name [%.16s].\n", Address, Header->SourceName); + } + + NbfSendNameRecognized( + Address, + 0, // LSN 0 == FIND_NAME response + Header, + SourceAddress, + SourceRouting, + SourceRoutingLength); + + return STATUS_ABANDONED; // don't allow multiple responses. + + } else { // (if Data2Low is non-zero) + + // + // This is an incoming connection request. If we have a listening + // connection on this address, then continue with the connection setup. + // If there is no outstanding listen, then indicate any kernel mode + // clients that want to know about this frame. If a listen was posted, + // then a connection has already been set up for it. The LSN field of + // the connection is set to 0, so we look for the first 0 LSN in the + // database. + // + + // + // First, check if we already have an active connection with + // this remote on this address. If so, we resend the NAME_RECOGNIZED + // if we have not yet received the SESSION_INITIALIZE; otherwise + // we ignore the frame. + // + + // + // If successful this adds a reference of type CREF_LISTENING. + // + + if (Connection = NbfLookupRemoteName(Address, (PUCHAR)Header->SourceName, Header->Data2Low)) { + + // + // We have an active connection on this guy, see if he + // still appears to be waiting to a NAME_RECOGNIZED. + // + + if (((Connection->Flags & CONNECTION_FLAGS_WAIT_SI) != 0) && + (Connection->Link != (PTP_LINK)NULL) && + (Connection->Link->State == LINK_STATE_ADM)) { + + // + // Yes, he must have dropped a previous NAME_RECOGNIZED + // so we send another one. + // + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint2("Dup NAME_QUERY found: %lx [%.16s]\n", Connection, Header->SourceName); + } + + NbfSendNameRecognized( + Address, + Connection->Lsn, + Header, + SourceAddress, + SourceRouting, + SourceRoutingLength); + + } else { + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint2("Dup NAME_QUERY ignored: %lx [%.16s]\n", Connection, Header->SourceName); + } + + } + + NbfDereferenceConnection ("Lookup done", Connection, CREF_LISTENING); + + return STATUS_ABANDONED; + + } + + // If successful, this adds a reference which is removed before + // this function returns. + + Connection = NbfLookupListeningConnection (Address, (PUCHAR)Header->SourceName); + if (Connection == NULL) { + + // + // not having a listening connection is not reason to bail out here. + // we need to indicate to the user that a connect attempt occurred, + // and see if there is a desire to use this connection. We + // indicate in order to all address files that are + // using this address. + // + // If we already have an indication pending on this address, + // we ignore this frame (the NAME_QUERY may have come from + // a different address, but we can't know that). Also, if + // there is already an active connection on this remote + // name, then we ignore the frame. + // + + + ACQUIRE_DPC_SPIN_LOCK (&Address->SpinLock); + + p = Address->AddressFileDatabase.Flink; + while (p != &Address->AddressFileDatabase) { + addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); + if (addressFile->State != ADDRESSFILE_STATE_OPEN) { + p = p->Flink; + continue; + } + NbfReferenceAddressFile(addressFile); + break; + } + + while (p != &Address->AddressFileDatabase) { + + RELEASE_DPC_SPIN_LOCK (&Address->SpinLock); + + if ((addressFile->RegisteredConnectionHandler == TRUE) && + (!addressFile->ConnectIndicationInProgress)) { + + + TdiBuildNetbiosAddress ( + (PUCHAR)Header->SourceName, + FALSE, + &RemoteAddress); + + addressFile->ConnectIndicationInProgress = TRUE; + + // + // we have a connection handler, now indicate that a connection + // attempt occurred. + // + + status = (addressFile->ConnectionHandler)( + addressFile->ConnectionHandlerContext, + sizeof (TA_NETBIOS_ADDRESS), + &RemoteAddress, + 0, + NULL, + 0, + NULL, + &connectionContext, + &acceptIrp); + + if (status == STATUS_MORE_PROCESSING_REQUIRED) { + + // + // the user has connected a currently open connection, but + // we have to figure out which one it is. + // + + // + // If successful this adds a reference of type LISTENING + // (the same what NbfLookupListeningConnection adds). + // + + Connection = NbfLookupConnectionByContext ( + Address, + connectionContext); + + if (Connection == NULL) { + + // + // BUGBUG: We have to tell the client that + // his connection is bogus (or has this + // already happened??). + // + + NbfPrint0("STATUS_MORE_PROCESSING, connection not found\n"); + addressFile->ConnectIndicationInProgress = FALSE; + acceptIrp->IoStatus.Status = STATUS_INVALID_CONNECTION; + IoCompleteRequest (acceptIrp, IO_NETWORK_INCREMENT); + + goto whileend; // try next address file + + } else { + + if (Connection->AddressFile->Address != Address) { + addressFile->ConnectIndicationInProgress = FALSE; + + NbfPrint0("STATUS_MORE_PROCESSING, address wrong\n"); + NbfStopConnection (Connection, STATUS_INVALID_ADDRESS); + NbfDereferenceConnection("Bad Address", Connection, CREF_LISTENING); + Connection = NULL; + acceptIrp->IoStatus.Status = STATUS_INVALID_ADDRESS; + IoCompleteRequest (acceptIrp, IO_NETWORK_INCREMENT); + + goto whileend; // try next address file + } + + // + // OK, we have a valid connection. If the response to + // this connection was disconnect, we need to reject + // the connection request and return. If it was accept + // or not specified (to be done later), we simply + // fall through and continue processing on the U Frame. + // + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + if ((Connection->Flags2 & CONNECTION_FLAGS2_DISCONNECT) != 0) { + +// Connection->Flags2 &= ~CONNECTION_FLAGS2_DISCONNECT; + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + NbfPrint0("STATUS_MORE_PROCESSING, disconnect\n"); + addressFile->ConnectIndicationInProgress = FALSE; + NbfDereferenceConnection("Disconnecting", Connection, CREF_LISTENING); + Connection = NULL; + acceptIrp->IoStatus.Status = STATUS_INVALID_CONNECTION; + IoCompleteRequest (acceptIrp, IO_NETWORK_INCREMENT); + + goto whileend; // try next address file + } + + } + + // + // Make a note that we have to set + // addressFile->ConnectIndicationInProgress to + // FALSE once the address is safely stored + // in the connection. + // + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint4 ("ProcessNameQuery %lx: indicate DONE, context %lx conn %lx [%.16s].\n", Address, connectionContext, Connection, Header->SourceName); + } + IF_NBFDBG (NBF_DEBUG_SETUP) { + NbfPrint6 ("Link is %x-%x-%x-%x-%x-%x\n", + SourceAddress->Address[0], + SourceAddress->Address[1], + SourceAddress->Address[2], + SourceAddress->Address[3], + SourceAddress->Address[4], + SourceAddress->Address[5]); + } + + // + // Set up our flags...we turn on REQ_COMPLETED + // so that disconnect will be indicated if the + // connection goes down before a session init + // is received. + // + + Connection->Flags2 &= ~CONNECTION_FLAGS2_STOPPING; + Connection->Status = STATUS_PENDING; + Connection->Flags2 |= (CONNECTION_FLAGS2_ACCEPTED | + CONNECTION_FLAGS2_REQ_COMPLETED); + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + ConnectIndicationBlocked = TRUE; + NbfDereferenceAddressFile (addressFile); + acceptIrp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest (acceptIrp, IO_NETWORK_INCREMENT); + ACQUIRE_DPC_SPIN_LOCK (&Address->SpinLock); + break; // exit the while + +#if 0 + } else if (status == STATUS_EVENT_PENDING) { + + // + // user has returned a connectionContext, use that for further + // processing of the connection. First validate it so + // we can know we won't just start a connection and never + // finish. + // + // + // If successful this adds a reference of type LISTENING + // (the same what NbfLookupListeningConnection adds). + // + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint3 ("ProcessNameQuery %lx: indicate PENDING, context %lx [%.16s].\n", Address, connectionContext, Header->SourceName); + } + + + Connection = NbfLookupConnectionByContext ( + Address, + connectionContext); + + if (Connection == NULL) { + + // + // BUGBUG: We have to tell the client that + // his connection is bogus (or has this + // already happened??). + // + + NbfPrint0("STATUS_MORE_PROCESSING, but connection not found\n"); + addressFile->ConnectIndicationInProgress = FALSE; + + goto whileend; // try next address file. + + } else { + + if (Connection->AddressFile->Address != Address) { + addressFile->ConnectIndicationInProgress = FALSE; + NbfStopConnection (Connection, STATUS_INVALID_ADDRESS); + NbfDereferenceConnection("Bad Address", Connection, CREF_LISTENING); + Connection = NULL; + + goto whileend; // try next address file. + } + + } + + // + // Make a note that we have to set + // addressFile->ConnectionIndicatInProgress to + // FALSE once the address is safely stored + // in the connection. + // + + ConnectIndicationBlocked = TRUE; + NbfDereferenceAddressFile (addressFile); + ACQUIRE_DPC_SPIN_LOCK (&Address->SpinLock); + break; // exit the while +#endif + + } else if (status == STATUS_INSUFFICIENT_RESOURCES) { + + // + // we know the address, but can't create a connection to + // use on it. This gets passed to the network as a response + // saying I'm here, but can't help. + // + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint2 ("ProcessNameQuery %lx: indicate RESOURCES [%.16s].\n", Address, Header->SourceName); + } + + addressFile->ConnectIndicationInProgress = FALSE; + + // + // We should send a NR with LSN 0xff, indicating + // no resources, but LM 2.0 does not interpret + // that correctly. So, we send LSN 0 (no listens) + // instead. + // + + NbfSendNameRecognized( + Address, + 0, + Header, + SourceAddress, + SourceRouting, + SourceRoutingLength); + + NbfDereferenceAddressFile (addressFile); + return STATUS_ABANDONED; + + } else { + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint2 ("ProcessNameQuery %lx: indicate invalid [%.16s].\n", Address, Header->SourceName); + } + + addressFile->ConnectIndicationInProgress = FALSE; + + goto whileend; // try next address file + + } // end status ifs + + } else { + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint2 ("ProcessNameQuery %lx: no handler [%.16s].\n", Address, Header->SourceName); + } + + goto whileend; // try next address file + + } // end no indication handler + +whileend: + // + // Jumping here is like a continue, except that the + // addressFile pointer is advanced correctly. + // + + // + // Save this to dereference it later. + // + + prevaddressFile = addressFile; + + // + // Reference the next address file on the list, so it + // stays around. + // + + ACQUIRE_DPC_SPIN_LOCK (&Address->SpinLock); + + p = p->Flink; + while (p != &Address->AddressFileDatabase) { + addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); + if (addressFile->State != ADDRESSFILE_STATE_OPEN) { + p = p->Flink; + continue; + } + NbfReferenceAddressFile(addressFile); + break; + } + + RELEASE_DPC_SPIN_LOCK (&Address->SpinLock); + + // + // Now dereference the previous address file with + // the lock released. + // + + NbfDereferenceAddressFile (prevaddressFile); + + ACQUIRE_DPC_SPIN_LOCK (&Address->SpinLock); + + } // end of loop through the address files. + + RELEASE_DPC_SPIN_LOCK (&Address->SpinLock); + + if (Connection == NULL) { + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint2 ("ProcessNameQuery %lx: no connection [%.16s].\n", Address, Header->SourceName); + } + + // + // We still did not find a connection after looping + // through the address files. + // + + NbfSendNameRecognized( + Address, + 0, // LSN 0 == No listens + Header, + SourceAddress, + SourceRouting, + SourceRoutingLength); + + // + // We used to return MORE_PROCESSING_REQUIRED, but + // since we matched with this address, no other + // address is going to match, so abandon it. + // + + return STATUS_ABANDONED; + + } + + } else { // end connection == null + + UsedListeningConnection = TRUE; + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint3 ("ProcessNameQuery %lx: found listen %lx: [%.16s].\n", Address, Connection, Header->SourceName); + } + + } + + + // + // At this point the connection has a reference of type + // CREF_LISTENING. Allocate a UI frame from the pool. + // + + status = NbfCreateConnectionlessFrame (DeviceContext, &RawFrame); + if (!NT_SUCCESS (status)) { // no resources to respond. + PANIC ("ProcessNameQuery: Can't get UI Frame, dropping query\n"); + if (ConnectIndicationBlocked) { + addressFile->ConnectIndicationInProgress = FALSE; + } + if (UsedListeningConnection) { + Connection->Flags2 |= CONNECTION_FLAGS2_WAIT_NQ; + } else { + Connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED; + NbfStopConnection (Connection, STATUS_INSUFFICIENT_RESOURCES); + } + NbfDereferenceConnection("Can't get UI Frame", Connection, CREF_LISTENING); + return STATUS_ABANDONED; + } + + // + // Build the MAC header. NAME_RECOGNIZED frames go out as + // general-route source routing. + // + + MacReturnGeneralRouteSR( + &DeviceContext->MacInfo, + &GeneralSR, + &GeneralSRLength); + + + MacConstructHeader ( + &DeviceContext->MacInfo, + RawFrame->Header, + SourceAddress->Address, + DeviceContext->LocalAddress.Address, + sizeof (DLC_FRAME) + sizeof (NBF_HDR_CONNECTIONLESS), + GeneralSR, + GeneralSRLength, + &HeaderLength); + + + // + // Build the DLC UI frame header. + // + + NbfBuildUIFrameHeader(&RawFrame->Header[HeaderLength]); + HeaderLength += sizeof(DLC_FRAME); + + + // + // Before we continue, store the remote guy's transport address + // into the TdiListen's TRANSPORT_CONNECTION buffer. This allows + // the client to determine who called him. + // + + Connection->CalledAddress.NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + + TdiCopyLookaheadData( + Connection->CalledAddress.NetbiosName, + Header->SourceName, + 16, + DeviceContext->MacInfo.CopyLookahead ? TDI_RECEIVE_COPY_LOOKAHEAD : 0); + + RtlCopyMemory( Connection->RemoteName, Connection->CalledAddress.NetbiosName, 16 ); + Connection->Flags2 |= CONNECTION_FLAGS2_REMOTE_VALID; + + if (ConnectIndicationBlocked) { + addressFile->ConnectIndicationInProgress = FALSE; + } + + // + // Now formulate a reply. + // + + NameType = (UCHAR)((Address->Flags & ADDRESS_FLAGS_GROUP) ? + NETBIOS_NAME_TYPE_GROUP : NETBIOS_NAME_TYPE_UNIQUE); + + // + // We have a listening connection on the address now. Create a link + // for it to be associated with and make that link ready. Respond to + // the sender with a name_recognized frame. then we will receive our + // first connection-oriented frame, SESSION_INITIALIZE, handled + // in IFRAMES.C. Then we respond with SESSION_CONFIRM, and then + // the TdiListen completes. + // + + // If successful, this adds a link reference which is removed + // in NbfDisconnectFromLink. It does NOT add a link reference. + + status = NbfCreateLink ( + DeviceContext, + SourceAddress, // remote hardware address. + SourceRouting, + SourceRoutingLength, + LISTENER_LINK, // for loopback link + &Link); // resulting link. + + if (NT_SUCCESS (status)) { // link established. + + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + // If successful, this adds a connection reference + // which is removed in NbfDisconnectFromLink + + if (((Connection->Flags2 & CONNECTION_FLAGS2_STOPPING) == 0) && + ((status = NbfConnectToLink (Link, Connection)) == STATUS_SUCCESS)) { + + Connection->Flags |= CONNECTION_FLAGS_WAIT_SI; // wait for SI. + Connection->Retries = 1; + Connection->Rsn = Header->Data2Low; // save remote LSN. + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + NbfWaitLink (Link); // start link going. + + ConstructNameRecognized ( // build a good response. + (PNBF_HDR_CONNECTIONLESS)&(RawFrame->Header[HeaderLength]), + NameType, // type of local name. + Connection->Lsn, // return our LSN. + RESPONSE_CORR(Header), // new xmit corr. + 0, // our response correlator (unused). + Header->DestinationName,// our NetBIOS name. + Header->SourceName); // his NetBIOS name. + + + HeaderLength += sizeof(NBF_HDR_CONNECTIONLESS); + NbfSetNdisPacketLength(RawFrame->NdisPacket, HeaderLength); + + // + // Now, to avoid problems with hanging listens, we'll start the + // connection timer and give a limited period for the connection + // to succeed. This avoids waiting forever for those first few + // frames to be exchanged. When the timeout occurs, the + // the dereference will cause the circuit to be torn down. + // + // The maximum delay we can accomodate on a link is + // NameQueryRetries * NameQueryTimeout (assuming the + // remote has the same timeous). There are three + // exchanges of packets until the SESSION_INITIALIZE + // shows up, to be safe we multiply by four. + // + + NbfStartConnectionTimer( + Connection, + NbfListenTimeout, + 4 * DeviceContext->NameQueryRetries * DeviceContext->NameQueryTimeout); + + NbfSendUIFrame ( + DeviceContext, + RawFrame, + TRUE); // loopback if needed. + + IF_NBFDBG (NBF_DEBUG_SETUP) { + NbfPrint2("Connection %lx on link %lx\n", Connection, Link); + } + + NbfDereferenceConnection("ProcessNameQuery", Connection, CREF_LISTENING); + return STATUS_ABANDONED; // successful! + } + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + // + // We don't have a free LSN to allocate, so fall through to + // report "no resources". + // + + // We did a link reference since NbfCreateLink succeeded, + // but since NbfConnectToLink failed we will never remove + // that reference in NbfDisconnectFromLink, so do it here. + + NbfDereferenceLink ("No more LSNS", Link, LREF_CONNECTION); + + ASSERT (Connection->Lsn == 0); + + } + + // + // If we fall through here, then we couldn't get resources to set + // up this connection, so just send him a "no resources" reply. + // + + if (UsedListeningConnection) { + + Connection->Flags2 |= CONNECTION_FLAGS2_WAIT_NQ; // put this back. + + } else { + + Connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED; + NbfStopConnection (Connection, STATUS_INSUFFICIENT_RESOURCES); + + } + + // + // We should send a NR with LSN 0xff, indicating + // no resources, but LM 2.0 does not interpret + // that correctly. So, we send LSN 0 (no listens) + // instead. + // + + ConstructNameRecognized ( + (PNBF_HDR_CONNECTIONLESS)&(RawFrame->Header[HeaderLength]), + NameType, + 0, // LSN=0 means no listens + RESPONSE_CORR(Header), + 0, + Header->DestinationName, // our NetBIOS name. + Header->SourceName); // his NetBIOS name. + + HeaderLength += sizeof(NBF_HDR_CONNECTIONLESS); + NbfSetNdisPacketLength(RawFrame->NdisPacket, HeaderLength); + + NbfSendUIFrame ( + DeviceContext, + RawFrame, + TRUE); // loopback if needed. + + NbfDereferenceConnection("ProcessNameQuery done", Connection, CREF_LISTENING); + } + + return STATUS_ABANDONED; + +} /* ProcessNameQuery */ + + +NTSTATUS +ProcessAddNameResponse( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS Address, + IN PNBF_HDR_CONNECTIONLESS Header, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ) + +/*++ + +Routine Description: + + This routine processes an incoming ADD_NAME_RESPONSE frame. + + When we return STATUS_MORE_PROCESSING_REQUIRED, the caller of + this routine will continue to call us for each address for the device + context. When we return STATUS_SUCCESS, the caller will switch to the + next address. When we return any other status code, including + STATUS_ABANDONED, the caller will stop distributing the frame. + +Arguments: + + DeviceContext - Pointer to our device context. + + Address - Pointer to the transport address object. + + Header - Pointer to the connectionless NetBIOS header of the frame. + + SourceAddress - Pointer to the source hardware address in the received + frame. + + SourceRouting - Pointer to the source routing information in + the frame. + + SourceRoutingLength - Length of the source routing information. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + BOOLEAN SendNameInConflict = FALSE; + UNREFERENCED_PARAMETER(DeviceContext); + + ACQUIRE_DPC_SPIN_LOCK (&Address->SpinLock); + + // + // If we aren't trying to register this address, then the sender of + // this frame is bogus. We cannot allow our state to change based + // on the reception of a random frame. + // + + if (!(Address->Flags & ADDRESS_FLAGS_REGISTERING)) { + RELEASE_DPC_SPIN_LOCK (&Address->SpinLock); + IF_NBFDBG (NBF_DEBUG_ADDRESS | NBF_DEBUG_UFRAMES) { + NbfPrint2("ProcessAddNameResponse %lx: not registering [%.16s]\n", Address, Header->SourceName); + } + return STATUS_ABANDONED; // just destroy the packet. + } + + // + // Unfortunately, we are registering this address and another host + // on the network is also attempting to register the same NetBIOS + // name on the same network. Because he got to us first, we cannot + // register our name. Thus, the address must die. We set this flag + // and on the next timeout we will shut down. + // + + Address->Flags |= ADDRESS_FLAGS_DUPLICATE_NAME; + + if (Header->Data2Low == NETBIOS_NAME_TYPE_UNIQUE) { + + // + // If we have already gotten a response from someone saying + // this address is uniquely owned, then make sure any future + // responses come from the same MAC address. + // + + if ((*((LONG UNALIGNED *)Address->UniqueResponseAddress) == 0) && + (*((SHORT UNALIGNED *)(&Address->UniqueResponseAddress[4])) == 0)) { + + RtlMoveMemory(Address->UniqueResponseAddress, SourceAddress->Address, 6); + + } else if (!RtlEqualMemory( + Address->UniqueResponseAddress, + SourceAddress->Address, + 6)) { + + if (!Address->NameInConflictSent) { + SendNameInConflict = TRUE; + } + + } + + } else { + + // + // For group names, make sure nobody else decided that it was + // a unique address. + // + + if ((*((LONG UNALIGNED *)Address->UniqueResponseAddress) != 0) || + (*((SHORT UNALIGNED *)(&Address->UniqueResponseAddress[4])) != 0)) { + + if (!Address->NameInConflictSent) { + SendNameInConflict = TRUE; + } + + } + + } + + RELEASE_DPC_SPIN_LOCK (&Address->SpinLock); + + if (SendNameInConflict) { + + Address->NameInConflictSent = TRUE; + NbfSendNameInConflict( + Address, + (PUCHAR)Header->DestinationName); + + } + + + IF_NBFDBG (NBF_DEBUG_ADDRESS | NBF_DEBUG_UFRAMES) { + NbfPrint2("ProcessAddNameResponse %lx: stopping [%.16s]\n", Address, Header->SourceName); + } + + return STATUS_ABANDONED; // done with this frame. +} /* ProcessAddNameResponse */ + + +NTSTATUS +ProcessNameRecognized( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS Address, + IN PNBF_HDR_CONNECTIONLESS Header, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ) + +/*++ + +Routine Description: + + This routine processes an incoming NAME_RECOGNIZED frame. This frame + is received because we issued a NAME_QUERY frame to actively initiate + a connection with a remote host. + + When we return STATUS_MORE_PROCESSING_REQUIRED, the caller of + this routine will continue to call us for each address for the device + context. When we return STATUS_SUCCESS, the caller will switch to the + next address. When we return any other status code, including + STATUS_ABANDONED, the caller will stop distributing the frame. + +Arguments: + + DeviceContext - Pointer to our device context. + + Address - Pointer to the transport address object. + + Header - Pointer to the connectionless NetBIOS header of the frame. + + SourceAddress - Pointer to the source hardware address in the received + frame. + + SourceRouting - Pointer to the source routing information in + the frame. + + SourceRoutingLength - Length of the source routing information. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PTP_CONNECTION Connection; + PTP_LINK Link; + BOOLEAN TimerCancelled; + + + if (Address->Flags & (ADDRESS_FLAGS_REGISTERING | ADDRESS_FLAGS_DEREGISTERING | ADDRESS_FLAGS_CONFLICT)) { + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint2 ("ProcessNameRecognized %lx: address not stable [%.16s].\n", Address, Header->SourceName); + } + return STATUS_ABANDONED; // invalid address state, drop packet. + } + + // + // Find names and connections both require a TP_CONNECTION to work. + // In either case, the ConnectionId field of the TP_CONNECTION object + // was sent as the response correlator in the NAME_QUERY frame, so + // we should get the same correlator back in this frame in the + // transmit correlator. Because this number is unique across + // all the connections on an address, we can determine if the frame + // was for this address or not. + // + + // this causes a reference which is removed before this function returns. + + Connection = NbfLookupConnectionById ( + Address, + TRANSMIT_CORR(Header)); + + // + // has he been deleted while we were waiting? + // + + if (Connection == NULL) { + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint2 ("ProcessNameRecognized %lx: no connection [%.16s].\n", Address, Header->SourceName); + } + return STATUS_ABANDONED; + } + + // + // This frame is a response to a NAME_QUERY frame that we previously + // sent to him. Either he's returning "insufficient resources", + // indicating that a session cannot be established, or he's initiated + // his side of the connection. + // + + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + if ((Connection->Flags2 & CONNECTION_FLAGS2_STOPPING) != 0) { + + // + // Connection is stopping, don't process this. + // + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint3 ("ProcessNameRecognized %lx: connection %lx stopping [%.16s].\n", Address, Connection, Header->SourceName); + } + + NbfDereferenceConnection("Name Recognized, stopping", Connection, CREF_BY_ID); + + return STATUS_ABANDONED; + } + + if (Header->Data2Low == 0x00 || + (Header->Data2Low > 0x00 && (Connection->Flags2 & CONNECTION_FLAGS2_WAIT_NR_FN))) { // no listens, or FIND.NAME response. + + if (!(Connection->Flags2 & CONNECTION_FLAGS2_CONNECTOR)) { + + // + // This is just a find name request, we are not trying to + // establish a connection. Currently, there is no reason + // for this to occur, so just save some room to add this + // extra feature later to support NETBIOS find name. + // + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint3 ("ProcessNameRecognized %lx: connection %lx not connector [%.16s].\n", Address, Connection, Header->SourceName); + } + + NbfDereferenceConnection("Unexpected FN Response", Connection, CREF_BY_ID); + return STATUS_ABANDONED; // we processed the frame. + } + + // + // We're setting up a session. If we are waiting for the first NAME + // RECOGNIZED, then setup the link and send the second NAME_QUERY. + // If we're waiting for the second NAME_RECOGNIZED, then he didn't + // have an LSN to finish the connection, so tear it down. + // + + if (Connection->Flags2 & CONNECTION_FLAGS2_WAIT_NR_FN) { + + // + // Now that we know the data link address of the remote host + // we're connecting to, we need to create a TP_LINK object to + // represent the data link between these two machines. If there + // is already a data link there, then the object will be reused. + // + + Connection->Flags2 &= ~CONNECTION_FLAGS2_WAIT_NR_FN; + + if (Header->Data2High == NETBIOS_NAME_TYPE_UNIQUE) { + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + // + // The Netbios address we are connecting to is a + // unique name + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint3 ("ProcessNameRecognized %lx: connection %lx send 2nd NQ [%.16s].\n", Address, Connection, Header->SourceName); + } + + + // If successful, this adds a link reference which is removed + // in NbfDisconnectFromLink + + status = NbfCreateLink ( + DeviceContext, + SourceAddress, // remote hardware address. + SourceRouting, + SourceRoutingLength, + CONNECTOR_LINK, // for loopback link + &Link); // resulting link. + + if (!NT_SUCCESS (status)) { // no resources. + NbfStopConnection (Connection, STATUS_INSUFFICIENT_RESOURCES); + NbfDereferenceConnection ("No Resources for link", Connection, CREF_BY_ID); + return STATUS_ABANDONED; + } + + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + // If successful, this adds a connection reference which is + // removed in NbfDisconnectFromLink. It does NOT add a link ref. + + if ((Connection->Flags2 & CONNECTION_FLAGS2_STOPPING) || + ((status = NbfConnectToLink (Link, Connection)) != STATUS_SUCCESS)) { + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + // Connection stopping or no LSN's available on this link. + // We did a link reference since NbfCreateLink succeeded, + // but since NbfConnectToLink failed we will never remove + // that reference in NbfDisconnectFromLink, so do it here. + + NbfDereferenceLink ("Can't connect to link", Link, LREF_CONNECTION); // most likely destroys this. + + NbfStopConnection (Connection, STATUS_INSUFFICIENT_RESOURCES); + NbfDereferenceConnection ("Cant connect to link", Connection, CREF_BY_ID); + return STATUS_ABANDONED; + } + + (VOID)InterlockedIncrement(&Link->NumberOfConnectors); + + } else { + + // + // We are connecting to a group name; we have to + // assign an LSN now, but we don't connect to + // the link until we get a committed name response. + // + + Connection->Flags2 |= CONNECTION_FLAGS2_GROUP_LSN; + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint3 ("ProcessNameRecognized %lx: connection %lx send 2nd NQ GROUP [%.16s].\n", Address, Connection, Header->SourceName); + } + + if (NbfAssignGroupLsn(Connection) != STATUS_SUCCESS) { + + // + // Could not find an empty LSN; have to fail. + // + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + NbfStopConnection (Connection, STATUS_INSUFFICIENT_RESOURCES); + NbfDereferenceConnection("Can't get group LSN", Connection, CREF_BY_ID); + return STATUS_ABANDONED; + + } + + } + + + // + // Send the second NAME_QUERY frame, committing our LSN to + // the remote guy. + // + + Connection->Flags2 |= CONNECTION_FLAGS2_WAIT_NR; + Connection->Retries = (USHORT)DeviceContext->NameQueryRetries; + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + NbfStartConnectionTimer ( + Connection, + ConnectionEstablishmentTimeout, + DeviceContext->NameQueryTimeout); + + KeQueryTickCount (&Connection->ConnectStartTime); + + NbfSendNameQuery( + Connection, + TRUE); + + NbfDereferenceConnection ("Done with lookup", Connection, CREF_BY_ID); // release lookup hold. + return STATUS_ABANDONED; // we processed the frame. + + } else if (Connection->Flags2 & CONNECTION_FLAGS2_WAIT_NR) { + + if (Connection->Link) { + + if (RtlEqualMemory( + Connection->Link->HardwareAddress.Address, + SourceAddress->Address, + 6)) { + + // + // Unfortunately, he's telling us that he doesn't have resources + // to allocate an LSN. We set a flag to record this and + // ignore the frame. + // + + Connection->Flags2 |= CONNECTION_FLAGS2_NO_LISTEN; + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint3 ("ProcessNameRecognized %lx: connection %lx no listens [%.16s].\n", Address, Connection, Header->SourceName); + } + + } else { + + // + // This response comes from a different remote from the + // last one. For unique names this indicates a duplicate + // name on the network. + // + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + if (Header->Data2High == NETBIOS_NAME_TYPE_UNIQUE) { + + if (!Address->NameInConflictSent) { + + Address->NameInConflictSent = TRUE; + NbfSendNameInConflict( + Address, + (PUCHAR)Header->SourceName); + + } + } + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint3 ("ProcessNameRecognized %lx: connection %lx name in conflict [%.16s].\n", Address, Connection, Header->SourceName); + } + + } + + } else { + + // + // The response came back so fast the connection is + // not stable, ignore it. + // + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + } + + NbfDereferenceConnection ("No remote resources", Connection, CREF_BY_ID); // release our lookup hold. + return STATUS_ABANDONED; // we processed the frame. + + } else { + + // + // Strange state. This should never happen, because we should be + // either waiting for a first or second name recognized frame. It + // is possible that the remote station received two frames because + // of our retransmits, and so he responded to both. Toss the frame. + // + + if (Connection->Link) { + + if (!RtlEqualMemory( + Connection->Link->HardwareAddress.Address, + SourceAddress->Address, + 6)) { + + // + // This response comes from a different remote from the + // last one. For unique names this indicates a duplicate + // name on the network. + // + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + if (Header->Data2High == NETBIOS_NAME_TYPE_UNIQUE) { + + if (!Address->NameInConflictSent) { + + Address->NameInConflictSent = TRUE; + NbfSendNameInConflict( + Address, + (PUCHAR)Header->SourceName); + + } + + } + + } else { + + // + // This is the same remote, just ignore it. + // + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + } + + } else { + + // + // The response came back so fast the connection is + // not stable, ignore it. + // + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + } + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint3 ("ProcessNameRecognized %lx: connection %lx unexpected [%.16s].\n", Address, Connection, Header->SourceName); + } + + NbfDereferenceConnection ("Tossing second response Done with lookup", Connection, CREF_BY_ID); // release our lookup hold. + return STATUS_ABANDONED; // we processed the frame. + + } + + } else if (Header->Data2Low == 0xff) { // no resources to complete connection. + + if (Connection->Flags2 & CONNECTION_FLAGS2_WAIT_NR) { + + // + // The recipient of our previously-sent NAME_QUERY frame that we sent + // to actively establish a connection has unfortunately run out of + // resources and cannot setup his side of the connection. We have to + // report "no resources" on the TdiConnect. + // + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint3 ("ProcessNameRecognized %lx: connection %lx no resources [%.16s].\n", Address, Connection, Header->SourceName); + } + + IF_NBFDBG (NBF_DEBUG_TEARDOWN) { + NbfPrint0 ("ProcessNameRecognized: No resources.\n"); + } + + NbfStopConnection (Connection, STATUS_REMOTE_RESOURCES); + NbfDereferenceConnection ("No Resources", Connection, CREF_BY_ID); // release our lookup hold. + return STATUS_ABANDONED; // we processed the frame. + + } else { + + // + // We don't have a committed NAME_QUERY out there, so + // we ignore this frame. + // + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint3 ("ProcessNameRecognized %lx: connection %lx unexpected no resources [%.16s].\n", Address, Connection, Header->SourceName); + } + + NbfDereferenceConnection ("Tossing second response Done with lookup", Connection, CREF_BY_ID); // release our lookup hold. + return STATUS_ABANDONED; // we processed the frame. + + } + + } else { // Data2Low is in the range 0x01-0xfe + + if (Connection->Flags2 & CONNECTION_FLAGS2_WAIT_NR) { + + // + // This is a successful response to a second NAME_QUERY we sent when + // we started processing a TdiConnect request. Clear the "waiting + // for Name Recognized" bit in the connection flags so that the + // connection timer doesn't blow us away when it times out. + // + // BUGBUG: What prevents the timeout routine from running while + // we're in here and destroying the connection/link by + // calling NbfStopConnection? + // + + Connection->Flags2 &= ~CONNECTION_FLAGS2_WAIT_NR; + + // + // Before we continue, store the remote guy's transport address + // into the TdiConnect's TRANSPORT_CONNECTION buffer. This allows + // the client to determine who responded to his TdiConnect. + // + // BUGBUG: this used to be done prior to sending the second + // Name Query, but since I fixed the Buffer2 problem, meaning + // that I really do overwrite the input buffer with the + // output buffer, that was screwing up the second query. + // Note that doing the copy after sending is probably unsafe + // in the case where the second Name Recognized arrives + // right away. + // + + Connection->CalledAddress.NetbiosNameType = TDI_ADDRESS_NETBIOS_TYPE_UNIQUE; + TdiCopyLookaheadData( + Connection->CalledAddress.NetbiosName, + Header->SourceName, + 16, + DeviceContext->MacInfo.CopyLookahead ? TDI_RECEIVE_COPY_LOOKAHEAD : 0); + + RtlCopyMemory( Connection->RemoteName, Header->SourceName, 16 ); + + Connection->Rsn = Header->Data2Low; // save his remote LSN. + + // + // Save the correlator from the NR for eventual use in the + // SESSION_INITIALIZE frame. + // + + Connection->NetbiosHeader.TransmitCorrelator = RESPONSE_CORR(Header); + + // + // Cancel the timer; it would have no effect since WAIT_NR + // is not set, but there is no need for it to run. We cancel + // it with the lock held so it won't interfere with the + // timer's use when a connection is closing. + // + + TimerCancelled = KeCancelTimer (&Connection->Timer); + + if ((Connection->Flags2 & CONNECTION_FLAGS2_GROUP_LSN) != 0) { + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + // + // The Netbios address we are connecting to is a + // group name; we need to connect to the link + // now that we have the committed session. + // + + // If successful, this adds a link reference which is removed + // in NbfDisconnectFromLink + + status = NbfCreateLink ( + DeviceContext, + SourceAddress, // remote hardware address. + SourceRouting, + SourceRoutingLength, + CONNECTOR_LINK, // for loopback link + &Link); // resulting link. + + if (!NT_SUCCESS (status)) { // no resources. + NbfStopConnection (Connection, STATUS_INSUFFICIENT_RESOURCES); + NbfDereferenceConnection ("No Resources for link", Connection, CREF_BY_ID); + + if (TimerCancelled) { + NbfDereferenceConnection("NR received, cancel timer", Connection, CREF_TIMER); + } + + return STATUS_ABANDONED; + } + + ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + // If successful, this adds a connection reference which is + // removed in NbfDisconnectFromLink. It does NOT add a link ref. + + if ((Connection->Flags2 & CONNECTION_FLAGS2_STOPPING) || + ((status = NbfConnectToLink (Link, Connection)) != STATUS_SUCCESS)) { + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + if (TimerCancelled) { + NbfDereferenceConnection("NR received, cancel timer", Connection, CREF_TIMER); + } + + // Connection stopping or no LSN's available on this link. + // We did a link reference since NbfCreateLink succeeded, + // but since NbfConnectToLink failed we will never remove + // that reference in NbfDisconnectFromLink, so do it here. + + NbfDereferenceLink ("Can't connect to link", Link, LREF_CONNECTION); // most likely destroys this. + + NbfStopConnection (Connection, STATUS_INSUFFICIENT_RESOURCES); + NbfDereferenceConnection ("Cant connect to link", Connection, CREF_BY_ID); + return STATUS_ABANDONED; + } + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + (VOID)InterlockedIncrement(&Link->NumberOfConnectors); + + } else { + + // + // It's to a unique address, we set up the link + // before we sent out the committed NAME_QUERY. + // + + Link = Connection->Link; + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + } + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint3 ("ProcessNameRecognized %lx: connection %lx session up! [%.16s].\n", Address, Connection, Header->SourceName); + } + + // + // When we sent the committed NAME_QUERY, we stored that + // time in Connection->ConnectStartTime; we can now use + // that for a rough estimate of the link delay, if this + // is the first connection on the link. For async lines + // we do not do this because the delay introduced by the + // gateway messes up the timing. + // + + if (!DeviceContext->MacInfo.MediumAsync) { + + ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock); + + if (Link->State == LINK_STATE_ADM) { + + // + // HACK: Set the necessary variables in the link + // so that FakeUpdateBaseT1Timeout works. These + // variables are the same ones that FakeStartT1 sets. + // + + Link->CurrentPollSize = Link->HeaderLength + sizeof(DLC_FRAME) + sizeof(NBF_HDR_CONNECTIONLESS); + Link->CurrentTimerStart = Connection->ConnectStartTime; + FakeUpdateBaseT1Timeout (Link); + + } + + RELEASE_DPC_SPIN_LOCK (&Link->SpinLock); + + } + + if (TimerCancelled) { + NbfDereferenceConnection("NR received, cancel timer", Connection, CREF_TIMER); + } + + NbfActivateLink (Connection->Link); // start link going. + + // + // We'll get control again in LINK.C when the data link has either + // been established, denied, or destroyed. This happens at I/O + // completion time from NbfCreateLink's PdiConnect request. + // + + } else { + + // + // We don't have a committed NAME_QUERY out there, so + // we ignore this frame. + // + + RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock); + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint3 ("ProcessNameRecognized %lx: connection %lx unexpected session up! [%.16s].\n", Address, Connection, Header->SourceName); + } + + NbfDereferenceConnection ("Tossing second response Done with lookup", Connection, CREF_BY_ID); // release our lookup hold. + return STATUS_ABANDONED; // we processed the frame. + + } + + + } + + NbfDereferenceConnection("ProcessNameRecognized lookup", Connection, CREF_BY_ID); + return STATUS_ABANDONED; // don't distribute packet. +} /* ProcessNameRecognized */ + + +NTSTATUS +NbfProcessUi( + IN PDEVICE_CONTEXT DeviceContext, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR Header, + IN PUCHAR DlcHeader, + IN ULONG DlcLength, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + OUT PTP_ADDRESS * DatagramAddress + ) + +/*++ + +Routine Description: + + This routine receives control from the data link provider as an + indication that a DLC UI-frame has been received on the data link. + Here we dispatch to the correct UI-frame handler. + + Part of this routine's job is to optionally distribute the frame to + every address that needs to look at it. + We accomplish this by lock-stepping through the address database, + and for each address that matches the address this frame is aimed at, + calling the frame handler. + +Arguments: + + DeviceContext - Pointer to our device context. + + SourceAddress - Pointer to the source hardware address in the received + frame. + + Header - Points to the MAC header of the incoming packet. + + DlcHeader - Points to the DLC header of the incoming packet. + + DlcLength - Actual length in bytes of the packet, starting at the + DlcHeader. + + SourceRouting - Source routing information in the MAC header. + + SourceRoutingLength - The length of SourceRouting. + + DatagramAddress - If this function returns STATUS_MORE_PROCESSING_ + REQUIRED, this will be the address the datagram should be + indicated to. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PTP_ADDRESS Address; + PNBF_HDR_CONNECTIONLESS UiFrame; + NTSTATUS status; + PLIST_ENTRY Flink; + UCHAR MatchType; + BOOLEAN MatchedAddress; + PUCHAR MatchName; + ULONG NetbiosLength = DlcLength - 3; + + UiFrame = (PNBF_HDR_CONNECTIONLESS)(DlcHeader + 3); + + // + // Verify that this frame is long enough to examine and that it + // has the proper signature. We can't test the signature as a + // 16-bit word as specified in the NetBIOS Formats and Protocols + // manual because this is processor-dependent. + // + + if ((NetbiosLength < sizeof (NBF_HDR_CONNECTIONLESS)) || + (HEADER_LENGTH(UiFrame) != sizeof (NBF_HDR_CONNECTIONLESS)) || + (HEADER_SIGNATURE(UiFrame) != NETBIOS_SIGNATURE)) { + + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint0 ("NbfProcessUi: Bad size or NetBIOS signature.\n"); + } + return STATUS_ABANDONED; // frame too small or too large. + } + + // + // If this frame has a correlator with the high bit on, it was due + // to a FIND.NAME request; we don't handle those here since they + // are not per-address. + // + + if ((UiFrame->Command == NBF_CMD_NAME_RECOGNIZED) && + (TRANSMIT_CORR(UiFrame) & 0x8000)) { + + // + // Make sure the frame is sent to our reserved address; + // if not, drop it. + // + + if (RtlEqualMemory( + UiFrame->DestinationName, + DeviceContext->ReservedNetBIOSAddress, + NETBIOS_NAME_LENGTH)) { + + return NbfProcessQueryNameRecognized( + DeviceContext, + Header, + UiFrame); + } else { + + return STATUS_ABANDONED; + + } + } + + // + // If this is a STATUS_RESPONSE, process that separately. + // + + if (UiFrame->Command == NBF_CMD_STATUS_RESPONSE) { + + // + // Make sure the frame is sent to our reserved address; + // if not, drop it. + // + + if (RtlEqualMemory( + UiFrame->DestinationName, + DeviceContext->ReservedNetBIOSAddress, + NETBIOS_NAME_LENGTH)) { + + return STATUS_MORE_PROCESSING_REQUIRED; + + } else { + + return STATUS_ABANDONED; + + } + } + + // + // If this is a STATUS_QUERY, check if it is to our reserved + // address. If so, we process it. If not, we fall through to + // the normal checking. This ensures that queries to our + // reserved address are always processed, even if nobody + // has opened that address yet. + // + + if (UiFrame->Command == NBF_CMD_STATUS_QUERY) { + + if (RtlEqualMemory( + UiFrame->DestinationName, + DeviceContext->ReservedNetBIOSAddress, + NETBIOS_NAME_LENGTH)) { + + return NbfProcessStatusQuery( + DeviceContext, + NULL, + UiFrame, + SourceAddress, + SourceRouting, + SourceRoutingLength); + + } + + } + + // + // We have a valid connectionless NetBIOS protocol frame that's not a + // datagram, so deliver it to every address which matches the destination + // name in the frame. Some frames + // (NAME_QUERY) cannot be delivered to multiple recipients. Therefore, + // if a frame handler returns STATUS_MORE_PROCESSING_REQUIRED, we continue + // through the remaining addresses. Otherwise simply get out and assume + // that the frame was eaten. Thus, STATUS_SUCCESS means that the handler + // ate the frame and that no other addresses can have it. + // + + // + // Determine what kind of lookup we want to do. + // + + switch (UiFrame->Command) { + + case NBF_CMD_NAME_QUERY: + case NBF_CMD_DATAGRAM: + case NBF_CMD_DATAGRAM_BROADCAST: + case NBF_CMD_ADD_NAME_QUERY: + case NBF_CMD_STATUS_QUERY: + case NBF_CMD_ADD_NAME_RESPONSE: + case NBF_CMD_NAME_RECOGNIZED: + + MatchType = NETBIOS_NAME_TYPE_EITHER; + break; + + case NBF_CMD_ADD_GROUP_NAME_QUERY: + case NBF_CMD_NAME_IN_CONFLICT: + + MatchType = NETBIOS_NAME_TYPE_UNIQUE; + break; + + default: + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint1 ("NbfProcessUi: Frame delivered; Unrecognized command %x.\n", + UiFrame->Command); + } + return STATUS_SUCCESS; + break; + + } + + if ((UiFrame->Command == NBF_CMD_ADD_GROUP_NAME_QUERY) || + (UiFrame->Command == NBF_CMD_ADD_NAME_QUERY)) { + + MatchName = (PUCHAR)UiFrame->SourceName; + + } else if (UiFrame->Command == NBF_CMD_DATAGRAM_BROADCAST) { + + MatchName = NULL; + + } else { + + MatchName = (PUCHAR)UiFrame->DestinationName; + + } + + if (MatchName && DeviceContext->AddressCounts[MatchName[0]] == 0) { + status = STATUS_ABANDONED; + goto RasIndication; + } + + + MatchedAddress = FALSE; + + ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + for (Flink = DeviceContext->AddressDatabase.Flink; + Flink != &DeviceContext->AddressDatabase; + Flink = Flink->Flink) { + + Address = CONTAINING_RECORD ( + Flink, + TP_ADDRESS, + Linkage); + + if ((Address->Flags & ADDRESS_FLAGS_STOPPING) != 0) { + continue; + } + + if (NbfMatchNetbiosAddress (Address, + MatchType, + MatchName)) { + + NbfReferenceAddress ("UI Frame", Address, AREF_PROCESS_UI); // prevent address from being destroyed. + MatchedAddress = TRUE; + break; + + } + } + + RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock); + + if (MatchedAddress) { + + // + // If the datagram's destination name does not match the address's + // network name and TSAP components, then skip this address. Some + // frames have the source and destination names backwards for this + // algorithm, so we account for that here. Also, broadcast datagrams + // have no destination name in the frame, but get delivered to every + // address anyway. + // + +#if 0 + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + USHORT i; + NbfPrint0 ("NbfProcessUi: SourceName: "); + for (i=0;i<16;i++) { + NbfPrint1 ("%c",UiFrame->SourceName[i]); + } + NbfPrint0 (" Destination Name:"); + for (i=0;i<16;i++) { + NbfPrint1 ("%c",UiFrame->DestinationName[i]); + } + NbfPrint0 ("\n"); + } +#endif + + // + // Deliver the frame to the current address. + // + + switch (UiFrame->Command) { + + case NBF_CMD_NAME_QUERY: + + status = ProcessNameQuery ( + DeviceContext, + Address, + UiFrame, + SourceAddress, + SourceRouting, + SourceRoutingLength); + + break; + + case NBF_CMD_DATAGRAM: + case NBF_CMD_DATAGRAM_BROADCAST: + + // + // Reference the datagram so it sticks around until the + // ReceiveComplete, when it is processed. + // + + if ((Address->Flags & ADDRESS_FLAGS_CONFLICT) == 0) { + NbfReferenceAddress ("Datagram indicated", Address, AREF_PROCESS_DATAGRAM); + *DatagramAddress = Address; + status = STATUS_MORE_PROCESSING_REQUIRED; + } else { + status = STATUS_ABANDONED; + } + break; + + case NBF_CMD_ADD_GROUP_NAME_QUERY: + + // + // did this frame originate with us? If so, we don't want to + // do any processing of it. + // + + if (RtlEqualMemory ( + SourceAddress, + DeviceContext->LocalAddress.Address, + DeviceContext->MacInfo.AddressLength)) { + + if ((Address->Flags & ADDRESS_FLAGS_REGISTERING) != 0) { + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint0 ("NbfProcessUI: loopback AddGroupNameQuery dropped\n"); + } + status = STATUS_ABANDONED; + break; + } + } + + status = ProcessAddGroupNameQuery ( + DeviceContext, + Address, + UiFrame, + SourceAddress, + SourceRouting, + SourceRoutingLength); + break; + + case NBF_CMD_ADD_NAME_QUERY: + + // + // did this frame originate with us? If so, we don't want to + // do any processing of it. + // + + if (RtlEqualMemory ( + SourceAddress, + DeviceContext->LocalAddress.Address, + DeviceContext->MacInfo.AddressLength)) { + + if ((Address->Flags & ADDRESS_FLAGS_REGISTERING) != 0) { + IF_NBFDBG (NBF_DEBUG_UFRAMES) { + NbfPrint0 ("NbfProcessUI: loopback AddNameQuery dropped\n"); + } + status = STATUS_ABANDONED; + break; + } + } + + status = ProcessAddNameQuery ( + DeviceContext, + Address, + UiFrame, + SourceAddress, + SourceRouting, + SourceRoutingLength); + break; + + case NBF_CMD_NAME_IN_CONFLICT: + + status = ProcessNameInConflict ( + DeviceContext, + Address, + UiFrame, + SourceAddress, + SourceRouting, + SourceRoutingLength); + + break; + + case NBF_CMD_STATUS_QUERY: + + status = NbfProcessStatusQuery ( + DeviceContext, + Address, + UiFrame, + SourceAddress, + SourceRouting, + SourceRoutingLength); + + break; + + case NBF_CMD_ADD_NAME_RESPONSE: + + status = ProcessAddNameResponse ( + DeviceContext, + Address, + UiFrame, + SourceAddress, + SourceRouting, + SourceRoutingLength); + + break; + + case NBF_CMD_NAME_RECOGNIZED: + + status = ProcessNameRecognized ( + DeviceContext, + Address, + UiFrame, + SourceAddress, + SourceRouting, + SourceRoutingLength); + + break; + + default: + + ASSERT(FALSE); + + } /* switch on NetBIOS frame command code */ + + NbfDereferenceAddress ("Done", Address, AREF_PROCESS_UI); // done with previous address. + + } else { + + status = STATUS_ABANDONED; + + } + + +RasIndication:; + + // + // Let the RAS clients have a crack at this if they want + // + + if (DeviceContext->IndicationQueuesInUse) { + + // + // If RAS has datagram indications posted, and this is a + // datagram that nobody wanted, then receive it anyway. + // + + if ((UiFrame->Command == NBF_CMD_DATAGRAM) && + (status == STATUS_ABANDONED)) { + + *DatagramAddress = NULL; + status = STATUS_MORE_PROCESSING_REQUIRED; + + } else if ((UiFrame->Command == NBF_CMD_ADD_NAME_QUERY) || + (UiFrame->Command == NBF_CMD_ADD_GROUP_NAME_QUERY) || + (UiFrame->Command == NBF_CMD_NAME_QUERY)) { + + NbfActionQueryIndication( + DeviceContext, + UiFrame); + + } + } + + + return status; + +} /* NbfProcessUi */ + |