Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

607 lines
12 KiB

/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
command.c
Abstract:
This file contains the code for managing Command Blocks on the
EtherLink's Command Queue.
Author:
Johnson R. Apacible (JohnsonA) 09-June-1991
Environment:
Kernel Mode - Or whatever is the equivalent on OS/2 and DOS.
Revision History:
--*/
#include <ndis.h>
//
// So we can trace things...
//
#define STATIC
#include <efilter.h>
#include <elnkhw.h>
#include <elnksw.h>
BOOLEAN
ElnkSyncStartCommandBlock(
IN PVOID Context
)
/*++
Routine Description:
This function is used to synchronize with the ISR the starting of a
command block.
Arguments:
see NDIS 3.0 spec.
Notes:
returns TRUE on success, else FALSE.
--*/
{
PELNK_ADAPTER Adapter = (PELNK_ADAPTER)Context;
IF_LOG('x');
WRITE_ADAPTER_REGISTER(
Adapter,
OFFSET_SCB_CB,
Adapter->TransmitInfo[Adapter->CommandToStart].CbOffset
);
WRITE_ADAPTER_REGISTER(
Adapter,
OFFSET_SCBCMD,
CUC_START
);
ELNK_CA;
return(TRUE);
}
VOID
ElnkAssignPacketToCommandBlock(
IN PELNK_ADAPTER Adapter,
IN PNDIS_PACKET Packet,
IN UINT CbIndex
)
/*++
Routine Description:
Given a packet and a pointer to a Command Block, assign all of the
buffers in the packet to Command Block.
Arguments:
Adapter - The adapter that the packets are coming through.
Packet - The packet whose buffers are to be assigned
ring entries.
CbIndex - The index of the Command Block to receive the
packet's buffers.
Return Value:
None.
--*/
{
//
// Points to the reserved portion of the packet.
//
PELNK_RESERVED Reserved = PELNK_RESERVED_FROM_PACKET(Packet);
//
// Pointer to the actual transmit command block
//
PTRANSMIT_CB TransmitBlock = Adapter->TransmitInfo[CbIndex].CommandBlock;
//
// index for for loop
//
UINT i;
//
// Points to the current ndis buffer being walked.
//
PNDIS_BUFFER SourceBuffer;
//
// The total amount of data in the ndis packet.
//
UINT TotalVirtualLength;
//
// The virtual address of data in the current ndis buffer.
//
PVOID SourceData;
//
// The length in bytes of data of the current ndis buffer.
//
UINT SourceLength;
//
// The number of ndis buffers in the packet.
//
UINT NdisBufferCount;
//
// We record the owning packet information in the ring packet packet
// structure.
//
Adapter->TransmitInfo[CbIndex].OwningPacket = Packet;
Adapter->TransmitInfo[CbIndex].NextCommand = ELNK_EMPTY;
Adapter->TransmitInfo[CbIndex].OwningOpenBinding = NULL;
//
// Initialize the various fields of the Command Block.
//
NdisWriteRegisterUshort(&TransmitBlock->Status, CB_STATUS_FREE);
NdisWriteRegisterUshort(&TransmitBlock->NextCbOffset, ELNK_NULL);
NdisWriteRegisterUshort(&TransmitBlock->Command, CB_TRANSMIT);
NdisQueryPacket(
Packet,
NULL,
&NdisBufferCount,
&SourceBuffer,
&TotalVirtualLength
);
NdisQueryBuffer(
SourceBuffer,
&SourceData,
&SourceLength
);
ASSERT(SourceLength >= ELNK_HEADER_SIZE);
#if 1
//
// Fill in fields of TFD
//
ELNK_MOVE_MEMORY_TO_SHARED_RAM(
&TransmitBlock->Destination[0],
SourceData,
ETH_LENGTH_OF_ADDRESS
);
SourceData = (PVOID)((PUCHAR)SourceData + 2 * ETH_LENGTH_OF_ADDRESS);
SourceLength -= 2 * ETH_LENGTH_OF_ADDRESS + sizeof(USHORT);
NdisWriteRegisterUshort(&TransmitBlock->Length,
(USHORT)(*((USHORT UNALIGNED *)SourceData))
);
SourceData = (PVOID)((PUCHAR)SourceData + sizeof(USHORT));
if (SourceLength == 0) {
NdisBufferCount--;
NdisGetNextBuffer(
SourceBuffer,
&SourceBuffer
);
NdisQueryBuffer(
SourceBuffer,
&SourceData,
&SourceLength
);
}
#endif
{
//
// Fill in the adapter buffer with the data from the users
// buffers.
//
PVOID CurrentDestination = Adapter->TransmitInfo[CbIndex].Buffer;
for (
i = NdisBufferCount;
i;
i--
) {
if (SourceLength) {
ELNK_MOVE_MEMORY_TO_SHARED_RAM(
CurrentDestination,
SourceData,
SourceLength
);
}
CurrentDestination = (PCHAR)CurrentDestination + SourceLength;
if (i > 1) {
NdisGetNextBuffer(
SourceBuffer,
&SourceBuffer
);
NdisQueryBuffer(
SourceBuffer,
&SourceData,
&SourceLength
);
}
}
//
// If the packet is less than the minimum Ethernet
// packet size, then clear the remaining part of
// the buffer up to the minimum packet size.
//
if (TotalVirtualLength < MINIMUM_ETHERNET_PACKET_SIZE) {
NdisZeroMappedMemory(
CurrentDestination,
MINIMUM_ETHERNET_PACKET_SIZE - TotalVirtualLength
);
TotalVirtualLength = MINIMUM_ETHERNET_PACKET_SIZE;
}
NdisWriteRegisterUshort(
&TransmitBlock->Tbd.Length,
(USHORT)((TotalVirtualLength - ELNK_HEADER_SIZE) | TBD_END_OF_LIST)
);
}
Reserved->CommandBlockIndex = CbIndex;
}
VOID
ElnkSubmitCommandBlock(
IN PELNK_ADAPTER Adapter,
IN UINT CbIndex
)
/*++
Routine Description:
Submit a complete Command Block for execution by the Elnk.
NOTE: This routine assumes that it is called with the lock held.
Arguments:
Adapter - The adapter that points to the ring entry structures.
CbIndex - Holds the index of the Command Block to be submitted.
Return Value:
None.
*--*/
{
//
// Pointer to the most recently submitted Command Block.
//
PTRANSMIT_CB CommandBlock = Adapter->TransmitInfo[CbIndex].CommandBlock;
//
// Index of last command submitted
//
UINT PreviousCommandBlock = Adapter->LastPendingCommand;
USHORT Command;
IF_LOG('s');
//
// Set the Command Block to be the last command block
//
NdisReadRegisterUshort(&CommandBlock->Command, &Command);
NdisWriteRegisterUshort(
&CommandBlock->Command,
(USHORT)(Command |(CB_COMMAND_END_OF_LIST | CB_COMMAND_INTERRUPT))
);
if ELNKDEBUG DPrint2("Submit: Command Block = %x\n",(Command |(CB_COMMAND_END_OF_LIST | CB_COMMAND_INTERRUPT)));
//
// Initialize our command timeout flag.
//
Adapter->TransmitInfo[CbIndex].Timeout = FALSE;
//
// Initialize the next command pointer
//
Adapter->TransmitInfo[CbIndex].NextCommand = ELNK_EMPTY;
//
// Update the pointer to the most recently submitted Command Block.
//
Adapter->LastPendingCommand = CbIndex;
if (PreviousCommandBlock == ELNK_EMPTY) {
if ELNKDEBUG DPrint1("Request sent\n");
if (Adapter->FirstPendingCommand == ELNK_EMPTY ) {
Adapter->FirstPendingCommand = CbIndex;
}
ELNK_WAIT;
Adapter->CommandToStart = CbIndex;
NdisSynchronizeWithInterrupt(
&(Adapter->Interrupt),
(PVOID)ElnkSyncStartCommandBlock,
(PVOID)(Adapter)
);
} else {
//
// Queue the request
//
if ELNKDEBUG DPrint1("Request queued\n");
Adapter->TransmitInfo[PreviousCommandBlock].NextCommand = CbIndex;
}
}
VOID
ElnkSubmitCommandBlockAndWait(
IN PELNK_ADAPTER Adapter
)
/*++
Routine Description:
Submit a command block and wait till it's done. This is done for
setups and configurations.
NOTE: This routine assumes that it is called with the lock held.
Arguments:
Adapter - The adapter that points to the ring entry structures.
Return Value:
None.
--*/
{
UINT i;
USHORT Status;
//
// The value of our scb
//
USHORT Command;
//
// Points to our transmit CB
//
PNON_TRANSMIT_CB CommandBlock = Adapter->MulticastBlock;
//
// Set the Command Block to be the last command block
//
IF_LOG('W');
NdisReadRegisterUshort(&CommandBlock->Command, &Command);
NdisWriteRegisterUshort(&CommandBlock->Command, (USHORT)(Command | CB_COMMAND_END_OF_LIST));
NdisWriteRegisterUshort(&CommandBlock->Status, CB_STATUS_FREE);
if ELNKDEBUG
DPrint2("Command Block requested = %x\n",Command | CB_COMMAND_END_OF_LIST);
ELNK_WAIT;
Adapter->CommandToStart = Adapter->NumberOfTransmitBuffers;
NdisSynchronizeWithInterrupt(
&(Adapter->Interrupt),
(PVOID)ElnkSyncStartCommandBlock,
(PVOID)(Adapter)
);
ELNK_WAIT;
for (i = 0; i <= 20000 ; i++ ) {
NdisReadRegisterUshort(&CommandBlock->Status, &Status);
if (Status & CB_STATUS_COMPLETE) {
break;
}
NdisStallExecution(50);
}
}
BOOLEAN
ElnkAcquireCommandBlock(
IN PELNK_ADAPTER Adapter,
OUT PUINT CbIndex
)
/*++
Routine Description:
Sees if a Command Block is available and if so returns its index.
NOTE: This routine assumes that the lock is held.
Arguments:
Adapter - The adapter that points to the ring entry structures.
CbIndex - will receive an index to a Command Block if one is
available. This value is unpredicable if there is not a free
Command Block.
Return Value:
Returns FALSE if there are no free Command Blocks.
--*/
{
if ELNKDEBUG DPrint1("acquire CB\n");
{
if (Adapter->NumberOfAvailableCommandBlocks) {
//
// Return the Command Block pointer.
//
*CbIndex = Adapter->NextCommandBlock;
//
// Update the head of the Command Queue.
//
Adapter->NextCommandBlock++;
if (Adapter->NextCommandBlock >= Adapter->NumberOfTransmitBuffers) {
Adapter->NextCommandBlock = 0;
}
//
// Update number of available Command Blocks.
//
Adapter->NumberOfAvailableCommandBlocks--;
return TRUE;
} else {
Adapter->StageOpen = FALSE;
return FALSE;
}
}
}
VOID
ElnkRelinquishCommandBlock(
IN PELNK_ADAPTER Adapter,
IN UINT CbIndex
)
/*++
Routine Description:
Relinquish the Command Block resource. If this is a "public"
Command Block, then update the TransmitQueue. If this is a
"private" Command Block, then free its memory.
NOTE: This routine assumes that the lock is held.
Arguments:
Adapter - The adapter that owns the Command Block.
CbIndex - The index of the Command Block to relinquish.
Return Value:
None.
--*/
{
PTRANSMIT_CB CommandBlock = Adapter->TransmitInfo[CbIndex].CommandBlock;
//
// Point the adapter's first pending command to the
// next command on the command queue.
//
if ELNKDEBUG DPrint1("relinquish CB\n");
Adapter->FirstPendingCommand = Adapter->TransmitInfo[CbIndex].NextCommand;
//
// If this is the last pending command block, then we
// can nuke the adapter's last pending command pointer.
//
if (CbIndex == Adapter->LastPendingCommand) {
Adapter->LastPendingCommand = ELNK_EMPTY;
}
NdisWriteRegisterUshort(&CommandBlock->NextCbOffset, ELNK_NULL);
NdisWriteRegisterUshort(&CommandBlock->Status, CB_STATUS_FREE);
Adapter->NumberOfAvailableCommandBlocks++;
}