diff options
Diffstat (limited to 'private/ntos/tdi/st')
31 files changed, 20703 insertions, 0 deletions
diff --git a/private/ntos/tdi/st/address.c b/private/ntos/tdi/st/address.c new file mode 100644 index 000000000..3a4434a95 --- /dev/null +++ b/private/ntos/tdi/st/address.c @@ -0,0 +1,2247 @@ +/*++ + +Copyright (c) 1989-1993 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. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "st.h" + + +// +// Map all generic accesses to the same one. +// + +STATIC GENERIC_MAPPING AddressGenericMapping = + { READ_CONTROL, READ_CONTROL, READ_CONTROL, READ_CONTROL }; + +VOID +StDestroyAddress( + IN PVOID Parameter + ); + + +NTSTATUS +StOpenAddress( + 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 ST 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; + PST_NETBIOS_ADDRESS networkName; // Network name string. + PFILE_FULL_EA_INFORMATION ea; + TRANSPORT_ADDRESS UNALIGNED *name; + TA_ADDRESS UNALIGNED *addressName; + TDI_ADDRESS_NETBIOS UNALIGNED *netbiosName; + ULONG DesiredShareAccess; + KIRQL oldirql; + PACCESS_STATE AccessState; + ACCESS_MASK GrantedAccess; + BOOLEAN AccessAllowed; + int i; + BOOLEAN found = 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) { + StPrint1("OpenAddress: IRP %lx has no EA\n", Irp); + return STATUS_NONEXISTENT_EA_ENTRY; + } + + // + // this may be a valid name; parse the name from the EA and use it if OK. + // + + name = (PTRANSPORT_ADDRESS)&ea->EaName[ea->EaNameLength+1]; + addressName = (PTA_ADDRESS)&name->Address[0]; + + // + // The name can be passed with multiple entries; we'll take and use only + // the first one. + // + + for (i=0;i<name->TAAddressCount;i++) { + if (addressName->AddressType == TDI_ADDRESS_TYPE_NETBIOS) { + if (addressName->AddressLength != 0) { + netbiosName = (PTDI_ADDRESS_NETBIOS)&addressName->Address[0]; + networkName = (PST_NETBIOS_ADDRESS)ExAllocatePool ( + NonPagedPool, + sizeof (ST_NETBIOS_ADDRESS)); + if (networkName == NULL) { + PANIC ("StOpenAddress: PANIC! could not allocate networkName!\n"); + StWriteResourceErrorLog (DeviceContext, sizeof(TA_NETBIOS_ADDRESS), 1); + 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); + + found = TRUE; + } else { + networkName = NULL; + found = TRUE; + } + + break; + + } else { + + addressName = (PTA_ADDRESS)(addressName->Address + + addressName->AddressLength); + + } + + } + + if (!found) { + StPrint1("OpenAddress: IRP %lx has no NETBIOS address\n", Irp); + return STATUS_NONEXISTENT_EA_ENTRY; + } + + // + // get an address file structure to represent this address. + // + + status = StCreateAddressFile (DeviceContext, &addressFile); + + if (!NT_SUCCESS (status)) { + 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 = StLookupAddress (DeviceContext, networkName); + + if (address == NULL) { + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + // + // This address doesn't exist. Create it, and start the process of + // registering it. + // + + status = StCreateAddress ( + DeviceContext, + networkName, + &address); + + 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->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 (!NT_SUCCESS(status)) { + + // + // Error, return status. + // + + IoRemoveShareAccess (IrpSp->FileObject, &address->ShareAccess); + ExReleaseResource (&DeviceContext->AddressResource); + StDereferenceAddress ("Device context stopping", address); + StDereferenceAddressFile (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_STOPPING) { + StDereferenceAddress ("Device context stopping", address); + StDereferenceAddressFile (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; + + ACQUIRE_SPIN_LOCK (&address->SpinLock, &oldirql); + InsertTailList (&address->AddressFileDatabase, &addressFile->Linkage); + RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); + + + // + // 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. + // + + if ((networkName != NULL) && + (!RtlEqualMemory (networkName->NetbiosName, + DeviceContext->ReservedNetBIOSAddress, + NETBIOS_NAME_LENGTH))) { + + StRegisterAddress (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; + + } + + } + + } 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); + } + + StDereferenceAddressFile (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 (AccessAllowed) { + + // + // Access was successful, make sure Status is right. + // + + status = STATUS_SUCCESS; + + // + // Check that the name is of the correct type (unique vs. group) + // We don't need to check this for the broadcast address. + // + + if (networkName != NULL) { + if (address->NetworkName->NetbiosNameType != + networkName->NetbiosNameType) { + + status = STATUS_DUPLICATE_NAME; + + } + } + + } + + + if (!NT_SUCCESS (status)) { + + ExReleaseResource (&DeviceContext->AddressResource); + + StDereferenceAddressFile (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->ShareAccess, + TRUE); + + if (!NT_SUCCESS (status)) { + + ExReleaseResource (&DeviceContext->AddressResource); + + StDereferenceAddressFile (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; + + StReferenceAddress("open ready", address); + + IrpSp->FileObject->FsContext = (PVOID)addressFile; + IrpSp->FileObject->FsContext2 = + (PVOID)TDI_TRANSPORT_ADDRESS_FILE; + + RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); + + 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; + + StReferenceAddress("open registering", address); + + IrpSp->FileObject->FsContext = (PVOID)addressFile; + IrpSp->FileObject->FsContext2 = + (PVOID)TDI_TRANSPORT_ADDRESS_FILE; + + RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); + + status = STATUS_PENDING; + + } else { + + RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); + + StDereferenceAddressFile (addressFile); + + status = STATUS_DRIVER_INTERNAL_ERROR; + + } + } + } + } + + // + // Remove the reference from StLookupAddress. + // + + StDereferenceAddress ("Done opening", address); + } + + return status; +} /* StOpenAddress */ + + +VOID +StAllocateAddress( + 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; + + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + sizeof(TP_ADDRESS)) > + DeviceContext->MemoryLimit)) { + PANIC("ST: Could not allocate address: limit\n"); + StWriteResourceErrorLog (DeviceContext, sizeof(TP_ADDRESS), 101); + *TransportAddress = NULL; + return; + } + + Address = (PTP_ADDRESS)ExAllocatePool (NonPagedPool, sizeof (TP_ADDRESS)); + if (Address == NULL) { + PANIC("ST: Could not allocate address: no pool\n"); + StWriteResourceErrorLog (DeviceContext, sizeof(TP_ADDRESS), 201); + *TransportAddress = NULL; + return; + } + RtlZeroMemory (Address, sizeof(TP_ADDRESS)); + + DeviceContext->MemoryUsage += sizeof(TP_ADDRESS); + ++DeviceContext->AddressAllocated; + + StAllocateSendPacket (DeviceContext, &(Address->Packet)); + if (Address->Packet == NULL) { + ExFreePool (Address); + *TransportAddress = NULL; + return; + } + --DeviceContext->PacketAllocated; // AllocatePacket added one + + // + // Need to modify the address packets to belong to + // the address, not the device context. + // + + SendTag = (PSEND_PACKET_TAG)(Address->Packet->NdisPacket->ProtocolReserved); + SendTag->Type = TYPE_G_FRAME; + SendTag->Packet = Address->Packet; + SendTag->Owner = (PVOID)Address; + + + Address->Type = ST_ADDRESS_SIGNATURE; + Address->Size = sizeof (TP_ADDRESS); + + Address->Provider = DeviceContext; + KeInitializeSpinLock (&Address->SpinLock); + + InitializeListHead (&Address->ConnectionDatabase); + InitializeListHead (&Address->AddressFileDatabase); + InitializeListHead (&Address->SendDatagramQueue); + + // + // For each address, allocate a receive packet, a receive buffer, + // and a UI frame. + // + + StAddReceivePacket (DeviceContext); + StAddReceiveBuffer (DeviceContext); + + *TransportAddress = Address; + +} /* StAllocateAddress */ + + +VOID +StDeallocateAddress( + 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. + +--*/ + +{ + + if (TransportAddress->NetworkName != NULL) { + ExFreePool (TransportAddress->NetworkName); + } + StDeallocateSendPacket (DeviceContext, TransportAddress->Packet); + ++DeviceContext->PacketAllocated; + + ExFreePool (TransportAddress); + --DeviceContext->AddressAllocated; + DeviceContext->MemoryUsage -= sizeof(TP_ADDRESS); + + // + // Remove the resources which allocating this caused. + // + + StRemoveReceivePacket (DeviceContext); + StRemoveReceiveBuffer (DeviceContext); + +} /* StDeallocateAddress */ + + +NTSTATUS +StCreateAddress( + IN PDEVICE_CONTEXT DeviceContext, + IN PST_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 ST_NETBIOS_ADDRESS type containing the network + name to be associated with this address, if any. + + 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)) { + + StAllocateAddress (DeviceContext, &pAddress); + + } else { + + StWriteResourceErrorLog (DeviceContext, sizeof(TP_ADDRESS), 401); + pAddress = NULL; + + } + + if (pAddress == NULL) { + ++DeviceContext->AddressExhausted; + PANIC ("StCreateConnection: 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; + + + // + // Initialize all of the static data for this address. + // + + pAddress->ReferenceCount = 1; + + pAddress->Flags = ADDRESS_FLAGS_NEEDS_REG; + InitializeListHead (&pAddress->AddressFileDatabase); + + ExInitializeWorkItem( + &pAddress->DestroyAddressQueueItem, + StDestroyAddress, + (PVOID)pAddress); + + pAddress->NetworkName = NetworkName; + if ((NetworkName != (PST_NETBIOS_ADDRESS)NULL) && + (NetworkName->NetbiosNameType == + TDI_ADDRESS_NETBIOS_TYPE_GROUP)) { + + pAddress->Flags |= ADDRESS_FLAGS_GROUP; + + } + + // + // 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; + StReferenceDeviceContext ("Create Address", DeviceContext); // count refs to the device context. + + *Address = pAddress; // return the address. + return STATUS_SUCCESS; // not finished yet. +} /* StCreateAddress */ + + +VOID +StRegisterAddress( + 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; + PLIST_ENTRY p; + PIRP irp; + PTP_ADDRESS_FILE addressFile; + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + if (!(Address->Flags & ADDRESS_FLAGS_NEEDS_REG)) { + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + return; + } + + Address->Flags &= ~ADDRESS_FLAGS_NEEDS_REG; + Address->Flags |= ADDRESS_FLAGS_REGISTERING; + + // + // 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. + // + + StReferenceAddress ("start registration", Address); + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + // + // Normally we would add the name on the network, then + // do the following in our timeout logic, but for ST + // we assume that all names are OK. + // + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + 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 (addressFile->Irp != NULL) { + irp = addressFile->Irp; + addressFile->Irp = NULL; + addressFile->State = ADDRESSFILE_STATE_OPEN; + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + irp->IoStatus.Information = 0; + irp->IoStatus.Status = STATUS_SUCCESS; + + IoCompleteRequest (irp, IO_NETWORK_INCREMENT); + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + } + + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + // + // Dereference the address if we're all done. + // + + StDereferenceAddress ("Timer, registered", Address); + +} /* StRegisterAddress */ + + +NTSTATUS +StVerifyAddressObject ( + 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->Size == sizeof (TP_ADDRESS_FILE)) && + (AddressFile->Type == ST_ADDRESSFILE_SIGNATURE) ) { +// (AddressFile->State != ADDRESSFILE_STATE_CLOSING) ) { + + address = AddressFile->Address; + + if ((address->Size == sizeof (TP_ADDRESS)) && + (address->Type == ST_ADDRESS_SIGNATURE) ) { + + ACQUIRE_SPIN_LOCK (&address->SpinLock, &oldirql); + + if ((address->Flags & ADDRESS_FLAGS_STOPPING) == 0) { + + StReferenceAddress ("verify", address); + + } else { + + StPrint1("StVerifyAddress: A %lx closing\n", address); + status = STATUS_INVALID_ADDRESS; + } + + RELEASE_SPIN_LOCK (&address->SpinLock, oldirql); + + } else { + + StPrint1("StVerifyAddress: A %lx bad signature\n", address); + status = STATUS_INVALID_ADDRESS; + } + + } else { + + StPrint1("StVerifyAddress: AF %lx bad signature\n", AddressFile); + status = STATUS_INVALID_ADDRESS; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + StPrint1("StVerifyAddress: AF %lx exception\n", address); + return GetExceptionCode(); + } + + return status; + +} + +VOID +StDestroyAddress( + 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 StfDerefAddress. 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; + + 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); + + RemoveEntryList (&Address->Linkage); + + // + // Now we can deallocate the transport address object. + // + + DeviceContext->AddressTotal += DeviceContext->AddressInUse; + ++DeviceContext->AddressSamples; + --DeviceContext->AddressInUse; + + if ((DeviceContext->AddressAllocated - DeviceContext->AddressInUse) > + DeviceContext->AddressInitAllocated) { + StDeallocateAddress (DeviceContext, Address); + } else { + InsertTailList (&DeviceContext->AddressPool, &Address->Linkage); + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + StDereferenceDeviceContext ("Destroy Address", DeviceContext); // just housekeeping. + +} /* StDestroyAddress */ + + +VOID +StRefAddress( + 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); + +} /* StRefAddress */ + + +VOID +StDerefAddress( + 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 + StDestroyAddress 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); + + // + // Defer the actual call to StDestroyAddress to a thread + // so the paged security descriptor can be accessed at IRQL 0. + // + + if (result == 0) { + ExQueueWorkItem(&Address->DestroyAddressQueueItem, DelayedWorkQueue); + } +} /* StDerefAddress */ + + + +VOID +StAllocateAddressFile( + 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("ST: Could not allocate address file: limit\n"); + StWriteResourceErrorLog (DeviceContext, sizeof(TP_ADDRESS_FILE), 102); + *TransportAddressFile = NULL; + return; + } + + AddressFile = (PTP_ADDRESS_FILE)ExAllocatePool (NonPagedPool, sizeof (TP_ADDRESS_FILE)); + if (AddressFile == NULL) { + PANIC("ST: Could not allocate address file: no pool\n"); + StWriteResourceErrorLog (DeviceContext, sizeof(TP_ADDRESS_FILE), 202); + *TransportAddressFile = NULL; + return; + } + RtlZeroMemory (AddressFile, sizeof(TP_ADDRESS_FILE)); + + DeviceContext->MemoryUsage += sizeof(TP_ADDRESS_FILE); + ++DeviceContext->AddressFileAllocated; + + AddressFile->Type = ST_ADDRESSFILE_SIGNATURE; + AddressFile->Size = sizeof (TP_ADDRESS_FILE); + + InitializeListHead (&AddressFile->ReceiveDatagramQueue); + InitializeListHead (&AddressFile->ConnectionDatabase); + + *TransportAddressFile = AddressFile; + +} /* StAllocateAddressFile */ + + +VOID +StDeallocateAddressFile( + 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); + +} /* StDeallocateAddressFile */ + + +NTSTATUS +StCreateAddressFile( + 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)) { + + StAllocateAddressFile (DeviceContext, &addressFile); + } else { + + StWriteResourceErrorLog (DeviceContext, sizeof(TP_ADDRESS_FILE), 402); + addressFile = NULL; + + } + + if (addressFile == NULL) { + ++DeviceContext->AddressFileExhausted; + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + PANIC ("StCreateConnection: 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; + +} /* StCreateAddress */ + + +NTSTATUS +StDestroyAddressFile( + 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 StDereferenceAddressFile. 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. + // + + StDereferenceAddress ("Close", address); // 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) { + StDeallocateAddressFile (DeviceContext, 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; + +} /* StDestroyAddress */ + + +VOID +StReferenceAddressFile( + 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); + +} /* StReferenceAddressFile */ + + +VOID +StDereferenceAddressFile( + 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 + StDestroyAddressFile 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) { + StDestroyAddressFile (AddressFile); + } +} /* StDerefAddressFile */ + + +PTP_ADDRESS +StLookupAddress( + IN PDEVICE_CONTEXT DeviceContext, + IN PST_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 ST_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. + // + + StReferenceAddress ("lookup", address); + return address; + + } /* for */ + + // + // The specified address was not found. + // + + return NULL; + +} /* StLookupAddress */ + + +PTP_CONNECTION +StLookupRemoteName( + IN PTP_ADDRESS Address, + IN PUCHAR RemoteName + ) + +/*++ + +Routine Description: + + This routine scans the connections associated with an + address, and determines if there is an connection + associated with the specific remote address. + +Arguments: + + Address - Pointer to the address. + + RemoteName - The 16-character Netbios name of the remote. + +Return Value: + + The connection if one is found, NULL otherwise. + +--*/ + +{ + KIRQL oldirql, oldirql1; + PLIST_ENTRY p; + PTP_CONNECTION connection; + + + // + // 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); + + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql1); + + if (((connection->Flags2 & CONNECTION_FLAGS2_REMOTE_VALID) != 0) && + ((connection->Flags & CONNECTION_FLAGS_READY) != 0)) { + + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql1); + + // + // If the remote names match, then return the + // connection. + // + + if (RtlEqualMemory(RemoteName, connection->RemoteName, NETBIOS_NAME_LENGTH)) { + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + StReferenceConnection ("Lookup found", connection); + return connection; + + } + + } else { + + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql1); + + } + + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + return (PTP_CONNECTION)NULL; + +} + + +BOOLEAN +StMatchNetbiosAddress( + IN PTP_ADDRESS Address, + 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. + + 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; + } + + // + // 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; + } + +} /* StMatchNetbiosAddress */ + + +VOID +StStopAddress( + 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)) { + + 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 StStopAddress + // 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. + // + + StStopAddressFile (addressFile, Address); + + // + // return the addressFile to the pool of address files + // + + StDereferenceAddressFile (addressFile); + + StDereferenceAddress ("stop address", Address); + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + return; + + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); +} /* StStopAddress */ + + +NTSTATUS +StStopAddressFile( + 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 localList; + PLIST_ENTRY p, pFlink; + PTP_REQUEST request; + PTP_CONNECTION connection; + + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + if (AddressFile->State == ADDRESSFILE_STATE_CLOSING) { + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + return STATUS_SUCCESS; + } + + + AddressFile->State = ADDRESSFILE_STATE_CLOSING; + InitializeListHead (&localList); + + // + // Run down all connections on this addressfile, and + // preform the equivalent of StDestroyAssociation + // on them. + // + + while (!IsListEmpty (&AddressFile->ConnectionDatabase)) { + p = RemoveHeadList (&AddressFile->ConnectionDatabase); + connection = CONTAINING_RECORD (p, TP_CONNECTION, AddressFileList); + + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql1); + + if ((connection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) == 0) { + + // + // It is in the process of being disassociated already. + // + + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql1); + continue; + } + + connection->Flags2 &= ~CONNECTION_FLAGS2_ASSOCIATED; + connection->Flags |= CONNECTION_FLAGS_DESTROY; // BUGBUG: Is this needed? + RemoveEntryList (&connection->AddressList); + InitializeListHead (&connection->AddressList); + InitializeListHead (&connection->AddressFileList); + connection->AddressFile = NULL; + + StReferenceConnection ("Close AddressFile", connection); + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql1); + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + StStopConnection (connection, STATUS_LOCAL_DISCONNECT); + StDereferenceConnection ("Close AddressFile", connection); + + StDereferenceAddress ("Destroy association", Address); + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + } + + // + // now remove all of the datagrams owned by this addressfile + // + + for (p = Address->SendDatagramQueue.Flink; + p != &Address->SendDatagramQueue; + p = pFlink ) { + + pFlink = p->Flink; + request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + if ((PTP_ADDRESS_FILE)(request->Owner) == AddressFile) { + RemoveEntryList (p); + InitializeListHead (p); + InsertTailList (&localList, p); + } + + } + + for (p = AddressFile->ReceiveDatagramQueue.Flink; + p != &AddressFile->ReceiveDatagramQueue; + p = pFlink ) { + + pFlink = p->Flink; + RemoveEntryList (p); + InitializeListHead (p); + InsertTailList (&localList, 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; + AddressFile->Irp = NULL; + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + irp->IoStatus.Information = 0; + irp->IoStatus.Status = STATUS_DUPLICATE_NAME; + + IoCompleteRequest (irp, IO_NETWORK_INCREMENT); + + } else { + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + } + + // + // cancel all the datagrams on this address file + // + + while (!IsListEmpty (&localList)) { + + p = RemoveHeadList (&localList); + request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + + StCompleteRequest (request, STATUS_NETWORK_NAME_DELETED, 0); + + } + + +} /* StStopAddressFile */ + + +NTSTATUS +StCloseAddress( + 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; + 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->ShareAccess); + ExReleaseResource (&addressFile->Provider->AddressResource); + + + StStopAddressFile (addressFile, address); + StDereferenceAddressFile (addressFile); + + // + // This removes a reference added by our caller. + // + + StDereferenceAddress ("IRP_MJ_CLOSE", address); + + return STATUS_PENDING; + +} /* StCloseAddress */ + + +NTSTATUS +StSendDatagramsOnAddress( + 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 StSendUIMdlFrame to actually do the work. When StSendUIMdlFrame + 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. + +Arguments: + + Address - a pointer to the address object to send the datagram on. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql; + PLIST_ENTRY p; + PTP_REQUEST request; + PTA_NETBIOS_ADDRESS remoteTA; + PIO_STACK_LOCATION irpSp; + PDEVICE_CONTEXT DeviceContext; + PUCHAR SourceRouting; + UINT SourceRoutingLength; + UINT HeaderLength; + PST_HEADER StHeader; + PSEND_PACKET_TAG SendTag; + + DeviceContext = Address->Provider; + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + StReferenceAddress ("Send datagram", Address); // keep it around + + 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); + StDereferenceAddress ("Queue empty", Address); + return STATUS_SUCCESS; + } + + // + // Mark the address's send datagram queue as held so that the + // MDL and ST 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); + + request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + + // + // 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 (request->IoRequestPacket); + + remoteTA = ((PTDI_REQUEST_KERNEL_SENDDG)(&irpSp->Parameters))-> + SendDatagramInformation->RemoteAddress; + + // + // Build the MAC header. DATAGRAM frames go out as + // single-route source routing. + // + + MacReturnSingleRouteSR( + &DeviceContext->MacInfo, + &SourceRouting, + &SourceRoutingLength); + + MacConstructHeader ( + &DeviceContext->MacInfo, + Address->Packet->Header, + DeviceContext->MulticastAddress.Address, + DeviceContext->LocalAddress.Address, + sizeof(ST_HEADER) + request->Buffer2Length, + SourceRouting, + SourceRoutingLength, + &HeaderLength); + + // + // Build the header: 'G', dest, source + // + + StHeader = (PST_HEADER)(&Address->Packet->Header[HeaderLength]); + + StHeader->Signature = ST_SIGNATURE; + StHeader->Command = ST_CMD_DATAGRAM; + StHeader->Flags = 0; + + RtlCopyMemory (StHeader->Source, Address->NetworkName->NetbiosName, 16); + + if (remoteTA->Address[0].AddressLength == 0) { + + // + // A broadcast datagram + // + + RtlZeroMemory (StHeader->Destination, 16); + StHeader->Flags |= ST_FLAGS_BROADCAST; + + } else { + + RtlCopyMemory (StHeader->Destination, remoteTA->Address[0].Address[0].NetbiosName, 16); + + } + + HeaderLength += sizeof(ST_HEADER); + + SendTag = (PSEND_PACKET_TAG)(Address->Packet->NdisPacket->ProtocolReserved); + SendTag->Type = TYPE_G_FRAME; + SendTag->Packet = Address->Packet; + SendTag->Owner = (PVOID)Address; + + // + // Update our statistics for this datagram. + // + + ++DeviceContext->DatagramsSent; + ADD_TO_LARGE_INTEGER( + &DeviceContext->DatagramBytesSent, + request->Buffer2Length); + + + // + // Munge the packet length, append the data, and send it. + // + + StSetNdisPacketLength(Address->Packet->NdisPacket, HeaderLength); + + if (request->Buffer2) { + NdisChainBufferAtBack (Address->Packet->NdisPacket, (PNDIS_BUFFER)request->Buffer2); + } + + (VOID)StSendAddressFrame ( + Address); + + + // + // The hold will be released in the I/O completion handler. + // 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); + } + + StDereferenceAddress ("Sent datagram", Address); // all done + + return STATUS_SUCCESS; + +} /* StSendDatagramsOnAddress */ diff --git a/private/ntos/tdi/st/connect.c b/private/ntos/tdi/st/connect.c new file mode 100644 index 000000000..8cb05b03b --- /dev/null +++ b/private/ntos/tdi/st/connect.c @@ -0,0 +1,1137 @@ +/*++ + +Copyright (c) 1989-1993 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 + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "st.h" + + +NTSTATUS +StTdiAccept( + 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; + + // + // Get the connection this is associated with; if there is none, get out. + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + connection = irpSp->FileObject->FsContext; + + // + // This adds a connection reference if successful. + // + + status = StVerifyConnectionObject (connection); + + if (!NT_SUCCESS (status)) { + return status; + } + + // + // just set the connection flags to allow reads and writes to proceed. + // + + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql); + + // + // Turn off the stopping flag for this connection. + // + + connection->Flags &= ~CONNECTION_FLAGS_STOPPING; + connection->Status = STATUS_PENDING; + + connection->Flags2 |= CONNECTION_FLAGS2_ACCEPTED; + + + if (connection->AddressFile->ConnectIndicationInProgress) { + connection->Flags2 |= CONNECTION_FLAGS2_INDICATING; + } + + if ((connection->Flags2 & CONNECTION_FLAGS2_WAIT_ACCEPT) != 0) { + + // + // We previously completed a listen, now the user is + // coming back with an accept, Set this flag to allow + // the connection to proceed. + // + + connection->Flags |= CONNECTION_FLAGS_READY; + + INCREMENT_COUNTER (connection->Provider, OpenConnections); + + // + // Set this flag to enable disconnect indications; once + // the client has accepted he expects those. + // + + connection->Flags2 &= ~CONNECTION_FLAGS2_WAIT_ACCEPT; + connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED; + + StReferenceConnection("Pended listen completed", connection); + + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + + } 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. + // + + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + + } + + StDereferenceConnection ("Temp TdiAccept", connection); + + return STATUS_SUCCESS; + +} /* StTdiAccept */ + + +NTSTATUS +StTdiAssociateAddress( + 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; + + 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 = StVerifyConnectionObject (connection); + if (!NT_SUCCESS (status)) { + return status; + } + + + // + // Make sure this connection is ready to be associated. + // + + oldAddress = (PTP_ADDRESS)NULL; + + ACQUIRE_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_SPIN_LOCK (&connection->SpinLock, oldirql2); + StDereferenceConnection ("Temp Ref Associate", connection); + + 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) { + + // + // 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_SPIN_LOCK (&connection->SpinLock, oldirql2); + + // + // If we removed an old association, dereference the + // address. + // + + if (oldAddress != (PTP_ADDRESS)NULL) { + + StDereferenceAddress("Removed old association", oldAddress); + + } + + + 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 (NT_SUCCESS (StVerifyAddressObject (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) { + + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql2); + + if ((connection->Flags2 & CONNECTION_FLAGS2_CLOSING) == 0) { + + StReferenceAddress ( + "Connection associated", + addressFile->Address); + + InsertTailList ( + &addressFile->Address->ConnectionDatabase, + &connection->AddressList); + + 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_SPIN_LOCK (&connection->SpinLock, oldirql2); + + } else { + + status = STATUS_INVALID_HANDLE; + } + + RELEASE_SPIN_LOCK (&addressFile->Address->SpinLock, oldirql); + + StDereferenceAddress ("Temp associate", addressFile->Address); + + } 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; + } + + StDereferenceConnection ("Temp Ref Associate", connection); + + return status; + +} /* TdiAssociateAddress */ + + +NTSTATUS +StTdiDisassociateAddress( + 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; + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + connection = irpSp->FileObject->FsContext; + + // + // If successful this adds a reference. + // + + status = StVerifyConnectionObject (connection); + + if (!NT_SUCCESS (status)) { + return status; + } + + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql); + if ((connection->Flags & CONNECTION_FLAGS_STOPPING) == 0) { + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + StStopConnection (connection, STATUS_LOCAL_DISCONNECT); + + } else { + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + } + + // + // 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_SPIN_LOCK (&connection->SpinLock, &oldirql); + if ((connection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) && + ((connection->Flags2 & CONNECTION_FLAGS2_DISASSOCIATED) == 0)) { + connection->Flags2 |= CONNECTION_FLAGS2_DISASSOCIATED; + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + } else { + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + } + + StDereferenceConnection ("Temp use in Associate", connection); + + return STATUS_SUCCESS; + +} /* TdiDisassociateAddress */ + + +NTSTATUS +StTdiConnect( + 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; + PSTRING GeneralBroadcastSourceRoute = NULL; // BUGBUG: define this later. + LARGE_INTEGER timeout = {0,0}; + KIRQL oldirql, cancelirql; + PTP_REQUEST tpRequest; + PIO_STACK_LOCATION irpSp; + PTDI_REQUEST_KERNEL parameters; + PTA_NETBIOS_ADDRESS RemoteAddress; + ULONG RemoteAddressLength; + + // + // is the file object a connection? + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + connection = irpSp->FileObject->FsContext; + + // + // If successful this adds a reference. + // + + status = StVerifyConnectionObject (connection); + + if (!NT_SUCCESS (status)) { + return status; + } + + 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)) { + + 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; + } + } + + // + // Check that the remote is a Netbios address. + // + + RemoteAddress = (PTA_NETBIOS_ADDRESS) + (parameters->RequestConnectionInformation->RemoteAddress); + + if (RemoteAddress->Address[0].AddressType != TDI_ADDRESS_TYPE_NETBIOS) { + + StDereferenceConnection ("Not Netbios", connection); + return STATUS_BAD_NETWORK_PATH; // don't even try to find it. + + } + + // + // copy the called address someplace we can use it. + // + + RemoteAddressLength = parameters->RequestConnectionInformation->RemoteAddressLength; + + if (RemoteAddressLength > sizeof(TA_NETBIOS_ADDRESS)) { + RemoteAddressLength = sizeof(TA_NETBIOS_ADDRESS); + } + + RtlCopyMemory( + connection->CalledAddress.NetbiosName, + RemoteAddress->Address[0].Address[0].NetbiosName, + 16); + + // + // We need a request object to keep track of this TDI request. + // Attach this request to the new connection object. + // + + status = StCreateRequest ( + 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. + StDereferenceConnection ("Throw away", connection); + return status; // return with failure. + } else { + + // Reference the connection since StDestroyRequest derefs it. + + StReferenceConnection("For connect request", connection); + + tpRequest->Owner = ConnectionType; + IoAcquireCancelSpinLock (&cancelirql); + ACQUIRE_SPIN_LOCK (&connection->SpinLock,&oldirql); + if ((connection->Flags & CONNECTION_FLAGS_STOPPING) != 0) { + RELEASE_SPIN_LOCK (&connection->SpinLock,oldirql); + IoReleaseCancelSpinLock (cancelirql); + StCompleteRequest ( + tpRequest, + connection->Status, + 0); + StDereferenceConnection("Temporary Use 1", connection); + return STATUS_PENDING; + } else { + InsertTailList (&connection->InProgressRequest,&tpRequest->Linkage); + + connection->Flags |= CONNECTION_FLAGS_CONNECTOR; // we're the initiator. + + connection->Flags &= ~CONNECTION_FLAGS_STOPPING; + connection->Status = STATUS_PENDING; + + connection->Flags2 &= ~CONNECTION_FLAGS2_INDICATING; + RELEASE_SPIN_LOCK (&connection->SpinLock,oldirql); + + // + // Check if the IRP has been cancelled. + // + + if (Irp->Cancel) { + Irp->CancelIrql = cancelirql; + StCancelConnection((PDEVICE_OBJECT)(connection->Provider), Irp); + StDereferenceConnection ("IRP cancelled", connection); // release lookup hold. + return STATUS_PENDING; + } + + Irp->CancelRoutine = StCancelConnection; + IoReleaseCancelSpinLock(cancelirql); + + } + } + + status = StSendConnect ( + connection); + + if (!NT_SUCCESS(status)) { // can't send the name request + StStopConnection (connection, status); + StDereferenceConnection("Temporary Use 2", connection); + + // + // Note that this return status isn't really a lie. We are waiting + // for the connection to run down. + // + + return STATUS_PENDING; + } + + + StDereferenceConnection("Temporary Use 3", connection); + + return STATUS_PENDING; // things are started. + +} /* TdiConnect */ + + +NTSTATUS +StTdiDisconnect( + 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; + + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + connection = irpSp->FileObject->FsContext; + + // + // If successful this adds a reference. + // + + status = StVerifyConnectionObject (connection); + if (!NT_SUCCESS (status)) { + return status; + } + + + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql); + + // + // if the connection is currently stopping, there's no reason to blow + // it away... + // + + if ((connection->Flags & CONNECTION_FLAGS_STOPPING) != 0) { + + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + StDereferenceConnection ("Ignoring disconnect", connection); // release our lookup reference. + return connection->Status; + + } else { + connection->Flags2 &= ~ (CONNECTION_FLAGS2_ACCEPTED | + CONNECTION_FLAGS2_PRE_ACCEPT | + CONNECTION_FLAGS2_WAIT_ACCEPT); + connection->Flags2 |= CONNECTION_FLAGS2_DISCONNECT; + + // + // Set this flag so the disconnect IRP is completed. + // + // BUGBUG: If the connection goes down before we can + // call StStopConnection with STATUS_LOCAL_DISCONNECT, + // the disconnect IRP won't get completed. + // + + connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED; + + connection->DisconnectIrp = Irp; + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + } + + // + // 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)) { + + 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->Flags |= CONNECTION_FLAGS_DESTROY; + } else if ((ULONG)(parameters->RequestFlags) & (ULONG)TDI_DISCONNECT_ABORT) { + connection->Flags |= CONNECTION_FLAGS_ABORT; + } else if ((ULONG)(parameters->RequestFlags) & (ULONG)TDI_DISCONNECT_WAIT) { + connection->Flags |= CONNECTION_FLAGS_ORDREL; + } + + // + // This will get passed to IoCompleteRequest during TdiDestroyConnection + // + + StStopConnection (connection, STATUS_LOCAL_DISCONNECT); // starts the abort sequence. + StDereferenceConnection ("Disconnecting", connection); // release our lookup reference. + + // + // This request will be completed by TdiDestroyConnection once + // the connection reference count drops to 0. + // + + return STATUS_PENDING; +} /* TdiDisconnect */ + + +NTSTATUS +StTdiListen( + 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; + + // + // validate this connection + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + connection = irpSp->FileObject->FsContext; + + // + // If successful this adds a reference. + // + + status = StVerifyConnectionObject (connection); + + if (!NT_SUCCESS (status)) { + return status; + } + + parameters = (PTDI_REQUEST_KERNEL_LISTEN)&irpSp->Parameters; + + // + // We need a request object to keep track of this TDI request. + // Attach this request to the new connection object. + // + + status = StCreateRequest ( + Irp, // IRP for this request. + connection, // context. + REQUEST_FLAGS_CONNECTION, // partial flags. + NULL, + 0, + timeout, // timeout value (can be 0). + &tpRequest); + + + if (!NT_SUCCESS (status)) { // couldn't make the request. + + StDereferenceConnection ("For create", connection); + return status; // return with failure. + } + + // Reference the connection since StDestroyRequest derefs it. + + IoAcquireCancelSpinLock (&cancelirql); + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql); + tpRequest->Owner = ConnectionType; + + StReferenceConnection("For listen request", connection); + + if ((connection->Flags & CONNECTION_FLAGS_STOPPING) != 0) { + + RELEASE_SPIN_LOCK (&connection->SpinLock,oldirql); + IoReleaseCancelSpinLock(cancelirql); + + StCompleteRequest ( + tpRequest, + connection->Status, + 0); + StDereferenceConnection("Temp create", connection); + return STATUS_PENDING; + + } else { + + InsertTailList (&connection->InProgressRequest,&tpRequest->Linkage); + connection->Flags |= CONNECTION_FLAGS_LISTENER | // we're the passive one. + CONNECTION_FLAGS_WAIT_LISTEN; // waiting for a connect + connection->Flags2 &= ~CONNECTION_FLAGS2_INDICATING; + connection->Flags &= ~CONNECTION_FLAGS_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_SPIN_LOCK (&connection->SpinLock,oldirql); + + // + // Check if the IRP has been cancelled. + // + + if (Irp->Cancel) { + Irp->CancelIrql = cancelirql; + StCancelConnection((PDEVICE_OBJECT)(connection->Provider), Irp); + StDereferenceConnection ("IRP cancelled", connection); // release lookup hold. + return STATUS_PENDING; + } + + Irp->CancelRoutine = StCancelConnection; + 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. + // + + StDereferenceConnection("Temp create", connection); + + return STATUS_PENDING; // things are started. +} /* TdiListen */ + + +NTSTATUS +StOpenConnection ( + 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); + + 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 StCreateConnection + // will create a second reference which is removed once the request + // references the connection, or if the function exits before that. + + status = StCreateConnection (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; + + return status; + +} /* StOpenConnection */ + + +NTSTATUS +StCloseConnection ( + 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); + + // + // is the file object a connection? + // + + connection = IrpSp->FileObject->FsContext; + + + // + // 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 == ST_CONNECTION_SIGNATURE)) { + + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql); + + if ((connection->Flags2 & CONNECTION_FLAGS2_CLOSING) == 0) { + + status = STATUS_SUCCESS; + + } else { + + status = STATUS_INVALID_CONNECTION; + } + + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + + } else { + + status = STATUS_INVALID_CONNECTION; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + return GetExceptionCode(); + } + + if (!NT_SUCCESS (status)) { + return status; + } + + // + // We recognize it; is it closing already? + // + + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql); + + if ((connection->Flags2 & CONNECTION_FLAGS2_CLOSING) != 0) { + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + StDereferenceConnection("Temp Close", connection); + return STATUS_INVALID_CONNECTION; + } + + connection->Flags2 |= CONNECTION_FLAGS2_CLOSING; + + // + // if there is activity on the connection, tear it down. + // + + if ((connection->Flags & CONNECTION_FLAGS_STOPPING) == 0) { + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + StStopConnection (connection, STATUS_LOCAL_DISCONNECT); + ACQUIRE_SPIN_LOCK (&connection->SpinLock, &oldirql); + } + + // + // 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_SPIN_LOCK (&connection->SpinLock, oldirql); + } else { + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + } + + + // + // Save this to complete the IRP later. + // + + connection->CloseIrp = Irp; + + // + // make it impossible to use this connection from the file object + // + + IrpSp->FileObject->FsContext = NULL; + IrpSp->FileObject->FsContext2 = NULL; + connection->FileObject = NULL; + + // + // dereference for the creation. Note that this dereference + // here won't have any effect until the regular reference count + // hits zero. + // + + StDereferenceConnectionSpecial (" Closing Connection", connection); + + return STATUS_PENDING; + +} /* StCloseConnection */ + diff --git a/private/ntos/tdi/st/connobj.c b/private/ntos/tdi/st/connobj.c new file mode 100644 index 000000000..27c28665a --- /dev/null +++ b/private/ntos/tdi/st/connobj.c @@ -0,0 +1,1375 @@ +/*++ + +Copyright (c) 1989-1993 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. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "st.h" + + + +VOID +StAllocateConnection( + 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("ST: Could not allocate connection: limit\n"); + StWriteResourceErrorLog (DeviceContext, sizeof(TP_CONNECTION), 103); + *TransportConnection = NULL; + return; + } + + Connection = (PTP_CONNECTION)ExAllocatePool (NonPagedPool, + sizeof (TP_CONNECTION)); + if (Connection == NULL) { + PANIC("ST: Could not allocate connection: no pool\n"); + StWriteResourceErrorLog (DeviceContext, sizeof(TP_CONNECTION), 203); + *TransportConnection = NULL; + return; + } + RtlZeroMemory (Connection, sizeof(TP_CONNECTION)); + + DeviceContext->MemoryUsage += sizeof(TP_CONNECTION); + ++DeviceContext->ConnectionAllocated; + + Connection->Type = ST_CONNECTION_SIGNATURE; + Connection->Size = sizeof (TP_CONNECTION); + + Connection->Provider = DeviceContext; + Connection->ProviderInterlock = &DeviceContext->Interlock; + KeInitializeSpinLock (&Connection->SpinLock); + + InitializeListHead (&Connection->LinkList); + InitializeListHead (&Connection->AddressFileList); + InitializeListHead (&Connection->AddressList); + InitializeListHead (&Connection->PacketWaitLinkage); + InitializeListHead (&Connection->PacketizeLinkage); + InitializeListHead (&Connection->SendQueue); + InitializeListHead (&Connection->ReceiveQueue); + InitializeListHead (&Connection->InProgressRequest); + + StAddSendPacket (DeviceContext); + + *TransportConnection = Connection; + +} /* StAllocateConnection */ + + +VOID +StDeallocateConnection( + 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); + + StRemoveSendPacket (DeviceContext); + +} /* StDeallocateConnection */ + + +NTSTATUS +StCreateConnection( + 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; + UINT TempDataLen; + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + p = RemoveHeadList (&DeviceContext->ConnectionPool); + if (p == &DeviceContext->ConnectionPool) { + + if ((DeviceContext->ConnectionMaxAllocated == 0) || + (DeviceContext->ConnectionAllocated < DeviceContext->ConnectionMaxAllocated)) { + + StAllocateConnection (DeviceContext, &Connection); + + } else { + + StWriteResourceErrorLog (DeviceContext, sizeof(TP_CONNECTION), 403); + Connection = NULL; + + } + + if (Connection == NULL) { + ++DeviceContext->ConnectionExhausted; + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + PANIC ("StCreateConnection: Could not allocate connection object!\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + } else { + + Connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList); + + } + + ++DeviceContext->ConnectionInUse; + if (DeviceContext->ConnectionInUse > DeviceContext->ConnectionMaxInUse) { + ++DeviceContext->ConnectionMaxInUse; + } + + DeviceContext->ConnectionTotal += DeviceContext->ConnectionInUse; + ++DeviceContext->ConnectionSamples; + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + + // + // 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 + + // + // 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->MessageBytesReceived = (USHORT)0; // no data yet + Connection->MessageBytesAcked = (USHORT)0; + Connection->Context = NULL; // no context yet. + Connection->Status = STATUS_PENDING; // default StStopConnection status. + Connection->SendState = CONNECTION_SENDSTATE_IDLE; + Connection->CurrentReceiveRequest = (PTP_REQUEST)NULL; + Connection->DisconnectIrp = (PIRP)NULL; + Connection->CloseIrp = (PIRP)NULL; + Connection->AddressFile = NULL; + Connection->IndicationInProgress = FALSE; + + MacReturnMaxDataSize( + &DeviceContext->MacInfo, + NULL, + 0, + DeviceContext->MaxSendPacketSize, + &TempDataLen); + Connection->MaximumDataSize = TempDataLen - sizeof(ST_HEADER); + + StReferenceDeviceContext ("Create Connection", DeviceContext); + + *TransportConnection = Connection; // return the connection. + + return STATUS_SUCCESS; +} /* StCreateConnection */ + + +NTSTATUS +StVerifyConnectionObject ( + 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->Size == sizeof (TP_CONNECTION)) && + (Connection->Type == ST_CONNECTION_SIGNATURE)) { + + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + if ((Connection->Flags2 & CONNECTION_FLAGS2_CLOSING) == 0) { + + StReferenceConnection ("Verify Temp Use", Connection); + + } else { + + status = STATUS_INVALID_CONNECTION; + } + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + + } else { + + status = STATUS_INVALID_CONNECTION; + } + + } except(EXCEPTION_EXECUTE_HANDLER) { + + return GetExceptionCode(); + } + + return status; + +} + + +NTSTATUS +StDestroyAssociation( + 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 StDereferenceConnection. 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; + + + ACQUIRE_SPIN_LOCK (&TransportConnection->SpinLock, &oldirql2); + if ((TransportConnection->Flags2 & CONNECTION_FLAGS2_ASSOCIATED) == 0) { + RELEASE_SPIN_LOCK (&TransportConnection->SpinLock, oldirql2); + return STATUS_SUCCESS; + } else { + TransportConnection->Flags2 &= ~CONNECTION_FLAGS2_ASSOCIATED; + RELEASE_SPIN_LOCK (&TransportConnection->SpinLock, oldirql2); + } + + 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); + ACQUIRE_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_SPIN_LOCK (&TransportConnection->SpinLock, oldirql2); + RELEASE_SPIN_LOCK (&addressFile->Address->SpinLock, oldirql); + + // + // and remove a reference to the address + // + + StDereferenceAddress ("Destroy association", addressFile->Address); + + + return STATUS_SUCCESS; + +} /* StDestroyAssociation */ + + +NTSTATUS +StIndicateDisconnect( + 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 StDereferenceConnection. 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; + + ACQUIRE_SPIN_LOCK (&TransportConnection->SpinLock, &oldirql); + + if (((TransportConnection->Flags2 & CONNECTION_FLAGS2_REQ_COMPLETED) != 0)) { + + // + // Turn off all but the still-relevant bits in the flags. + // + + ASSERT (TransportConnection->Flags & CONNECTION_FLAGS_STOPPING); + + TransportConnection->Flags = CONNECTION_FLAGS_STOPPING; + TransportConnection->Flags2 &= + (CONNECTION_FLAGS2_ASSOCIATED | + CONNECTION_FLAGS2_DISASSOCIATED | + CONNECTION_FLAGS2_CLOSING); + + // + // 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->MessageBytesReceived = (USHORT)0; // no data yet + TransportConnection->MessageBytesAcked = (USHORT)0; + + TransportConnection->CurrentReceiveRequest = (PTP_REQUEST)NULL; + + DisconnectIrp = TransportConnection->DisconnectIrp; + TransportConnection->DisconnectIrp = (PIRP)NULL; + + RELEASE_SPIN_LOCK (&TransportConnection->SpinLock, oldirql); + + + DeviceContext = TransportConnection->Provider; + addressFile = TransportConnection->AddressFile; + + + // + // 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) { + + // + // 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->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 + // StStopConnection, where we know addressFile is valid). + // + + // + // 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. + // + + // + // 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->Flags & CONNECTION_FLAGS_ABORT) { + DisconnectReason |= TDI_DISCONNECT_ABORT; + } + if (TransportConnection->Flags & CONNECTION_FLAGS_DESTROY) { + DisconnectReason |= TDI_DISCONNECT_RELEASE; + } + + (*addressFile->DisconnectHandler)( + addressFile->DisconnectHandlerContext, + TransportConnection->Context, + 0, + NULL, + 0, + NULL, + DisconnectReason); + + } + + } else { + + // + // The client does not yet think that this connection + // is up...generally this happens due to request count + // fluctuation during connection setup. + // + + RELEASE_SPIN_LOCK (&TransportConnection->SpinLock, oldirql); + + } + + + return STATUS_SUCCESS; + +} /* StIndicateDisconnect */ + + +NTSTATUS +StDestroyConnection( + 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 StDereferenceConnection. 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; + + + DeviceContext = TransportConnection->Provider; + + // + // Destroy any association that this connection has. + // + + StDestroyAssociation (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. + // + + TransportConnection->Flags = CONNECTION_FLAGS_STOPPING; + TransportConnection->Flags2 = CONNECTION_FLAGS2_CLOSING; + TransportConnection->MessageBytesReceived = (USHORT)0; // no data yet + TransportConnection->MessageBytesAcked = (USHORT)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); + + } + + // + // 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) { + StDeallocateConnection (DeviceContext, TransportConnection); + } else { + InsertTailList (&DeviceContext->ConnectionPool, &TransportConnection->LinkList); + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + StDereferenceDeviceContext ("Destroy Connection", DeviceContext); + + return STATUS_SUCCESS; + +} /* StDestroyConnection */ + + +VOID +StRefConnection( + 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; + + 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); + + } + + ASSERT (result >= 0); + +} /* StRefConnection */ + + +VOID +StDerefConnection( + 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 + StDestroyConnection to remove it from the system. + +Arguments: + + TransportConnection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + 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 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. + // + + StIndicateDisconnect (TransportConnection); + + // + // Now it is OK to let the connection go away. + // + + StDereferenceConnectionSpecial ("Regular ref gone", TransportConnection); + + } + +} /* StDerefConnection */ + + +VOID +StDerefConnectionSpecial( + 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; + + 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. + // + + RELEASE_SPIN_LOCK (TransportConnection->ProviderInterlock, oldirql); + + StDestroyConnection (TransportConnection); + + } else { + + RELEASE_SPIN_LOCK (TransportConnection->ProviderInterlock, oldirql); + + } + +} /* StDerefConnectionSpecial */ + + +PTP_CONNECTION +StFindConnection( + IN PDEVICE_CONTEXT DeviceContext, + IN PUCHAR LocalName, + IN PUCHAR RemoteName + ) + +/*++ + +Routine Description: + + This routine scans the connections associated with a + device context, and determines if there is an connection + associated with the specific remote address on the + specific local address. + +Arguments: + + DeviceContext - Pointer to the device context. + + LocalName - The 16-character Netbios name of the local address. + + RemoteName - The 16-character Netbios name of the remote. + +Return Value: + + The connection if one is found, NULL otherwise. + +--*/ + +{ + KIRQL oldirql; + PLIST_ENTRY Flink; + PTP_ADDRESS Address; + BOOLEAN MatchedAddress = FALSE; + PTP_CONNECTION Connection; + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + 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 (StMatchNetbiosAddress (Address, LocalName)) { + + StReferenceAddress ("Looking for connection", Address); // prevent address from being destroyed. + MatchedAddress = TRUE; + break; + + } + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + if (!MatchedAddress) { + return NULL; + } + + Connection = StLookupRemoteName (Address, RemoteName); + + StDereferenceAddress ("Looking for connection", Address); + + return Connection; + +} + + +PTP_CONNECTION +StLookupConnectionByContext( + 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; + PTP_CONNECTION Connection; + + // + // 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); + + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql1); + + if ((Connection->Context == ConnectionContext) && + ((Connection->Flags2 & CONNECTION_FLAGS2_CLOSING) == 0)) { + // This reference is removed by the calling function + StReferenceConnection ("Lookup up for request", Connection); + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql1); + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + return Connection; + } + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql1); + + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + return NULL; + +} /* StLookupConnectionByContext */ + + +PTP_CONNECTION +StLookupListeningConnection( + IN PTP_ADDRESS Address + ) + +/*++ + +Routine Description: + + This routine scans the connection database on an address to find + a TP_CONNECTION object which has 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. + +Arguments: + + Address - Pointer to a transport address object. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql, oldirql1; + PTP_CONNECTION Connection; + PLIST_ENTRY p; + + + 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->Flags & CONNECTION_FLAGS_WAIT_LISTEN) { + + // This reference is removed by the calling function + StReferenceConnection ("Found Listening", Connection); + + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql1); + Connection->Flags &= ~CONNECTION_FLAGS_WAIT_LISTEN; + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql1); + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + return Connection; + } + + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + return NULL; + +} /* StLookupListeningConnection */ + + +VOID +StStopConnection( + 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. + +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 oldirql, oldirql1, cancelirql; + PLIST_ENTRY p; + PIRP Irp; + PTP_REQUEST Request; + ULONG DisconnectReason; + PULONG StopCounter; + PDEVICE_CONTEXT DeviceContext; + BOOLEAN WasConnected; + + + DeviceContext = Connection->Provider; + + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + if (!(Connection->Flags & CONNECTION_FLAGS_STOPPING)) { + + // + // We are stopping the connection, record statistics + // about it. + // + + if (Connection->Flags & CONNECTION_FLAGS_READY) { + + DECREMENT_COUNTER (DeviceContext, OpenConnections); + WasConnected = TRUE; + + } else { + + WasConnected = FALSE; + + } + + Connection->Flags &= ~CONNECTION_FLAGS_READY; // no longer open for business + Connection->Flags |= CONNECTION_FLAGS_STOPPING; + Connection->Flags2 &= ~CONNECTION_FLAGS2_REMOTE_VALID; + Connection->SendState = CONNECTION_SENDSTATE_IDLE; + Connection->Status = Status; + + // + // 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->Flags & CONNECTION_FLAGS_SUSPENDED) { + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql1); + RemoveEntryList (&Connection->PacketWaitLinkage); + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql1); + } + + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + + // + // Run down all TdiSend requests on this connection. + // + + while (TRUE) { + p = RemoveHeadList (&Connection->SendQueue); + if (p == &Connection->SendQueue) { + break; + } + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + Irp->CancelRoutine = (PDRIVER_CANCEL)NULL; + IoReleaseCancelSpinLock(cancelirql); + StCompleteSendIrp (Irp, Connection->Status, 0); + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + } + + // + // 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; + } + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + Request->IoRequestPacket->CancelRoutine = (PDRIVER_CANCEL)NULL; + IoReleaseCancelSpinLock(cancelirql); + + StCompleteRequest (Request, Connection->Status, 0); + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + } + + + // + // NOTE: We hold the connection spinlock AND the + // cancel spinlock here. + // + + // + // Run down all TdiConnect/TdiDisconnect/TdiListen requests. + // + + while (TRUE) { + p = RemoveHeadList (&Connection->InProgressRequest); + if (p == &Connection->InProgressRequest) { + break; + } + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + Request->IoRequestPacket->CancelRoutine = (PDRIVER_CANCEL)NULL; + IoReleaseCancelSpinLock(cancelirql); + + StCompleteRequest (Request, Connection->Status, 0); + + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + } + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + 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. + // + + DisconnectReason = 0; + if (Connection->Flags & CONNECTION_FLAGS_ABORT) { + DisconnectReason |= TDI_DISCONNECT_ABORT; + } + if (Connection->Flags & CONNECTION_FLAGS_DESTROY) { + DisconnectReason |= TDI_DISCONNECT_RELEASE; + } + + // + // When this completes we will dereference the connection. + // + + if (WasConnected) { + StSendDisconnect (Connection); + } + + + switch (Status) { + + case STATUS_LOCAL_DISCONNECT: + StopCounter = &DeviceContext->LocalDisconnects; + break; + case STATUS_REMOTE_DISCONNECT: + StopCounter = &DeviceContext->RemoteDisconnects; + break; + case STATUS_LINK_FAILED: + StopCounter = &DeviceContext->LinkFailures; + break; + case STATUS_IO_TIMEOUT: + StopCounter = &DeviceContext->SessionTimeouts; + break; + case STATUS_CANCELLED: + StopCounter = &DeviceContext->CancelledConnections; + break; + case STATUS_REMOTE_RESOURCES: + StopCounter = &DeviceContext->RemoteResourceFailures; + break; + case STATUS_INSUFFICIENT_RESOURCES: + StopCounter = &DeviceContext->LocalResourceFailures; + break; + case STATUS_BAD_NETWORK_PATH: + StopCounter = &DeviceContext->NotFoundFailures; + break; + case STATUS_REMOTE_NOT_LISTENING: + StopCounter = &DeviceContext->NoListenFailures; + break; + + default: + StopCounter = NULL; + break; + + } + + if (StopCounter != NULL) { + + *StopCounter = *StopCounter + 1; + + } + + + // + // Note that we've blocked all new requests being queued during the + // time we have been in this teardown code; StDestroyConnection 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. + // + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + + } + +} /* StStopConnection */ + + +VOID +StCancelConnection( + 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; + + 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_SPIN_LOCK (&Connection->SpinLock, &oldirql); + StReferenceConnection ("Cancelling Send", Connection); + + p = RemoveHeadList (&Connection->InProgressRequest); + ASSERT (p != &Connection->InProgressRequest); + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + ASSERT (Request->IoRequestPacket == Irp); + Request->IoRequestPacket->CancelRoutine = (PDRIVER_CANCEL)NULL; + + IoReleaseCancelSpinLock(Irp->CancelIrql); + + StCompleteRequest (Request, STATUS_CANCELLED, 0); + StStopConnection (Connection, STATUS_CANCELLED); + + StDereferenceConnection ("Cancel done", Connection); + +} + diff --git a/private/ntos/tdi/st/devctx.c b/private/ntos/tdi/st/devctx.c new file mode 100644 index 000000000..94cc2f385 --- /dev/null +++ b/private/ntos/tdi/st/devctx.c @@ -0,0 +1,256 @@ +/*++ + +Copyright (c) 1989-1993 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. + + 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. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "st.h" + + +VOID +StRefDeviceContext( + 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. + +--*/ + +{ + ASSERT (DeviceContext->ReferenceCount > 0); // not perfect, but... + + (VOID)InterlockedIncrement (&DeviceContext->ReferenceCount); + +} /* StRefDeviceContext */ + + +VOID +StDerefDeviceContext( + 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; + + result = InterlockedDecrement (&DeviceContext->ReferenceCount); + + ASSERT (result >= 0); + + if (result == 0) { + StDestroyDeviceContext (DeviceContext); + } + +} /* StDerefDeviceContext */ + + + +NTSTATUS +StCreateDeviceContext( + 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 the sample transport, allowing + // room at the end for the device name to be stored (for use + // in logging errors). + // + + 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 the reference count. + // + + deviceContext->ReferenceCount = 1; + + // + // initialize the various fields in the device context + // + + KeInitializeSpinLock (&deviceContext->Interlock); + KeInitializeSpinLock (&deviceContext->SpinLock); + + deviceContext->ControlChannelIdentifier = 1; + + InitializeListHead (&deviceContext->ConnectionPool); + InitializeListHead (&deviceContext->AddressPool); + InitializeListHead (&deviceContext->AddressFilePool); + InitializeListHead (&deviceContext->AddressDatabase); + InitializeListHead (&deviceContext->PacketWaitQueue); + InitializeListHead (&deviceContext->PacketizeQueue); + InitializeListHead (&deviceContext->RequestPool); + deviceContext->PacketPool.Next = NULL; + deviceContext->ReceivePacketPool.Next = NULL; + deviceContext->ReceiveBufferPool.Next = NULL; + InitializeListHead (&deviceContext->ReceiveInProgress); + InitializeListHead (&deviceContext->IrpCompletionQueue); + + + deviceContext->State = DEVICECONTEXT_STATE_CLOSED; + + // + // Initialize the resource that guards address ACLs. + // + + ExInitializeResource (&deviceContext->AddressResource); + + // + // 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->MulticastAddress.Address [i] = 0; + } + + deviceContext->Type = ST_DEVICE_CONTEXT_SIGNATURE; + deviceContext->Size - sizeof (DEVICE_CONTEXT); + + *DeviceContext = deviceContext; + return STATUS_SUCCESS; +} + + +VOID +StDestroyDeviceContext( + 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/st/event.c b/private/ntos/tdi/st/event.c new file mode 100644 index 000000000..9c98c9596 --- /dev/null +++ b/private/ntos/tdi/st/event.c @@ -0,0 +1,185 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + event.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiSetEventHandler + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "st.h" + + +NTSTATUS +StTdiSetEventHandler( + 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 = StVerifyAddressObject (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); + + StDereferenceAddress ("Set event handler", address); + + return rc; +} /* TdiSetEventHandler */ diff --git a/private/ntos/tdi/st/framesnd.c b/private/ntos/tdi/st/framesnd.c new file mode 100644 index 000000000..0d9f5cff7 --- /dev/null +++ b/private/ntos/tdi/st/framesnd.c @@ -0,0 +1,371 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + framesnd.c + +Abstract: + + This module contains routines which build and send Sample transport + frames for other modules. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "st.h" + + + +NTSTATUS +StSendConnect( + IN PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine sends a CONNECT frame of the appropriate type given the + state of the specified connection. + +Arguments: + + Connection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PDEVICE_CONTEXT DeviceContext; + PUCHAR SourceRouting; + UINT SourceRoutingLength; + UINT HeaderLength; + PSEND_PACKET_TAG SendTag; + PTP_PACKET Packet; + PST_HEADER StHeader; + + + DeviceContext = Connection->Provider; + + // + // Allocate a packet from the pool. + // + + Status = StCreatePacket (DeviceContext, &Packet); + if (!NT_SUCCESS (Status)) { // couldn't make frame. + return STATUS_INSUFFICIENT_RESOURCES; + } + + SendTag = (PSEND_PACKET_TAG)(Packet->NdisPacket->ProtocolReserved); + SendTag->Type = TYPE_C_FRAME; + SendTag->Packet = Packet; + SendTag->Owner = (PVOID)Connection; + + // + // Build the MAC header. + // + + // + // CONNECT frames go out as + // single-route source routing. + // + + MacReturnSingleRouteSR( + &DeviceContext->MacInfo, + &SourceRouting, + &SourceRoutingLength); + + MacConstructHeader ( + &DeviceContext->MacInfo, + Packet->Header, + DeviceContext->MulticastAddress.Address, + DeviceContext->LocalAddress.Address, + sizeof(ST_HEADER), + SourceRouting, + SourceRoutingLength, + &HeaderLength); + + + // + // Build the header: 'C', dest, source + // + + StHeader = (PST_HEADER)(&Packet->Header[HeaderLength]); + + StHeader->Signature = ST_SIGNATURE; + StHeader->Command = ST_CMD_CONNECT; + StHeader->Flags = 0; + + RtlCopyMemory (StHeader->Destination, Connection->CalledAddress.NetbiosName, 16); + RtlCopyMemory (StHeader->Source, Connection->AddressFile->Address->NetworkName->NetbiosName, 16); + + HeaderLength += sizeof(ST_HEADER); + + // + // Modify the packet length and send the it. + // + + StSetNdisPacketLength(Packet->NdisPacket, HeaderLength); + + StNdisSend (Packet); + + return STATUS_SUCCESS; +} /* StSendConnect */ + + +NTSTATUS +StSendDisconnect( + IN PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine sends a DISCONNECT frame of the appropriate type given the + state of the specified connection. + +Arguments: + + Connection - Pointer to a transport connection object. + +Return Value: + + none. + +--*/ + +{ + NTSTATUS Status; + PDEVICE_CONTEXT DeviceContext; + PUCHAR SourceRouting; + UINT SourceRoutingLength; + UINT HeaderLength; + PSEND_PACKET_TAG SendTag; + PTP_PACKET Packet; + PST_HEADER StHeader; + + + DeviceContext = Connection->Provider; + + // + // Allocate a packet from the pool. + // + + Status = StCreatePacket (DeviceContext, &Packet); + if (!NT_SUCCESS (Status)) { // couldn't make frame. + return STATUS_INSUFFICIENT_RESOURCES; + } + + SendTag = (PSEND_PACKET_TAG)(Packet->NdisPacket->ProtocolReserved); + SendTag->Type = TYPE_D_FRAME; + SendTag->Packet = Packet; + SendTag->Owner = (PVOID)Connection; + + // + // Build the MAC header. + // + + // + // CONNECT frames go out as + // single-route source routing. + // + + MacReturnSingleRouteSR( + &DeviceContext->MacInfo, + &SourceRouting, + &SourceRoutingLength); + + MacConstructHeader ( + &DeviceContext->MacInfo, + Packet->Header, + DeviceContext->MulticastAddress.Address, + DeviceContext->LocalAddress.Address, + sizeof(ST_HEADER), + SourceRouting, + SourceRoutingLength, + &HeaderLength); + + + // + // Build the header: 'D', dest, source + // + + StHeader = (PST_HEADER)(&Packet->Header[HeaderLength]); + + StHeader->Signature = ST_SIGNATURE; + StHeader->Command = ST_CMD_DISCONNECT; + StHeader->Flags = 0; + + RtlCopyMemory (StHeader->Destination, Connection->CalledAddress.NetbiosName, 16); + RtlCopyMemory (StHeader->Source, Connection->AddressFile->Address->NetworkName->NetbiosName, 16); + + HeaderLength += sizeof(ST_HEADER); + + // + // Modify the packet length and send the it. + // + + StSetNdisPacketLength(Packet->NdisPacket, HeaderLength); + + StNdisSend (Packet); + + return STATUS_SUCCESS; + +} /* StSendDisconnect */ + + +NTSTATUS +StSendAddressFrame( + PTP_ADDRESS Address + ) + +/*++ + +Routine Description: + + 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 + frames are sequenced through the address they are sent on. + +Arguments: + + Address - pointer to the address from which to send this datagram. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + + + // + // Send the packet. + // + + DeviceContext = Address->Provider; + + INCREMENT_COUNTER (DeviceContext, PacketsSent); + + StNdisSend (Address->Packet); + + return STATUS_PENDING; +} /* StSendAddressFrame */ + + +VOID +StSendDatagramCompletion( + 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 + StSendUIMdlFrame send request is completed. Because this handler is only + associated with StSendUIMdlFrame, and because StSendUIMdlFrame 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. + +--*/ + +{ + PTP_REQUEST Request; + PLIST_ENTRY p; + KIRQL oldirql; + PNDIS_BUFFER HeaderBuffer; + + UNREFERENCED_PARAMETER(NdisPacket); + + StReferenceAddress ("Complete datagram", Address); + + // + // 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. + // + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + p = RemoveHeadList (&Address->SendDatagramQueue); + + if (p != &Address->SendDatagramQueue) { + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + + // + // Strip off and unmap the buffers describing data and header. + // + + NdisUnchainBufferAtFront (Address->Packet->NdisPacket, &HeaderBuffer); + + // drop the rest of the packet + + NdisReinitializePacket (Address->Packet->NdisPacket); + + NDIS_BUFFER_LINKAGE(HeaderBuffer) = (PNDIS_BUFFER)NULL; + NdisChainBufferAtFront (Address->Packet->NdisPacket, HeaderBuffer); + + // + // Ignore NdisStatus; datagrams always "succeed". + // + + StCompleteRequest (Request, STATUS_SUCCESS, Request->Buffer2Length); + + 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. + // + + StSendDatagramsOnAddress (Address); // do more datagrams. + + } else { + + Address->Flags &= ~ADDRESS_FLAGS_SEND_IN_PROGRESS; + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + } + + StDereferenceAddress ("Complete datagram", Address); + +} /* StSendDatagramCompletion */ diff --git a/private/ntos/tdi/st/iframes.c b/private/ntos/tdi/st/iframes.c new file mode 100644 index 000000000..59b171c92 --- /dev/null +++ b/private/ntos/tdi/st/iframes.c @@ -0,0 +1,808 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + iframes.c + +Abstract: + + This module contains routines called to handle i-frames received + from the NDIS driver. Most of these routines are called at receive + indication time. + +Environment: + + Kernel mode, DISPATCH_LEVEL. + +Revision History: + +--*/ + +#include "st.h" +#if 27 +ULONG StNoisyReceives = 0; +ULONG StRcvLoc = 0; +ULONG StRcvs[10]; +#endif + + + +NTSTATUS +StProcessIIndicate( + IN PTP_CONNECTION Connection, + IN PST_HEADER StHeader, + IN UINT StIndicatedLength, + IN UINT StTotalLength, + IN NDIS_HANDLE ReceiveContext, + IN BOOLEAN Last + ) + +/*++ + +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. + +Arguments: + + Connection - The connection that the data is destined for. + + StHeader - A pointer to the start of the ST header in the packet. + + StIndicatedLength - The length of the packet indicated, starting at + StHeader. + + StTotalLength - The total length of the packet, starting at StHeader. + + ReceiveContext - A magic value for NDIS that indicates which packet we're + talking about, used for calling TransferData + + Last - TRUE if this is the last packet in a send. + +Return Value: + + STATUS_SUCCESS if we've consumed the packet, but + STATUS_MORE_PROCESSING_REQUIRED if we did so and also + activated a receive; this tells the caller not to + remove the connection refcount. + +--*/ + +{ + KIRQL oldirql; + NTSTATUS status, tmpstatus; + KIRQL cancelirql; + 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 ndisBytesTransferred; + PUCHAR DataHeader; + ULONG DataTotalLength; + ULONG DataIndicatedLength; + UINT BytesToTransfer; + ULONG bytesIndicated; + PRECEIVE_PACKET_TAG receiveTag; + PTP_ADDRESS address; + PTP_ADDRESS_FILE addressFile; + PMDL SavedCurrentMdl; + ULONG SavedCurrentByteOffset; + LARGE_INTEGER time; + ULONG DumpData[2]; + BOOLEAN CancelSpinLockHeld = FALSE; +#if 27 + if (StNoisyReceives) { + DbgPrint ("Indicate %d, Total %d\n", StIndicatedLength, StTotalLength); + } + if (StTotalLength > 1000) { + StRcvs[StRcvLoc] = StTotalLength; + StRcvLoc = (StRcvLoc + 1) % 10; + } +#endif + + + // + // copy this packet into our receive buffer. + // + + deviceContext = Connection->Provider; + addressFile = Connection->AddressFile; + address = addressFile->Address; + + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + // + // If we have a previous receive that is pending + // completion, then we need to ignore this frame. + // This may be common on MP. + // + + if (Connection->Flags2 & CONNECTION_FLAGS2_RC_PENDING) { + + Connection->IndicationInProgress = FALSE; + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + + return STATUS_SUCCESS; + } + + DataHeader = (PUCHAR)StHeader + sizeof(ST_HEADER); + DataTotalLength = StTotalLength - sizeof(ST_HEADER); + DataIndicatedLength = StIndicatedLength - sizeof(ST_HEADER); + + // + // 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) { + + // + // 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->CurrentReceiveRequest = + CONTAINING_RECORD (Connection->ReceiveQueue.Flink, + TP_REQUEST, Linkage); + Connection->CurrentReceiveMdl = + Connection->CurrentReceiveRequest->Buffer2; + Connection->ReceiveLength = + Connection->CurrentReceiveRequest->Buffer2Length; + Connection->ReceiveByteOffset = 0; + status = STATUS_SUCCESS; + goto NormalReceive; + } + + // + // A receive is not active. Post a receive event. + // + + if (!addressFile->RegisteredReceiveHandler) { + + // + // 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. + // + + Connection->IndicationInProgress = FALSE; + return STATUS_SUCCESS; + } + + if ((Connection->Flags & CONNECTION_FLAGS_READY) == 0) { + Connection->IndicationInProgress = FALSE; + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + + return STATUS_SUCCESS; + } + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + + // + // Indicate to the user. For BytesAvailable we + // always use DataTotalLength; for BytesIndicated we use + // MIN (DataIndicatedLength, 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 DataIndicatedLength + // will be shorter. + // + + bytesIndicated = DataIndicatedLength; + if (DataTotalLength < bytesIndicated) { + bytesIndicated = DataTotalLength; + } + + status = (*addressFile->ReceiveHandler)( + addressFile->ReceiveHandlerContext, + Connection->Context, + deviceContext->MacInfo.CopyLookahead ? + TDI_RECEIVE_COPY_LOOKAHEAD : 0, // ReceiveFlags + bytesIndicated, + DataTotalLength, // BytesAvailable + &indicateBytesTransferred, + DataHeader, + &irp); + + if (status == STATUS_SUCCESS) { + + // + // The client has accepted some or all of the indicated data in + // the event handler. Update MessageBytesReceived variable in + // the Connection. + // + + Connection->MessageBytesReceived += indicateBytesTransferred; + Connection->IndicationInProgress = FALSE; + + return STATUS_SUCCESS; + + } else if (status == STATUS_DATA_NOT_ACCEPTED) { + + // + // 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_SPIN_LOCK (&Connection->SpinLock, &oldirql); + 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. + // + + Connection->IndicationInProgress = FALSE; + return STATUS_SUCCESS; + + } else { + + // + // 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->CurrentReceiveRequest = + CONTAINING_RECORD (Connection->ReceiveQueue.Flink, + TP_REQUEST, Linkage); + Connection->CurrentReceiveMdl = + Connection->CurrentReceiveRequest->Buffer2; + Connection->ReceiveLength = + Connection->CurrentReceiveRequest->Buffer2Length; + Connection->ReceiveByteOffset = 0; + } + + } else if (status == STATUS_MORE_PROCESSING_REQUIRED) { + + PTP_REQUEST SpecialIrpRequest; + ULONG SpecialIrpLength; + + // + // 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. + // + + // + // 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); + + SpecialIrpLength = + ((PTDI_REQUEST_KERNEL_RECEIVE)&irpSp->Parameters)->ReceiveLength; + + // + // The normal path, for longer receives. + // + + time.HighPart = 0; + time.LowPart = 0; + + status = StCreateRequest ( + irp, + Connection, + REQUEST_FLAGS_CONNECTION | REQUEST_FLAGS_SEND_RCV, + irp->MdlAddress, + ((PTDI_REQUEST_KERNEL_RECEIVE )&irpSp->Parameters)->ReceiveLength, + time, + &SpecialIrpRequest); + + if (!NT_SUCCESS (status)) { + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + Connection->ReceiveByteOffset = 0; + Connection->Flags |= CONNECTION_FLAGS_RECEIVE_WAKEUP; + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + + Connection->IndicationInProgress = FALSE; + + irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; + + IoCompleteRequest (irp, IO_NETWORK_INCREMENT); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // If the Connection is stopping, abort this request. + // + + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + if ((Connection->Flags & CONNECTION_FLAGS_STOPPING) != 0) { + Connection->IndicationInProgress = FALSE; + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + + IoReleaseCancelSpinLock(cancelirql); + StCompleteRequest ( + SpecialIrpRequest, + Connection->Status, + 0); + return STATUS_SUCCESS; // we have consumed the packet + + } + + // + // Insert the request on the head of the connection's + // receive queue, so it can be handled like a normal + // receive. + // + + InsertHeadList (&Connection->ReceiveQueue, &SpecialIrpRequest->Linkage); + + Connection->Flags |= CONNECTION_FLAGS_ACTIVE_RECEIVE; + Connection->ReceiveLength = ((PTDI_REQUEST_KERNEL_RECEIVE )&irpSp->Parameters)->ReceiveLength; + Connection->MessageBytesReceived = indicateBytesTransferred; + Connection->MessageBytesAcked = 0; + Connection->CurrentReceiveRequest = SpecialIrpRequest; + Connection->CurrentReceiveMdl = irp->MdlAddress; + Connection->ReceiveByteOffset = 0; + Connection->CurrentReceiveRequest->Owner = ConnectionType; + + // + // If this IRP has been cancelled, then call the + // cancel routine. + // + + if (irp->Cancel) { + + Connection->Flags |= CONNECTION_FLAGS_RECEIVE_WAKEUP; + Connection->IndicationInProgress = FALSE; + RELEASE_SPIN_LOCK (&Connection->SpinLock,oldirql); + irp->CancelIrql = cancelirql; + StCancelReceive((PDEVICE_OBJECT)deviceContext, irp); + + return STATUS_SUCCESS; + + } else { + + irp->CancelRoutine = StCancelReceive; + + status = STATUS_MORE_PROCESSING_REQUIRED; + + // + // Make a note so we know to release the cancel + // spinlock below. + // + + CancelSpinLockHeld = TRUE; + + } + + } else { + + // + // An unknown return code has been returned by the + // client's event handler. This is a client programming + // error. Because this can only occur when kernel-mode + // clients have been coded incorrectly, we should beat + // him with a stick. We have to do SOMETHING, or this + // Connection will HANG. + // + + Connection->IndicationInProgress = FALSE; + return STATUS_SUCCESS; + + } + + } 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); + + // + // The status should be SUCCESS (we found an active receive) + // or MORE_PROCESSING_REQUIRED (we made a new receive active). + // + + ASSERT ((status == STATUS_SUCCESS) || (status == STATUS_MORE_PROCESSING_REQUIRED)); + + destBytes = Connection->ReceiveLength - Connection->MessageBytesReceived; + StReferenceRequest ("Transfer Data", Connection->CurrentReceiveRequest); + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + if (CancelSpinLockHeld) { + IoReleaseCancelSpinLock (cancelirql); + } + + // + // 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 { + (VOID)InterlockedIncrement((PLONG)&deviceContext->ReceivePacketExhausted); + + StDereferenceRequest ("No receive packet", Connection->CurrentReceiveRequest); + + // We could not get a receive packet. + // + + 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; + + + // + // Determine how much data remains to be transferred. + // + + BytesToTransfer = DataTotalLength - indicateBytesTransferred; + ASSERT (BytesToTransfer >= 0); + + 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. + // + + receiveTag->EndOfMessage = FALSE; + receiveTag->CompleteReceive = 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. + // + + receiveTag->EndOfMessage = Last; + receiveTag->CompleteReceive = TRUE; + + } else { + + // + // Complete the receive if this is a DOL. + // + + receiveTag->EndOfMessage = Last; + receiveTag->CompleteReceive = Last; + + } + + // + // if we've got zero bytes left, avoid the TransferData below and + // just deliver. + // + + if (BytesToTransfer <= 0) { + Connection->IndicationInProgress = FALSE; + receiveTag->NdisStatus = NDIS_STATUS_SUCCESS; + receiveTag->TransferDataPended = FALSE; + StTransferDataComplete ( + 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 it. 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) && + (receiveTag->CompleteReceive)) { + + // + // 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->NdisBufferPoolHandle, + 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; + + StWriteGeneralErrorLog( + deviceContext, + EVENT_TRANSPORT_TRANSFER_DATA, + 604, + tmpstatus, + NULL, + 2, + DumpData); + + StDereferenceRequest ("No MDL chain", Connection->CurrentReceiveRequest); + + // + // Restore our old state. + // + + Connection->CurrentReceiveMdl = SavedCurrentMdl; + Connection->ReceiveByteOffset = SavedCurrentByteOffset; + + Connection->IndicationInProgress = FALSE; + + ExInterlockedPushEntryList( + &deviceContext->ReceivePacketPool, + (PSINGLE_LIST_ENTRY)&receiveTag->Linkage, + &deviceContext->Interlock); + + return status; + } + + NdisChainBufferAtFront (ndisPacket, ndisBuffer); + + // + // 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 + // + + receiveTag->NdisStatus = NDIS_STATUS_PENDING; + + // + // update the number of bytes received; OK to do this + // unprotected since IndicationInProgress is still FALSE. + // + // + + Connection->MessageBytesReceived += BytesToTransfer; + + // + // We have now updated all the connection counters (BUG, + // assuming the TransferData will succeed) and this + // packet's location in the request is secured, so we + // can be reentered. + // + + Connection->IndicationInProgress = FALSE; + + NdisTransferData ( + &ndisStatus, + deviceContext->NdisBindingHandle, + ReceiveContext, + sizeof (ST_HEADER) + indicateBytesTransferred, + BytesToTransfer, + ndisPacket, + (PUINT)&ndisBytesTransferred); + + // + // handle the various completion codes + // + + switch (ndisStatus) { + + case NDIS_STATUS_SUCCESS: + + receiveTag->NdisStatus = NDIS_STATUS_SUCCESS; + if (ndisBytesTransferred != BytesToTransfer) { // Did we get the entire packet? + + DumpData[0] = ndisBytesTransferred; + DumpData[1] = BytesToTransfer; + + StWriteGeneralErrorLog( + deviceContext, + EVENT_TRANSPORT_TRANSFER_DATA, + 604, + ndisStatus, + NULL, + 2, + DumpData); + + if (receiveTag->AllocatedNdisBuffer) { + NdisUnchainBufferAtFront (ndisPacket, &ndisBuffer); + while (ndisBuffer != NULL) { + NdisFreeBuffer (ndisBuffer); + NdisUnchainBufferAtFront (ndisPacket, &ndisBuffer); + } + } else { + NdisReinitializePacket (ndisPacket); + } + + ExInterlockedPushEntryList( + &deviceContext->ReceivePacketPool, + (PSINGLE_LIST_ENTRY)&receiveTag->Linkage, + &deviceContext->Interlock); + + StDereferenceRequest ("Bad byte count", Connection->CurrentReceiveRequest); + + // + // Restore our old state. + // + + Connection->CurrentReceiveMdl = SavedCurrentMdl; + Connection->ReceiveByteOffset = SavedCurrentByteOffset; + Connection->MessageBytesReceived -= BytesToTransfer; + + return status; + } + + // + // deallocate the buffers and such that we've used if at indicate + // + + receiveTag->TransferDataPended = FALSE; + + StTransferDataComplete ( + deviceContext, + ndisPacket, + ndisStatus, + BytesToTransfer); + break; + + case NDIS_STATUS_PENDING: // waiting async complete from NdisTransferData + + // + // 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". + // + + StReferenceConnection ("TransferData pended", Connection); + break; + + default: + + // + // Something broke; certainly we'll never get NdisTransferData + // asynch completion. + // + // BUGBUG: The driver should recover from this situation. + // + + StWriteGeneralErrorLog( + deviceContext, + EVENT_TRANSPORT_TRANSFER_DATA, + 604, + ndisStatus, + NULL, + 0, + NULL); + + if (receiveTag->AllocatedNdisBuffer) { + NdisUnchainBufferAtFront (ndisPacket, &ndisBuffer); + while (ndisBuffer != NULL) { + NdisFreeBuffer (ndisBuffer); + NdisUnchainBufferAtFront (ndisPacket, &ndisBuffer); + } + } else { + NdisReinitializePacket (ndisPacket); + } + + ExInterlockedPushEntryList( + &deviceContext->ReceivePacketPool, + (PSINGLE_LIST_ENTRY)&receiveTag->Linkage, + &deviceContext->Interlock); + + StDereferenceRequest ("TransferData failed", Connection->CurrentReceiveRequest); + + // + // Restore our old state. + // + + Connection->CurrentReceiveMdl = SavedCurrentMdl; + Connection->ReceiveByteOffset = SavedCurrentByteOffset; + Connection->MessageBytesReceived -= BytesToTransfer; + + return status; + } // switch ndisStatus + + return status; // which only means we've dealt with the packet + +} /* ProcessIIndicate */ diff --git a/private/ntos/tdi/st/ind.c b/private/ntos/tdi/st/ind.c new file mode 100644 index 000000000..68dc3bffb --- /dev/null +++ b/private/ntos/tdi/st/ind.c @@ -0,0 +1,829 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + ind.c + +Abstract: + + This module contains code which implements the indication handler + for the NT Sample transport provider. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "st.h" + + + +NDIS_STATUS +StReceiveIndication ( + 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; + HARDWARE_ADDRESS SourceAddressBuffer; + PHARDWARE_ADDRESS SourceAddress; + UINT RealPacketSize; + PST_HEADER StHeader; + + 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 + ); + + if (RealPacketSize < 2) { + 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. + // + + StHeader = (PST_HEADER)LookaheadBuffer; + + if (StHeader->Signature != ST_SIGNATURE) { + return NDIS_STATUS_NOT_RECOGNIZED; // packet was processed. + } + + + // + // Check that the packet is not too long. + // + + if (PacketSize > DeviceContext->MaxReceivePacketSize) { +#if DBG + StPrint2("StReceiveIndication: Ignoring packet length %d, max %d\n", + PacketSize, DeviceContext->MaxReceivePacketSize); +#endif + return NDIS_STATUS_NOT_RECOGNIZED; + } + + MacReturnSourceAddress( + &DeviceContext->MacInfo, + HeaderBuffer, + &SourceAddressBuffer, + &SourceAddress + ); + + + return StGeneralReceiveHandler( + DeviceContext, + ReceiveContext, + SourceAddress, + HeaderBuffer, // header + RealPacketSize, // total data length in packet + (PST_HEADER)LookaheadBuffer, // lookahead data + LookaheadBufferSize // lookahead data length + ); + +} + + +NDIS_STATUS +StGeneralReceiveHandler ( + IN PDEVICE_CONTEXT DeviceContext, + IN NDIS_HANDLE ReceiveContext, + IN PHARDWARE_ADDRESS SourceAddress, + IN PVOID HeaderBuffer, + IN UINT PacketSize, + IN PST_HEADER StHeader, + IN UINT StSize + ) + +/*++ + +Routine Description: + + This routine receives control from StReceiveIndication. + It continues the processing of indicated data. + + 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. + + 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. + +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. + +--*/ +{ + + KIRQL oldirql; + NTSTATUS Status; + NDIS_STATUS NdisStatus; + PNDIS_PACKET NdisPacket; + PNDIS_BUFFER NdisBuffer; + PSINGLE_LIST_ENTRY linkage; + UINT BytesTransferred; + PRECEIVE_PACKET_TAG ReceiveTag; + PBUFFER_TAG BufferTag; + PUCHAR SourceRouting; + UINT SourceRoutingLength; + PTP_ADDRESS DatagramAddress; + UINT NdisBufferLength; + PTP_CONNECTION Connection; + PVOID BufferPointer; + + + INCREMENT_COUNTER (DeviceContext, PacketsReceived); + + Status = STATUS_SUCCESS; // assume no further processing required + + + // + // See what type of frame this is. + // + + if ((StHeader->Command == ST_CMD_CONNECT) || + (StHeader->Command == ST_CMD_DATAGRAM)) { + + MacReturnSourceRouting( + &DeviceContext->MacInfo, + HeaderBuffer, + &SourceRouting, + &SourceRoutingLength); + + Status = StProcessConnectionless ( + DeviceContext, + SourceAddress, + StHeader, + StSize, + SourceRouting, + SourceRoutingLength, + &DatagramAddress); + + } else if ((StHeader->Command == ST_CMD_INFORMATION) || + (StHeader->Command == ST_CMD_DISCONNECT)) { + + // + // If successful this adds a connection reference. + // + + if (!(Connection = StFindConnection(DeviceContext, StHeader->Destination, StHeader->Source))) { + return NDIS_STATUS_NOT_RECOGNIZED; + } + + + if (StHeader->Command == ST_CMD_INFORMATION) { + + Status = StProcessIIndicate ( + Connection, + StHeader, + StSize, + PacketSize, + ReceiveContext, + (BOOLEAN)((StHeader->Flags & ST_FLAGS_LAST) != 0) + ); + + if (Status != STATUS_MORE_PROCESSING_REQUIRED) { + StDereferenceConnection ("Information done", Connection); + } else { + Status = STATUS_SUCCESS; + } + + } else { + + StStopConnection (Connection, STATUS_REMOTE_DISCONNECT); + StDereferenceConnection ("Disconnect done", Connection); + Status = STATUS_SUCCESS; + + } + + } else { + + // + // An unrecognized frame. + // + + Status = STATUS_SUCCESS; + + } + + + // + // If the above routines return success, the packet has been processed + // and can be discarded. If they return anything else, the packet needs + // to be copied to local storage for handling in a more lesurely + // fashion. + // + + if (Status != STATUS_MORE_PROCESSING_REQUIRED) { + return NDIS_STATUS_SUCCESS; + } + + linkage = ExInterlockedPopEntryList( + &DeviceContext->ReceivePacketPool, + &DeviceContext->Interlock); + + if (linkage != NULL) { + NdisPacket = CONTAINING_RECORD( linkage, NDIS_PACKET, ProtocolReserved[0] ); + } else { + (VOID)InterlockedIncrement((PLONG)&DeviceContext->ReceivePacketExhausted); + + return NDIS_STATUS_RESOURCES; + } + 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, + (PSINGLE_LIST_ENTRY)&ReceiveTag->Linkage, + &DeviceContext->Interlock); + (VOID)InterlockedIncrement((PLONG)&DeviceContext->ReceiveBufferExhausted); + + return NDIS_STATUS_RESOURCES; + } + + NdisAdjustBufferLength (BufferTag->NdisBuffer, PacketSize); + NdisChainBufferAtFront (NdisPacket, (PNDIS_BUFFER)BufferTag->NdisBuffer); + + // + // DatagramAddress has a reference added already. + // + + 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 + // + + ReceiveTag->NdisStatus = NDIS_STATUS_PENDING; + ReceiveTag->PacketType = TYPE_AT_COMPLETE; + + ExInterlockedInsertTailList( + &DeviceContext->ReceiveInProgress, + &ReceiveTag->Linkage, + &DeviceContext->SpinLock); + + // + // receive packet is mapped at initalize + // + + NdisTransferData ( + &NdisStatus, + DeviceContext->NdisBindingHandle, + ReceiveContext, + 0, + PacketSize, + NdisPacket, + &BytesTransferred); + + // + // handle the various error codes + // + + switch (NdisStatus) { + case NDIS_STATUS_SUCCESS: // received packet + ReceiveTag->NdisStatus = NDIS_STATUS_SUCCESS; + if (BytesTransferred == PacketSize) { // Did we get the entire packet? + return NDIS_STATUS_SUCCESS; + } + break; + + case NDIS_STATUS_PENDING: // waiting async complete from NdisTransferData + return NDIS_STATUS_SUCCESS; + 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 + // + + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + RemoveEntryList (&ReceiveTag->Linkage); + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + ReceiveTag->PacketType = TYPE_AT_INDICATE; + + ExInterlockedPushEntryList( + &DeviceContext->ReceivePacketPool, + (PSINGLE_LIST_ENTRY)&ReceiveTag->Linkage, + &DeviceContext->Interlock); + + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + NdisQueryBuffer (NdisBuffer, &BufferPointer, &NdisBufferLength); + BufferTag = CONTAINING_RECORD ( + BufferPointer, + BUFFER_TAG, + Buffer[0] + ); + NdisAdjustBufferLength (NdisBuffer, BufferTag->Length); // reset to good value + + ExInterlockedPushEntryList( + &DeviceContext->ReceiveBufferPool, + &BufferTag->Linkage, + &DeviceContext->Interlock); + + if (DatagramAddress) { + StDereferenceAddress ("DG TransferData failed", DatagramAddress); + } + + return NDIS_STATUS_FAILURE; + +} // StReceiveIndication + + + +VOID +StTransferDataComplete ( + 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; + PRECEIVE_PACKET_TAG ReceiveTag; + PTP_CONNECTION Connection; + PNDIS_BUFFER NdisBuffer; + KIRQL oldirql, cancelirql; + + // + // 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. + // + + 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: // normal handling + ReceiveTag->NdisStatus = NdisStatus; + break; + + case TYPE_AT_INDICATE: + + DeviceContext = (PDEVICE_CONTEXT)BindingContext; + Connection = ReceiveTag->Connection; + + // + // The transfer for this packet is complete. Was it successful?? + // + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + + ULONG DumpData[1]; + DumpData[0] = BytesTransferred; + + StWriteGeneralErrorLog( + DeviceContext, + EVENT_TRANSPORT_TRANSFER_DATA, + 603, + NdisStatus, + NULL, + 1, + DumpData); + + // + // Drop the packet. BUGBUG: The driver should recover + // from this, but this transport has no way to cause + // the remote to resend. + // + + } + + // + // Now dereference the request to say we've got no more local + // references to the memory owned by it. + // + + Connection->CurrentReceiveRequest->IoRequestPacket->IoStatus.Information += BytesTransferred; + StDereferenceRequest ("TransferData complete", Connection->CurrentReceiveRequest); + + // + // see if we've completed the current receive. If so, move to the next one. + // + + if (ReceiveTag->CompleteReceive) { + + if (ReceiveTag->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_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + Connection->Flags2 |= CONNECTION_FLAGS2_RC_PENDING; + + } else { + + // + // If there is a receive posted, make it current and + // send a receive outstanding. + // + + ActivateReceive (Connection); + + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + } + + // + // NOTE: This releases the cancel and connection locks. + // + + CompleteReceive (Connection, ReceiveTag->EndOfMessage, oldirql, cancelirql); + + } + + // + // dereference the connection to say we've done the I frame processing. + // This reference was done before calling NdisTransferData. + // + + if (ReceiveTag->TransferDataPended) { + StDereferenceConnection("TransferData done", 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); + } + + + ExInterlockedPushEntryList( + &DeviceContext->ReceivePacketPool, + (PSINGLE_LIST_ENTRY)&ReceiveTag->Linkage, + &DeviceContext->Interlock); + + break; + + default: + + break; + } + + return; + +} /* StTransferDataComplete */ + + +VOID +StReceiveComplete ( + 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. + ST uses the DeviceContext for this parameter. + +Return Value: + + None + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + NTSTATUS Status; + KIRQL oldirql, oldirql1; + PLIST_ENTRY linkage; + PNDIS_PACKET NdisPacket; + PNDIS_BUFFER NdisBuffer; + UINT NdisBufferLength; + PVOID BufferPointer; + PRECEIVE_PACKET_TAG ReceiveTag; + PBUFFER_TAG BufferTag; + PTP_ADDRESS Address; + PIRP Irp; + PIO_STACK_LOCATION IrpSp; + PTP_CONNECTION Connection; + + + DeviceContext = (PDEVICE_CONTEXT) BindingContext; + + // + // Complete all pending receives. Do a quick check + // without the lock. + // + + while (!IsListEmpty (&DeviceContext->IrpCompletionQueue)) { + + linkage = ExInterlockedRemoveHeadList( + &DeviceContext->IrpCompletionQueue, + &DeviceContext->SpinLock); + + if (linkage != NULL) { + + Irp = CONTAINING_RECORD (linkage, IRP, Tail.Overlay.ListEntry); + IrpSp = IoGetCurrentIrpStackLocation (Irp); + + Connection = (PTP_CONNECTION)IrpSp->FileObject->FsContext; + + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql1); + + if (Connection->Flags2 & CONNECTION_FLAGS2_RC_PENDING) { + Connection->Flags2 &= ~CONNECTION_FLAGS2_RC_PENDING; + } + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql1); + + StDereferenceConnection ("receive completed", Connection); + + } else { + + // + // ExInterlockedRemoveHeadList returned NULL, so don't + // bother looping back. + // + + break; + + } + + } + + + // + // Packetize all waiting connections + // + + if (!IsListEmpty(&DeviceContext->PacketizeQueue)) { + + PacketizeConnections (DeviceContext); + + } + + + // + // Get every waiting packet, in order... + // + + + if (!IsListEmpty (&DeviceContext->ReceiveInProgress)) { + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + while (!IsListEmpty (&DeviceContext->ReceiveInProgress)) { + + linkage = RemoveHeadList (&DeviceContext->ReceiveInProgress); + NdisPacket = CONTAINING_RECORD( linkage, NDIS_PACKET, ProtocolReserved[0]); + + // + // 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. + // + + ReceiveTag = (PRECEIVE_PACKET_TAG)(NdisPacket->ProtocolReserved); + if (ReceiveTag->NdisStatus == NDIS_STATUS_PENDING) { + InsertHeadList (&DeviceContext->ReceiveInProgress, linkage); + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + return; + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + if (ReceiveTag->NdisStatus != NDIS_STATUS_SUCCESS) { + goto FreePacket; // skip the packet, continue with while loop + } + + NdisQueryPacket (NdisPacket, NULL, NULL, &NdisBuffer, NULL); + + // + // Have a packet. Since I allocated the storage for it, I know it's + // virtually contiguous and can treat it that way, which I will + // henceforth. + // + + NdisQueryBuffer (NdisBuffer, &BufferPointer, &NdisBufferLength); + + // + // Determine what address this is for, which is stored + // in the buffer tag header. + // + + BufferTag = CONTAINING_RECORD( BufferPointer, BUFFER_TAG, Buffer[0]); + 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. + // + + ASSERT (Address != NULL); + + // + // Indicate it or complete posted datagrams. + // + + Status = StIndicateDatagram ( + DeviceContext, + Address, + BufferPointer, + NdisBufferLength); + + + // + // Dereference the address. + // + + StDereferenceAddress ("Datagram done", Address); + + // + // Finished with packet; return to pool. + // + +FreePacket:; + + NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer); + ReceiveTag->PacketType = TYPE_AT_INDICATE; + + ExInterlockedPushEntryList( + &DeviceContext->ReceivePacketPool, + (PSINGLE_LIST_ENTRY)&ReceiveTag->Linkage, + &DeviceContext->Interlock); + + NdisAdjustBufferLength (NdisBuffer, BufferTag->Length); + ExInterlockedPushEntryList( + &DeviceContext->ReceiveBufferPool, + &BufferTag->Linkage, + &DeviceContext->Interlock); + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + } // if queue not empty + + return; + +} /* StReceiveComplete */ + diff --git a/private/ntos/tdi/st/info.c b/private/ntos/tdi/st/info.c new file mode 100644 index 000000000..2a2122753 --- /dev/null +++ b/private/ntos/tdi/st/info.c @@ -0,0 +1,865 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + info.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiQueryInformation + o TdiSetInformation + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "st.h" + + +// +// Useful macro to obtain the total length of an MDL chain. +// + +#define StGetMdlChainLength(Mdl, Length) { \ + PMDL _Mdl = (Mdl); \ + *(Length) = 0; \ + while (_Mdl) { \ + *(Length) += MmGetMdlByteCount(_Mdl); \ + _Mdl = _Mdl->Next; \ + } \ +} + +// +// Local functions used to satisfy various requests. +// + +VOID +StStoreProviderStatistics( + IN PDEVICE_CONTEXT DeviceContext, + IN PTDI_PROVIDER_STATISTICS ProviderStatistics + ); + +VOID +StStoreAdapterStatus( + IN PDEVICE_CONTEXT DeviceContext, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + IN PVOID StatusBuffer + ); + +VOID +StStoreNameBuffers( + IN PDEVICE_CONTEXT DeviceContext, + IN PVOID Buffer, + IN ULONG BufferLength, + IN ULONG NamesToSkip, + OUT PULONG NamesWritten, + OUT PULONG TotalNameCount OPTIONAL, + OUT PBOOLEAN Truncated + ); + + +NTSTATUS +StTdiQueryInformation( + 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; + LARGE_INTEGER timeout = {0,0}; + PTP_CONNECTION Connection; + PTP_ADDRESS_FILE AddressFile; + PTP_ADDRESS Address; + struct { + ULONG ActivityCount; + TA_NETBIOS_ADDRESS TaAddressBuffer; + } AddressInfo; + ULONG NamesWritten, TotalNameCount, BytesWritten; + PLIST_ENTRY p; + KIRQL oldirql; + BOOLEAN Truncated; + BOOLEAN UsedConnection; + + // + // what type of status do we want? + // + + irpSp = IoGetCurrentIrpStackLocation (Irp); + + query = (PTDI_REQUEST_KERNEL_QUERY_INFORMATION)&irpSp->Parameters; + + switch (query->QueryType) { + + case TDI_QUERY_CONNECTION_INFO: + + // + // Connection info is queried on a connection, + // verify this. + // + + Connection = irpSp->FileObject->FsContext; + + status = StVerifyConnectionObject (Connection); + + if (!NT_SUCCESS (status)) { + return status; + } + + ConnectionInfo = ExAllocatePool ( + NonPagedPool, + sizeof (TDI_CONNECTION_INFO)); + + if (ConnectionInfo == NULL) { + + PANIC ("StQueryInfo: Cannot allocate connection info!\n"); + StWriteResourceErrorLog (DeviceContext, sizeof(TDI_CONNECTION_INFO), 6); + status = STATUS_INSUFFICIENT_RESOURCES; + + } else if ((Connection->Flags & CONNECTION_FLAGS_STOPPING) != 0) { + + status = Connection->Status; + ExFreePool (ConnectionInfo); + + } else if ((Connection->Flags & CONNECTION_FLAGS_READY) == 0) { + + status = STATUS_INVALID_CONNECTION; + ExFreePool (ConnectionInfo); + + } else { + + RtlZeroMemory ((PVOID)ConnectionInfo, sizeof(TDI_CONNECTION_INFO)); + + // + // Fill in connection information here. + // + + status = TdiCopyBufferToMdl ( + (PVOID)ConnectionInfo, + 0L, + sizeof(TDI_CONNECTION_INFO), + Irp->MdlAddress, + 0, + &(Irp->IoStatus.Information)); + + ExFreePool (ConnectionInfo); + } + + StDereferenceConnection ("query connection info", Connection); + + break; + + case TDI_QUERY_ADDRESS_INFO: + + // + // Information about an address, can also be queried on a + // connection object to get information about its address. + // + + if (irpSp->FileObject->FsContext2 == (PVOID)TDI_TRANSPORT_ADDRESS_FILE) { + + AddressFile = irpSp->FileObject->FsContext; + + status = StVerifyAddressObject(AddressFile); + + if (!NT_SUCCESS (status)) { + return status; + } + + UsedConnection = FALSE; + + } else if (irpSp->FileObject->FsContext2 == (PVOID)TDI_CONNECTION_FILE) { + + Connection = irpSp->FileObject->FsContext; + + status = StVerifyConnectionObject (Connection); + + if (!NT_SUCCESS (status)) { + 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) { + + StDereferenceConnection ("query address info", Connection); + + } else { + + StDereferenceAddress ("query address info", Address); + + } + + break; + + case TDI_QUERY_BROADCAST_ADDRESS: + + // + // for this provider, the broadcast address is a zero byte name, + // contained in a Transport address structure. + // + + broadcastAddress = ExAllocatePool ( + NonPagedPool, + sizeof (TA_NETBIOS_ADDRESS)); + if (broadcastAddress == NULL) { + PANIC ("StQueryInfo: Cannot allocate broadcast address!\n"); + StWriteResourceErrorLog (DeviceContext, sizeof(TA_NETBIOS_ADDRESS), 2); + 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: + + StGetMdlChainLength (Irp->MdlAddress, &TargetBufferLength); + + if (TargetBufferLength < sizeof(TDI_PROVIDER_STATISTICS) + ((ST_TDI_RESOURCES-1) * sizeof(TDI_PROVIDER_RESOURCE_STATS))) { + + Irp->IoStatus.Information = 0; + status = STATUS_BUFFER_OVERFLOW; + + } else { + + ProviderStatistics = ExAllocatePool( + NonPagedPool, + sizeof(TDI_PROVIDER_STATISTICS) + + ((ST_TDI_RESOURCES-1) * sizeof(TDI_PROVIDER_RESOURCE_STATS))); + + if (ProviderStatistics == NULL) { + + PANIC ("StQueryInfo: Cannot allocate provider statistics!\n"); + StWriteResourceErrorLog (DeviceContext, sizeof(TDI_PROVIDER_STATISTICS), 7); + status = STATUS_INSUFFICIENT_RESOURCES; + + } else { + + StStoreProviderStatistics (DeviceContext, ProviderStatistics); + + status = TdiCopyBufferToMdl ( + (PVOID)ProviderStatistics, + 0L, + sizeof(TDI_PROVIDER_STATISTICS) + + ((ST_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: + + StGetMdlChainLength (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. + // + + if ((query->RequestConnectionInformation != NULL) && + (!RtlEqualMemory( + ((PTA_NETBIOS_ADDRESS)(query->RequestConnectionInformation->RemoteAddress))-> + Address[0].Address[0].NetbiosName, + DeviceContext->ReservedNetBIOSAddress, + NETBIOS_NAME_LENGTH))) { + + // + // Remote, not supported here. + // + + status = STATUS_NOT_IMPLEMENTED; + + } else { + + // + // Local. + // + + adapterStatus = ExAllocatePool ( + NonPagedPool, + TargetBufferLength); + + if (adapterStatus == NULL) { + PANIC("StQueryInfo: PANIC! Could not allocate adapter status buffer\n"); + StWriteResourceErrorLog (DeviceContext, TargetBufferLength, 3); + return STATUS_INSUFFICIENT_RESOURCES; + } + + StStoreAdapterStatus ( + DeviceContext, + NULL, + 0, + adapterStatus); + + StStoreNameBuffers ( + 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: + + // + // Find name, not supported here. + // + + status = STATUS_NOT_IMPLEMENTED; + break; + + default: + status = STATUS_INVALID_DEVICE_REQUEST; + break; + } + + return status; + +} /* StTdiQueryInformation */ + +// +// 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 +StStoreProviderStatistics( + 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. + +--*/ + +{ + + ProviderStatistics->Version = 0x0100; + + // + // Copy all the statistics from OpenConnections to WastedSpace + // Packets in one move. + // + + RtlCopyMemory( + (PVOID)&(ProviderStatistics->OpenConnections), + (PVOID)&(DeviceContext->OpenConnections), + sizeof(TDI_PROVIDER_STATISTICS)); + + // + // Copy the resource statistics. + // + + ProviderStatistics->NumberOfResources = ST_TDI_RESOURCES; + + STORE_RESOURCE_STATS_1 (0, 12, Address); + STORE_RESOURCE_STATS_1 (1, 13, AddressFile); + STORE_RESOURCE_STATS_1 (2, 14, Connection); + STORE_RESOURCE_STATS_1 (3, 15, Request); + + STORE_RESOURCE_STATS_2 (4, 22, Packet); + STORE_RESOURCE_STATS_2 (5, 23, ReceivePacket); + STORE_RESOURCE_STATS_2 (6, 24, ReceiveBuffer); + +} /* StStoreProviderStatistics */ + + +VOID +StStoreAdapterStatus( + 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; StStoreNameBuffers 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 = 0; + AdapterStatus->frmr_xmit = 0; + + AdapterStatus->recv_buff_unavail = (WORD)(DeviceContext->ReceivePacketExhausted + DeviceContext->ReceiveBufferExhausted); + AdapterStatus->xmit_buf_unavail = (WORD)DeviceContext->PacketExhausted; + + AdapterStatus->xmit_success = (WORD)(DeviceContext->IFramesSent - DeviceContext->IFramesResent); + AdapterStatus->recv_success = (WORD)DeviceContext->IFramesReceived; + AdapterStatus->iframe_recv_err = (WORD)DeviceContext->IFramesRejected; + AdapterStatus->iframe_xmit_err = (WORD)DeviceContext->IFramesResent; + + AdapterStatus->t1_timeouts = 0; + AdapterStatus->ti_timeouts = 0; + AdapterStatus->xmit_aborts = 0; + + + AdapterStatus->free_ncbs = 0xffff; + AdapterStatus->max_cfg_ncbs = 0xffff; + AdapterStatus->max_ncbs = 0xffff; + AdapterStatus->pending_sess = (WORD)DeviceContext->OpenConnections; + AdapterStatus->max_cfg_sess = 0xffff; + AdapterStatus->max_sess = 0xffff; + + + MacReturnMaxDataSize( + &DeviceContext->MacInfo, + SourceRouting, + SourceRoutingLength, + DeviceContext->MaxSendPacketSize, + &MaxUserData); + + AdapterStatus->max_dgram_size = (WORD)(MaxUserData - sizeof(ST_HEADER)); + AdapterStatus->max_sess_pkt_size = (WORD)(MaxUserData - sizeof(ST_HEADER)); + + return; + +} /* StStoreAdapterStatus */ + + +VOID +StStoreNameBuffers( + 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; + +} /* StStoreNameBuffers */ + + +NTSTATUS +StTdiSetInformation( + 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); + + return STATUS_NOT_IMPLEMENTED; + +} /* StTdiQueryInformation */ + diff --git a/private/ntos/tdi/st/makefile b/private/ntos/tdi/st/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/ntos/tdi/st/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/st/oemnxpts.inf b/private/ntos/tdi/st/oemnxpts.inf new file mode 100644 index 000000000..da0cdaab7 --- /dev/null +++ b/private/ntos/tdi/st/oemnxpts.inf @@ -0,0 +1,446 @@ +[Identification] + OptionType = NetTransport +[Options] + ST +[FileConstants] +UtilityInf = "UTILITY.INF" +subroutineinf = "SUBROUTN.INF" +SoftwareType = "transport" +Exit_Code = 0 +NetEventDLL = "%SystemRoot%\System32\netevent.dll" +Manufacturer = "Microsoft" +ProductMajorVersion = "3" +ProductMinorVersion = "1" +ProductVersion = $(ProductMajorVersion)"."$(ProductMinorVersion) +ProductSoftwareName = "St" +ProductSoftwareImagePath = "\SystemRoot\System32\drivers\st.sys" +NetRuleSoftwareType = "st netBiosTransport" +NetRuleSoftwareClass = {"netBiosTransport"} +NetRuleSoftwareUse = $(SoftwareType)" yes yes" +NetRuleSoftwareBindForm = """St"" yes yes simple" +ProductKeyName = $(!NTN_SoftwareBase)"\"$(Manufacturer)"\"$(ProductSoftwareName)"\CurrentVersion" +ParamKeyName = $(!NTN_ServiceBase)"\"$(ProductSoftwareName)"\Parameters" +[GeneralConstants] +from = "" +to = "" +ExitCodeOk = 0 +ExitCodeCancel = 1 +ExitCodeFatal = 2 +KeyNull = "" +MAXIMUM_ALLOWED = 33554432 +RegistryErrorIndex = NO_ERROR +KeyProduct = "" +KeyParameters = "" +TRUE = 1 +FALSE = 0 +NoTitle = 0 +ExitState = "Active" +OldVersionExisted = $(FALSE) +DriverPath = $(!STF_NTPATH)\drivers +[date] + Now = {} ? $(!LIBHANDLE) GetSystemDate +[Identify] + read-syms Identification + set Status = STATUS_SUCCESSFUL + set Identifier = $(OptionType) + set Media = #("Source Media Descriptions", 1, 1) + Return $(Status) $(Identifier) $(Media) +[ReturnOptions] + set Status = STATUS_FAILED + set OptionList = {} + set OptionTextList = {} + set LanguageList = ^(LanguagesSupported, 1) + Ifcontains(i) $($0) in $(LanguageList) + goto returnoptions + else + set Status = STATUS_NOLANGUAGE + goto finish_ReturnOptions + endif +returnoptions = + + set OptionList = ^(Options, 1) + set OptionTextList = ^(OptionsText$($0), 1) + set Status = STATUS_SUCCESSFUL +finish_ReturnOptions = + + Return $(Status) $(OptionList) $(OptionTextList) +[InstallOption] + set Option = $($1) + set SrcDir = $($2) + set AddCopy = $($3) + set DoCopy = $($4) + set DoConfig = $($5) + set LanguageList = ^(LanguagesSupported, 1) + Ifcontains(i) $($0) NOT-IN $(LanguageList) + Return STATUS_NOLANGUAGE + endif + Debug-Output "OEMNXPNB.INF: STF_CWDDIR is: "$(!STF_CWDDIR) + Debug-Output "OEMNXPNB.INF: STF_LANGUAGE is: "$(!STF_LANGUAGE) + set-subst LF = "\n" + read-syms GeneralConstants + read-syms FileConstants + read-syms DialogConstants$(!STF_LANGUAGE) + ifstr(i) $(!NTN_Origination) == "NCPA" + set Continue = $(OK) + endif + read-syms FileConstants$(!STF_LANGUAGE) + detect date + set-title $(FunctionTitle) + set to = Begin + set from = Begin + set CommonStatus = STATUS_SUCCESSFUL + EndWait +Begin = + + Ifstr(i) $(!NTN_InstallMode) == deinstall + set StartLabel = removeadapter + else-Ifstr(i) $(!NTN_InstallMode) == Update + set StartLabel = UpgradeSoftware + else-Ifstr(i) $(!NTN_InstallMode) == bind + set StartLabel = bindingadapter + else-Ifstr(i) $(!NTN_InstallMode) == configure + Shell $(UtilityInf),RegistryErrorString,CANNOT_CONFIGURE_SOFTWARE + ifint $($ShellCode) != $(!SHELL_CODE_OK) + Debug-Output "OEMNXPNB.INF: ShellCode error: cannot get an error string." + goto ShellCodeError + endif + set Error = $($R0) + set from = end + set to = end + goto nonfatalinfo + else + set StartLabel = installadapter + endif + set RadioDefault = 2 + set RadioIn = {$(RadioDefault)} + set Size = 2 + set from = $(fatal) + set to = $(fatal) + goto $(StartLabel) +installadapter = + + OpenRegKey $(!REG_H_LOCAL) "" $(ProductKeyName) $(MAXIMUM_ALLOWED) KeyProduct + Ifstr $(KeyProduct) != $(KeyNull) + CloseRegKey $(KeyProduct) + Shell $(UtilityInf), VerExistedDlg, $(ProductSoftwareTitle),+ + $(ProductVersion) + ifint $($ShellCode) != $(!SHELL_CODE_OK) + Debug-Output "ShellCode error: cannot get an error string." + goto ShellCodeError + endif + goto end + endif + CloseRegKey $(KeyProduct) + goto installproduct +installproduct = + + StartWait + ifint $(OldVersionExisted) == $(FALSE) + Ifstr(i) $(DoCopy) == "YES" + Shell $(UtilityInf), DoAskSource, $(!STF_CWDDIR), $(SrcDir) YES + Ifint $($ShellCode) != $(!SHELL_CODE_OK) + Goto ShellCodeError + Else-Ifstr(i) $($R0) == STATUS_FAILED + Shell $(UtilityInf) RegistryErrorString "ASK_SOURCE_FAIL" + ifint $($ShellCode) != $(!SHELL_CODE_OK) + goto ShellCodeError + endif + set Error = $($R0) + Goto fatal + Else-Ifstr(i) $($R0) == STATUS_USERCANCEL + Goto successful + Endif + Set SrcDir = $($R1) + Endif + install "Install-Option" + ifstr(i) $(STF_INSTALL_OUTCOME) != STF_SUCCESS + Shell $(UtilityInf) RegistryErrorString "UNABLE_COPY_FILE" + ifint $($ShellCode) != $(!SHELL_CODE_OK) + goto ShellCodeError + endif + set Error = $($R0) + goto fatal + endif + set OEM_ABANDON_ON = TRUE + Shell $(UtilityInf), AddSoftwareComponent, $(Manufacturer), + + $(ProductSoftwareName), + + $(ProductSoftwareName), + + $(ProductSoftwareDisplayName), $(STF_CONTEXTINFNAME), + + $(ProductSoftwareImagePath), "kernel", "TDI", {}, "",+ + $(NetEventDLL) + set RegistryErrorIndex = $($R0) + Ifstr(i) $(RegistryErrorIndex) != NO_ERROR + EndWait + CloseRegKey $($R1) + CloseRegKey $($R2) + CloseRegKey $($R3) + CloseRegKey $($R4) + CloseRegKey $($R5) + goto fatalRegistry + endif + Set SoftProductKey = $($R1) + Set SoftNetRuleKey = $($R2) + Set SoftServiceKey = $($R3) + set KeyParameters = $($R4) + Set SoftLinkageKey = $($R5) + set NewValueList = {{SoftwareType,$(NoTitle),$(!REG_VT_SZ),$(SoftwareType)},+ + {MajorVersion,$(NoTitle),$(!REG_VT_DWORD),$(ProductMajorVersion)},+ + {MinorVersion,$(NoTitle),$(!REG_VT_DWORD),$(ProductMinorVersion)},+ + {Title,$(NoTitle),$(!REG_VT_SZ),$(ProductSoftwareTitle)},+ + {Description,$(NoTitle),$(!REG_VT_SZ),$(ProductSoftwareDescription)},+ + {ServiceName,$(NoTitle),$(!REG_VT_SZ),$(ProductSoftwareName)},+ + {InstallDate,$(NoTitle),$(!REG_VT_DWORD),*($(Now),1)}} + Shell $(UtilityInf), AddValueList, $(SoftProductKey), $(NewValueList) + set RegistryErrorIndex = $($R0) + Ifstr $(RegistryErrorIndex) != NO_ERROR + CloseRegKey $(SoftProductKey) + CloseRegKey $(SoftNetRuleKey) + CloseRegKey $(SoftServiceKey) + CloseRegKey $(SoftLinkageKey) + CloseRegKey $(KeyParameters) + goto fatalRegistry + endif + set NewValueList = {{type ,$(NoTitle),$(!REG_VT_SZ),$(NetRuleSoftwareType)}, + + {use ,$(NoTitle),$(!REG_VT_SZ),$(NetRuleSoftwareUse)}, + + {class,$(NoTitle),$(!REG_VT_MULTI_SZ),$(NetRuleSoftwareClass)}, + + {bindform,$(NoTitle),$(!REG_VT_SZ),$(NetRuleSoftwareBindForm)}, + + {InfOption,$(NoTitle),$(!REG_VT_SZ),$(Option)}} + Shell $(UtilityInf), AddValueList, $(SoftNetRuleKey), $(NewValueList) + set RegistryErrorIndex = $($R0) + Ifstr $(RegistryErrorIndex) != NO_ERROR + CloseRegKey $(SoftProductKey) + CloseRegKey $(SoftNetRuleKey) + CloseRegKey $(SoftServiceKey) + CloseRegKey $(SoftLinkageKey) + CloseRegKey $(KeyParameters) + goto fatalRegistry + endif + Set NewValueList = {{NbProvider,$(NoTitle),$(!REG_VT_SZ),"_nb"}} + Shell $(UtilityInf), AddValueList, $(KeyParameters), $(NewValueList) + Ifstr $(RegistryErrorIndex) != NO_ERROR + CloseRegKey $(SoftProductKey) + CloseRegKey $(SoftNetRuleKey) + CloseRegKey $(SoftServiceKey) + CloseRegKey $(SoftLinkageKey) + goto fatalRegistry + endif + CreateRegKey $(SoftServiceKey) {"Performance",$(NoTitle),GenericClass} "" + + $(MAXIMUM_ALLOWED) "" KeyPerformance + set NewValueList = {{Library,$(NoTitle),$(!REG_VT_SZ),"Perfctrs.dll"},+ + {Open,$(NoTitle),$(!REG_VT_SZ),"OpenNbfPerformanceData"},+ + {Collect,$(NoTitle),$(!REG_VT_SZ),"CollectNbfPerformanceData"},+ + {Close,$(NoTitle),$(!REG_VT_SZ),"CloseNbfPerformanceData"}} + Shell $(UtilityInf), AddValueList, $(KeyPerformance), $(NewValueList) + set RegistryErrorIndex = $($R0) + Ifstr $(RegistryErrorIndex) != NO_ERROR + CloseRegKey $(SoftProductKey) + CloseRegKey $(SoftNetRuleKey) + CloseRegKey $(SoftServiceKey) + CloseRegKey $(SoftLinkageKey) + CloseRegKey $(KeyParameters) + goto fatalRegistry + endif + CloseRegKey $(KeyPerformance) + CloseRegKey $(SoftProductKey) + CloseRegKey $(SoftNetRuleKey) + CloseRegKey $(SoftServiceKey) + CloseRegKey $(SoftLinkageKey) + endif + goto writeparameters +writeparameters = + + set NewValueList = {{Size,$(NoTitle),$(!REG_VT_DWORD),$(Size)}} + ifint $(OldVersionExisted) == $(TRUE) + OpenRegKey $(!REG_H_LOCAL) "" $(ParamKeyName) $(MAXIMUM_ALLOWED) KeyParameters + endif + Shell $(UtilityInf), AddValueList, $(KeyParameters), $(NewValueList) + set RegistryErrorIndex = $($R0) + CloseRegKey $(KeyParameters) + Ifstr(i) $(RegistryErrorIndex) != NO_ERROR + goto fatalRegistry + endif + EndWait + goto successful +bindingadapter =+ + set Error = "Binding: Sorry, not yet implemented." + goto fatal +removeadapter = + + Shell $(UtilityInf), RemoveSoftwareComponent, $(Manufacturer), + + $(ProductSoftwareName) + ifint $($ShellCode) != $(!SHELL_CODE_OK) + Debug-Output "ShellCode error" + goto ShellCodeError + endif + set RegistryErrorIndex = $($R0) + Ifstr(i) $(RegistryErrorIndex) != NO_ERROR + goto fatalregistry + endif + goto end +UpgradeSoftware = + + ifstr(i) $(ProductKeyName) == $(!NTN_RegBase) + OpenRegKey $(!REG_H_LOCAL) "" $(ProductKeyName) $(MAXIMUM_ALLOWED) KeyProduct + Ifstr $(KeyProduct) != $(KeyNull) + GetRegValue $(KeyProduct),"MajorVersion", VersionInfo + set Version = *($(VersionInfo), 4) + Shell $(UtilityInf), GetInfFileNameFromRegistry, $(KeyProduct) + ifint $($ShellCode) != $(!SHELL_CODE_OK) + Debug-Output "ShellCode error" + goto ShellCodeError + endif + set !UG_Filename = $($R0) + ifstr(i) $(!UG_Filename) != "" + install "Install-Update" + ifstr(i) $(STF_INSTALL_OUTCOME) != STF_SUCCESS + goto fatal + endif + endif + SetRegValue $(KeyProduct) {MajorVersion,$(NoTitle),$(!REG_VT_SZ),$(ProductMajorVersion)} + SetRegValue $(KeyProduct) {MinorVersion,$(NoTitle),$(!REG_VT_SZ),$(ProductMinorVersion)} + ifint $(Version) != $(ProductVersion) + endif + CloseRegKey $(KeyProduct) + else + goto fatalregistry + endif + endif + goto end +successful = + + goto end +warning = + + Shell $(subroutineinf) SetupMessage, $(!STF_LANGUAGE), "WARNING", $(Error) + ifint $($ShellCode) != $(!SHELL_CODE_OK) + goto ShellCodeError + endif + ifstr(i) $($R1) == "OK" + goto $(to) + else-ifstr(i) $($R1) == "CANCEL" + goto $(from) + else + goto "end" + endif +nonfatalinfo = + + Set CommonStatus = STATUS_USERCANCEL + Set Severity = STATUS + goto nonfatalmsg +nonfatal = + + Set Severity = NONFATAL + goto nonfatalmsg +nonfatalmsg = + + ifstr(i) $(Error) == "" + Shell $(UtilityInf) RegistryErrorString "SETUP_FAIL" + ifint $($ShellCode) != $(!SHELL_CODE_OK) + goto ShellCodeError + endif + set Error = $($R0) + endif + Shell $(subroutineinf) SetupMessage, $(!STF_LANGUAGE), $(Severity), $(Error) + ifint $($ShellCode) != $(!SHELL_CODE_OK) + goto ShellCodeError + endif + ifstr(i) $($R1) == "OK" + goto $(from) + else + goto "end" + endif +fatalregistry = + + Shell $(UtilityInf) RegistryErrorString $(RegistryErrorIndex) + ifint $($ShellCode) != $(!SHELL_CODE_OK) + goto ShellCodeError + endif + set Error = $($R0) + goto fatal +fatal = + + ifstr(i) $(Error) == "" + Shell $(UtilityInf) RegistryErrorString "SETUP_FAIL" + ifint $($ShellCode) != $(!SHELL_CODE_OK) + goto ShellCodeError + endif + set Error = $($R0) + endif + Shell $(subroutineinf) SetupMessage, $(!STF_LANGUAGE), "FATAL", $(Error) + ifint $($ShellCode) != $(!SHELL_CODE_OK) + goto ShellCodeError + endif + goto setfailed +ShellCodeError = + + set DlgType = "MessageBox" + set STF_MB_TITLE = $(ShellCodeErrorTitle) + set STF_MB_TEXT = $(ShellCodeErrorText) + set STF_MB_TYPE = 1 + set STF_MB_ICON = 3 + set STF_MB_DEF = 1 + ui start "Error Message" + goto setfailed +setfailed = + + set CommonStatus = STATUS_FAILED + ifstr(i) $(OEM_ABANDON_ON) == TRUE + set OEM_ABANDON_ON = FALSE + goto removeadapter + endif + goto end +end = + + goto term +term = + + Return $(CommonStatus) +[Install-Option] + set STF_VITAL = "" + ifstr(i) $(AddCopy) == "YES" + AddSectionFilesToCopyList Files-$(Option) $(SrcDir) $(!STF_WINDOWSSYSPATH)\drivers + endif + ifstr(i) $(DoCopy) == "YES" + set !STF_NCPA_FLUSH_COPYLIST = TRUE + CopyFilesInCopyList + endif + ifstr(i) $(DoConfig) == "YES" + endif + Exit +[Install-Update] + set STF_VITAL = "" + set STF_OVERWRITE = "VERIFYSOURCEOLDER" + AddSectionFilesToCopyList Files-$(Option) $(SrcDir) $(!STF_WINDOWSSYSPATH)\drivers + AddSectionFilesToCopyList Files-Inf $(SrcDir) $(!STF_WINDOWSSYSPATH) + set !STF_NCPA_FLUSH_COPYLIST = TRUE + CopyFilesInCopyList + exit +[Source Media Descriptions] + 1 = "Windows NT Setup Disk #1" , TAGFILE = disk1 + 2 = "Windows NT Setup CD-ROM Disk" , TAGFILE = disk2 +[ProductType] +STF_PRODUCT = Winnt +STF_PLATFORM = Mips +[Files-Inf] +2, oemsetup.inf, SIZE=1000, RENAME=$(!UG_Filename) +[Files-ST] +2,ST.SYS , SIZE=88888 +[LanguagesSupported] + ENG +[OptionsTextENG] + ST = "Sample Transport" +[FileConstantsENG] +ProCaption = "Windows NT Setup" +ProCancel = "Cancel" +ProCancelMsg = "Windows NT Networking is not correctly installed. "+ + "Are you sure you want to cancel copying files?" +ProCancelCap = "Network Setup Message" +ProText1 = "Copying:" +ProText2 = "To:" +FunctionTitle = "Sample Transport" +ProductSoftwareDescription = "Microsoft Sample TDI Transport" +ProductSoftwareDisplayName = "Sample Transport" +ProductSoftwareTitle = "Sample Transport" +ShellCodeErrorTitle = "Error: "$(FunctionTitle) +ShellCodeErrorText = "Shell Code Error." +[DialogConstantsENG] +Help = "&Help" +Exit = "Cancel" +OK = "OK" +HelpContext = "" +Continue = "Continue" +Cancel = "Cancel" +[FileDependentDlgENG] +GroupLabel = "Optimization:" +Radio1 = "&Minimize Memory Used" +Radio2 = "&Balance" +Radio3 = "M&aximize Throughput && Connections" +DlgType = "Radio" +DlgTemplate = "ST" +Caption = $(FunctionTitle) +OptionsGreyed = {} +HelpContext = $(!IDH_DB_OEMNXPNB_INS) + + + diff --git a/private/ntos/tdi/st/packet.c b/private/ntos/tdi/st/packet.c new file mode 100644 index 000000000..29e9fe1b8 --- /dev/null +++ b/private/ntos/tdi/st/packet.c @@ -0,0 +1,597 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + packet.c + +Abstract: + + This module contains code that implements the TP_PACKET object, which + describes an NDIS packet. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "st.h" + +// +// 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 + + + + +VOID +StAllocateSendPacket( + 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; + + if ((DeviceContext->MemoryLimit != 0) && + ((DeviceContext->MemoryUsage + DeviceContext->PacketLength) > + DeviceContext->MemoryLimit)) { + PANIC("ST: Could not allocate send packet: limit\n"); + StWriteResourceErrorLog (DeviceContext, DeviceContext->PacketLength, 107); + *TransportSendPacket = NULL; + return; + } + + Packet = (PTP_PACKET)ExAllocatePool (NonPagedPool, DeviceContext->PacketLength); + if (Packet == NULL) { + PANIC("ST: Could not allocate send packet: no pool\n"); + StWriteResourceErrorLog (DeviceContext, DeviceContext->PacketLength, 207); + *TransportSendPacket = NULL; + return; + } + RtlZeroMemory (Packet, DeviceContext->PacketLength); + + DeviceContext->MemoryUsage += DeviceContext->PacketLength; + + NdisAllocatePacket ( + &NdisStatus, + &NdisPacket, + DeviceContext->SendPacketPoolHandle); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + ExFreePool (Packet); + StWriteResourceErrorLog (DeviceContext, 0, 307); + *TransportSendPacket = NULL; + return; + } + + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + DeviceContext->NdisBufferPoolHandle, + Packet->Header, + DeviceContext->PacketHeaderLength); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + 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->Packet = Packet; + SendTag->Owner = NULL; + + Packet->Type = ST_PACKET_SIGNATURE; + Packet->Size = sizeof (TP_PACKET); + Packet->Provider = DeviceContext; + + ++DeviceContext->PacketAllocated; + + *TransportSendPacket = Packet; + +} /* StAllocateSendPacket */ + + +VOID +StDeallocateSendPacket( + 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; + +} /* StDeallocateSendPacket */ + + +VOID +StAllocateReceivePacket( + 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. + // + + NdisAllocatePacket ( + &NdisStatus, + &NdisPacket, + DeviceContext->ReceivePacketPoolHandle); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + StWriteResourceErrorLog (DeviceContext, 0, 309); + *TransportReceivePacket = NULL; + return; + } + + ReceiveTag = (PRECEIVE_PACKET_TAG)(NdisPacket->ProtocolReserved); + ReceiveTag->PacketType = TYPE_AT_INDICATE; + + ++DeviceContext->ReceivePacketAllocated; + + *TransportReceivePacket = NdisPacket; + +} /* StAllocateReceivePacket */ + + +VOID +StDeallocateReceivePacket( + 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; + +} /* StDeallocateReceivePacket */ + + +VOID +StAllocateReceiveBuffer( + 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("ST: Could not allocate receive buffer: limit\n"); + StWriteResourceErrorLog (DeviceContext, RECEIVE_BUFFER_QUOTA(DeviceContext), 108); + *TransportReceiveBuffer = NULL; + return; + } + + BufferTag = (PBUFFER_TAG)ExAllocatePool ( + NonPagedPoolCacheAligned, + DeviceContext->ReceiveBufferLength); + + if (BufferTag == NULL) { + PANIC("ST: Could not allocate receive buffer: no pool\n"); + StWriteResourceErrorLog (DeviceContext, DeviceContext->ReceiveBufferLength, 208); + *TransportReceiveBuffer = NULL; + return; + } + + DeviceContext->MemoryUsage += RECEIVE_BUFFER_QUOTA(DeviceContext); + + // + // point to the buffer for NDIS + // + + NdisAllocateBuffer( + &NdisStatus, + &NdisBuffer, + DeviceContext->NdisBufferPoolHandle, + BufferTag->Buffer, + DeviceContext->MaxReceivePacketSize); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + ExFreePool (BufferTag); + *TransportReceiveBuffer = NULL; + return; + } + + BufferTag->Length = DeviceContext->MaxReceivePacketSize; + BufferTag->NdisBuffer = NdisBuffer; + + ++DeviceContext->ReceiveBufferAllocated; + + *TransportReceiveBuffer = BufferTag; + +} /* StAllocateReceiveBuffer */ + + +VOID +StDeallocateReceiveBuffer( + 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); + +} /* StDeallocateReceiveBuffer */ + + +NTSTATUS +StCreatePacket( + PDEVICE_CONTEXT DeviceContext, + 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. + + 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; + + s = ExInterlockedPopEntryList ( + &DeviceContext->PacketPool, + &DeviceContext->Interlock); + + if (s == NULL) { + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + ++DeviceContext->PacketExhausted; + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + return STATUS_INSUFFICIENT_RESOURCES; + } + + ThePacket = CONTAINING_RECORD (s, TP_PACKET, Linkage); + + ThePacket->Provider = DeviceContext; // who owns this packet + ThePacket->PacketSent = FALSE; + ThePacket->PacketNoNdisBuffer = FALSE; + + *Packet = ThePacket; // return pointer to the packet. + return STATUS_SUCCESS; +} /* StCreatePacket */ + + +VOID +StDestroyPacket( + 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. + +--*/ + +{ + KIRQL oldirql1; + PDEVICE_CONTEXT DeviceContext; + PTP_CONNECTION Connection; + PLIST_ENTRY p; + PNDIS_BUFFER HeaderBuffer; + PNDIS_BUFFER NdisBuffer; + + + // + // Strip off and unmap the buffers describing data and header. + // + + NdisUnchainBufferAtFront (Packet->NdisPacket, &HeaderBuffer); + + // data buffers get thrown away + + 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. + // + + NdisReinitializePacket (Packet->NdisPacket); + + } else { + + // + // Return all the NDIS_BUFFERs to the system. + // + + NdisUnchainBufferAtFront (Packet->NdisPacket, &NdisBuffer); + while (NdisBuffer != NULL) { + NdisFreeBuffer (NdisBuffer); + NdisUnchainBufferAtFront (Packet->NdisPacket, &NdisBuffer); + } + + } + + ASSERT (HeaderBuffer != NULL); + NDIS_BUFFER_LINKAGE(HeaderBuffer) = (PNDIS_BUFFER)NULL; + + NdisChainBufferAtFront (Packet->NdisPacket, HeaderBuffer); + + + // + // Put the packet back for use again. + // + + DeviceContext = Packet->Provider; + + ExInterlockedPushEntryList ( + &DeviceContext->PacketPool, + (PSINGLE_LIST_ENTRY)&Packet->Linkage, + &DeviceContext->Interlock); + + // + // 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; + } + + p = ExInterlockedRemoveHeadList( + &DeviceContext->PacketWaitQueue, + &DeviceContext->SpinLock); + + if (p != NULL) { + + // + // Remove a connection from the "packet starved" queue. + // + + Connection = CONTAINING_RECORD (p, TP_CONNECTION, PacketWaitLinkage); + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql1); + Connection->Flags &= ~CONNECTION_FLAGS_SUSPENDED; + + // + // 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. + // + + Connection->SendState = CONNECTION_SENDSTATE_PACKETIZE; + + if (!(Connection->Flags & CONNECTION_FLAGS_STOPPING) && + !(Connection->Flags & CONNECTION_FLAGS_PACKETIZE)) { + + Connection->Flags |= CONNECTION_FLAGS_PACKETIZE; + + StReferenceConnection ("Packet available", Connection); + + ExInterlockedInsertTailList( + &DeviceContext->PacketizeQueue, + &Connection->PacketizeLinkage, + &DeviceContext->SpinLock); + } + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql1); + PacketizeConnections (DeviceContext); + + } + +} /* StDestroyPacket */ + diff --git a/private/ntos/tdi/st/rcv.c b/private/ntos/tdi/st/rcv.c new file mode 100644 index 000000000..b2f0e779d --- /dev/null +++ b/private/ntos/tdi/st/rcv.c @@ -0,0 +1,247 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + rcv.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiReceive + o TdiReceiveDatagram + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "st.h" + + +NTSTATUS +StTdiReceive( + 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, cancelirql; + PTP_REQUEST tpRequest; + LARGE_INTEGER timeout = {0,0}; + PIO_STACK_LOCATION irpSp; + PMDL ReceiveBuffer; + ULONG ReceiveBufferLength; + PTDI_REQUEST_KERNEL_RECEIVE parameters; + + // + // 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; + + status = StVerifyConnectionObject (connection); + + if (!NT_SUCCESS (status)) { + return status; + } + + // + // Initialize bytes transferred here. + // + + Irp->IoStatus.Information = 0; // reset byte transfer count. + + + parameters = (PTDI_REQUEST_KERNEL_RECEIVE)(&irpSp->Parameters); + ReceiveBuffer = Irp->MdlAddress; + ReceiveBufferLength =parameters->ReceiveLength; + + // + // Queue up this receive to the connection object. + // + + status = StCreateRequest ( + Irp, // IRP for this request. + connection, // context. + REQUEST_FLAGS_CONNECTION, // partial flags. + ReceiveBuffer, + ReceiveBufferLength, + timeout, + &tpRequest); + + // + // We have a request, now queue it. If the connection has gone south on us + // while we were getting things going, we will avoid actually queing the + // request. + // + + if (NT_SUCCESS (status)) { + + // This reference is removed by StDestroyRequest. + + StReferenceConnection("TdiReceive request", connection); + tpRequest->Owner = ConnectionType; + + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_SPIN_LOCK (&connection->SpinLock,&oldirql); + if ((connection->Flags & CONNECTION_FLAGS_STOPPING) != 0) { + RELEASE_SPIN_LOCK (&connection->SpinLock,oldirql); + IoReleaseCancelSpinLock(cancelirql); + StCompleteRequest ( + tpRequest, + connection->Status, + 0); + status = STATUS_PENDING; + } else { + + // + // Insert onto the receive queue, and make the IRP + // cancellable. + // + + InsertTailList (&connection->ReceiveQueue,&tpRequest->Linkage); + RELEASE_SPIN_LOCK (&connection->SpinLock,oldirql); + + // + // If this IRP has been cancelled, then call the + // cancel routine. + // + + if (Irp->Cancel) { + Irp->CancelIrql = cancelirql; + StCancelReceive((PDEVICE_OBJECT)(connection->Provider), Irp); + StDereferenceConnection ("IRP cancelled", connection); // release lookup hold. + return STATUS_PENDING; + } + + Irp->CancelRoutine = StCancelReceive; + IoReleaseCancelSpinLock(cancelirql); + + AwakenReceive (connection); // awaken if sleeping. + + status = STATUS_PENDING; + } + } + + StDereferenceConnection("temp TdiReceive", connection); + + return status; +} /* TdiReceive */ + + +NTSTATUS +StTdiReceiveDatagram( + 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; + PTP_REQUEST tpRequest; + LARGE_INTEGER timeout = {0,0}; + PIO_STACK_LOCATION irpSp; + PMDL ReceiveBuffer; + ULONG ReceiveBufferLength; + + // + // 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 = StVerifyAddressObject (addressFile); + + if (!NT_SUCCESS (status)) { + return status; + } + + address = addressFile->Address; + + ReceiveBuffer = Irp->MdlAddress; + ReceiveBufferLength = + ((PTDI_REQUEST_KERNEL_RECEIVEDG)(&irpSp->Parameters))->ReceiveLength; + + status = StCreateRequest ( + Irp, // IRP for this request. + address, // context + REQUEST_FLAGS_ADDRESS, // partial flags. + ReceiveBuffer, + ReceiveBufferLength, + timeout, + &tpRequest); + + if (NT_SUCCESS (status)) { + StReferenceAddress ("Receive datagram", address); + tpRequest->Owner = AddressType; + ACQUIRE_SPIN_LOCK (&address->SpinLock,&oldirql); + if ((address->Flags & ADDRESS_FLAGS_STOPPING) != 0) { + RELEASE_SPIN_LOCK (&address->SpinLock,oldirql); + StCompleteRequest (tpRequest, STATUS_NETWORK_NAME_DELETED, 0); + status = STATUS_PENDING; + } else { + InsertTailList (&addressFile->ReceiveDatagramQueue,&tpRequest->Linkage); + RELEASE_SPIN_LOCK (&address->SpinLock,oldirql); + } + + status = STATUS_PENDING; + } + + StDereferenceAddress ("Temp rcv datagram", address); + + return status; +} /* TdiReceiveDatagram */ + diff --git a/private/ntos/tdi/st/rcveng.c b/private/ntos/tdi/st/rcveng.c new file mode 100644 index 000000000..8e104a954 --- /dev/null +++ b/private/ntos/tdi/st/rcveng.c @@ -0,0 +1,383 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + rcveng.c + +Abstract: + + This module contains code that implements the receive engine for the + Sample transport provider. + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "st.h" + + +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. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql; + PTP_REQUEST Request; + + // + // 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_SPIN_LOCK (&Connection->SpinLock, &oldirql); + 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; + + Request = CONTAINING_RECORD ( + Connection->ReceiveQueue.Flink, + TP_REQUEST, + Linkage); + Connection->MessageBytesReceived = 0; + Connection->MessageBytesAcked = 0; + Connection->CurrentReceiveRequest = Request; + Connection->CurrentReceiveMdl = Request->Buffer2; + Connection->ReceiveLength = Request->Buffer2Length; + Connection->ReceiveByteOffset = 0; + } + } + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + +} /* 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. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql; + + // + // If the RECEIVE_WAKEUP bitflag is set, then awaken the connection. + // + + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + 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, and activate + // the next receive. + // + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + ActivateReceive (Connection); + + return; + } + } + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); +} /* AwakenReceive */ + + +VOID +CompleteReceive( + PTP_CONNECTION Connection, + BOOLEAN EndOfRecord, + KIRQL ConnectionIrql, + KIRQL CancelIrql + ) + +/*++ + +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 EndOfRecord flag will be set accordingly + by the caller to indicate that a message boundary was received. + + NOTE: This function is called with the connection and cancel + IRQLs held, and returns with them released. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + + EndOfRecord - BOOLEAN set to true if TDI_END_OF_RECORD should be reported. + + ConnectionIrql - The IRQL at which the connection spinlock was acquired. + + CancelIrql - The IRQL at which the cancel spinlock was acquired. + +Return Value: + + none. + +--*/ + +{ + PLIST_ENTRY p; + PTP_REQUEST Request; + KIRQL oldirql = ConnectionIrql; + KIRQL cancelirql = CancelIrql; + ULONG BytesReceived; + + + if (IsListEmpty (&Connection->ReceiveQueue)) { + + ASSERT ((Connection->Flags & CONNECTION_FLAGS_STOPPING) != 0); + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + IoReleaseCancelSpinLock(cancelirql); + return; + } + + Connection->Flags &= ~CONNECTION_FLAGS_ACTIVE_RECEIVE; + BytesReceived = Connection->MessageBytesReceived; + + + // + // Complete the TdiReceive request at the head of the + // connection's ReceiveQueue. + // + + p = RemoveHeadList (&Connection->ReceiveQueue); + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + + Request->IoRequestPacket->CancelRoutine = (PDRIVER_CANCEL)NULL; + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + IoReleaseCancelSpinLock(cancelirql); + + + Request->Flags |= REQUEST_FLAGS_DELAY; + + StCompleteRequest( + Request, + EndOfRecord ? STATUS_SUCCESS : STATUS_BUFFER_OVERFLOW, + BytesReceived); + +} /* CompleteReceive */ + + +VOID +StCancelReceive( + 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; + PTP_CONNECTION Connection; + PTP_REQUEST Request; + 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->SpinLock, &oldirql); + + BytesReceived = Connection->MessageBytesReceived; + + p = Connection->ReceiveQueue.Flink; + + // + // If there is a receive active, then see if this is it. + // + + if ((Connection->Flags & CONNECTION_FLAGS_ACTIVE_RECEIVE) != 0) { + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + + if (Request->IoRequestPacket == 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->Flags2 |= CONNECTION_FLAGS2_RCV_CANCELLED; + Connection->Flags &= ~CONNECTION_FLAGS_ACTIVE_RECEIVE; + + (VOID)RemoveHeadList (&Connection->ReceiveQueue); + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + IoReleaseCancelSpinLock (Irp->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. + // + + StCompleteRequest (Request, 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) { + + Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + if (Request->IoRequestPacket == Irp) { + + // + // Found it, remove it from the list here. + // + + RemoveEntryList (p); + Found = TRUE; + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + IoReleaseCancelSpinLock (Irp->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. + // + + StCompleteRequest (Request, STATUS_CANCELLED, 0); + break; + + } + + p = p->Flink; + + } + + if (!Found) { + + // + // We didn't find it! + // + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + IoReleaseCancelSpinLock (Irp->CancelIrql); + } + +} diff --git a/private/ntos/tdi/st/request.c b/private/ntos/tdi/st/request.c new file mode 100644 index 000000000..de553c0cc --- /dev/null +++ b/private/ntos/tdi/st/request.c @@ -0,0 +1,801 @@ +/*++ + +Copyright (c) 1989-1993 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. + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "st.h" + + +VOID +StTdiRequestTimeoutHandler( + 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; + PIO_STACK_LOCATION IrpSp; + + UNREFERENCED_PARAMETER(Dpc); + UNREFERENCED_PARAMETER(SystemArgument1); + UNREFERENCED_PARAMETER(SystemArgument2); + + + Request = (PTP_REQUEST)DeferredContext; + + ACQUIRE_SPIN_LOCK (&Request->SpinLock, &oldirql); + Request->Flags &= ~REQUEST_FLAGS_TIMER; + if ((Request->Flags & REQUEST_FLAGS_STOPPING) == 0) { + + // + // 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: + + ASSERT (FALSE); + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + StCompleteRequest (Request, STATUS_IO_TIMEOUT, 0); + break; + + + case TDI_LISTEN: + case TDI_CONNECT: + + 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. + // + + StStopConnection (Connection, STATUS_IO_TIMEOUT); + break; + + case TDI_DISCONNECT: + + // + // We don't create requests for TDI_DISCONNECT any more. + // + + ASSERT(FALSE); + break; + + default: + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + break; + + } // end of switch + + } else { + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + + } + + StDereferenceRequest ("Timeout", Request); // for the timeout + + } else { + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + StDereferenceRequest ("Timeout: stopping", Request); // for the timeout + + } + + return; + +} /* RequestTimeoutHandler */ + + +VOID +StAllocateRequest( + 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("ST: Could not allocate request: limit\n"); + StWriteResourceErrorLog (DeviceContext, sizeof(TP_REQUEST), 104); + *TransportRequest = NULL; + return; + } + + Request = (PTP_REQUEST)ExAllocatePool (NonPagedPool, sizeof (TP_REQUEST)); + if (Request == NULL) { + PANIC("ST: Could not allocate request: no pool\n"); + StWriteResourceErrorLog (DeviceContext, sizeof(TP_REQUEST), 204); + *TransportRequest = NULL; + return; + } + RtlZeroMemory (Request, sizeof(TP_REQUEST)); + + DeviceContext->MemoryUsage += sizeof(TP_REQUEST); + ++DeviceContext->RequestAllocated; + + Request->Type = ST_REQUEST_SIGNATURE; + Request->Size = sizeof (TP_REQUEST); + + Request->Provider = DeviceContext; + Request->ProviderInterlock = &DeviceContext->Interlock; + KeInitializeSpinLock (&Request->SpinLock); + KeInitializeDpc (&Request->Dpc, StTdiRequestTimeoutHandler, (PVOID)Request); + KeInitializeTimer (&Request->Timer); // set to not-signaled state. + + *TransportRequest = Request; + +} /* StAllocateRequest */ + + +VOID +StDeallocateRequest( + 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); + +} /* StDeallocateRequest */ + + +NTSTATUS +StCreateRequest( + 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; + + + irpSp = IoGetCurrentIrpStackLocation(Irp); + DeviceContext = (PDEVICE_CONTEXT)irpSp->FileObject->DeviceObject; + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + p = RemoveHeadList (&DeviceContext->RequestPool); + if (p == &DeviceContext->RequestPool) { + + if ((DeviceContext->RequestMaxAllocated == 0) || + (DeviceContext->RequestAllocated < DeviceContext->RequestMaxAllocated)) { + + StAllocateRequest (DeviceContext, &Request); + + } else { + + StWriteResourceErrorLog (DeviceContext, sizeof(TP_REQUEST), 404); + Request = NULL; + + } + + if (Request == NULL) { + ++DeviceContext->RequestExhausted; + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + PANIC ("StCreateConnection: 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 ((Timeout.LowPart == 0) && (Timeout.HighPart == 0)) { + + // no timeout + } else { + + Request->Flags |= REQUEST_FLAGS_TIMER; // there is a timeout on this request. + KeInitializeTimer (&Request->Timer); // set to not-signaled state. + StReferenceRequest ("Create: timer", Request); // one for the timer + KeSetTimer (&Request->Timer, Timeout, &Request->Dpc); + } + + *TpRequest = Request; + + return STATUS_SUCCESS; +} /* StCreateRequest */ + + +VOID +StDestroyRequest( + 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; + + // + // Return the request to the caller with whatever status is in the IRP. + // + + // + // 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: + if (!(Request->Flags & REQUEST_FLAGS_DELAY)) { + StDereferenceConnection ("Removing Connection",((PTP_CONNECTION)Request->Context)); + } + break; + + case AddressType: + StDereferenceAddress ("Removing Address", ((PTP_ADDRESS)Request->Context)); + break; + + case DeviceContextType: + StDereferenceDeviceContext ("Removing Address", ((PDEVICE_CONTEXT)Request->Context)); + break; + } + + irpSp = IoGetCurrentIrpStackLocation (Request->IoRequestPacket); + DeviceContext = Request->Provider; + + if (Request->Flags & REQUEST_FLAGS_DELAY) { + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + InsertTailList( + &DeviceContext->IrpCompletionQueue, + &Request->IoRequestPacket->Tail.Overlay.ListEntry); + + } else { + + IoCompleteRequest (Request->IoRequestPacket, IO_NETWORK_INCREMENT); + + 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) { + StDeallocateRequest (DeviceContext, Request); + } else { + InsertTailList (&DeviceContext->RequestPool, &Request->Linkage); + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + +} /* StDestroyRequest */ + + +VOID +StRefRequest( + 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. + +--*/ + +{ + ASSERT (Request->ReferenceCount > 0); + + InterlockedIncrement (&Request->ReferenceCount); + +} /* StRefRequest */ + + +VOID +StDerefRequest( + 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 + StDestroyRequest to remove it from the system. + +Arguments: + + Request - Pointer to a transport request object. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + 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) { + StDestroyRequest (Request); + } + +} /* StDerefRequest */ + + +VOID +StCompleteRequest( + 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; + NTSTATUS FinalStatus = Status; + BOOLEAN TimerWasSet; + + ASSERT (Status != STATUS_PENDING); + + if (Request->Flags & REQUEST_FLAGS_SEND_RCV) { + + // + // Sends and receives we check for since we know + // they don't have timers and should only complete + // once. + // + + Request->Flags |= REQUEST_FLAGS_STOPPING; + Irp = Request->IoRequestPacket; + Irp->IoStatus.Status = FinalStatus; + Irp->IoStatus.Information = Information; + + StDereferenceRequest ("Complete", Request); // remove creation reference. + return; + } + + + 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) { + StDereferenceRequest ("Complete: stop timer", Request); + } + + } else { + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + } + + Irp = Request->IoRequestPacket; + + + // + // Install the return code in the IRP so that when we call StDestroyRequest, + // 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. + // + + StDereferenceRequest ("Complete", Request); // remove creation reference. + + } else { + + RELEASE_SPIN_LOCK (&Request->SpinLock, oldirql); + + } + +} /* StCompleteRequest */ + + +VOID +StRefSendIrp( + 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. + +--*/ + +{ + ASSERT (IRP_REFCOUNT(IrpSp) > 0); + + InterlockedIncrement (&IRP_REFCOUNT(IrpSp)); + +} /* StRefSendIrp */ + + +VOID +StDerefSendIrp( + 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. + + NOTE: This assume that IRP_CONNECTION(IrpSp) has been changed + to point to the IRP instead of the connection. + +Arguments: + + Request - Pointer to a transport send IRP's stack location. + +Return Value: + + none. + +--*/ + +{ + LONG result; + + result = InterlockedDecrement (&IRP_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 = (PIRP)IRP_CONNECTION(IrpSp); + + IRP_REFCOUNT(IrpSp) = 0; + IRP_CONNECTION (IrpSp) = NULL; + + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + + } + +} /* StDerefSendIrp */ + + +VOID +StCompleteSendIrp( + 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_CONNECTION(IrpSp); + + + // + // Sends and receives we check for since we know + // they don't have timers and should only complete + // once. + // + + Irp->IoStatus.Status = Status; + Irp->IoStatus.Information = Information; + + IRP_CONNECTION(IrpSp) = Irp; + + StDereferenceSendIrp ("Complete", IrpSp); // remove creation reference. + + StDereferenceConnection ("Removing Connection", Connection); + +} /* StCompleteSendIrp */ diff --git a/private/ntos/tdi/st/send.c b/private/ntos/tdi/st/send.c new file mode 100644 index 000000000..0e6a2b9f6 --- /dev/null +++ b/private/ntos/tdi/st/send.c @@ -0,0 +1,385 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + send.c + +Abstract: + + This module contains code which performs the following TDI services: + + o TdiSend + o TdiSendDatagram + +Environment: + + Kernel mode + +Revision History: + +--*/ + +#include "st.h" + + +NTSTATUS +StTdiSend( + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine performs the TdiSend request for the transport provider. + +Arguments: + + Irp - Pointer to the I/O Request Packet for this request. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + KIRQL oldirql, cancelirql; + NTSTATUS status; + PTP_CONNECTION connection; + PMDL SendBuffer; + ULONG SendBufferLength; + 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 != ST_CONNECTION_SIGNATURE)) { + return STATUS_INVALID_CONNECTION; + } + + // + // Now map the data in to SVA space. + // + + parameters = (PTDI_REQUEST_KERNEL_SEND)(&irpSp->Parameters); + SendBuffer = Irp->MdlAddress; + SendBufferLength = parameters->SendLength; + + // + // Interpret send options. + // + + // + // Now we have a reference on the connection object. Queue up this + // send to the connection object. + // + + + // This reference is removed by TdiDestroyRequest + + StReferenceConnection("TdiSend", connection); + + IRP_CONNECTION(irpSp) = connection; + IRP_REFCOUNT(irpSp) = 1; + + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_SPIN_LOCK (&connection->SpinLock,&oldirql); + + if ((connection->Flags & CONNECTION_FLAGS_STOPPING) != 0) { + RELEASE_SPIN_LOCK (&connection->SpinLock,oldirql); + IoReleaseCancelSpinLock(cancelirql); + StCompleteSendIrp( + Irp, + connection->Status, + 0); + status = STATUS_PENDING; + } else { + + StReferenceConnection ("Verify Temp Use", connection); + + // + // Insert onto the send queue, and make the IRP + // cancellable. + // + + InsertTailList (&connection->SendQueue,&Irp->Tail.Overlay.ListEntry); + + + // + // If this IRP has been cancelled, then call the + // cancel routine. + // + + if (Irp->Cancel) { + RELEASE_SPIN_LOCK (&connection->SpinLock,oldirql); + Irp->CancelIrql = cancelirql; + StCancelSend((PDEVICE_OBJECT)(connection->Provider), Irp); + StDereferenceConnection ("IRP cancelled", connection); // release lookup hold. + return STATUS_PENDING; + } + + Irp->CancelRoutine = StCancelSend; + + // + // 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) { + + RELEASE_SPIN_LOCK (&connection->SpinLock,oldirql); + IoReleaseCancelSpinLock(cancelirql); + + // + // BUGBUG: Should we save status from real failure? + // + + FailSend (connection, STATUS_LINK_FAILED, TRUE); + + if ( (parameters->SendFlags & TDI_SEND_PARTIAL) == 0) { + connection->Flags &= ~CONNECTION_FLAGS_FAILING_TO_EOR; + } + + StDereferenceConnection ("Failing to EOR", connection); // 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. + // + + 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)) && + (!(connection->Flags & CONNECTION_FLAGS_STOPPING))) { + + connection->Flags |= CONNECTION_FLAGS_PACKETIZE; + + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + IoReleaseCancelSpinLock(cancelirql); + + PacketizeSend (connection); + + } else { + + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + IoReleaseCancelSpinLock(cancelirql); + + StDereferenceConnection ("Stopping or already packetizing", connection); // release lookup hold. + + } + + 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 (connection, TRUE, oldirql, cancelirql); + break; + + default: + + // + // 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; + } + RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql); + IoReleaseCancelSpinLock(cancelirql); + + StDereferenceConnection("temp TdiSend", connection); + + } + + } + + status = STATUS_PENDING; + + + return status; +} /* TdiSend */ + + +NTSTATUS +StTdiSendDatagram( + 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_REQUEST tpRequest; + PTP_ADDRESS_FILE addressFile; + PTP_ADDRESS address; + PMDL SendBuffer; + ULONG SendBufferLength; + PIO_STACK_LOCATION irpSp; + PTDI_REQUEST_KERNEL_SENDDG parameters; + LARGE_INTEGER timeout = {0,0}; + UINT MaxUserData; + + irpSp = IoGetCurrentIrpStackLocation (Irp); + addressFile = irpSp->FileObject->FsContext; + + status = StVerifyAddressObject (addressFile); + if (!NT_SUCCESS (status)) { + return status; + } + + address = addressFile->Address; + parameters = (PTDI_REQUEST_KERNEL_SENDDG)(&irpSp->Parameters); + SendBuffer = Irp->MdlAddress; + SendBufferLength = parameters->SendLength; + + // + // Check that the length is short enough. + // + + MacReturnMaxDataSize( + &address->Provider->MacInfo, + NULL, + 0, + address->Provider->MaxSendPacketSize, + &MaxUserData); + + if (SendBufferLength > + (MaxUserData - sizeof(ST_HEADER))) { + + return STATUS_INVALID_PARAMETER; + + } + + // + // We need a request object to keep track of this TDI request. + // Attach this request to the address object. + // + + status = StCreateRequest ( + Irp, // IRP for this request. + address, // context. + REQUEST_FLAGS_ADDRESS, // partial flags. + SendBuffer, // the data to be sent. + SendBufferLength, // length of the data. + timeout, + &tpRequest); + + if (!NT_SUCCESS (status)) { + StDereferenceAddress ("no send request", address); + return status; // if we couldn't queue the request. + } + + StReferenceAddress ("Send datagram", address); + tpRequest->Owner = AddressType; + + ACQUIRE_SPIN_LOCK (&address->SpinLock,&oldirql); + + if ((address->Flags & ADDRESS_FLAGS_STOPPING) != 0) { + RELEASE_SPIN_LOCK (&address->SpinLock,oldirql); + StCompleteRequest (tpRequest, STATUS_NETWORK_NAME_DELETED, 0); + return STATUS_PENDING; + } else { + InsertTailList ( + &address->SendDatagramQueue, + &tpRequest->Linkage); + 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 ST datagram header needs to be statically + // allocated for reuse by all send datagram requests. + // + + (VOID)StSendDatagramsOnAddress (address); + + StDereferenceAddress("tmp send datagram", address); + + return STATUS_PENDING; + +} /* StTdiSendDatagram */ diff --git a/private/ntos/tdi/st/sendeng.c b/private/ntos/tdi/st/sendeng.c new file mode 100644 index 000000000..4019291de --- /dev/null +++ b/private/ntos/tdi/st/sendeng.c @@ -0,0 +1,1505 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + sendeng.c + +Abstract: + + This module contains code that implements the send engine for the + Sample transport provider. + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "st.h" +#if 27 +ULONG StNoisySend = 0; +ULONG StSndLoc = 0; +ULONG StSnds[10]; +#endif + + +VOID +StartPacketizingConnection( + PTP_CONNECTION Connection, + IN BOOLEAN Immediate, + IN KIRQL ConnectionIrql, + IN KIRQL CancelIrql + ) + +/*++ + +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 spin lock must be held on entry to this routine. + Optionally, the cancel spin lock may be held. If so, the cancel + spin lock must have been acquired first. + +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. + + ConnectionIrql - The OldIrql value when the connection spin lock + was acquired. + + CancelIrql - The OldIrql value when the cancel spin lock was + acquired. -1 means that the cancel spin lock isn't held. + +Return Value: + + none. + +--*/ + +{ + PDEVICE_CONTEXT DeviceContext; + + 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_STOPPING))) { + + Connection->Flags |= CONNECTION_FLAGS_PACKETIZE; + + if (!Immediate) { + StReferenceConnection ("Packetize", Connection); + } + + ExInterlockedInsertTailList( + &DeviceContext->PacketizeQueue, + &Connection->PacketizeLinkage, + &DeviceContext->SpinLock); + + RELEASE_SPIN_LOCK (&Connection->SpinLock, ConnectionIrql); + + } else { + + RELEASE_SPIN_LOCK (&Connection->SpinLock, ConnectionIrql); + if (Immediate) { + StDereferenceConnection("temp TdiSend", Connection); + } + } + + if (CancelIrql != (KIRQL)-1) { + IoReleaseCancelSpinLock (CancelIrql); + } + + if (Immediate) { + PacketizeConnections (DeviceContext); + } + +} /* StartPacketizingConnection */ + + +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; + + // + // 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); + PacketizeSend (Connection); + } + +} /* PacketizeConnections */ + + +VOID +PacketizeSend( + PTP_CONNECTION Connection + ) + +/*++ + +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. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql, oldirql1; + ULONG MaxFrameSize, FrameSize; + ULONG PacketBytes; + TP_SEND_POINTER SavedSendPointer; + PNDIS_BUFFER PacketDescriptor; + PUCHAR SourceRouting; + UINT SourceRoutingLength; + PDEVICE_CONTEXT DeviceContext; + PTP_PACKET Packet; + NTSTATUS Status; + PST_HEADER StHeader; + UINT HeaderLength; + PIO_STACK_LOCATION IrpSp; + PSEND_PACKET_TAG SendTag; + ULONG LastPacketLength; + + DeviceContext = Connection->Provider; + + // + // Just loop until one of three events happens: (1) we run out of + // packets from StCreatePacket, (2) we completely packetize the send. + // + + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + if (Connection->SendState != CONNECTION_SENDSTATE_PACKETIZE) { + Connection->Flags &= ~CONNECTION_FLAGS_PACKETIZE; + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + StDereferenceConnection ("No longer packetizing", Connection); + return; + } + + MaxFrameSize = Connection->MaximumDataSize; + + // + // 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 (StCreatePacket (DeviceContext, &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 StDestroyPacket 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. + // + + 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; + + if (!(Connection->Flags & CONNECTION_FLAGS_STOPPING)) { + Connection->Flags |= CONNECTION_FLAGS_SUSPENDED; + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql1); + InsertTailList (&DeviceContext->PacketWaitQueue, &Connection->PacketWaitLinkage); + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql1); + } + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + StDereferenceConnection ("No packet", Connection); + return; + + } + + // + // Add a reference count to the IRP, and keep track of + // which request it is. Send completion will remove the + // reference. + + IrpSp = IoGetCurrentIrpStackLocation(Connection->sp.CurrentSendIrp); + + SendTag = (PSEND_PACKET_TAG)(Packet->NdisPacket->ProtocolReserved); + SendTag->Type = TYPE_I_FRAME; + SendTag->Packet = Packet; + SendTag->Owner = (PVOID)IrpSp; + + Packet->CompleteSend = FALSE; + + + // + // Build the MAC header. All frames go out as + // single-route source routing. + // + + + MacReturnSingleRouteSR( + &DeviceContext->MacInfo, + &SourceRouting, + &SourceRoutingLength); + + MacConstructHeader ( + &DeviceContext->MacInfo, + Packet->Header, + DeviceContext->MulticastAddress.Address, + DeviceContext->LocalAddress.Address, + sizeof(ST_HEADER), + SourceRouting, + SourceRoutingLength, + &HeaderLength); + + // + // Build the header: 'I', dest, source + // + + StHeader = (PST_HEADER)(&Packet->Header[HeaderLength]); + + StHeader->Signature = ST_SIGNATURE; + StHeader->Command = ST_CMD_INFORMATION; + StHeader->Flags = 0; + + RtlCopyMemory (StHeader->Destination, Connection->CalledAddress.NetbiosName, 16); + RtlCopyMemory (StHeader->Source, Connection->AddressFile->Address->NetworkName->NetbiosName, 16); + + HeaderLength += sizeof(ST_HEADER); + + + // + // Modify the packet length and send the it. + // + + StSetNdisPacketLength(Packet->NdisPacket, HeaderLength); + + StReferenceSendIrp ("Packetize", IrpSp); + + // + // Save our complex send pointer in case we have to restore it + // because StNdisSend fails. + // + + SavedSendPointer = Connection->sp; + + // + // 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; + + } +#if 27 + if (StNoisySend) { + DbgPrint ("Send %d of %d\n", FrameSize, Connection->CurrentSendLength); + } + if (FrameSize > 1000) { + StSnds[StSndLoc] = FrameSize; + StSndLoc = (StSndLoc + 1) % 10; + } +#endif + + + // + // 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; + Status = STATUS_SUCCESS; + + } else { + + Status = BuildBufferChainFromMdlChain ( + DeviceContext->NdisBufferPoolHandle, + Connection->sp.CurrentSendMdl, + Connection->sp.SendByteOffset, + FrameSize, + &PacketDescriptor, + &Connection->sp.CurrentSendMdl, + &Connection->sp.SendByteOffset, + &PacketBytes); + + } + + } else { + + PacketBytes = 0; + Connection->sp.CurrentSendMdl = NULL; + Status = STATUS_SUCCESS; + + } + + if (NT_SUCCESS (Status)) { + + Connection->sp.MessageBytesSent += PacketBytes; + + // + // Chain the buffers to the packet, unless there + // are zero bytes of data. + // + + if (FrameSize != 0) { + NdisChainBufferAtBack (Packet->NdisPacket, PacketDescriptor); + } + + + // + // Have we run out of Mdl Chain in this request? + // + + if ((PacketBytes < FrameSize) || + (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 (!(IRP_SEND_FLAGS(IrpSp) & TDI_SEND_PARTIAL)) { + + // + // We are sending the last packet in a message. Change + // the packet type to a "last" frame. + // + + StHeader->Flags |= ST_FLAGS_LAST; + Packet->CompleteSend = TRUE; + Connection->SendState = CONNECTION_SENDSTATE_IDLE; + + } 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. + // + + if (Connection->sp.CurrentSendIrp->Tail.Overlay.ListEntry.Flink == &Connection->SendQueue) { + + Connection->SendState = CONNECTION_SENDSTATE_W_EOR; + + } 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)); + } + } + } + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + + LastPacketLength = sizeof(ST_HEADER) + PacketBytes; + + MacModifyHeader( + &DeviceContext->MacInfo, + Packet->Header, + LastPacketLength); + + Packet->NdisIFrameLength = LastPacketLength; + + StNdisSend (Packet); + + // + // Update our counters (this is done unprotected by a lock). + // + + ADD_TO_LARGE_INTEGER( + &DeviceContext->IFrameBytesSent, + PacketBytes); + ++DeviceContext->IFramesSent; + + } else { + + // + // BuildBufferChainFromMdlChain failed; we need to + // release the lock since the long if() above + // exits with it released. + // + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + + } + + // + // 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)) { + + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + // + // Restore old complex send pointer. + // + + Connection->sp = SavedSendPointer; + + + // + // 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_SPIN_LOCK (&Connection->SpinLock, oldirql); + + StDestroyPacket (Packet); + StDereferenceSendIrp("Send failed", IrpSp); + StDereferenceConnection ("Send failed", Connection); + + return; + } + + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + // + // It is probable that a 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_SPIN_LOCK (&Connection->SpinLock, oldirql); + + StDereferenceConnection ("PacketizeSend done", Connection); + +} /* PacketizeSend */ + + +VOID +CompleteSend( + PTP_CONNECTION Connection + ) + +/*++ + +Routine Description: + + This routine is called to complete a TDI send back to the + caller. In the sample transport we assume that all sends + complete successfully, but in other transports we would + wait for a response from the remote. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql, cancelirql; + PIRP Irp; + PIO_STACK_LOCATION IrpSp; + PLIST_ENTRY p; + BOOLEAN EndOfRecord; + + // + // Pick off TP_REQUEST objects from the connection's SendQueue until + // we find one with an END_OF_RECORD mark embedded in it. + // + + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + while (TRUE) { + + // + // We know for a fact that we wouldn't be calling this routine if + // we hadn't completed sending an entire message, since we + // only set the ST_LAST bit in that case. 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. + // + + if (Connection->SendQueue.Flink == &Connection->SendQueue) { + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + IoReleaseCancelSpinLock(cancelirql); + + // + // no requests to complete, things must have failed; just get out. + // + + break; + } + + p = RemoveHeadList (&Connection->SendQueue); + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + Irp = CONTAINING_RECORD (p, IRP, Tail.Overlay.ListEntry); + IrpSp = IoGetCurrentIrpStackLocation (Irp); + + EndOfRecord = !(IRP_SEND_FLAGS(IrpSp) & TDI_SEND_PARTIAL); + + Irp->CancelRoutine = (PDRIVER_CANCEL)NULL; + IoReleaseCancelSpinLock(cancelirql); + + // + // 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. + // + + StCompleteSendIrp ( + Irp, + STATUS_SUCCESS, + IRP_SEND_LENGTH(IrpSp)); + + if (EndOfRecord) { + break; + } + + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + }; + + // + // Acquire the lock that we will return with. + // + + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + // + // We've finished processing the current send. Update our state. + // + + 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_STOPPING))) { + + Connection->Flags |= CONNECTION_FLAGS_PACKETIZE; + + StReferenceConnection ("Packetize", Connection); + + ExInterlockedInsertTailList( + &Connection->Provider->PacketizeQueue, + &Connection->PacketizeLinkage, + &Connection->Provider->SpinLock); + + } + + } + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + +} /* 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. + +Arguments: + + Connection - Pointer to a TP_CONNECTION object. + +Return Value: + + none. + +--*/ + +{ + KIRQL oldirql, cancelirql; + PIRP Irp; + PIO_STACK_LOCATION IrpSp; + PLIST_ENTRY p; + BOOLEAN EndOfRecord; + BOOLEAN GotCurrent = FALSE; + + + // + // 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 ST does not provide stream mode sends.) + // + + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + StReferenceConnection ("Failing Send", Connection); + + 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); + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + Irp->CancelRoutine = (PDRIVER_CANCEL)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. + // + + StCompleteSendIrp (Irp, RequestStatus, 0); + IoAcquireCancelSpinLock(&cancelirql); + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + } while (!EndOfRecord & !GotCurrent); + + // + // We've finished processing the current send. Update our state. + // + + Connection->SendState = CONNECTION_SENDSTATE_IDLE; + Connection->sp.CurrentSendIrp = NULL; + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + IoReleaseCancelSpinLock(cancelirql); + + + if (StopConnection) { + StStopConnection (Connection, STATUS_LINK_FAILED); + } + + StDereferenceConnection ("FailSend", Connection); + +} /* FailSend */ + + +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 (Connection->SendQueue.Flink != &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)); + + } +} /* InitializeSend */ + + +VOID +StCancelSend( + 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; + 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->SpinLock, &oldirql); + StReferenceConnection ("Cancelling Send", Connection); + + 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. + // + + p = RemoveHeadList (&Connection->SendQueue); + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + IoReleaseCancelSpinLock (Irp->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. + // + + StCompleteSendIrp (SendIrp, STATUS_CANCELLED, 0); + + // + // Since we are cancelling the current send, blow away + // the connection. + // + + StStopConnection (Connection, STATUS_CANCELLED); + + } else { + + // + // Scan through the list, looking for this IRP. + // + + 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; + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + IoReleaseCancelSpinLock (Irp->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. + // + + StCompleteSendIrp (SendIrp, STATUS_CANCELLED, 0); + break; + + } + + p = p->Flink; + + } + + if (!Found) { + + // + // We didn't find it! + // + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + IoReleaseCancelSpinLock (Irp->CancelIrql); + } + + } + + StDereferenceConnection ("Cancelling Send", Connection); + +} + + + +VOID +StSendCompletionHandler( + 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 oldirql, cancelirql; + PDEVICE_CONTEXT DeviceContext; + PTP_CONNECTION Connection; + PLIST_ENTRY p; + PIO_STACK_LOCATION IrpSp; + PTP_REQUEST request; + TA_NETBIOS_ADDRESS TempAddress; + ULONG returnLength; + NTSTATUS status; + PTDI_CONNECTION_INFORMATION remoteInformation; + + UNREFERENCED_PARAMETER(ProtocolBindingContext); + + SendContext = (PSEND_PACKET_TAG)&NdisPacket->ProtocolReserved[0]; + Packet = SendContext->Packet; + + DeviceContext = Packet->Provider; + + Packet->PacketSent = TRUE; + + switch (SendContext->Type) { + + case TYPE_I_FRAME: + + // + // Dereference the IRP that this packet was sent for. + // + + IrpSp = (PIO_STACK_LOCATION)(SendContext->Owner); + + if (Packet->CompleteSend) { + CompleteSend(IRP_CONNECTION(IrpSp)); + } + StDereferenceSendIrp("Destroy packet", IrpSp); + break; + + case TYPE_D_FRAME: + + // + // Finish tearing down the connection. + // + + StDereferenceConnection("Disconnect completed", (PTP_CONNECTION)(SendContext->Owner)); + break; + + case TYPE_G_FRAME: + + // + // Addresses get their own frames; let the address know it's ok to + // use the frame again, and exit to avoid normal packet completion. + // + + StSendDatagramCompletion ((PTP_ADDRESS)(SendContext->Owner), + NdisPacket, + NdisStatus); + return; + + case TYPE_C_FRAME: + + // + // Complete the TdiConnect request; note that he better + // have accepted it quickly since we will immediately + // start sending data if required. + // + + Connection = (PTP_CONNECTION)(SendContext->Owner); + + IoAcquireCancelSpinLock (&cancelirql); + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + p = RemoveHeadList (&Connection->InProgressRequest); + + // + // 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. + // + + Connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED; + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + + // + // Now complete the request and get out. + // + + if (p == &Connection->InProgressRequest) { + Connection->IndicationInProgress = FALSE; + PANIC ("ProcessSessionConfirm: TdiConnect evaporated!\n"); + IoReleaseCancelSpinLock (cancelirql); + break; + } + + // + // We have a completed connection with a queued connect. Complete + // the connect. + // + + request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + request->IoRequestPacket->CancelRoutine = (PDRIVER_CANCEL)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; + + } + + RtlCopyMemory( Connection->RemoteName, Connection->CalledAddress.NetbiosName, 16 ); + Connection->Flags2 |= CONNECTION_FLAGS2_REMOTE_VALID; + + // + // Reference the connection so it stays around after + // the request is completed. + // + + StReferenceConnection("Connect completed", Connection); + + StCompleteRequest (request, status, returnLength); + + break; + + } + + StDestroyPacket(Packet); + +} /* StSendCompletionHandler */ + + +VOID +StNdisSend( + IN PTP_PACKET Packet + ) + +/*++ + +Routine Description: + + This routine sends an NDIS packet + 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. + +Arguments: + + Packet - Pointer to a TP_PACKET object. + +Return Value: + + None. + +--*/ + +{ + + NDIS_STATUS NdisStatus; + + NdisSend ( + &NdisStatus, + ((PDEVICE_CONTEXT)(Packet->Provider))->NdisBindingHandle, + Packet->NdisPacket); + + if (NdisStatus != NDIS_STATUS_PENDING) { + + StSendCompletionHandler( + Packet->Provider, + Packet->NdisPacket, + NdisStatus); + } + +} /* StNdisSend */ + + + +NTSTATUS +BuildBufferChainFromMdlChain ( + IN NDIS_HANDLE BufferPoolHandle, + 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; + + + 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, + BufferPoolHandle, + OldMdl, + ByteOffset, + AvailableBytes); + + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + *NewByteOffset = ByteOffset; + *TrueLength = 0; + *Destination = NULL; + return STATUS_INSUFFICIENT_RESOURCES; + } + + *Destination = NewNdisBuffer; + + // + // 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)), + BufferPoolHandle, + 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 (*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/st/sources b/private/ntos/tdi/st/sources new file mode 100644 index 000000000..f47518792 --- /dev/null +++ b/private/ntos/tdi/st/sources @@ -0,0 +1,62 @@ +!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=st + +TARGETNAME=st +TARGETPATH=\nt\public\sdk\lib +TARGETTYPE=DRIVER + +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\tdi.lib \ + $(BASEDIR)\public\sdk\lib\*\ndis.lib + +INCLUDES=..\..\inc;..\..\..\inc + +SOURCES=\ + address.c \ + connect.c \ + connobj.c \ + devctx.c \ + event.c \ + framesnd.c \ + iframes.c \ + ind.c \ + info.c \ + packet.c \ + rcv.c \ + rcveng.c \ + request.c \ + send.c \ + sendeng.c \ + st.rc \ + stcnfg.c \ + stdrvr.c \ + stmac.c \ + stndis.c \ + uframes.c + +!IFNDEF 386_WARNING_LEVEL +386_WARNING_LEVEL=/W3 +!ENDIF diff --git a/private/ntos/tdi/st/st.h b/private/ntos/tdi/st/st.h new file mode 100644 index 000000000..590ccac89 --- /dev/null +++ b/private/ntos/tdi/st/st.h @@ -0,0 +1,46 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + st.h + +Abstract: + + Private include file for the NT Sample transport provider. + +Revision History: + +--*/ + +#ifndef _ST_ +#define _ST_ + +#include <ntddk.h> + +#include <windef.h> // these two are needed by info.c +#include <nb30.h> + +#include <tdikrnl.h> // Transport Driver Interface. +#include <ndis.h> // Network Driver Interface. + +#if DEVL +#define STATIC +#else +#define STATIC static +#endif + +#include "stconst.h" // private constants. +#include "stmac.h" // mac-specific definitions +#include "sthdrs.h" // private protocol headers. +#include "sttypes.h" // private types. +#include "stcnfg.h" // configuration information. +#include "stprocs.h" // private function prototypes. + + +#define ACQUIRE_SPIN_LOCK(lock,irql) KeAcquireSpinLock(lock,irql) +#define RELEASE_SPIN_LOCK(lock,irql) KeReleaseSpinLock(lock,irql) + + +#endif // def _ST_ diff --git a/private/ntos/tdi/st/st.rc b/private/ntos/tdi/st/st.rc new file mode 100644 index 000000000..591897aa9 --- /dev/null +++ b/private/ntos/tdi/st/st.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 "Sample TDI 3.0 Transport Driver" +#define VER_INTERNALNAME_STR "st.sys" +#define VER_ORIGINALFILENAME_STR "st.sys" + +#include "common.ver" + diff --git a/private/ntos/tdi/st/stcnfg.c b/private/ntos/tdi/st/stcnfg.c new file mode 100644 index 000000000..40438eedb --- /dev/null +++ b/private/ntos/tdi/st/stcnfg.c @@ -0,0 +1,1277 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + stcnfg.c + +Abstract: + + This contains all routines necessary for the support of the dynamic + configuration of ST. + +Revision History: + +--*/ + +#include "st.h" + + +// +// Local functions used to access the registry. +// + +NTSTATUS +StConfigureTransport ( + IN PUNICODE_STRING RegistryPath, + IN PCONFIG_DATA * ConfigurationInfoPtr + ); + +VOID +StFreeConfigurationInfo ( + IN PCONFIG_DATA ConfigurationInfo + ); + +NTSTATUS +StOpenParametersKey( + IN HANDLE StConfigHandle, + OUT PHANDLE ParametersHandle + ); + +VOID +StCloseParametersKey( + IN HANDLE ParametersHandle + ); + +NTSTATUS +StCountEntries( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +StAddBind( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +NTSTATUS +StAddExport( + IN PWSTR ValueName, + IN ULONG ValueType, + IN PVOID ValueData, + IN ULONG ValueLength, + IN PVOID Context, + IN PVOID EntryContext + ); + +VOID +StReadLinkageInformation( + IN PWSTR RegistryPathBuffer, + IN PCONFIG_DATA * ConfigurationInfo + ); + +UINT +StReadSizeInformation( + IN HANDLE ParametersHandle + ); + +ULONG +StReadSingleParameter( + IN HANDLE ParametersHandle, + IN PWCHAR ValueName, + IN ULONG DefaultValue + ); + +VOID +StWriteSingleParameter( + IN HANDLE ParametersHandle, + IN PWCHAR ValueName, + IN ULONG ValueData + ); + +VOID +StSaveConfigInRegistry( + IN HANDLE ParametersHandle, + IN PCONFIG_DATA ConfigurationInfo + ); + +UINT +StWstrLength( + IN PWSTR Wstr + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,StWstrLength) +#pragma alloc_text(INIT,StConfigureTransport) +#pragma alloc_text(INIT,StFreeConfigurationInfo) +#pragma alloc_text(INIT,StOpenParametersKey) +#pragma alloc_text(INIT,StCloseParametersKey) +#pragma alloc_text(INIT,StCountEntries) +#pragma alloc_text(INIT,StAddBind) +#pragma alloc_text(INIT,StAddExport) +#pragma alloc_text(INIT,StReadLinkageInformation) +#pragma alloc_text(INIT,StReadSingleParameter) +#pragma alloc_text(INIT,StWriteSingleParameter) +#pragma alloc_text(INIT,StSaveConfigInRegistry) +#endif + + +UINT +StWstrLength( + 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 = StWstrLength(_N)+sizeof(WCHAR); \ + _S = (PWSTR)ExAllocatePool(NonPagedPool, _L); \ + if (_S != NULL) { \ + RtlCopyMemory(_S, _N, _L); \ + RtlInitUnicodeString (&(ConfigurationInfo)->Names[Subscript], _S); \ + } \ +} + +#define InsertDevice(ConfigurationInfo, Subscript, Name) \ +{ \ + PWSTR _S; \ + PWSTR _N = (Name); \ + UINT _L = StWstrLength(_N)+sizeof(WCHAR); \ + _S = (PWSTR)ExAllocatePool(NonPagedPool, _L); \ + 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_) STATIC WCHAR Str ## _str_[] = L#_str_ + +DECLARE_STRING(Large); +DECLARE_STRING(Medium); +DECLARE_STRING(Small); + +DECLARE_STRING(InitRequests); +DECLARE_STRING(InitConnections); +DECLARE_STRING(InitAddressFiles); +DECLARE_STRING(InitAddresses); + +DECLARE_STRING(MaxRequests); +DECLARE_STRING(MaxConnections); +DECLARE_STRING(MaxAddressFiles); +DECLARE_STRING(MaxAddresses); + +DECLARE_STRING(InitPackets); +DECLARE_STRING(InitReceivePackets); +DECLARE_STRING(InitReceiveBuffers); + +DECLARE_STRING(SendPacketPoolSize); +DECLARE_STRING(ReceivePacketPoolSize); +DECLARE_STRING(MaxMemoryUsage); + + +#define READ_HIDDEN_CONFIG(_Field) \ +{ \ + ConfigurationInfo->_Field = \ + StReadSingleParameter( \ + ParametersHandle, \ + Str ## _Field, \ + ConfigurationInfo->_Field); \ +} + +#define WRITE_HIDDEN_CONFIG(_Field) \ +{ \ + StWriteSingleParameter( \ + ParametersHandle, \ + Str ## _Field, \ + ConfigurationInfo->_Field); \ +} + + + +NTSTATUS +StConfigureTransport ( + IN PUNICODE_STRING RegistryPath, + IN PCONFIG_DATA * ConfigurationInfoPtr + ) +/*++ + +Routine Description: + + This routine is called by ST 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 ST'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; + UINT StSize; + HANDLE StConfigHandle; + NTSTATUS Status; + ULONG Disposition; + PWSTR RegistryPathBuffer; + OBJECT_ATTRIBUTES TmpObjectAttributes; + PCONFIG_DATA ConfigurationInfo; + + + // + // Open the registry. + // + + InitializeObjectAttributes( + &TmpObjectAttributes, + RegistryPath, // name + OBJ_CASE_INSENSITIVE, // attributes + NULL, // root + NULL // security descriptor + ); + + Status = ZwCreateKey( + &StConfigHandle, + KEY_WRITE, + &TmpObjectAttributes, + 0, // title index + NULL, // class + 0, // create options + &Disposition); // disposition + + if (!NT_SUCCESS(Status)) { + StPrint1("ST: Could not open/create ST key: %lx\n", Status); + return Status; + } + + + OpenStatus = StOpenParametersKey (StConfigHandle, &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). + // + // StReadLinkageInformation expects a null-terminated path, + // so we have to create one from the UNICODE_STRING. + // + + RegistryPathBuffer = (PWSTR)ExAllocatePool( + NonPagedPool, + RegistryPath->Length + sizeof(WCHAR)); + if (RegistryPathBuffer == NULL) { + StCloseParametersKey (ParametersHandle); + return STATUS_INSUFFICIENT_RESOURCES; + } + RtlCopyMemory (RegistryPathBuffer, RegistryPath->Buffer, RegistryPath->Length); + *(PWCHAR)(((PUCHAR)RegistryPathBuffer)+RegistryPath->Length) = (WCHAR)'\0'; + + StReadLinkageInformation (RegistryPathBuffer, ConfigurationInfoPtr); + + if (*ConfigurationInfoPtr == NULL) { + ExFreePool (RegistryPathBuffer); + StCloseParametersKey (ParametersHandle); + return STATUS_INSUFFICIENT_RESOURCES; + } + ConfigurationInfo = *ConfigurationInfoPtr; + + + // + // Read the size parameter; this returns 0 if none is + // present, or 1 (Small), 2 (Medium) and 3 (Large). + // + + StSize = StReadSizeInformation (ParametersHandle); + + switch (StSize) { + + case 0: + case 1: + + // + // Default is Small. + // + + // + // These are the initial value used; the comment after + // each one shows the expected maximum (if every resource + // is at the expected maximum, ST should be very close + // to being out of memory). + // + // For now the "Max" values default to 0 (no limit). + // + + ConfigurationInfo->InitRequests = 5; // 30 + ConfigurationInfo->InitConnections = 1; // 10 + ConfigurationInfo->InitAddressFiles = 0; // 10 + ConfigurationInfo->InitAddresses = 0; // 10 + + // + // 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 + conn (40) + ConfigurationInfo->InitReceivePackets = 10; // + link + addr (30) + ConfigurationInfo->InitReceiveBuffers = 5; // + addr (15) + + // + // Set the size of the packet pools and the total + // allocateable by ST. + // + + ConfigurationInfo->SendPacketPoolSize = 100; + ConfigurationInfo->ReceivePacketPoolSize = 30; + ConfigurationInfo->MaxMemoryUsage = 100000; + + break; + + case 2: + + // + // Medium ST. + // + + // + // These are the initial value used; the comment after + // each one shows the expected maximum (if every resource + // is at the expected maximum, ST should be very close + // to being out of memory). + // + // For now the "Max" values default to 0 (no limit). + // + + ConfigurationInfo->InitRequests = 10; // 100 + ConfigurationInfo->InitConnections = 2; // 64 + ConfigurationInfo->InitAddressFiles = 1; // 20 + ConfigurationInfo->InitAddresses = 1; // 20 + + // + // 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 = 50; // + link + conn (150) + ConfigurationInfo->InitReceivePackets = 15; // + link + addr (100) + ConfigurationInfo->InitReceiveBuffers = 10; // + addr (30) + + // + // Set the size of the packet pools and the total + // allocateable by ST. + // + + ConfigurationInfo->SendPacketPoolSize = 250; + ConfigurationInfo->ReceivePacketPoolSize = 100; + ConfigurationInfo->MaxMemoryUsage = 250000; + + break; + + case 3: + + // + // Big ST. + // + + // + // These are the initial value used. + // + // For now the "Max" values default to 0 (no limit). + // + + ConfigurationInfo->InitRequests = 15; + ConfigurationInfo->InitConnections = 3; + ConfigurationInfo->InitAddressFiles = 2; + ConfigurationInfo->InitAddresses = 2; + + // + // 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 = 75; // + link + conn + ConfigurationInfo->InitReceivePackets = 25; // + link + addr + ConfigurationInfo->InitReceiveBuffers = 20; // + addr + + // + // Set the size of the packet pools and the total + // allocateable by ST. + // + + ConfigurationInfo->SendPacketPoolSize = 500; + ConfigurationInfo->ReceivePacketPoolSize = 200; + ConfigurationInfo->MaxMemoryUsage = 0; // no limit + + break; + + default: + + ASSERT(FALSE); + break; + + } + + + // + // 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 (InitConnections); + READ_HIDDEN_CONFIG (InitAddressFiles); + READ_HIDDEN_CONFIG (InitAddresses); + + READ_HIDDEN_CONFIG (MaxRequests); + 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 (SendPacketPoolSize); + READ_HIDDEN_CONFIG (ReceivePacketPoolSize); + READ_HIDDEN_CONFIG (MaxMemoryUsage); + + + // + // Now that we are completely configured, save the information + // in the registry. + // + + StSaveConfigInRegistry (ParametersHandle, ConfigurationInfo); + + ExFreePool (RegistryPathBuffer); + StCloseParametersKey (ParametersHandle); + ZwClose (StConfigHandle); + + return STATUS_SUCCESS; + +} /* StConfigureTransport */ + + +VOID +StFreeConfigurationInfo ( + IN PCONFIG_DATA ConfigurationInfo + ) + +/*++ + +Routine Description: + + This routine is called by ST to get free any storage that was allocated + by StConfigureTransport 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); + +} /* StFreeConfigurationInfo */ + + +NTSTATUS +StOpenParametersKey( + IN HANDLE StConfigHandle, + OUT PHANDLE ParametersHandle + ) + +/*++ + +Routine Description: + + This routine is called by ST to open the ST "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 ST parameters key. + // + + RtlInitUnicodeString (&ParametersKeyName, ParametersString); + + InitializeObjectAttributes( + &TmpObjectAttributes, + &ParametersKeyName, // name + OBJ_CASE_INSENSITIVE, // attributes + StConfigHandle, // root + NULL // security descriptor + ); + + + Status = ZwOpenKey( + &ParamHandle, + KEY_READ, + &TmpObjectAttributes); + + if (!NT_SUCCESS(Status)) { + + StPrint1("Could not open parameters key: %lx\n", Status); + return Status; + + } + + *ParametersHandle = ParamHandle; + + + // + // All keys successfully opened or created. + // + + return STATUS_SUCCESS; + +} /* StOpenParametersKey */ + +VOID +StCloseParametersKey( + IN HANDLE ParametersHandle + ) + +/*++ + +Routine Description: + + This routine is called by ST 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); + +} /* StCloseParametersKey */ + + +NTSTATUS +StCountEntries( + 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; + + ASSERT (ValueType == REG_MULTI_SZ); + + // + // 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 = ExAllocatePool( + NonPagedPool, + sizeof (CONFIG_DATA) + + ((*TotalCount-1) * sizeof(NDIS_STRING))); + + if (*ConfigurationInfo == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + RtlZeroMemory( + *ConfigurationInfo, + sizeof(CONFIG_DATA) + ((*TotalCount-1) * sizeof(NDIS_STRING))); + + (*ConfigurationInfo)->DevicesOffset = OldTotalCount; + + } + + return STATUS_SUCCESS; + +} /* StCountEntries */ + + +NTSTATUS +StAddBind( + 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; + +} /* StAddBind */ + + +NTSTATUS +StAddExport( + 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; + +} /* StAddExport */ + + +VOID +StReadLinkageInformation( + IN PWSTR RegistryPathBuffer, + IN PCONFIG_DATA * ConfigurationInfo + ) + +/*++ + +Routine Description: + + This routine is called by ST 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 ST. + +Arguments: + + RegistryPathBuffer - The null-terminated root of the ST registry tree. + + ConfigurationInfo - Returns ST'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 ST + // + + QueryTable[0].QueryRoutine = NULL; + QueryTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY; + QueryTable[0].Name = Subkey; + + // + // 2) Call StCountEntries for the "Bind" multi-string + // + + QueryTable[1].QueryRoutine = StCountEntries; + 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 StCountEntries for the "Export" multi-string + // + + QueryTable[2].QueryRoutine = StCountEntries; + 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 StAddBind for each string in "Bind" + // + + QueryTable[3].QueryRoutine = StAddBind; + QueryTable[3].Flags = 0; + QueryTable[3].Name = Bind; + QueryTable[3].EntryContext = (PVOID)&BindCount; + QueryTable[3].DefaultType = REG_NONE; + + // + // 5) Call StAddExport for each string in "Export" + // + + QueryTable[4].QueryRoutine = StAddExport; + 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; + +} /* StReadLinkageInformation */ + + +UINT +StReadSizeInformation( + IN HANDLE ParametersHandle + ) + +/*++ + +Routine Description: + + This routine is called by ST to read the Size information + from the registry. + +Arguments: + + RegistryHandle - A pointer to the open registry. + +Return Value: + + 0 - no Size specified + 1 - Small + 2 - Medium + 3 - Big / Large + +--*/ + +{ + + UINT SizeToReturn; +// STRING KeywordName; +// PCONFIG_KEYWORD Keyword; + + ULONG InformationBuffer[16]; // declare ULONG to get it aligned + PKEY_VALUE_FULL_INFORMATION Information = + (PKEY_VALUE_FULL_INFORMATION)InformationBuffer; + ULONG InformationLength; + WCHAR SizeString[] = L"Size"; + UNICODE_STRING SizeValueName; + NTSTATUS Status; + PUCHAR InformationData; + ULONG InformationLong; + + + // + // Read the size parameter out of the registry. + // + + RtlInitUnicodeString (&SizeValueName, SizeString); + + Status = ZwQueryValueKey( + ParametersHandle, + &SizeValueName, + KeyValueFullInformation, + (PVOID)Information, + sizeof (InformationBuffer), + &InformationLength); + + // + // Compare to the expected values. + // + + if (Status == STATUS_SUCCESS) { + + InformationData = ((PUCHAR)Information) + Information->DataOffset; + InformationLong = *((PULONG)InformationData); + + if ((Information->DataLength == sizeof(ULONG)) && + (InformationLong >= 1 && InformationLong <= 3)) { + + SizeToReturn = InformationLong; + + } else { + + if ((Information->DataLength >= 10) && + (RtlEqualMemory (StrLarge, InformationData, 10))) { + + SizeToReturn = 3; + + } else if ((Information->DataLength >= 12) && + (RtlEqualMemory (StrMedium, InformationData, 12))) { + + SizeToReturn = 2; + + } else if ((Information->DataLength >= 10) && + (RtlEqualMemory (StrSmall, InformationData, 10))) { + + SizeToReturn = 1; + + } else { + + SizeToReturn = 0; + + } + + } + + } else { + + SizeToReturn = 0; + + } + + return SizeToReturn; + +} /* StReadSizeInformation */ + + +ULONG +StReadSingleParameter( + IN HANDLE ParametersHandle, + IN PWCHAR ValueName, + IN ULONG DefaultValue + ) + +/*++ + +Routine Description: + + This routine is called by ST 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[16]; // 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; + +} /* StReadSingleParameter */ + + +VOID +StWriteSingleParameter( + IN HANDLE ParametersHandle, + IN PWCHAR ValueName, + IN ULONG ValueData + ) + +/*++ + +Routine Description: + + This routine is called by ST 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)) { + StPrint1("ST: Could not write dword key: %lx\n", Status); + } + +} /* StWriteSingleParameter */ + + +VOID +StSaveConfigInRegistry( + IN HANDLE ParametersHandle, + IN PCONFIG_DATA ConfigurationInfo + ) + +/*++ + +Routine Description: + + This routine is called by ST to save its configuraition + information in the registry. It saves the information if + the registry structure did not exist before this boot. + +Arguments: + + ParametersHandle - The handle used to read other parameters. + + ConfigurationInfo - Describes ST's current configuration. + +Return Value: + + None. + +--*/ + +{ + + // + // Save the "hidden" parameters, these may not exist in + // the registry. + // + // NOTE: These macros expect "ConfigurationInfo" and + // "ParametersHandle" to exist when they are expanded. + // + + // + // 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 (MaxConnections); + WRITE_HIDDEN_CONFIG (MaxAddressFiles); + WRITE_HIDDEN_CONFIG (MaxAddresses); + +} /* StSaveConfigInRegistry */ + diff --git a/private/ntos/tdi/st/stcnfg.h b/private/ntos/tdi/st/stcnfg.h new file mode 100644 index 000000000..d09098a6c --- /dev/null +++ b/private/ntos/tdi/st/stcnfg.h @@ -0,0 +1,57 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + stcnfg.h + +Abstract: + + Private include file for the NT Sample transport. This + file defines all constants and structures necessary for support of + the dynamic configuration of ST. + +Revision History: + +--*/ + +#ifndef _STCONFIG_ +#define _STCONFIG_ + +// +// configuration structure. +// + +typedef struct { + + ULONG InitRequests; + ULONG InitConnections; + ULONG InitAddressFiles; + ULONG InitAddresses; + ULONG MaxRequests; + ULONG MaxConnections; + ULONG MaxAddressFiles; + ULONG MaxAddresses; + ULONG InitPackets; + ULONG InitReceivePackets; + ULONG InitReceiveBuffers; + ULONG SendPacketPoolSize; + ULONG ReceivePacketPoolSize; + ULONG MaxMemoryUsage; + + // + // 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/st/stconst.h b/private/ntos/tdi/st/stconst.h new file mode 100644 index 000000000..45837dc71 --- /dev/null +++ b/private/ntos/tdi/st/stconst.h @@ -0,0 +1,127 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + stconst.h + +Abstract: + + This header file defines manifest constants for the NT Sample transport + provider. It is included by st.h. + +Revision History: + +--*/ + +#ifndef _STCONST_ +#define _STCONST_ + + +// +// 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 + + +// +// MAJOR PROTOCOL IDENTIFIERS THAT CHARACTERIZE THIS DRIVER. +// + +#define ST_DEVICE_NAME "\\Device\\St" // name of our driver. +#define ST_DEVICE_NAME_LENGTH 10 +#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 ST_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 + + +// +// NETBIOS PROTOCOL CONSTANTS. +// + +// +// 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 ST_MAX_TSDU_SIZE 65535 // maximum TSDU size supported by NetBIOS. +#define ST_MAX_DATAGRAM_SIZE 512 // maximum Datagram size supported by NetBIOS. +#define ST_MAX_CONNECTION_USER_DATA 0 // no user data supported on connect. +#define ST_SERVICE_FLAGS ( \ + TDI_SERVICE_CONNECTION_MODE | \ + TDI_SERVICE_CONNECTIONLESS_MODE | \ + TDI_SERVICE_ERROR_FREE_DELIVERY | \ + TDI_SERVICE_BROADCAST_SUPPORTED | \ + TDI_SERVICE_MULTICAST_SUPPORTED | \ + TDI_SERVICE_DELAYED_ACCEPTANCE ) + +#define ST_MIN_LOOKAHEAD_DATA 256 // minimum guaranteed lookahead data. +#define ST_MAX_LOOKAHEAD_DATA 256 // maximum guaranteed lookahead data. + +#define ST_MAX_LOOPBACK_LOOKAHEAD 192 // how much is copied over for loopback + +// +// Number of TDI resources that we report. +// + +#define ST_TDI_RESOURCES 7 + + +// +// More debugging stuff +// + +#define ST_REQUEST_SIGNATURE ((CSHORT)0x5501) +#define ST_CONNECTION_SIGNATURE ((CSHORT)0x5502) +#define ST_ADDRESSFILE_SIGNATURE ((CSHORT)0x5503) +#define ST_ADDRESS_SIGNATURE ((CSHORT)0x5504) +#define ST_DEVICE_CONTEXT_SIGNATURE ((CSHORT)0x5505) +#define ST_PACKET_SIGNATURE ((CSHORT)0x5506) + +#endif // _STCONST_ diff --git a/private/ntos/tdi/st/stdrvr.c b/private/ntos/tdi/st/stdrvr.c new file mode 100644 index 000000000..d04fcd2a1 --- /dev/null +++ b/private/ntos/tdi/st/stdrvr.c @@ -0,0 +1,1627 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + stdrvr.c + +Abstract: + + This module contains code which defines the NT Sample + transport provider's device object. + +Environment: + + Kernel mode + +Revision History: + + +--*/ + +#include "st.h" + + +// +// This is a list of all the device contexts that ST owns, +// used while unloading. +// + +LIST_ENTRY StDeviceList = {0,0}; // initialized for real at runtime. + + + +// +// Forward declaration of various routines used in this module. +// + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ); + +VOID +StUnload( + IN PDRIVER_OBJECT DriverObject + ); + +NTSTATUS +StConfigureTransport ( + IN PUNICODE_STRING RegistryPath, + IN PCONFIG_DATA * ConfigData + ); + +VOID +StFreeConfigurationInfo ( + IN PCONFIG_DATA ConfigurationInfo + ); + +NTSTATUS +StDeviceControl( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +StOpenAddress( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +StCloseAddress( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +StOpenConnection( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +StCloseConnection( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +NTSTATUS +StTdiAccept( + IN PIRP Irp + ); + +NTSTATUS +StTdiConnect( + IN PIRP Irp + ); + +NTSTATUS +StTdiDisconnect( + IN PIRP Irp + ); + +NTSTATUS +StTdiDisassociateAddress ( + IN PIRP Irp + ); + +NTSTATUS +StTdiAssociateAddress( + IN PIRP Irp + ); + +NTSTATUS +StTdiListen( + IN PIRP Irp + ); + +NTSTATUS +StTdiQueryInformation( + IN PDEVICE_CONTEXT DeviceContext, + IN PIRP Irp + ); + +NTSTATUS +StTdiReceive( + IN PIRP Irp + ); + +NTSTATUS +StTdiReceiveDatagram( + IN PIRP Irp + ); + +NTSTATUS +StTdiSend( + IN PIRP Irp + ); + +NTSTATUS +StTdiSendDatagram( + IN PIRP Irp + ); + +NTSTATUS +StTdiSetEventHandler( + IN PIRP Irp + ); + +NTSTATUS +StTdiSetInformation( + IN PIRP Irp + ); + +VOID +StDeallocateResources( + IN PDEVICE_CONTEXT DeviceContext + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,DriverEntry) +#endif + + + +NTSTATUS +DriverEntry( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING RegistryPath + ) + +/*++ + +Routine Description: + + This routine performs initialization of the sample + 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 ST's node in the registry. + +Return Value: + + The function value is the final status from the initialization operation. + +--*/ + +{ + ULONG i, j; + STRING nameString; + PDEVICE_CONTEXT DeviceContext; + PTP_REQUEST Request; + PTP_CONNECTION Connection; + PTP_ADDRESS_FILE AddressFile; + PTP_ADDRESS Address; + PTP_PACKET Packet; + PNDIS_PACKET NdisPacket; + PRECEIVE_PACKET_TAG ReceiveTag; + PBUFFER_TAG BufferTag; + NTSTATUS status; + UINT SuccessfulOpens; + UINT MaxUserData; + + PCONFIG_DATA StConfig = NULL; + + + ASSERT (sizeof (SHORT) == 2); + + // + // This allocates the CONFIG_DATA structure and returns + // it in StConfig. + // + + status = StConfigureTransport(RegistryPath, &StConfig); + + if (!NT_SUCCESS (status)) { + PANIC (" Failed to initialize transport, St initialization failed.\n"); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // make ourselves known to the NDIS wrapper. + // + + RtlInitString( &nameString, ST_DEVICE_NAME ); + + status = StRegisterProtocol (&nameString); + + if (!NT_SUCCESS (status)) { + + StFreeConfigurationInfo(StConfig); + PANIC ("StInitialize: RegisterProtocol failed!\n"); + + StWriteGeneralErrorLog( + (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] = StDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLOSE] = StDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_CLEANUP] = StDispatchOpenClose; + DriverObject->MajorFunction [IRP_MJ_INTERNAL_DEVICE_CONTROL] = StDispatchInternal; + DriverObject->MajorFunction [IRP_MJ_DEVICE_CONTROL] = StDispatch; + + DriverObject->DriverUnload = StUnload; + + // + // Initialize the global list of devices. + // + + InitializeListHead (&StDeviceList); + + SuccessfulOpens = 0; + + for (j=0;j<StConfig->NumAdapters;j++ ) { + + + // + // Loop through all the adapters that are in the configuration + // information structure. Allocate a device object for each + // one that we find. + // + + status = StCreateDeviceContext (DriverObject, &StConfig->Names[StConfig->DevicesOffset+j], &DeviceContext); + + if (!NT_SUCCESS (status)) { + continue; + } + + // + // Initialize our counter that records memory usage. + // + + DeviceContext->MemoryUsage = 0; + DeviceContext->MemoryLimit = StConfig->MaxMemoryUsage; + + // + // Now fire up NDIS so this adapter talks + // + + status = StInitializeNdis (DeviceContext, + StConfig, + j); + + if (!NT_SUCCESS (status)) { + + // + // Log an error. + // + + StWriteGeneralErrorLog( + DeviceContext, + EVENT_TRANSPORT_BINDING_FAILED, + 601, + status, + StConfig->Names[j].Buffer, + 0, + NULL); + + StDereferenceDeviceContext ("Initialize NDIS failed", DeviceContext); + continue; + + } + + + // + // 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, + &MaxUserData); + + DeviceContext->Information.Version = 0x0100; + DeviceContext->Information.MaxSendSize = 0x1fffe; // 128k - 2 + DeviceContext->Information.MaxConnectionUserData = 0; + DeviceContext->Information.MaxDatagramSize = MaxUserData - sizeof(ST_HEADER); + DeviceContext->Information.ServiceFlags = ST_SERVICE_FLAGS; + DeviceContext->Information.MinimumLookaheadData = 128; + DeviceContext->Information.MaximumLookaheadData = + DeviceContext->MaxReceivePacketSize - sizeof(ST_HEADER); + DeviceContext->Information.NumberOfResources = ST_TDI_RESOURCES; + KeQuerySystemTime (&DeviceContext->Information.StartTime); + + + // + // Allocate various structures we will need. + // + + + // + // 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. + // + + DeviceContext->PacketHeaderLength = + DeviceContext->MacInfo.MaxHeaderLength + + sizeof (ST_HEADER); + + 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]); + + + for (i=0; i<StConfig->InitRequests; i++) { + + StAllocateRequest (DeviceContext, &Request); + + if (Request == NULL) { + PANIC ("StInitialize: insufficient memory to allocate requests.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + InsertTailList (&DeviceContext->RequestPool, &Request->Linkage); + } + + DeviceContext->RequestInitAllocated = StConfig->InitRequests; + DeviceContext->RequestMaxAllocated = StConfig->MaxRequests; + + + for (i=0; i<StConfig->InitConnections; i++) { + + StAllocateConnection (DeviceContext, &Connection); + + if (Connection == NULL) { + PANIC ("StInitialize: insufficient memory to allocate connections.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + InsertTailList (&DeviceContext->ConnectionPool, &Connection->LinkList); + } + + DeviceContext->ConnectionInitAllocated = StConfig->InitConnections; + DeviceContext->ConnectionMaxAllocated = StConfig->MaxConnections; + + + for (i=0; i<StConfig->InitAddressFiles; i++) { + + StAllocateAddressFile (DeviceContext, &AddressFile); + + if (AddressFile == NULL) { + PANIC ("StInitialize: insufficient memory to allocate Address Files.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + InsertTailList (&DeviceContext->AddressFilePool, &AddressFile->Linkage); + } + + DeviceContext->AddressFileInitAllocated = StConfig->InitAddressFiles; + DeviceContext->AddressFileMaxAllocated = StConfig->MaxAddressFiles; + + + for (i=0; i<StConfig->InitAddresses; i++) { + + StAllocateAddress (DeviceContext, &Address); + if (Address == NULL) { + PANIC ("StInitialize: insufficient memory to allocate addresses.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + InsertTailList (&DeviceContext->AddressPool, &Address->Linkage); + } + + DeviceContext->AddressInitAllocated = StConfig->InitAddresses; + DeviceContext->AddressMaxAllocated = StConfig->MaxAddresses; + + + for (i=0; i<StConfig->InitPackets; i++) { + + StAllocateSendPacket (DeviceContext, &Packet); + if (Packet == NULL) { + PANIC ("StInitialize: insufficient memory to allocate packets.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + PushEntryList (&DeviceContext->PacketPool, (PSINGLE_LIST_ENTRY)&Packet->Linkage); + } + + DeviceContext->PacketInitAllocated = StConfig->InitPackets; + + + for (i=0; i<StConfig->InitReceivePackets; i++) { + + StAllocateReceivePacket (DeviceContext, &NdisPacket); + + if (NdisPacket == NULL) { + PANIC ("StInitialize: insufficient memory to allocate packet MDLs.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + ReceiveTag = (PRECEIVE_PACKET_TAG)NdisPacket->ProtocolReserved; + PushEntryList (&DeviceContext->ReceivePacketPool, (PSINGLE_LIST_ENTRY)&ReceiveTag->Linkage); + + } + + DeviceContext->ReceivePacketInitAllocated = StConfig->InitReceivePackets; + + + for (i=0; i<StConfig->InitReceiveBuffers; i++) { + + StAllocateReceiveBuffer (DeviceContext, &BufferTag); + + if (BufferTag == NULL) { + PANIC ("StInitialize: Unable to allocate receive packet.\n"); + status = STATUS_INSUFFICIENT_RESOURCES; + goto cleanup; + } + + PushEntryList (&DeviceContext->ReceiveBufferPool, &BufferTag->Linkage); + + } + + DeviceContext->ReceiveBufferInitAllocated = StConfig->InitReceiveBuffers; + + + // + // Now link the device into the global list. + // + + InsertTailList (&StDeviceList, &DeviceContext->Linkage); + + DeviceContext->State = DEVICECONTEXT_STATE_OPEN; + + ++SuccessfulOpens; + + continue; + +cleanup: + + StWriteResourceErrorLog (DeviceContext, DeviceContext->MemoryUsage, 501); + + // + // Cleanup whatever device context we were initializing + // when we failed. + // + + StFreeResources (DeviceContext); + StCloseNdis (DeviceContext); + StDereferenceDeviceContext ("Load failed", DeviceContext); + + } + + StFreeConfigurationInfo(StConfig); + + return ((SuccessfulOpens > 0) ? STATUS_SUCCESS : STATUS_DEVICE_DOES_NOT_EXIST); + +} + +VOID +StUnload( + IN PDRIVER_OBJECT DriverObject + ) + +/*++ + +Routine Description: + + This routine unloads the sample 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 ST 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); + + // + // Walk the list of device contexts. + // + + while (!IsListEmpty (&StDeviceList)) { + + p = RemoveHeadList (&StDeviceList); + DeviceContext = CONTAINING_RECORD (p, DEVICE_CONTEXT, Linkage); + + // + // Remove all the storage associated with the device. + // + + StFreeResources (DeviceContext); + + // + // Free the packet pools, etc. and close the + // adapter. + // + + StCloseNdis (DeviceContext); + + // + // And remove the creation reference from the device + // context. + // + + StDereferenceDeviceContext ("Unload", DeviceContext); + + } + + + // + // Finally, remove ourselves as an NDIS protocol. + // + + StDeregisterProtocol(); + + return; + +} + + +VOID +StFreeResources ( + IN PDEVICE_CONTEXT DeviceContext + ) +/*++ + +Routine Description: + + This routine is called by ST 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_ADDRESS address; + PTP_CONNECTION connection; + PTP_REQUEST request; + PTP_ADDRESS_FILE addressFile; + PNDIS_PACKET ndisPacket; + PBUFFER_TAG BufferTag; + + + // + // Clean up packet pool. + // + + while ( DeviceContext->PacketPool.Next != NULL ) { + s = PopEntryList( &DeviceContext->PacketPool ); + packet = CONTAINING_RECORD( s, TP_PACKET, Linkage ); + + StDeallocateSendPacket (DeviceContext, packet); + } + + // + // Clean up address pool. + // + + while ( !IsListEmpty (&DeviceContext->AddressPool) ) { + p = RemoveHeadList (&DeviceContext->AddressPool); + address = CONTAINING_RECORD (p, TP_ADDRESS, Linkage); + + StDeallocateAddress (DeviceContext, address); + } + + // + // Clean up address file pool. + // + + while ( !IsListEmpty (&DeviceContext->AddressFilePool) ) { + p = RemoveHeadList (&DeviceContext->AddressFilePool); + addressFile = CONTAINING_RECORD (p, TP_ADDRESS_FILE, Linkage); + + StDeallocateAddressFile (DeviceContext, addressFile); + } + + // + // Clean up connection pool. + // + + while ( !IsListEmpty (&DeviceContext->ConnectionPool) ) { + p = RemoveHeadList (&DeviceContext->ConnectionPool); + connection = CONTAINING_RECORD (p, TP_CONNECTION, LinkList); + + StDeallocateConnection (DeviceContext, connection); + } + + // + // Clean up request pool. + // + + while ( !IsListEmpty( &DeviceContext->RequestPool ) ) { + p = RemoveHeadList( &DeviceContext->RequestPool ); + request = CONTAINING_RECORD (p, TP_REQUEST, Linkage ); + + StDeallocateRequest (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]); + + StDeallocateReceivePacket (DeviceContext, ndisPacket); + } + + + // + // Clean up receive buffer pool. + // + + while ( DeviceContext->ReceiveBufferPool.Next != NULL ) { + s = PopEntryList( &DeviceContext->ReceiveBufferPool ); + BufferTag = CONTAINING_RECORD (s, BUFFER_TAG, Linkage ); + + StDeallocateReceiveBuffer (DeviceContext, BufferTag); + } + + + return; + +} /* StFreeResources */ + + +NTSTATUS +StDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is the main dispatch routine for the ST 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; + + // + // Check to see if ST 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) { + 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: + Status = StDeviceControl (DeviceObject, Irp, IrpSp); + break; + + default: + Status = STATUS_INVALID_DEVICE_REQUEST; + + } /* major function switch */ + + if (Status != STATUS_PENDING) { + IrpSp->Control &= ~SL_PENDING_RETURNED; + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + } + + // + // Return the immediate status code to the caller. + // + + return Status; +} /* StDispatch */ + + +NTSTATUS +StDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine is the main dispatch routine for the ST 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; + + // + // Check to see if ST 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) { + 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: + + 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 = StOpenAddress (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 = StOpenConnection (DeviceObject, Irp, IrpSp); + break; + } + + } else { + + 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)ST_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. + // + + 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 StCloseAddress. + // + + Status = StVerifyAddressObject(AddressFile); + + if (!NT_SUCCESS (Status)) { + Status = STATUS_INVALID_HANDLE; + } else { + Status = StCloseAddress (DeviceObject, Irp, IrpSp); + } + + break; + + case TDI_CONNECTION_FILE: + + // + // This is a connection + // + + Connection = (PTP_CONNECTION)IrpSp->FileObject->FsContext; + Status = StVerifyConnectionObject (Connection); + if (NT_SUCCESS (Status)) { + + Status = StCloseConnection (DeviceObject, Irp, IrpSp); + StDereferenceConnection ("Temporary Use",Connection); + + } + + break; + + case ST_FILE_TYPE_CONTROL: + + // + // this always succeeds + // + + Status = STATUS_SUCCESS; + break; + + default: + 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. + // + + switch ((ULONG)IrpSp->FileObject->FsContext2) { + case TDI_TRANSPORT_ADDRESS_FILE: + AddressFile = (PTP_ADDRESS_FILE)IrpSp->FileObject->FsContext; + Status = StVerifyAddressObject(AddressFile); + if (!NT_SUCCESS (Status)) { + + Status = STATUS_INVALID_HANDLE; + + } else { + + StStopAddressFile (AddressFile, AddressFile->Address); + StDereferenceAddress ("IRP_MJ_CLEANUP", AddressFile->Address); + Status = STATUS_SUCCESS; + } + + break; + + case TDI_CONNECTION_FILE: + + Connection = (PTP_CONNECTION)IrpSp->FileObject->FsContext; + Status = StVerifyConnectionObject (Connection); + if (NT_SUCCESS (Status)) { + StStopConnection (Connection, STATUS_LOCAL_DISCONNECT); + Status = STATUS_SUCCESS; + StDereferenceConnection ("Temporary Use",Connection); + } + + break; + + case ST_FILE_TYPE_CONTROL: + + Status = STATUS_SUCCESS; + break; + + default: + Status = STATUS_INVALID_HANDLE; + } + + break; + + default: + Status = STATUS_INVALID_DEVICE_REQUEST; + + } /* major function switch */ + + if (Status != STATUS_PENDING) { + IrpSp->Control &= ~SL_PENDING_RETURNED; + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + } + + + // + // Return the immediate status code to the caller. + // + + return Status; +} /* StDispatchOpenClose */ + + +NTSTATUS +StDeviceControl( + 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; + + + // + // 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) { + + default: + + // + // 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. + // + // StDispatchInternal expects to complete the IRP, + // so we change Status to PENDING so we don't. + // + + (VOID)StDispatchInternal (DeviceObject, Irp); + Status = STATUS_PENDING; + + } + } + + return Status; +} /* StDeviceControl */ + + +NTSTATUS +StDispatchInternal ( + 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; + + + // + // 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) { + 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; + + + // + // 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: + Status = StTdiAccept (Irp); + break; + + case TDI_ACTION: + Status = STATUS_SUCCESS; + break; + + case TDI_ASSOCIATE_ADDRESS: + Status = StTdiAssociateAddress (Irp); + break; + + case TDI_DISASSOCIATE_ADDRESS: + Status = StTdiDisassociateAddress (Irp); + break; + + case TDI_CONNECT: + Status = StTdiConnect (Irp); + break; + + case TDI_DISCONNECT: + Status = StTdiDisconnect (Irp); + break; + + case TDI_LISTEN: + Status = StTdiListen (Irp); + break; + + case TDI_QUERY_INFORMATION: + Status = StTdiQueryInformation (DeviceContext, Irp); + break; + + case TDI_RECEIVE: + Status = StTdiReceive (Irp); + break; + + case TDI_RECEIVE_DATAGRAM: + Status = StTdiReceiveDatagram (Irp); + break; + + case TDI_SEND: + Status = StTdiSend (Irp); + break; + + case TDI_SEND_DATAGRAM: + Status = StTdiSendDatagram (Irp); + break; + + case TDI_SET_EVENT_HANDLER: + + // + // 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 = StTdiSetEventHandler (Irp); + break; + + case TDI_SET_INFORMATION: + Status = StTdiSetInformation (Irp); + break; + + + // + // Something we don't know about was submitted. + // + + default: + Status = STATUS_INVALID_DEVICE_REQUEST; + } + + if (Status != STATUS_PENDING) { + IrpSp->Control &= ~SL_PENDING_RETURNED; + Irp->IoStatus.Status = Status; + IoCompleteRequest (Irp, IO_NETWORK_INCREMENT); + } + + + // + // Return the immediate status code to the caller. + // + + return Status; + +} /* StDispatchInternal */ + + +VOID +StWriteResourceErrorLog( + IN PDEVICE_CONTEXT DeviceContext, + IN ULONG BytesNeeded, + IN ULONG UniqueErrorValue + ) + +/*++ + +Routine Description: + + This routine allocates and writes an error log entry indicating + an out of resources condition. + +Arguments: + + DeviceContext - Pointer to the device context. + + BytesNeeded - If applicable, the number of bytes that could not + be allocated. + + UniqueErrorValue - Used as the UniqueErrorValue in the error log + packet. + +Return Value: + + None. + +--*/ + +{ + PIO_ERROR_LOG_PACKET errorLogEntry; + UCHAR EntrySize; + PUCHAR StringLoc; + ULONG TempUniqueError; + static WCHAR UniqueErrorBuffer[4] = L"000"; + UINT i; + + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + + DeviceContext->DeviceNameLength + + sizeof(UniqueErrorBuffer); + + errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( + (PDEVICE_OBJECT)DeviceContext, + EntrySize + ); + + // + // Convert the error value into a buffer. + // + + TempUniqueError = UniqueErrorValue; + for (i=1; i>=0; i--) { + UniqueErrorBuffer[i] = (WCHAR)((TempUniqueError % 10) + L'0'); + TempUniqueError /= 10; + } + + if (errorLogEntry != NULL) { + + errorLogEntry->MajorFunctionCode = (UCHAR)-1; + errorLogEntry->RetryCount = (UCHAR)-1; + errorLogEntry->DumpDataSize = sizeof(ULONG); + errorLogEntry->NumberOfStrings = 2; + errorLogEntry->StringOffset = sizeof(IO_ERROR_LOG_PACKET); + errorLogEntry->EventCategory = 0; + errorLogEntry->ErrorCode = EVENT_TRANSPORT_RESOURCE_POOL; + errorLogEntry->UniqueErrorValue = UniqueErrorValue; + 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, UniqueErrorBuffer, sizeof(UniqueErrorBuffer)); + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* StWriteResourceErrorLog */ + + +VOID +StWriteGeneralErrorLog( + 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; + static WCHAR DriverName[3] = L"St"; + + EntrySize = sizeof(IO_ERROR_LOG_PACKET) + + (DumpDataCount * sizeof(ULONG)); + + if (DeviceContext->Type == IO_TYPE_DEVICE) { + EntrySize += (UCHAR)DeviceContext->DeviceNameLength; + } else { + EntrySize += sizeof(DriverName); + } + + 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, sizeof(DriverName)); + StringLoc += sizeof(DriverName); + } + if (SecondString) { + RtlCopyMemory (StringLoc, SecondString, SecondStringSize); + } + + IoWriteErrorLogEntry(errorLogEntry); + + } + +} /* StWriteGeneralErrorLog */ + + +VOID +StWriteOidErrorLog( + 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; + static WCHAR OidBuffer[9] = L"00000000"; + UINT 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'); + } + } + + 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); + + } + +} /* StWriteOidErrorLog */ diff --git a/private/ntos/tdi/st/sthdrs.h b/private/ntos/tdi/st/sthdrs.h new file mode 100644 index 000000000..737832fc1 --- /dev/null +++ b/private/ntos/tdi/st/sthdrs.h @@ -0,0 +1,69 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + sthdrs.h + +Abstract: + + This module defines private structure definitions describing the layout + of the NT Sample Transport frames. + +Revision History: + +--*/ + +#ifndef _STHDRS_ +#define _STHDRS_ + +// +// 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 ST_SIGNATURE 0x37 + +#define ST_CMD_CONNECT 'C' +#define ST_CMD_DISCONNECT 'D' +#define ST_CMD_INFORMATION 'I' +#define ST_CMD_DATAGRAM 'G' + +#define ST_FLAGS_LAST 0x0001 // for information frames +#define ST_FLAGS_BROADCAST 0x0002 // for datagrams + +typedef struct _ST_HEADER { + UCHAR Signature; // set to ST_SIGNATURE + UCHAR Command; // command byte + UCHAR Flags; // packet flags + UCHAR Reserved; // unused + UCHAR Destination[16]; // destination Netbios address + UCHAR Source[16]; // source Netbios address +} ST_HEADER, *PST_HEADER; + + +// +// Resume previous structure packing method. +// + +#ifdef PACKING + +#ifdef __STDC__ +#pragma Pop(Align_members) +#else +#pragma pack() +#endif // def __STDC__ + +#endif // def PACKING + +#endif // def _STHDRS_ diff --git a/private/ntos/tdi/st/stmac.c b/private/ntos/tdi/st/stmac.c new file mode 100644 index 000000000..157c96a17 --- /dev/null +++ b/private/ntos/tdi/st/stmac.c @@ -0,0 +1,356 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + nbfmac.c + +Abstract: + + This module contains code which implements Mac type dependent code for + the ST transport. + +Environment: + + Kernel mode (Actually, unimportant) + +Revision History: + +--*/ + +#include "st.h" + +UCHAR SingleRouteSourceRouting[] = { 0xc2, 0x70 }; +UCHAR GeneralRouteSourceRouting[] = { 0x82, 0x70 }; +ULONG DefaultSourceRoutingLength = 2; + +VOID +MacInitializeMacInfo( + IN NDIS_MEDIUM MacType, + OUT PST_NDIS_IDENTIFICATION MacInfo + ); + +// +// 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(INIT,MacInitializeMacInfo) +#pragma alloc_text(INIT,MacSetMulticastAddress) +#endif + + + +VOID +MacInitializeMacInfo( + IN NDIS_MEDIUM MacType, + OUT PST_NDIS_IDENTIFICATION MacInfo + ) + +/*++ + +Routine Description: + + Fills in the MacInfo table based on MacType. + +Arguments: + + MacType - The MAC type we wish to decode. + + 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; + MacInfo->MaxHeaderLength = 14; + MacInfo->MediumType = NdisMedium802_3; + break; + case NdisMedium802_5: + MacInfo->DestinationOffset = 2; + MacInfo->SourceOffset = 8; + MacInfo->SourceRouting = TRUE; + MacInfo->AddressLength = 6; + MacInfo->MaxHeaderLength = 32; + MacInfo->MediumType = NdisMedium802_5; + break; + case NdisMediumFddi: + MacInfo->DestinationOffset = 1; + MacInfo->SourceOffset = 7; + MacInfo->SourceRouting = FALSE; + MacInfo->AddressLength = 6; + MacInfo->MaxHeaderLength = 13; + MacInfo->MediumType = NdisMediumFddi; + break; + default: + ASSERT(FALSE); + } +} + +VOID +MacConstructHeader ( + IN PST_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 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 PST_NDIS_IDENTIFICATION MacInfo, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + IN UINT DeviceMaxFrameSize, + 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 headers + 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. + + 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 NdisMedium802_5: + + // + // For 802.5, if we have source routing information then + // use that, otherwise assume the worst. + // + + 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 (DeviceMaxFrameSize < 548) { + *MaxFrameSize = DeviceMaxFrameSize - 32; + } else { + *MaxFrameSize = 516; + } + } + + break; + + case NdisMediumFddi: + + // + // For FDDI, we always have a 13-byte MAC header. + // + + *MaxFrameSize = DeviceMaxFrameSize - 13; + break; + + } +} + + + +VOID +MacSetMulticastAddress ( + IN NDIS_MEDIUM Type, + IN PUCHAR Buffer + ) +/*++ + +Routine Description: + + This routine sets the multicast 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 NdisMediumFddi: + Buffer[0] = 0x03; + Buffer[1] = 0x07; + Buffer[2] = 0x03; + Buffer[3] = 0x07; + Buffer[4] = 0x03; + Buffer[5] = 0x07; + break; + + case NdisMedium802_5: + Buffer[0] = 0xc0; + Buffer[3] = 0x20; + break; + + default: + PANIC ("MacSetMulticastAddress: PANIC! called with unsupported Mac type.\n"); + } +} diff --git a/private/ntos/tdi/st/stmac.h b/private/ntos/tdi/st/stmac.h new file mode 100644 index 000000000..cea2feff0 --- /dev/null +++ b/private/ntos/tdi/st/stmac.h @@ -0,0 +1,635 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + stmac.h + +Abstract: + + This header file defines manifest constants and necessary macros for use + by transports dealing with multiple MAC cards through the NDIS interface. + +Revision History: + +--*/ + +#ifndef _STMAC_ +#define _STMAC_ + +// +// 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 stmac routines, while the +// DeviceContext knows about and uses the last two. +// + +typedef struct _ST_NDIS_IDENTIFICATION { + NDIS_MEDIUM MediumType; + BOOLEAN SourceRouting; + BOOLEAN MediumAsync; + BOOLEAN CopyLookahead; + ULONG DestinationOffset; + ULONG SourceOffset; + ULONG AddressLength; + ULONG MaxHeaderLength; +} ST_NDIS_IDENTIFICATION, *PST_NDIS_IDENTIFICATION; + + + +VOID +MacConstructHeader( + IN PST_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 PST_NDIS_IDENTIFICATION MacInfo, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + IN UINT DeviceMaxFrameSize, + OUT PUINT MaxFrameSize + ); + +VOID +MacInitializeMacInfo( + IN NDIS_MEDIUM MacType, + OUT PST_NDIS_IDENTIFICATION MacInfo + ); + + +extern UCHAR SingleRouteSourceRouting[]; +extern UCHAR GeneralRouteSourceRouting[]; +extern ULONG DefaultSourceRoutingLength; + + +//++ +// +// VOID +// MacReturnDestinationAddress( +// IN PST_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 PST_NDIS_IDENTIFICATION MacInfo, +// IN PVOID Packet, +// OUT PHARDWARE_ADDRESS SourceAddressBuffer, +// OUT PHARDWARE_ADDRESS * SourceAddress, +// ); +// +// 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) \ +{ \ + PUCHAR TmpPacket = (PUCHAR)(_Packet); \ + PUCHAR SrcBuffer = (PUCHAR)(_SourceAddressBuffer); \ + \ + switch ((_MacInfo)->MediumType) { \ + case NdisMedium802_5: \ + *(PULONG)SrcBuffer = *(ULONG UNALIGNED *)(&TmpPacket[8]) & ~0x80;\ + SrcBuffer[4] = TmpPacket[12]; \ + SrcBuffer[5] = TmpPacket[13]; \ + *(_SourceAddress) = (PHARDWARE_ADDRESS)SrcBuffer; \ + break; \ + default: \ + *(_SourceAddress) = (PHARDWARE_ADDRESS)(TmpPacket + \ + (_MacInfo)->SourceOffset); \ + break; \ + } \ +} + + +//++ +// +// VOID +// MacReturnSourceRouting( +// IN PST_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); \ + if ((_MacInfo)->SourceRouting) { \ + if (TmpPacket[8] & 0x80) { \ + *(_SourceRouting) = TmpPacket + 14; \ + *(_SourceRoutingLength) = TmpPacket[14] & 0x1f; \ + } else { \ + *(_SourceRouting) = NULL; \ + } \ + } else { \ + *(_SourceRouting) = NULL; \ + } \ +} + +//++ +// +// VOID +// MacReturnPacketLength( +// IN PST_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) \ +{ \ + 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; \ + } \ +} + +//++ +// +// VOID +// MacReturnHeaderLength( +// IN PST_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: \ + *(_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 PST_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 PST_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 PST_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) | 0x70; \ + *(_NewSR) = (_ExistingSR); \ + break; \ + default: \ + *(_NewSR) = (_ExistingSR); \ + break; \ + } \ + } else { \ + *(_NewSR) = NULL; \ + } \ +} +#endif + + +//++ +// +// VOID +// MacCreateNonBroadcastReplySR( +// IN PST_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) | 0x70; \ + *(_NewSR) = (_ExistingSR); \ + } \ + break; \ + default: \ + *(_NewSR) = (_ExistingSR); \ + break; \ + } \ + } else { \ + *(_NewSR) = NULL; \ + } \ +} + + +//++ +// +// VOID +// MacModifyHeader( +// IN PST_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; \ + } \ +} + + +VOID +MacSetMulticastAddress( + IN NDIS_MEDIUM Type, + IN PUCHAR Buffer + ); + + + +// VOID +// StSetNdisPacketLength ( +// 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 StSetNdisPacketLength(_packet,_length) { \ + PNDIS_BUFFER NdisBuffer; \ + NdisQueryPacket((_packet), NULL, NULL, &NdisBuffer, NULL); \ + NdisAdjustBufferLength(NdisBuffer, (_length)); \ + NdisRecalculatePacketCounts(_packet); \ +} + +#endif // ifdef _STMAC_ + diff --git a/private/ntos/tdi/st/stndis.c b/private/ntos/tdi/st/stndis.c new file mode 100644 index 000000000..43279f815 --- /dev/null +++ b/private/ntos/tdi/st/stndis.c @@ -0,0 +1,986 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + stndis.c + +Abstract: + + This module contains code which implements the routines used to interface + ST and NDIS. All callback routines (except for Transfer Data, + Send Complete, and ReceiveIndication) are here, as well as those routines + called to initialize NDIS. + +Environment: + + Kernel mode + +--*/ + +#include "st.h" + + +// +// This is a one-per-driver variable used in binding +// to the NDIS interface. +// + +NDIS_HANDLE StNdisProtocolHandle = (NDIS_HANDLE)NULL; + +NDIS_STATUS +StSubmitNdisRequest( + IN PDEVICE_CONTEXT DeviceContext, + IN PNDIS_REQUEST NdisRequest, + IN PNDIS_STRING AdapterString + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(INIT,StRegisterProtocol) +#pragma alloc_text(INIT,StSubmitNdisRequest) +#pragma alloc_text(INIT,StInitializeNdis) +#endif + + +NTSTATUS +StRegisterProtocol ( + IN 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; + + NDIS_PROTOCOL_CHARACTERISTICS ProtChars; // Used temporarily to register + + + // + // Set up the characteristics of this protocol + // + + ProtChars.MajorNdisVersion = 3; + ProtChars.MinorNdisVersion = 0; + + ProtChars.Name.Length = NameString->Length; + ProtChars.Name.Buffer = (PVOID)NameString->Buffer; + + ProtChars.OpenAdapterCompleteHandler = StOpenAdapterComplete; + ProtChars.CloseAdapterCompleteHandler = StCloseAdapterComplete; + ProtChars.ResetCompleteHandler = StResetComplete; + ProtChars.RequestCompleteHandler = StRequestComplete; + + ProtChars.SendCompleteHandler = StSendCompletionHandler; + ProtChars.TransferDataCompleteHandler = StTransferDataComplete; + + ProtChars.ReceiveHandler = StReceiveIndication; + ProtChars.ReceiveCompleteHandler = StReceiveComplete; + ProtChars.StatusHandler = StStatusIndication; + ProtChars.StatusCompleteHandler = StStatusComplete; + + NdisRegisterProtocol ( + &ndisStatus, + &StNdisProtocolHandle, + &ProtChars, + (UINT)sizeof(NDIS_PROTOCOL_CHARACTERISTICS) + NameString->Length); + + if (ndisStatus != NDIS_STATUS_SUCCESS) { + return (NTSTATUS)ndisStatus; + } + + return STATUS_SUCCESS; +} + + +VOID +StDeregisterProtocol ( + VOID + ) + +/*++ + +Routine Description: + + This routine removes this transport to the NDIS interface. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + NDIS_STATUS ndisStatus; + + if (StNdisProtocolHandle != (NDIS_HANDLE)NULL) { + NdisDeregisterProtocol ( + &ndisStatus, + StNdisProtocolHandle); + StNdisProtocolHandle = (NDIS_HANDLE)NULL; + } +} + + +NDIS_STATUS +StSubmitNdisRequest( + 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) { + + // + // 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) { + + StWriteOidErrorLog( + 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 +StInitializeNdis ( + IN PDEVICE_CONTEXT DeviceContext, + IN PCONFIG_DATA StConfig, + 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 StSupportedMedia[] = { NdisMedium802_3, NdisMedium802_5, NdisMediumFddi }; + UINT SelectedMedium; + NDIS_REQUEST StRequest; + UCHAR StDataBuffer[6]; + NDIS_OID StOid; + ULONG MinimumLookahead = 128 + sizeof(ST_HEADER); + ULONG ProtocolOptions, MacOptions; + PNDIS_STRING AdapterString; + + // + // Initialize this adapter for ST 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)&StConfig->Names[ConfigInfoNameIndex]; + + NdisOpenAdapter ( + &NdisStatus, + &OpenErrorStatus, + &DeviceContext->NdisBindingHandle, + &SelectedMedium, + StSupportedMedia, + sizeof (StSupportedMedia) / sizeof(NDIS_MEDIUM), + StNdisProtocolHandle, + (NDIS_HANDLE)DeviceContext, + AdapterString, + 0, + NULL); + + if (NdisStatus == NDIS_STATUS_PENDING) { + + // + // 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) { + + StWriteGeneralErrorLog( + 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( + StSupportedMedia[SelectedMedium], + &DeviceContext->MacInfo); + + + // + // Set the multicast/functional addresses first so we avoid windows where we + // receive only part of the addresses. + // + + MacSetMulticastAddress ( + DeviceContext->MacInfo.MediumType, + DeviceContext->MulticastAddress.Address); + + + switch (DeviceContext->MacInfo.MediumType) { + + case NdisMedium802_3: + + // + // Fill in the data for our multicast list. + // + + RtlCopyMemory(StDataBuffer, DeviceContext->MulticastAddress.Address, 6); + + // + // Now fill in the NDIS_REQUEST. + // + + StRequest.RequestType = NdisRequestSetInformation; + StRequest.DATA.SET_INFORMATION.Oid = OID_802_3_MULTICAST_LIST; + StRequest.DATA.SET_INFORMATION.InformationBuffer = &StDataBuffer; + StRequest.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 data for our functional address. + // + + RtlCopyMemory(StDataBuffer, ((PUCHAR)(DeviceContext->MulticastAddress.Address)) + 2, 4); + + // + // Now fill in the NDIS_REQUEST. + // + + StRequest.RequestType = NdisRequestSetInformation; + StRequest.DATA.SET_INFORMATION.Oid = OID_802_5_CURRENT_FUNCTIONAL; + StRequest.DATA.SET_INFORMATION.InformationBuffer = &StDataBuffer; + StRequest.DATA.SET_INFORMATION.InformationBufferLength = 4; + + break; + + case NdisMediumFddi: + + // + // Fill in the data for our multicast list. + // + + RtlCopyMemory(StDataBuffer, DeviceContext->MulticastAddress.Address, 6); + + // + // Now fill in the NDIS_REQUEST. + // + + StRequest.RequestType = NdisRequestSetInformation; + StRequest.DATA.SET_INFORMATION.Oid = OID_FDDI_LONG_MULTICAST_LIST; + StRequest.DATA.SET_INFORMATION.InformationBuffer = &StDataBuffer; + StRequest.DATA.SET_INFORMATION.InformationBufferLength = 6; + + break; + + } + + NdisStatus = StSubmitNdisRequest (DeviceContext, &StRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + StCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + + switch (DeviceContext->MacInfo.MediumType) { + + case NdisMedium802_3: + + StOid = OID_802_3_CURRENT_ADDRESS; + break; + + case NdisMedium802_5: + + StOid = OID_802_5_CURRENT_ADDRESS; + break; + + case NdisMediumFddi: + + StOid = OID_FDDI_LONG_CURRENT_ADDR; + break; + + default: + + NdisStatus = NDIS_STATUS_FAILURE; + break; + + } + + StRequest.RequestType = NdisRequestQueryInformation; + StRequest.DATA.QUERY_INFORMATION.Oid = StOid; + StRequest.DATA.QUERY_INFORMATION.InformationBuffer = DeviceContext->LocalAddress.Address; + StRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 6; + + NdisStatus = StSubmitNdisRequest (DeviceContext, &StRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + StCloseNdis (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. + // + + StRequest.RequestType = NdisRequestQueryInformation; + StRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_MAXIMUM_FRAME_SIZE; + StRequest.DATA.QUERY_INFORMATION.InformationBuffer = &(DeviceContext->MaxReceivePacketSize); + StRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = StSubmitNdisRequest (DeviceContext, &StRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + StCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + StRequest.RequestType = NdisRequestQueryInformation; + StRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_MAXIMUM_TOTAL_SIZE; + StRequest.DATA.QUERY_INFORMATION.InformationBuffer = &(DeviceContext->MaxSendPacketSize); + StRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = StSubmitNdisRequest (DeviceContext, &StRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + StCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Now set the minimum lookahead size. + // + + StRequest.RequestType = NdisRequestSetInformation; + StRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_CURRENT_LOOKAHEAD; + StRequest.DATA.QUERY_INFORMATION.InformationBuffer = &MinimumLookahead; + StRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = StSubmitNdisRequest (DeviceContext, &StRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + StCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Now query the link speed + // + + StRequest.RequestType = NdisRequestQueryInformation; + StRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_LINK_SPEED; + StRequest.DATA.QUERY_INFORMATION.InformationBuffer = &(DeviceContext->MediumSpeed); + StRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = StSubmitNdisRequest (DeviceContext, &StRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + StCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + // + // Now query the MAC's optional characteristics. + // + + StRequest.RequestType = NdisRequestQueryInformation; + StRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_MAC_OPTIONS; + StRequest.DATA.QUERY_INFORMATION.InformationBuffer = &MacOptions; + StRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = StSubmitNdisRequest (DeviceContext, &StRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + StCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Since the sample transport does not try to optimize for the + // cases where transfer data is always synchronous or indications + // are not reentered, we ignore those bits in MacOptions. + // + + DeviceContext->MacInfo.CopyLookahead = + (BOOLEAN)((MacOptions & NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA) != 0); + + + // + // Now set our options if needed. We can only support + // partial indications if running over 802.3 where the + // real packet length can be obtained from the header. + // + + if (DeviceContext->MacInfo.MediumType == NdisMedium802_3) { + + ProtocolOptions = NDIS_PROT_OPTION_ESTIMATED_LENGTH; + + StRequest.RequestType = NdisRequestSetInformation; + StRequest.DATA.QUERY_INFORMATION.Oid = OID_GEN_PROTOCOL_OPTIONS; + StRequest.DATA.QUERY_INFORMATION.InformationBuffer = &ProtocolOptions; + StRequest.DATA.QUERY_INFORMATION.InformationBufferLength = 4; + + NdisStatus = StSubmitNdisRequest (DeviceContext, &StRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + StCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + } + + + // + // 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 = StConfig->SendPacketPoolSize; + + // + // The receive packet pool is used in transfer data. + // + + ReceivePacketPoolSize = StConfig->ReceivePacketPoolSize; + + + NdisAllocatePacketPool ( + &NdisStatus, + &DeviceContext->SendPacketPoolHandle, + SendPacketPoolSize, + SendPacketReservedLength); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + DeviceContext->SendPacketPoolHandle = NULL; + StCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + DeviceContext->MemoryUsage += + (SendPacketPoolSize * + (sizeof(NDIS_PACKET) + SendPacketReservedLength)); + + + NdisAllocatePacketPool( + &NdisStatus, + &DeviceContext->ReceivePacketPoolHandle, + ReceivePacketPoolSize, + ReceivePacketReservedLen); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + DeviceContext->ReceivePacketPoolHandle = NULL; + StCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + DeviceContext->MemoryUsage += + (ReceivePacketPoolSize * + (sizeof(NDIS_PACKET) + ReceivePacketReservedLen)); + + + // + // Allocate the buffer pool; as an estimate, allocate + // one per send or receive packet. + // + + NdisAllocateBufferPool ( + &NdisStatus, + &DeviceContext->NdisBufferPoolHandle, + SendPacketPoolSize + ReceivePacketPoolSize); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + DeviceContext->NdisBufferPoolHandle = NULL; + StCloseNdis (DeviceContext); + 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 NdisMediumFddi: + + RtlStoreUlong((PULONG)StDataBuffer, + (NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_MULTICAST)); + break; + + case NdisMedium802_5: + + RtlStoreUlong((PULONG)StDataBuffer, + (NDIS_PACKET_TYPE_DIRECTED | NDIS_PACKET_TYPE_FUNCTIONAL)); + break; + + default: + + ASSERT (FALSE); + break; + + } + + // + // Now fill in the NDIS_REQUEST. + // + + StRequest.RequestType = NdisRequestSetInformation; + StRequest.DATA.SET_INFORMATION.Oid = OID_GEN_CURRENT_PACKET_FILTER; + StRequest.DATA.SET_INFORMATION.InformationBuffer = &StDataBuffer; + StRequest.DATA.SET_INFORMATION.InformationBufferLength = sizeof(ULONG); + + NdisStatus = StSubmitNdisRequest (DeviceContext, &StRequest, AdapterString); + + if (NdisStatus != NDIS_STATUS_SUCCESS) { + StCloseNdis (DeviceContext); + return STATUS_INSUFFICIENT_RESOURCES; + } + + + return STATUS_SUCCESS; + +} /* StInitializeNdis */ + + +VOID +StCloseNdis ( + 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 StInitializeNdis. + It is written so that it can be called from within StInitializeNdis + 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) { + + // + // 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->SendPacketPoolHandle != NULL) { + NdisFreePacketPool (DeviceContext->SendPacketPoolHandle); + } + + if (DeviceContext->ReceivePacketPoolHandle != NULL) { + NdisFreePacketPool (DeviceContext->ReceivePacketPoolHandle); + } + + if (DeviceContext->NdisBufferPoolHandle != NULL) { + NdisFreeBufferPool (DeviceContext->NdisBufferPoolHandle); + } + +} /* StCloseNdis */ + + +VOID +StOpenAdapterComplete ( + 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; + + DeviceContext->NdisRequestStatus = NdisStatus; + KeSetEvent( + &DeviceContext->NdisRequestEvent, + 0L, + FALSE); + + return; +} + +VOID +StCloseAdapterComplete ( + 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; + + DeviceContext->NdisRequestStatus = NdisStatus; + KeSetEvent( + &DeviceContext->NdisRequestEvent, + 0L, + FALSE); + + return; +} + +VOID +StResetComplete ( + 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); + + return; +} + +VOID +StRequestComplete ( + 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; + + DeviceContext->NdisRequestStatus = NdisStatus; + KeSetEvent( + &DeviceContext->NdisRequestEvent, + 0L, + FALSE); + + return; +} + +VOID +StStatusIndication ( + IN NDIS_HANDLE NdisBindingContext, + IN NDIS_STATUS NdisStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferSize + ) + +{ + PDEVICE_CONTEXT DeviceContext; + + DeviceContext = (PDEVICE_CONTEXT)NdisBindingContext; + + switch (NdisStatus) { + + // + // Handle various status codes here. + // + + default: + break; + + } +} + + +VOID +StStatusComplete ( + IN NDIS_HANDLE NdisBindingContext + ) +{ + UNREFERENCED_PARAMETER (NdisBindingContext); + +} diff --git a/private/ntos/tdi/st/stprocs.h b/private/ntos/tdi/st/stprocs.h new file mode 100644 index 000000000..480927f48 --- /dev/null +++ b/private/ntos/tdi/st/stprocs.h @@ -0,0 +1,923 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + stprocs.h + +Abstract: + + This header file defines private functions for the NT Sample transport + provider. + +Author: + + David Beaver (dbeaver) 1-July-1991 + +Revision History: + +--*/ + +#ifndef _STPROCS_ +#define _STPROCS_ + +// +// MACROS. +// +// +// Debugging aids +// + +// +// VOID +// IF_STDBG( +// IN PSZ Message +// ); +// + +#if DBG +#define IF_STDBG(flags) \ + if (StDebug & (flags)) +#else +#define IF_STDBG(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 StPrint0(fmt) DbgPrint(fmt) +#define StPrint1(fmt,v0) DbgPrint(fmt,v0) +#define StPrint2(fmt,v0,v1) DbgPrint(fmt,v0,v1) +#define StPrint3(fmt,v0,v1,v2) DbgPrint(fmt,v0,v1,v2) +#define StPrint4(fmt,v0,v1,v2,v3) DbgPrint(fmt,v0,v1,v2,v3) +#define StPrint5(fmt,v0,v1,v2,v3,v4) DbgPrint(fmt,v0,v1,v2,v3,v4) +#define StPrint6(fmt,v0,v1,v2,v3,v4,v5) DbgPrint(fmt,v0,v1,v2,v3,v4,v5) +#else +#define StPrint0(fmt) +#define StPrint1(fmt,v0) +#define StPrint2(fmt,v0,v1) +#define StPrint3(fmt,v0,v1,v2) +#define StPrint4(fmt,v0,v1,v2,v3) +#define StPrint5(fmt,v0,v1,v2,v3,v4) +#define StPrint6(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_STDBG (ST_DEBUG_REFCOUNTS) +#else +#define IF_REFDBG if (0) +#endif + +#define StReferenceConnection(Reason, Connection)\ + StRefConnection (Connection) + +#define StDereferenceConnection(Reason, Connection)\ + StDerefConnection (Connection) + +#define StDereferenceConnectionSpecial(Reason, Connection)\ + StDerefConnectionSpecial (Connection) + +#define StReferenceRequest(Reason, Request)\ + (VOID)InterlockedIncrement( \ + &(Request)->ReferenceCount) + +#define StDereferenceRequest(Reason, Request)\ + StDerefRequest (Request) + +#define StReferenceSendIrp(Reason, IrpSp)\ + (VOID)InterlockedIncrement( \ + &IRP_REFCOUNT(IrpSp)) + +#define StDereferenceSendIrp(Reason, IrpSp)\ + StDerefSendIrp (IrpSp) + +#define StReferenceAddress(Reason, Address)\ + (VOID)InterlockedIncrement( \ + &(Address)->ReferenceCount) + +#define StDereferenceAddress(Reason, Address)\ + StDerefAddress (Address) + +#define StReferenceDeviceContext(Reason, DeviceContext)\ + StRefDeviceContext (DeviceContext) + +#define StDereferenceDeviceContext(Reason, DeviceContext)\ + StDerefDeviceContext (DeviceContext) + +#define StReferencePacket(Packet) \ + (VOID)InterlockedIncrement( \ + &(Packet)->ReferenceCount) + +// +// These macros are used to create and destroy packets, due +// to the allocation or deallocation of structure which +// need them. +// + + +#define StAddSendPacket(DeviceContext) { \ + PTP_PACKET _SendPacket; \ + StAllocateSendPacket ((DeviceContext), &_SendPacket); \ + if (_SendPacket != NULL) { \ + ExInterlockedPushEntryList( \ + &(DeviceContext)->PacketPool, \ + (PSINGLE_LIST_ENTRY)&_SendPacket->Linkage, \ + &(DeviceContext)->Interlock); \ + } \ +} + +#define StRemoveSendPacket(DeviceContext) { \ + PSINGLE_LIST_ENTRY s; \ + if (DeviceContext->PacketAllocated > DeviceContext->PacketInitAllocated) { \ + s = ExInterlockedPopEntryList( \ + &(DeviceContext)->PacketPool, \ + &(DeviceContext)->Interlock); \ + if (s != NULL) { \ + StDeallocateSendPacket((DeviceContext), \ + (PTP_PACKET)CONTAINING_RECORD(s, TP_PACKET, Linkage)); \ + } \ + } \ +} + + +#define StAddReceivePacket(DeviceContext) { \ + PNDIS_PACKET _ReceivePacket; \ + StAllocateReceivePacket ((DeviceContext), &_ReceivePacket); \ + if (_ReceivePacket != NULL) { \ + ExInterlockedPushEntryList( \ + &(DeviceContext)->ReceivePacketPool, \ + (PSINGLE_LIST_ENTRY)&((PRECEIVE_PACKET_TAG)_ReceivePacket->ProtocolReserved)->Linkage, \ + &(DeviceContext)->Interlock); \ + } \ +} + +#define StRemoveReceivePacket(DeviceContext) { \ + PSINGLE_LIST_ENTRY s; \ + if (DeviceContext->ReceivePacketAllocated > DeviceContext->ReceivePacketInitAllocated) { \ + s = ExInterlockedPopEntryList( \ + &(DeviceContext)->ReceivePacketPool, \ + &(DeviceContext)->Interlock); \ + if (s != NULL) { \ + StDeallocateReceivePacket((DeviceContext), \ + (PNDIS_PACKET)CONTAINING_RECORD(s, NDIS_PACKET, ProtocolReserved[0])); \ + } \ + } \ +} + + +#define StAddReceiveBuffer(DeviceContext) { \ + PBUFFER_TAG _ReceiveBuffer; \ + StAllocateReceiveBuffer ((DeviceContext), &_ReceiveBuffer); \ + if (_ReceiveBuffer != NULL) { \ + ExInterlockedPushEntryList( \ + &(DeviceContext)->ReceiveBufferPool, \ + &_ReceiveBuffer->Linkage, \ + &(DeviceContext)->Interlock); \ + } \ +} + +#define StRemoveReceiveBuffer(DeviceContext) { \ + PSINGLE_LIST_ENTRY s; \ + if (DeviceContext->ReceiveBufferAllocated > DeviceContext->ReceiveBufferInitAllocated) { \ + s = ExInterlockedPopEntryList( \ + &(DeviceContext)->ReceiveBufferPool, \ + &(DeviceContext)->Interlock); \ + if (s != NULL) { \ + StDeallocateReceiveBuffer(DeviceContext, \ + (PBUFFER_TAG)CONTAINING_RECORD(s, BUFFER_TAG, Linkage)); \ + } \ + } \ +} + + +// +// These routines are used to maintain counters. +// + +#define INCREMENT_COUNTER(_DeviceContext,_Field) \ + ++(_DeviceContext)->_Field + +#define DECREMENT_COUNTER(_DeviceContext,_Field) \ + --(_DeviceContext)->_Field + +#define ADD_TO_LARGE_INTEGER(_LargeInteger,_Ulong) \ + ExInterlockedAddLargeStatistic((_LargeInteger), (ULONG)(_Ulong)) + + + +// +// Routines in PACKET.C (TP_PACKET object manager). +// + +VOID +StAllocateSendPacket( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_PACKET *TransportSendPacket + ); + +VOID +StAllocateReceivePacket( + IN PDEVICE_CONTEXT DeviceContext, + OUT PNDIS_PACKET *TransportReceivePacket + ); + +VOID +StAllocateReceiveBuffer( + IN PDEVICE_CONTEXT DeviceContext, + OUT PBUFFER_TAG *TransportReceiveBuffer + ); + +VOID +StDeallocateSendPacket( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_PACKET TransportSendPacket + ); + +VOID +StDeallocateReceivePacket( + IN PDEVICE_CONTEXT DeviceContext, + IN PNDIS_PACKET TransportReceivePacket + ); + +VOID +StDeallocateReceiveBuffer( + IN PDEVICE_CONTEXT DeviceContext, + IN PBUFFER_TAG TransportReceiveBuffer + ); + +NTSTATUS +StCreatePacket( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_PACKET *Packet + ); + +VOID +StDestroyPacket( + IN PTP_PACKET Packet + ); + +VOID +StWaitPacket( + IN PTP_CONNECTION Connection, + IN ULONG Flags + ); + +// +// 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 EndOfRecord, + KIRQL ConnectionIrql, + KIRQL CancelIrql + ); + +VOID +StCancelReceive( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +// +// Routines in SENDENG.C (Send engine). +// + +VOID +InitializeSend( + PTP_CONNECTION Connection + ); + +VOID +StartPacketizingConnection( + PTP_CONNECTION Connection, + IN BOOLEAN Immediate, + IN KIRQL ConnectionIrql, + IN KIRQL CancelIrql + ); + +VOID +PacketizeConnections( + IN PDEVICE_CONTEXT DeviceContext + ); + +VOID +PacketizeSend( + IN PTP_CONNECTION Connection + ); + +VOID +CompleteSend( + IN PTP_CONNECTION Connection + ); + +VOID +FailSend( + IN PTP_CONNECTION Connection, + IN NTSTATUS RequestStatus, + IN BOOLEAN StopConnection + ); + +VOID +StCancelSend( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +VOID +StNdisSend( + IN PTP_PACKET Packet + ); + +VOID +StSendCompletionHandler( + IN NDIS_HANDLE ProtocolBindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus + ); + +NTSTATUS +BuildBufferChainFromMdlChain ( + IN NDIS_HANDLE BufferPoolHandle, + 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 +StRefDeviceContext( + IN PDEVICE_CONTEXT DeviceContext + ); + +VOID +StDerefDeviceContext( + IN PDEVICE_CONTEXT DeviceContext + ); + +NTSTATUS +StCreateDeviceContext( + IN PDRIVER_OBJECT DriverObject, + IN PUNICODE_STRING DeviceName, + IN OUT PDEVICE_CONTEXT *DeviceContext + ); + +VOID +StDestroyDeviceContext( + IN PDEVICE_CONTEXT DeviceContext + ); + + +// +// Routines in ADDRESS.C (TP_ADDRESS object manager). +// + +VOID +StRefAddress( + IN PTP_ADDRESS Address + ); + +VOID +StDerefAddress( + IN PTP_ADDRESS Address + ); + +VOID +StAllocateAddressFile( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_ADDRESS_FILE *TransportAddressFile + ); + +VOID +StDeallocateAddressFile( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS_FILE TransportAddressFile + ); + +NTSTATUS +StCreateAddressFile( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_ADDRESS_FILE * AddressFile + ); + +VOID +StReferenceAddressFile( + IN PTP_ADDRESS_FILE AddressFile + ); + +VOID +StDereferenceAddressFile( + IN PTP_ADDRESS_FILE AddressFile + ); + +VOID +StStopAddress( + IN PTP_ADDRESS Address + ); + +VOID +StRegisterAddress( + IN PTP_ADDRESS Address + ); + +BOOLEAN +StMatchNetbiosAddress( + IN PTP_ADDRESS Address, + IN PUCHAR NetBIOSName + ); + +VOID +StAllocateAddress( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_ADDRESS *TransportAddress + ); + +VOID +StDeallocateAddress( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS TransportAddress + ); + +NTSTATUS +StCreateAddress( + IN PDEVICE_CONTEXT DeviceContext, + IN PST_NETBIOS_ADDRESS NetworkName, + OUT PTP_ADDRESS *Address + ); + +PTP_ADDRESS +StLookupAddress( + IN PDEVICE_CONTEXT DeviceContext, + IN PST_NETBIOS_ADDRESS NetworkName + ); + +PTP_CONNECTION +StLookupRemoteName( + IN PTP_ADDRESS Address, + IN PUCHAR RemoteName + ); + +NTSTATUS +StStopAddressFile( + IN PTP_ADDRESS_FILE AddressFile, + IN PTP_ADDRESS Address + ); + +NTSTATUS +StVerifyAddressObject ( + IN PTP_ADDRESS_FILE AddressFile + ); + +NTSTATUS +StSendDatagramsOnAddress( + PTP_ADDRESS Address + ); + +// +// +// Routines in CONNOBJ.C (TP_CONNECTION object manager). +// + +VOID +StRefConnection( + IN PTP_CONNECTION TransportConnection + ); + +VOID +StDerefConnection( + IN PTP_CONNECTION TransportConnection + ); + +VOID +StDerefConnectionSpecial( + IN PTP_CONNECTION TransportConnection + ); + +VOID +StStopConnection( + IN PTP_CONNECTION TransportConnection, + IN NTSTATUS Status + ); + +VOID +StCancelConnection( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +PTP_CONNECTION +StLookupListeningConnection( + IN PTP_ADDRESS Address + ); + +VOID +StAllocateConnection( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_CONNECTION *TransportConnection + ); + +VOID +StDeallocateConnection( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_CONNECTION TransportConnection + ); + +NTSTATUS +StCreateConnection( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_CONNECTION *TransportConnection + ); + +PTP_CONNECTION +StLookupConnectionByContext( + IN PTP_ADDRESS Address, + IN CONNECTION_CONTEXT ConnectionContext + ); + +PTP_CONNECTION +StFindConnection( + IN PDEVICE_CONTEXT DeviceContext, + IN PUCHAR LocalName, + IN PUCHAR RemoteName + ); + +NTSTATUS +StVerifyConnectionObject ( + IN PTP_CONNECTION Connection + ); + +// +// Routines in REQUEST.C (TP_REQUEST object manager). +// + + +VOID +TdiRequestTimeoutHandler( + IN PKDPC Dpc, + IN PVOID DeferredContext, + IN PVOID SystemArgument1, + IN PVOID SystemArgument2 + ); + +VOID +StRefRequest( + IN PTP_REQUEST Request + ); + +VOID +StDerefRequest( + IN PTP_REQUEST Request + ); + +VOID +StCompleteRequest( + IN PTP_REQUEST Request, + IN NTSTATUS Status, + IN ULONG Information + ); + +VOID +StRefSendIrp( + IN PIO_STACK_LOCATION IrpSp + ); + +VOID +StDerefSendIrp( + IN PIO_STACK_LOCATION IrpSp + ); + +VOID +StCompleteSendIrp( + IN PIRP Irp, + IN NTSTATUS Status, + IN ULONG Information + ); + +VOID +StAllocateRequest( + IN PDEVICE_CONTEXT DeviceContext, + OUT PTP_REQUEST *TransportRequest + ); + +VOID +StDeallocateRequest( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_REQUEST TransportRequest + ); + +NTSTATUS +StCreateRequest( + 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 DLC.C (entrypoints from NDIS interface). +// + +NDIS_STATUS +StReceiveIndication( + IN NDIS_HANDLE BindingContext, + IN NDIS_HANDLE ReceiveContext, + IN PVOID HeaderBuffer, + IN UINT HeaderBufferSize, + IN PVOID LookaheadBuffer, + IN UINT LookaheadBufferSize, + IN UINT PacketSize + ); + +NDIS_STATUS +StGeneralReceiveHandler ( + IN PDEVICE_CONTEXT DeviceContext, + IN NDIS_HANDLE ReceiveContext, + IN PHARDWARE_ADDRESS SourceAddress, + IN PVOID HeaderBuffer, + IN UINT PacketSize, + IN PST_HEADER StHeader, + IN UINT StSize + ); + +VOID +StReceiveComplete ( + IN NDIS_HANDLE BindingContext + ); + +VOID +StTransferDataComplete( + IN NDIS_HANDLE BindingContext, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS Status, + IN UINT BytesTransferred + ); + +// +// Routines in UFRAMES.C, the UI-frame ST frame processor. +// + +NTSTATUS +StIndicateDatagram( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS Address, + IN PUCHAR Header, + IN ULONG Length + ); + +NTSTATUS +StProcessConnectionless( + IN PDEVICE_CONTEXT DeviceContext, + IN PHARDWARE_ADDRESS SourceAddress, + IN PST_HEADER StHeader, + IN ULONG StLength, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength, + OUT PTP_ADDRESS * DatagramAddress + ); + +// +// Routines in IFRAMES.C, the I-frame ST frame processor. +// + +NTSTATUS +StProcessIIndicate( + IN PTP_CONNECTION Connection, + IN PST_HEADER StHeader, + IN UINT StIndicatedLength, + IN UINT StTotalLength, + IN NDIS_HANDLE ReceiveContext, + IN BOOLEAN Last + ); + +// +// Routines in RCV.C (data copying routines for receives). +// + +NTSTATUS +StCopyMdlToBuffer( + IN PMDL SourceMdlChain, + IN ULONG SourceOffset, + IN PVOID DestinationBuffer, + IN ULONG DestinationOffset, + IN ULONG DestinationBufferSize, + IN PULONG BytesCopied + ); + +// +// Routines in FRAMESND.C, the UI-frame (non-link) shipper. +// + +NTSTATUS +StSendConnect( + IN PTP_CONNECTION Connection + ); + +NTSTATUS +StSendDisconnect( + IN PTP_CONNECTION Connection + ); + +NTSTATUS +StSendAddressFrame( + IN PTP_ADDRESS Address + ); + +VOID +StSendDatagramCompletion( + IN PTP_ADDRESS Address, + IN PNDIS_PACKET NdisPacket, + IN NDIS_STATUS NdisStatus + ); + + +// +// Routines in stdrvr.c +// + +NTSTATUS +StDispatchOpenClose( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +StDispatchInternal( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +NTSTATUS +StDispatch( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ); + +// +// Routine in stndis.c +// + +VOID +StOpenAdapterComplete( + IN NDIS_HANDLE NdisBindingContext, + IN NDIS_STATUS Status, + IN NDIS_STATUS OpenErrorStatus + ); + +VOID +StCloseAdapterComplete( + IN NDIS_HANDLE NdisBindingContext, + IN NDIS_STATUS Status + ); + +VOID +StResetComplete( + IN NDIS_HANDLE NdisBindingContext, + IN NDIS_STATUS Status + ); + +VOID +StRequestComplete( + IN NDIS_HANDLE NdisBindingContext, + IN PNDIS_REQUEST NdisRequest, + IN NDIS_STATUS Status + ); + +VOID +StStatusIndication ( + IN NDIS_HANDLE NdisBindingContext, + IN NDIS_STATUS NdisStatus, + IN PVOID StatusBuffer, + IN UINT StatusBufferLength + ); + +VOID +StStatusComplete ( + IN NDIS_HANDLE NdisBindingContext + ); + +#if DBG +PUCHAR +StGetNdisStatus ( + IN NDIS_STATUS NdisStatus + ); +#endif + +VOID +StWriteResourceErrorLog( + IN PDEVICE_CONTEXT DeviceContext, + IN ULONG BytesNeeded, + IN ULONG UniqueErrorValue + ); + +VOID +StWriteGeneralErrorLog( + IN PDEVICE_CONTEXT DeviceContext, + IN NTSTATUS ErrorCode, + IN ULONG UniqueErrorValue, + IN NTSTATUS FinalStatus, + IN PWSTR SecondString, + IN ULONG DumpDataCount, + IN ULONG DumpData[] + ); + +VOID +StWriteOidErrorLog( + IN PDEVICE_CONTEXT DeviceContext, + IN NTSTATUS ErrorCode, + IN NTSTATUS FinalStatus, + IN PWSTR AdapterString, + IN ULONG OidValue + ); + +VOID +StFreeResources( + IN PDEVICE_CONTEXT DeviceContext + ); + + +// +// routines in stcnfg.c +// + +NTSTATUS +StConfigureProvider( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp, + IN PIO_STACK_LOCATION IrpSp + ); + +// +// Routines in stndis.c +// + +NTSTATUS +StRegisterProtocol ( + IN STRING *NameString + ); + +VOID +StDeregisterProtocol ( + VOID + ); + + +NTSTATUS +StInitializeNdis ( + IN PDEVICE_CONTEXT DeviceContext, + IN PCONFIG_DATA ConfigInfo, + IN UINT ConfigInfoNameIndex + ); + +VOID +StCloseNdis ( + IN PDEVICE_CONTEXT DeviceContext + ); + + +#endif // def _STPROCS_ diff --git a/private/ntos/tdi/st/sttypes.h b/private/ntos/tdi/st/sttypes.h new file mode 100644 index 000000000..fdbc2195a --- /dev/null +++ b/private/ntos/tdi/st/sttypes.h @@ -0,0 +1,1103 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + sttypes.h + +Abstract: + + This module defines private data structures and types for the NT + Sample transport provider. + +Revision History: + +--*/ + +#ifndef _STTYPES_ +#define _STTYPES_ + +// +// 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_LENGTH 16 + +typedef struct _ST_NETBIOS_ADDRESS { + UCHAR NetbiosName[NETBIOS_NAME_LENGTH]; + USHORT NetbiosNameType; +} ST_NETBIOS_ADDRESS, *PST_NETBIOS_ADDRESS; + + +// +// 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. +// + +// +// the types of potential owners of requests +// + +typedef enum _REQUEST_OWNER { + ConnectionType, + AddressType, + DeviceContextType +} REQUEST_OWNER; + +// +// The request itself +// + +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) + 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. + + KTIMER Timer; // kernel timer for this request. + KDPC Dpc; // DPC object for timeouts. + +} TP_REQUEST, *PTP_REQUEST; + +#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_DC 0x0080 // request is attached to a TP_DEVICE_CONTEXT +#define REQUEST_FLAGS_SEND_RCV 0x0100 // request is a TdiSend or TdiReceive +#define REQUEST_FLAGS_DELAY 0x0200 // delay IoCompleteRequest until later + +// +// This defines the TP_IRP_PARAMETERS, which is masked onto the +// Parameters section of a send IRP's stack location. +// + +typedef struct _TP_IRP_PARAMETERS { + TDI_REQUEST_KERNEL_SEND Request; + LONG ReferenceCount; + PVOID Connection; +} TP_IRP_PARAMETERS, *PTP_IRP_PARAMETERS; + +#define IRP_SEND_LENGTH(_IrpSp) \ + (((PTP_IRP_PARAMETERS)&(_IrpSp)->Parameters)->Request.SendLength) + +#define IRP_SEND_FLAGS(_IrpSp) \ + (((PTP_IRP_PARAMETERS)&(_IrpSp)->Parameters)->Request.SendFlags) + +#define IRP_REFCOUNT(_IrpSp) \ + (((PTP_IRP_PARAMETERS)&(_IrpSp)->Parameters)->ReferenceCount) + +#define IRP_CONNECTION(_IrpSp) \ + (((PTP_IRP_PARAMETERS)&(_IrpSp)->Parameters)->Connection) + +#define IRP_DEVICE_CONTEXT(_IrpSp) \ + ((PDEVICE_CONTEXT)((_IrpSp)->DeviceObject)) + + +// +// This structure defines the packet object, used to represent a packet +// 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. + BOOLEAN PacketSent; // packet completed by NDIS. + BOOLEAN PacketNoNdisBuffer; // chain on this packet was not allocated. + BOOLEAN CompleteSend; // last packet in send. + PVOID Provider; // The device context of this packet. + + UCHAR Header[1]; // the headers + +} TP_PACKET, *PTP_PACKET; + + + +// +// This structure defines a TP_CONNECTION, or active transport connection, +// maintained on a transport address. +// + +// +// 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 { + + CSHORT Type; + USHORT Size; + + LIST_ENTRY LinkList; // used for link thread or for free + // resource list + KSPIN_LOCK SpinLock; // spinlock for connection protection. + + 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_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 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 + // requests with this connection. New arrivals are placed at the end of + // the queues (really a linked list) and requests are processed at the + // front of the queues. The first TdiSend request on the SendQueue is + // the current TdiSend being processed, and the first TdiReceive request + // 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 request on the ReceiveQueue is not active. + // These queues are managed by the EXECUTIVE interlocked list manipuation + // routines. The actual objects that are on the queues are request control + // blocks (TP_REQUESTs). + // + + 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. + + // + // These fields are only valid if the CONNECTION_FLAGS_ACTIVE_RECEIVE + // flag is set. + // + + PIRP SpecialReceiveIrp; // a "no-request" receive IRP exists. + PTP_REQUEST CurrentReceiveRequest; // ptr, current receive request. + PMDL CurrentReceiveMdl; // ptr, current MDL in receive chain. + ULONG ReceiveByteOffset; // current byte offset in current MDL. + ULONG ReceiveLength; // current receive length, in bytes (total) + + // + // 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) + + // + // 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. + // + + UINT IndicationInProgress; + + // + // 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; + + // + // The following fields are used for connection housekeeping. + // + + ULONG Flags; // attributes of the connection. + ULONG Flags2; // more attributes of the connection. + NTSTATUS Status; // status code for connection rundown. + ST_NETBIOS_ADDRESS CalledAddress; // TdiConnect request's T.A. + USHORT MaximumDataSize; // maximum I-frame data size. + + CHAR RemoteName[16]; + +} TP_CONNECTION, *PTP_CONNECTION; + + +#define CONNECTION_FLAGS_REMOTE_BUSY 0x00000002 // remote netbios reported NO RECEIVE. +#define CONNECTION_FLAGS_VERSION2 0x00000004 // remote netbios is version 2.0. +#define CONNECTION_FLAGS_RECEIVE_WAKEUP 0x00000008 // send a RECEIVE_OUTSTANDING when a receive arrives. +#define CONNECTION_FLAGS_ACTIVE_RECEIVE 0x00000010 // a receive is active. +#define CONNECTION_FLAGS_LISTENER 0x00000020 // we were the passive listener. +#define CONNECTION_FLAGS_CONNECTOR 0x00000040 // we were the active connector. +#define CONNECTION_FLAGS_WAIT_LISTEN 0x00001000 // waiting for listen. +#define CONNECTION_FLAGS_DESTROY 0x00002000 // destroy this connection. +#define CONNECTION_FLAGS_ABORT 0x00004000 // abort this connection. +#define CONNECTION_FLAGS_ORDREL 0x00008000 // we're in orderly release. +#define CONNECTION_FLAGS_STOPPING 0x00020000 // connection is running down. +#define CONNECTION_FLAGS_READY 0x00040000 // sends/rcvs/discons valid. +#define CONNECTION_FLAGS_SUSPENDED 0x00100000 // we're on the PacketWaitQueue. +#define CONNECTION_FLAGS_PACKETIZE 0x00200000 // we're on the PacketizeQueue. +#define CONNECTION_FLAGS_NO_INDICATE 0x40000000 // don't take packets at indication time +#define CONNECTION_FLAGS_FAILING_TO_EOR 0x80000000 // wait for an EOF in an incoming request before sending + +#define CONNECTION_FLAGS2_CLOSING 0x00000002 // connection is closing +#define CONNECTION_FLAGS2_ASSOCIATED 0x00000004 // associated with address +#define CONNECTION_FLAGS2_DISCONNECT 0x00000008 // disconnect done on connection +#define CONNECTION_FLAGS2_ACCEPTED 0x00000010 // accept done on connection +#define CONNECTION_FLAGS2_INDICATING 0x00000020 // connection was manipulated while + // indication was in progress +#define CONNECTION_FLAGS2_WAIT_ACCEPT 0x00000040 // the connection is waiting for + // and accept to send the + // session confirm +#define CONNECTION_FLAGS2_REQ_COMPLETED 0x00000080 // Listen/Connect request completed. +#define CONNECTION_FLAGS2_DISASSOCIATED 0x00000100 // associate CRef has been removed +#define CONNECTION_FLAGS2_DISCONNECTED 0x00000200 // disconnect has been indicated +#define CONNECTION_FLAGS2_REMOTE_VALID 0x00000800 // Connection->RemoteName is valid +#define CONNECTION_FLAGS2_RCV_CANCELLED 0x00002000 // current receive was cancelled +#define CONNECTION_FLAGS2_PRE_ACCEPT 0x00008000 // no TdiAccept after listen completes +#define CONNECTION_FLAGS2_RC_PENDING 0x00010000 // a receive is pending completion +#define CONNECTION_FLAGS2_PEND_INDICATE 0x00020000 // new data received during RC_PENDING + + +#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. + + +// +// 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 ST, 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. +// + +typedef struct _TP_ADDRESS { + + 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. + PST_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 following structure contains statistics counters for use + // by TdiQueryInformation and TdiSetInformation. They should not + // be used for maintenance of internal data structures. + // + + PTP_PACKET Packet; // header for datagram sends. + + // + // This structure is used for checking share access. + // + + SHARE_ACCESS ShareAccess; + + // + // This structure is used to hold ACLs on the address. + // WARNING: It is allocated from paged pool and can + // only be accessed at IRQL 0. + // + + PSECURITY_DESCRIPTOR SecurityDescriptor; + + // + // Used for delaying StDestroyAddress to a thread so + // we can access the security descriptor. + // + + WORK_QUEUE_ITEM DestroyAddressQueueItem; + +} 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 + + + +// +// This structure defines the DEVICE_OBJECT and its extension allocated at +// the time the transport provider creates its device object. +// + +typedef struct _DEVICE_CONTEXT { + + DEVICE_OBJECT DeviceObject; // the I/O system's device object. + + CSHORT Type; // type of this structure + USHORT Size; // size of this structure + + LIST_ENTRY Linkage; // links them on StDeviceList; + + KSPIN_LOCK Interlock; // GLOBAL spinlock for reference count. + // (used in ExInterlockedXxx calls) + LONG ReferenceCount; // activity count/this provider. + + + // + // The queue of (currently receive only) IRPs waiting to complete. + // + + LIST_ENTRY IrpCompletionQueue; + + // + // 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; + + + // + // 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 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 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 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. + + // + // 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. + // + + NDIS_HANDLE SendPacketPoolHandle; + NDIS_HANDLE ReceivePacketPoolHandle; + NDIS_HANDLE NdisBufferPoolHandle; + PVOID BufferPoolPointer; + + // + // These are kept around for error logging, and stored right + // after this structure. + // + + PWCHAR DeviceName; + ULONG DeviceNameLength; + + // + // This is the Mac type we must build the packet header for and know the + // offsets for. + // + + ST_NDIS_IDENTIFICATION MacInfo; // MAC type and other info + ULONG MaxReceivePacketSize; // does not include the MAC header + ULONG MaxSendPacketSize; // includes the MAC header + + // + // some MAC addresses we use in the transport + // + + HARDWARE_ADDRESS LocalAddress; // our local hardware address. + HARDWARE_ADDRESS MulticastAddress; // used as dest in all send + + // + // The reserved Netbios address; consists of 10 zeroes + // followed by LocalAddress; + // + + UCHAR ReservedNetBIOSAddress[NETBIOS_NAME_LENGTH]; + + // + // These are used while initializing the MAC driver. + // + + KEVENT NdisRequestEvent; // used for pended requests. + NDIS_STATUS NdisRequestStatus; // records request status. + + // + // 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; + + // + // This information is used to keep track of the speed of + // the underlying medium. + // + + ULONG MediumSpeed; // in units of 100 bytes/sec + + + // + // Counters for most of the statistics that ST maintains; + // some of these are kept elsewhere. + // + // *** NOTE: THE ELEMENTS THAT FOLLOW MATCH THE *** + // *** TDI_PROVIDER_STATISTICS STRUCTURE EXACTLY, *** + // *** ALLOWING THEM TO BE COPIED EASILY. DO NOT *** + // *** CHANGE THEM UNLESS THAT STRUCTURE CHANGES. *** + // + + // + // Basic connections counters. + // + + ULONG OpenConnections; + ULONG ConnectionsAfterNoRetry; + ULONG ConnectionsAfterRetry; + + // + // Counters of previous connections, by disconnect reason. + // + + ULONG LocalDisconnects; + ULONG RemoteDisconnects; + ULONG LinkFailures; + ULONG AdapterFailures; + ULONG SessionTimeouts; + ULONG CancelledConnections; + + // + // Keep track of why connect attempts failed. + // + + ULONG RemoteResourceFailures; + ULONG LocalResourceFailures; + ULONG NotFoundFailures; + ULONG NoListenFailures; // where WE sent "no listen" response + + // + // Counters for datagrams. + // + + ULONG DatagramsSent; + LARGE_INTEGER DatagramBytesSent; + ULONG DatagramsReceived; + LARGE_INTEGER DatagramBytesReceived; + + // + // Counters for NDIS packets. + // + + ULONG PacketsSent; + ULONG PacketsReceived; + + // + // Counters for data packets. + // + + ULONG IFramesSent; + LARGE_INTEGER IFrameBytesSent; + ULONG IFramesReceived; + LARGE_INTEGER IFrameBytesReceived; + ULONG IFramesResent; + LARGE_INTEGER IFrameBytesResent; + ULONG IFramesRejected; + LARGE_INTEGER IFrameBytesRejected; + + + // + // LLC stats. + // + + ULONG T1Expirations; + ULONG T2Expirations; + ULONG MaximumSendWindow; + ULONG AverageSendWindow; + + // + // Netbios stats. + // + + ULONG PiggybackAckQueued; + ULONG PiggybackAckTimeouts; + + // + // Keeps track of "wasted" packet space. + // + + LARGE_INTEGER WastedPacketSpace; + ULONG WastedSpacePackets; + + // + // *** END OF SECTION THAT MATCHES TDI_PROVIDER_STATISTICS *** + // + + // + // Counters for "active" time. + // + + LARGE_INTEGER StStartTime; + + // + // This resource guards access to the ShareAccess + // and SecurityDescriptor fields in addresses. + // + + ERESOURCE AddressResource; + + // + // 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. + +} DEVICE_CONTEXT, *PDEVICE_CONTEXT; + +// +// device context state definitions +// + +#define DEVICECONTEXT_STATE_CLOSED 0x00 +#define DEVICECONTEXT_STATE_OPEN 0x01 +#define DEVICECONTEXT_STATE_STOPPING 0x02 + + + +// +// Types used to hold information in the send and receive NDIS packets. +// These are storied in the ProtocolReserved section of the packet. +// + +typedef struct _SEND_PACKET_TAG { + USHORT Type; // identifier for packet type + PTP_PACKET Packet; // backpointer to owning TP_PACKET + PVOID Owner; // backpointer to owning structure +} SEND_PACKET_TAG, *PSEND_PACKET_TAG; + +// +// Packet types used in send completion +// + +#define TYPE_I_FRAME 1 // information +#define TYPE_G_FRAME 2 // datagram +#define TYPE_C_FRAME 3 // connect +#define TYPE_D_FRAME 4 // disconnect + + +// +// receive packet used to hold information about this receive +// + +typedef struct _RECEIVE_PACKET_TAG { + LIST_ENTRY Linkage; // used for threading on receive queue + NDIS_STATUS NdisStatus; // completion status for send + PTP_CONNECTION Connection; // connection this receive is occuring on + 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 + +// +// receive buffer descriptor (built in memory at the beginning of the buffer) +// + +typedef struct _BUFFER_TAG { + SINGLE_LIST_ENTRY Linkage; // so we always know where it is + 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 _TYPES_ + diff --git a/private/ntos/tdi/st/uframes.c b/private/ntos/tdi/st/uframes.c new file mode 100644 index 000000000..c05f6eaa6 --- /dev/null +++ b/private/ntos/tdi/st/uframes.c @@ -0,0 +1,980 @@ +/*++ + +Copyright (c) 1989-1993 Microsoft Corporation + +Module Name: + + uframes.c + +Abstract: + + This module contains a routine called StProcessConnectionless, + that gets control from routines in IND.C when a connectionless + frame is received. + +Environment: + + Kernel mode, DISPATCH_LEVEL. + +Revision History: + +--*/ + +#include "st.h" + + + +NTSTATUS +StIndicateDatagram( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS Address, + IN PUCHAR Header, + 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. + + StHeader - Pointer to a buffer that contains the receive datagram. + The first byte of information is the ST header. + + Length - The length of the MDL pointed to by StHeader. + +Return Value: + + NTSTATUS - status of operation. + +--*/ + +{ + NTSTATUS status; + PLIST_ENTRY p, q; + PIRP irp; + PIO_STACK_LOCATION irpSp; + PTP_REQUEST Request; + ULONG IndicateBytesCopied, MdlBytesCopied; + KIRQL oldirql; + TA_NETBIOS_ADDRESS SourceName; + TA_NETBIOS_ADDRESS DestinationName; + PTDI_CONNECTION_INFORMATION remoteInformation; + ULONG returnLength; + PTP_ADDRESS_FILE addressFile, prevaddressFile; + PST_HEADER StHeader; + + // + // If this datagram wasn't big enough for a transport header, then don't + // let the caller look at any data. + // + + if (Length < sizeof(ST_HEADER)) { + return STATUS_ABANDONED; + } + + // + // Update our statistics. + // + + ++DeviceContext->DatagramsReceived; + ADD_TO_LARGE_INTEGER( + &DeviceContext->DatagramBytesReceived, + Length - sizeof(ST_HEADER)); + + + // + // Call the client's ReceiveDatagram indication handler. He may + // want to accept the datagram that way. + // + + StHeader = (PST_HEADER)Header; + + TdiBuildNetbiosAddress (StHeader->Source, FALSE, &SourceName); + TdiBuildNetbiosAddress (StHeader->Destination, FALSE, &DestinationName); + + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + // + // 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; + } + StReferenceAddressFile(addressFile); + break; + } + + while (p != &Address->AddressFileDatabase) { + + // + // do we have a datagram receive request outstanding? If so, we will + // satisfy it first. + // + // NOTE: We should check if this receive dataframs is for + // a specific address. + // + + q = RemoveHeadList (&addressFile->ReceiveDatagramQueue); + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + if (q != &addressFile->ReceiveDatagramQueue) { + + Request = CONTAINING_RECORD (q, TP_REQUEST, Linkage); + + // + // Copy the actual user data. + // + + MdlBytesCopied = 0; + + status = TdiCopyBufferToMdl ( + StHeader, + sizeof(ST_HEADER), // offset + Length - sizeof(ST_HEADER), // length + Request->IoRequestPacket->MdlAddress, + 0, + &MdlBytesCopied); + + irpSp = IoGetCurrentIrpStackLocation (Request->IoRequestPacket); + 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 (); + + } + + } + + StCompleteRequest (Request, STATUS_SUCCESS, MdlBytesCopied); + + } else { + + // + // no receive datagram requests; is there a kernel client? + // + + if (addressFile->RegisteredReceiveDatagramHandler) { + + IndicateBytesCopied = 0; + + // + // Note that we can always set the COPY_LOOKAHEAD + // flag because we are indicating from our own + // buffer, not directly from a lookahead indication. + // + + status = (*addressFile->ReceiveDatagramHandler)( + addressFile->ReceiveDatagramHandlerContext, + sizeof (TA_NETBIOS_ADDRESS), + &SourceName, + 0, + NULL, + TDI_RECEIVE_COPY_LOOKAHEAD, + Length - sizeof(ST_HEADER), // indicated + Length - sizeof(ST_HEADER), // available + &IndicateBytesCopied, + Header + sizeof(ST_HEADER), + &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. + // + + 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; + + status = TdiCopyBufferToMdl ( + StHeader, + sizeof(ST_HEADER) + IndicateBytesCopied, + Length - sizeof(ST_HEADER) - IndicateBytesCopied, + irp->MdlAddress, + 0, + &MdlBytesCopied); + + irp->IoStatus.Information = MdlBytesCopied; + irp->IoStatus.Status = status; + IoCompleteRequest (irp, IO_NETWORK_INCREMENT); + } + } + } + + // + // Save this to dereference it later. + // + + prevaddressFile = addressFile; + + // + // Reference the next address file on the list, so it + // stays around. + // + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + 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; + } + StReferenceAddressFile(addressFile); + break; + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + // + // Now dereference the previous address file with + // the lock released. + // + + StDereferenceAddressFile (prevaddressFile); + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + } // end of while loop + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + return status; // to dispatcher. +} /* StIndicateDatagram */ + + +NTSTATUS +StProcessConnect( + IN PDEVICE_CONTEXT DeviceContext, + IN PTP_ADDRESS Address, + IN PST_HEADER Header, + IN PHARDWARE_ADDRESS SourceAddress, + IN PUCHAR SourceRouting, + IN UINT SourceRoutingLength + ) + +/*++ + +Routine Description: + + This routine processes an incoming connect frame. It scans for + posted listens, otherwise it indicates to connect handlers + on this address if they are registered. + +Arguments: + + DeviceContext - Pointer to our device context. + + Address - Pointer to the transport address object. + + Header - Pointer to the ST 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. + +--*/ + +{ + KIRQL oldirql, oldirql1, cancelirql; + NTSTATUS status; + PTP_CONNECTION Connection; + BOOLEAN ConnectIndicationBlocked = FALSE; + PLIST_ENTRY p; + BOOLEAN UsedListeningConnection = FALSE; + PTP_ADDRESS_FILE addressFile, prevaddressFile; + + PTP_REQUEST request; + PIO_STACK_LOCATION irpSp; + ULONG returnLength; + PTDI_CONNECTION_INFORMATION remoteInformation; + TA_NETBIOS_ADDRESS TempAddress; + PIRP acceptIrp; + + CONNECTION_CONTEXT connectionContext; + + // + // 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. + // + + if (Address->Flags & (ADDRESS_FLAGS_REGISTERING | ADDRESS_FLAGS_DEREGISTERING)) { + return STATUS_SUCCESS; + } + + // + // 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. + // + + // + // First, check if we already have an active connection with + // this remote on this address. If so, we ignore this + // (NOTE: This is not the correct behaviour for a real + // transport). + // + + // + // If successful this adds a reference. + // + + if (Connection = StLookupRemoteName(Address, Header->Source)) { + + StDereferenceConnection ("Lookup done", Connection); + return STATUS_ABANDONED; + + } + + // If successful, this adds a reference which is removed before + // this function returns. + + Connection = StLookupListeningConnection (Address); + 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_SPIN_LOCK (&Address->SpinLock, &oldirql); + + 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; + } + StReferenceAddressFile(addressFile); + break; + } + + while (p != &Address->AddressFileDatabase) { + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + if ((addressFile->RegisteredConnectionHandler == TRUE) && + (!addressFile->ConnectIndicationInProgress)) { + + + TdiBuildNetbiosAddress ( + Header->Source, + FALSE, + &TempAddress); + + addressFile->ConnectIndicationInProgress = TRUE; + + // + // we have a connection handler, now indicate that a connection + // attempt occurred. + // + + status = (addressFile->ConnectionHandler)( + addressFile->ConnectionHandlerContext, + sizeof (TDI_ADDRESS_NETBIOS), + &TempAddress, + 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 StLookupListeningConnection adds). + // + + Connection = StLookupConnectionByContext ( + Address, + connectionContext); + + if (Connection == NULL) { + + // + // BUGBUG: We have to tell the client that + // his connection is bogus (or has this + // already happened??). + // + + StPrint0("MORE_PROCESSING_REQUIRED, 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; + + StPrint0("MORE_PROCESSING_REQUIRED, address wrong\n"); + StStopConnection (Connection, STATUS_INVALID_ADDRESS); + StDereferenceConnection("Bad Address", Connection); + Connection = NULL; + acceptIrp->IoStatus.Status = STATUS_INVALID_CONNECTION; + 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_SPIN_LOCK (&Connection->SpinLock, &oldirql1); + if ((Connection->Flags2 & CONNECTION_FLAGS2_DISCONNECT) != 0) { + + Connection->Flags2 &= ~CONNECTION_FLAGS2_DISCONNECT; + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql1); + StPrint0("MORE_PROCESSING_REQUIRED, disconnect\n"); + addressFile->ConnectIndicationInProgress = FALSE; + StDereferenceConnection("Disconnecting", Connection); + Connection = NULL; + acceptIrp->IoStatus.Status = STATUS_INVALID_CONNECTION; + IoCompleteRequest (acceptIrp, IO_NETWORK_INCREMENT); + + goto whileend; // try next address file + } + + } + + // + // This connection is ready. + // + + Connection->Flags &= ~CONNECTION_FLAGS_STOPPING; + Connection->Status = STATUS_PENDING; + Connection->Flags2 |= CONNECTION_FLAGS2_ACCEPTED; + + Connection->Flags |= CONNECTION_FLAGS_READY; + INCREMENT_COUNTER (Connection->Provider, OpenConnections); + + Connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED; + + StReferenceConnection("Indication completed", Connection); + + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql1); + + // + // Make a note that we have to set + // addressFile->ConnectIndicationInProgress to + // FALSE once the address is safely stored + // in the connection. + // + + ConnectIndicationBlocked = TRUE; + StDereferenceAddressFile (addressFile); + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + break; // exit the while + + } 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. + // + + addressFile->ConnectIndicationInProgress = FALSE; + + StDereferenceAddressFile (addressFile); + return STATUS_ABANDONED; + + } else { + + addressFile->ConnectIndicationInProgress = FALSE; + goto whileend; // try next address file + + } // end status ifs + + } else { + + 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_SPIN_LOCK (&Address->SpinLock, &oldirql); + + 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; + } + StReferenceAddressFile(addressFile); + break; + } + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + // + // Now dereference the previous address file with + // the lock released. + // + + StDereferenceAddressFile (prevaddressFile); + + ACQUIRE_SPIN_LOCK (&Address->SpinLock, &oldirql); + + } // end of loop through the address files. + + RELEASE_SPIN_LOCK (&Address->SpinLock, oldirql); + + if (Connection == NULL) { + + // + // 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; + + IoAcquireCancelSpinLock (&cancelirql); + ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql); + + p = RemoveHeadList (&Connection->InProgressRequest); + if (p == &Connection->InProgressRequest) { + + Connection->IndicationInProgress = FALSE; + RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql); + IoReleaseCancelSpinLock (cancelirql); + return STATUS_SUCCESS; + + } + + // + // If this listen indicated that we should wait for a + // TdiAccept, then do that, otherwise the connection is + // ready. + // + + if ((Connection->Flags2 & CONNECTION_FLAGS2_PRE_ACCEPT) == 0) { + + Connection->Flags2 |= CONNECTION_FLAGS2_WAIT_ACCEPT; + + } else { + + Connection->Flags |= CONNECTION_FLAGS_READY; + INCREMENT_COUNTER (Connection->Provider, OpenConnections); + + Connection->Flags2 |= CONNECTION_FLAGS2_REQ_COMPLETED; + + StReferenceConnection("Listen completed", Connection); + + } + + // + // 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_SPIN_LOCK (&Connection->SpinLock, oldirql); + + request = CONTAINING_RECORD (p, TP_REQUEST, Linkage); + request->IoRequestPacket->CancelRoutine = (PDRIVER_CANCEL)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; + + StCompleteRequest (request, status, 0); + + } + + + // + // 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; + NdisMoveFromMappedMemory( + Connection->CalledAddress.NetbiosName, + Header->Source, + 16); + + NdisMoveFromMappedMemory( + Connection->RemoteName, + Header->Source, + 16); + + Connection->Flags2 |= CONNECTION_FLAGS2_REMOTE_VALID; + + if (ConnectIndicationBlocked) { + addressFile->ConnectIndicationInProgress = FALSE; + } + + StDereferenceConnection("ProcessNameQuery done", Connection); + + return STATUS_ABANDONED; + +} /* StProcessConnect */ + + +NTSTATUS +StProcessConnectionless( + IN PDEVICE_CONTEXT DeviceContext, + IN PHARDWARE_ADDRESS SourceAddress, + IN PST_HEADER StHeader, + IN ULONG StLength, + 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 connectionless frame has been received on the data link. + Here we dispatch to the correct handler. + +Arguments: + + DeviceContext - Pointer to our device context. + + SourceAddress - Pointer to the source hardware address in the received + frame. + + StHeader - Points to the ST header of the incoming packet. + + StLength - Actual length in bytes of the packet, starting at the + StHeader. + + 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; + KIRQL oldirql; + NTSTATUS status; + PLIST_ENTRY Flink; + BOOLEAN MatchedAddress; + PUCHAR MatchName; + + // + // Verify that this frame is long enough to examine. + // + + if (StLength < sizeof(ST_HEADER)) { + return STATUS_ABANDONED; // frame too small. + } + + // + // We have a valid connectionless protocol frame that's not a + // datagram, so deliver it to every address which matches the + // destination name in the frame. + // + + MatchedAddress = FALSE; + + // + // Search for the address; for broadcast datagrams we + // search for the special "broadcast" address. + // + + if ((StHeader->Command == ST_CMD_DATAGRAM) && + (StHeader->Flags & ST_FLAGS_BROADCAST)) { + + MatchName = NULL; + + } else { + + MatchName = StHeader->Destination; + + } + + ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql); + + 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 (StMatchNetbiosAddress (Address, MatchName)) { + + StReferenceAddress ("UI Frame", Address); // prevent address from being destroyed. + MatchedAddress = TRUE; + break; + + } + } + + RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql); + + if (MatchedAddress) { + + // + // Deliver the frame to the current address. + // + + switch (StHeader->Command) { + + case ST_CMD_CONNECT: + + status = StProcessConnect ( + DeviceContext, + Address, + StHeader, + SourceAddress, + SourceRouting, + SourceRoutingLength); + + break; + + case ST_CMD_DATAGRAM: + + // + // Reference the datagram so it sticks around until the + // ReceiveComplete, when it is processed. + // + + StReferenceAddress ("Datagram indicated", Address); + *DatagramAddress = Address; + status = STATUS_MORE_PROCESSING_REQUIRED; + break; + + default: + + ASSERT(FALSE); + + } /* switch on frame command code */ + + StDereferenceAddress ("Done", Address); // done with previous address. + + } else { + + status = STATUS_ABANDONED; + + } + + return status; + +} /* StProcessConnectionless */ + |