summaryrefslogtreecommitdiffstats
path: root/private/ntos/ndis/elnkmc/command.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/ndis/elnkmc/command.c')
-rw-r--r--private/ntos/ndis/elnkmc/command.c607
1 files changed, 607 insertions, 0 deletions
diff --git a/private/ntos/ndis/elnkmc/command.c b/private/ntos/ndis/elnkmc/command.c
new file mode 100644
index 000000000..e813eadec
--- /dev/null
+++ b/private/ntos/ndis/elnkmc/command.c
@@ -0,0 +1,607 @@
+/*++
+
+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++;
+}