/*++ 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 // // So we can trace things... // #define STATIC #include #include #include 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++; }