diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/ndis/ne3200/send.c | 816 |
1 files changed, 816 insertions, 0 deletions
diff --git a/private/ntos/ndis/ne3200/send.c b/private/ntos/ndis/ne3200/send.c new file mode 100644 index 000000000..e2eee44bb --- /dev/null +++ b/private/ntos/ndis/ne3200/send.c @@ -0,0 +1,816 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + send.c + +Abstract: + + This file contains the code for putting a packet through the + staged allocation for transmission. + + This is a process of + + 1) Calculating the what would need to be done to the + packet so that the packet can be transmitted on the hardware. + + 2) Potentially allocating adapter buffers and copying user data + to those buffers so that the packet data is transmitted under + the hardware constraints. + + 3) Allocating enough hardware ring entries so that the packet + can be transmitted. + + 4) Relinquish those ring entries to the hardware. + +Author: + + Anthony V. Ercolano (Tonye) 12-Sept-1990 + Keith Moore (KeithMo) 08-Jan-1991 + +Environment: + + Kernel Mode - Or whatever is the equivalent on OS/2 and DOS. + +Revision History: + + +--*/ + +#include <ne3200sw.h> + +// +// Forward declarations of functions in this file. +// +VOID +NE3200ConstrainPacket( + IN PNE3200_ADAPTER Adapter, + IN PNDIS_PACKET Packet + ); + +NDIS_STATUS +NE3200TransmitPacket( + IN PNE3200_ADAPTER Adapter, + PNDIS_PACKET FirstPacket, + UINT TotalDataLength, + UINT NdisBufferCount, + PNDIS_BUFFER CurrentBuffer + ); + +NDIS_STATUS +NE3200TransmitMergedPacket( + IN PNE3200_ADAPTER Adapter, + PNDIS_PACKET FirstPacket + ); + + +NDIS_STATUS +NE3200Send( + IN NDIS_HANDLE MiniportAdapterContext, + IN PNDIS_PACKET Packet, + IN UINT Flags + ) + +/*++ + +Routine Description: + + The NE3200Send request instructs a Miniport to transmit a packet through + the adapter onto the medium. + +Arguments: + + MiniportAdapterContext - The context value returned by the Miniport when the + adapter was initialized. In reality, it is a pointer to NE3200_ADAPTER. + + Packet - A pointer to a descriptor for the packet that is to be + transmitted. + + Flags - The send options to use. + +Return Value: + + The function value is the status of the operation. + +--*/ + +{ + // + // Pointer to the adapter. + // + PNE3200_ADAPTER Adapter; + + // + // The number of physical buffers in the entire packet. + // + UINT PhysicalBufferCount; + + // + // The total amount of data in the ndis packet. + // + UINT TotalDataLength; + + // + // The number of ndis buffers in the packet. + // + UINT NdisBufferCount; + + // + // Points to the current ndis buffer being walked. + // + PNDIS_BUFFER CurrentBuffer; + + // + // Points to the miniport reserved portion of this packet. This + // interpretation of the reserved section is only valid during + // the allocation phase of the packet. + // + PNE3200_RESERVED Reserved = PNE3200_RESERVED_FROM_PACKET(Packet); + + // + // Status of the transmit. + // + NDIS_STATUS Status; + + // + // The adapter upon which to transmit the packet. + // + Adapter = PNE3200_ADAPTER_FROM_CONTEXT_HANDLE(MiniportAdapterContext); + + IF_LOG('s'); + + // + // Check if this is a packet we rejected earlier due to a lack + // of resources. If so, we don't have to recalculate all the + // constraints. + // + if (!Adapter->PacketResubmission) { + + ASSERT(sizeof(NE3200_RESERVED) <= sizeof(Packet->MiniportReserved)); + + // + // Determine if and how much adapter space would need to be allocated + // to meet hardware constraints. + // + NdisQueryPacket( + Packet, + &PhysicalBufferCount, + &NdisBufferCount, + &CurrentBuffer, + &TotalDataLength + ); + + + // + // See if the packet exceeds NE3200_MAXIMUM_BLOCKS_PER_PACKET. + // Keep in mind that if the total virtual packet length is less than + // MINIMUM_ETHERNET_PACKET_SIZE then we'll have to chain on an + // additional buffer to pad the packet out to the minimum size. + // + if ( PhysicalBufferCount < NE3200_MAXIMUM_BLOCKS_PER_PACKET ) { + + // + // This packet will not need a merge buffer + // + Reserved->UsedNE3200Buffer = FALSE; + + // + // See if we can send it now. + // + Status = NE3200TransmitPacket( + Adapter, + Packet, + TotalDataLength, + NdisBufferCount, + CurrentBuffer + ); + + Adapter->PacketResubmission = + (BOOLEAN)(Status == NDIS_STATUS_RESOURCES); + + IF_LOG('S'); + + return(Status); + + } else { + + // + // We will have to use a merge buffer. Let the processing + // below handle this. + // + if ( (PhysicalBufferCount > NE3200_MAXIMUM_BLOCKS_PER_PACKET) || + (TotalDataLength < MINIMUM_ETHERNET_PACKET_SIZE) ) { + + Reserved->UsedNE3200Buffer = TRUE; + + } else { + + Reserved->UsedNE3200Buffer = FALSE; + + } + + } + + } + + // + // Check if we have to merge this packet. + // + if ( Reserved->UsedNE3200Buffer ) { + + // + // Try and send it now. + // + Status = NE3200TransmitMergedPacket(Adapter, Packet); + + } else { + + // + // Determine if and how much adapter space would need to be allocated + // to meet hardware constraints. + // + + NdisQueryPacket( + Packet, + NULL, + &NdisBufferCount, + &CurrentBuffer, + &TotalDataLength + ); + + + Status = NE3200TransmitPacket( + Adapter, + Packet, + TotalDataLength, + NdisBufferCount, + CurrentBuffer); + + } + + // + // Save if this packet was rejected due to lack of resources. + // + Adapter->PacketResubmission = (BOOLEAN)(Status == NDIS_STATUS_RESOURCES); + + IF_LOG('S'); + + return(Status); +} + + +// +// Put this code inline to save the overhead of the function call. +// +#ifdef _X86_ +__inline +#endif + +NDIS_STATUS +NE3200TransmitPacket( + IN PNE3200_ADAPTER Adapter, + PNDIS_PACKET FirstPacket, + UINT TotalDataLength, + UINT NdisBufferCount, + PNDIS_BUFFER CurrentBuffer + ) + +/*++ + +Routine Description: + + This routine attempts to take a packet through a stage of allocation + and transmit it. + +Arguments: + + Adapter - The adapter that the packets are coming through. + +Return Value: + + NDIS_STATUS_RESOURCES - if there are not enough resources + NDIS_STATUS_PENDING - if sending. + +--*/ + +{ + // + // If we successfully acquire a command block, this + // is a pointer to it. + // + PNE3200_SUPER_COMMAND_BLOCK CommandBlock; + + // + // Pointer to the NE3200 data block descriptor being filled. + // + PNE3200_DATA_BLOCK DataBlock; + + // + // Array to hold the physical segments + // + NDIS_PHYSICAL_ADDRESS_UNIT PhysicalSegmentArray[NE3200_MAXIMUM_BLOCKS_PER_PACKET]; + + // + // Number of physical segments in the buffer + // + UINT BufferPhysicalSegments; + + // + // map register to use for this buffer + // + UINT CurMapRegister; + + // + // Iteration variable + // + UINT i; + + // + // We look to see if there is an available Command Block. + // If there isn't then stage 3 will close. + // + + NE3200AcquireCommandBlock( + Adapter, + &CommandBlock + ); + + if (CommandBlock != NULL) { + + // + // We have a command block. Assign all packet + // buffers to the command block. + // + + // + // Get a pointer to the the first data block descriptor + // in the Command Block. + // + DataBlock = &CommandBlock->Hardware.TransmitDataBlocks[0]; + + // + // We record the owning packet information in the ring packet packet + // structure. + // + CommandBlock->OwningPacket = FirstPacket; + CommandBlock->UsedNE3200Buffer = FALSE; + CommandBlock->NextCommand = NULL; + + // + // Initialize the various fields of the Command Block. + // + CommandBlock->Hardware.State = NE3200_STATE_WAIT_FOR_ADAPTER; + CommandBlock->Hardware.Status = 0; + CommandBlock->Hardware.NextPending = NE3200_NULL; + CommandBlock->Hardware.CommandCode = NE3200_COMMAND_TRANSMIT; + CommandBlock->Hardware.PARAMETERS.TRANSMIT.ImmediateDataLength = 0; + + CommandBlock->Hardware.NumberOfDataBlocks = 0; + CommandBlock->Hardware.TransmitFrameSize = (USHORT)TotalDataLength; + + // + // Set the map registers to use + // + CurMapRegister = CommandBlock->CommandBlockIndex * + NE3200_MAXIMUM_BLOCKS_PER_PACKET; + + // + // Go through all of the buffers in the packet getting + // the actual physical buffers from each NDIS_BUFFER. + // + do { + + NdisMStartBufferPhysicalMapping( + Adapter->MiniportAdapterHandle, + CurrentBuffer, + CurMapRegister, + TRUE, + PhysicalSegmentArray, + &BufferPhysicalSegments + ); + + // + // Go to the next map register + // + CurMapRegister++; + + // + // Store segments into command block + // + for (i = 0; i < BufferPhysicalSegments ; i++, DataBlock++ ) { + + DataBlock->BlockLength = (USHORT)PhysicalSegmentArray[i].Length; + DataBlock->PhysicalAddress = NdisGetPhysicalAddressLow(PhysicalSegmentArray[i].PhysicalAddress); + + } + + // + // Update the number of fragments. + // + CommandBlock->Hardware.NumberOfDataBlocks += BufferPhysicalSegments; + + NdisFlushBuffer(CurrentBuffer, TRUE); + + // + // Go to the next buffer. + // + NdisGetNextBuffer( + CurrentBuffer, + &CurrentBuffer + ); + + } while (CurrentBuffer != NULL); + + // + // If the total packet length is less than MINIMUM_ETHERNET_PACKET_SIZE + // then we must chain the Padding buffer onto the end and update + // the transfer size. + // + if (TotalDataLength >= MINIMUM_ETHERNET_PACKET_SIZE) { + + PNE3200_RESERVED_FROM_PACKET(FirstPacket)->CommandBlockIndex = + CommandBlock->CommandBlockIndex; + + IF_LOG('x'); + + NE3200SubmitCommandBlock( + Adapter, + CommandBlock + ); + + return(NDIS_STATUS_PENDING); + } + + // + // Must do padding + // + DataBlock->BlockLength = + (USHORT)(MINIMUM_ETHERNET_PACKET_SIZE - TotalDataLength); + + DataBlock->PhysicalAddress = NdisGetPhysicalAddressLow(Adapter->PaddingPhysicalAddress); + + DataBlock++; + CommandBlock->Hardware.NumberOfDataBlocks++; + + CommandBlock->Hardware.TransmitFrameSize = MINIMUM_ETHERNET_PACKET_SIZE; + + PNE3200_RESERVED_FROM_PACKET(FirstPacket)->CommandBlockIndex = + CommandBlock->CommandBlockIndex; + + IF_LOG('x'); + + NE3200SubmitCommandBlock( + Adapter, + CommandBlock + ); + + return(NDIS_STATUS_PENDING); + + } else { + + // + // Not enough resources + // + return(NDIS_STATUS_RESOURCES); + + } + +} + + +NDIS_STATUS +NE3200TransmitMergedPacket( + IN PNE3200_ADAPTER Adapter, + PNDIS_PACKET FirstPacket + ) + +/*++ + +Routine Description: + + This routine attempts to take a packet through a stage of allocation + and tranmit it. The packet needs to be merged into a single + before transmitting because it contains more fragments than the + adapter can handle. + +Arguments: + + Adapter - The adapter that the packets are coming through. + +Return Value: + + NDIS_STATUS_RESOURCES - if there are not enough resources + NDIS_STATUS_PENDING - if sending. + +--*/ + +{ + // + // If we successfully acquire a command block, this + // is a pointer to it. + // + PNE3200_SUPER_COMMAND_BLOCK CommandBlock; + + // + // Points to the reserved portion of the packet. + // + PNE3200_RESERVED Reserved; + + // + // Pointer to the NE3200 data block descriptor being filled. + // + PNE3200_DATA_BLOCK DataBlock; + + // + // Points to the adapter buffer descriptor allocated + // for this packet. + // + PNE3200_BUFFER_DESCRIPTOR BufferDescriptor; + + // + // Check that we have a merge buffer if one will be necessary. + // + if ( Adapter->NE3200BufferListHead == -1 ) { + + // + // Not enough space for the packet -- save state + // + return NDIS_STATUS_RESOURCES; + + } + + // + // We look to see if there is an available Command Block. + // If there isn't then stage 3 will close. + // + NE3200AcquireCommandBlock( + Adapter, + &CommandBlock + ); + + if (CommandBlock != NULL) { + + // + // We have a command block. Assign all packet + // buffers to the command block. + // + Reserved = PNE3200_RESERVED_FROM_PACKET(FirstPacket); + + // + // Get a pointer to the the first data block descriptor + // in the Command Block. + // + DataBlock = &CommandBlock->Hardware.TransmitDataBlocks[0]; + + // + // Now we merge the packet into a buffer + // + NE3200ConstrainPacket(Adapter, FirstPacket); + + // + // We record the owning packet information in the ring packet packet + // structure. + // + CommandBlock->OwningPacket = FirstPacket; + CommandBlock->UsedNE3200Buffer = TRUE; + CommandBlock->NE3200BuffersIndex = Reserved->NE3200BuffersIndex; + CommandBlock->NextCommand = NULL; + + // + // Initialize the various fields of the Command Block. + // + CommandBlock->Hardware.State = NE3200_STATE_WAIT_FOR_ADAPTER; + CommandBlock->Hardware.Status = 0; + CommandBlock->Hardware.NextPending = NE3200_NULL; + CommandBlock->Hardware.CommandCode = NE3200_COMMAND_TRANSMIT; + CommandBlock->Hardware.PARAMETERS.TRANSMIT.ImmediateDataLength = 0; + + // + // Get the buffer descriptor + // + BufferDescriptor = Adapter->NE3200Buffers + Reserved->NE3200BuffersIndex; + + // + // Since this packet used one of the adapter buffers, the + // following is known: + // + // o There is exactly one physical buffer for this packet. + // o The buffer's length is the transmit frame size. + // + + // + // Set the number of data blocks and the transmit frame size. + // + NdisFlushBuffer(BufferDescriptor->FlushBuffer, TRUE); + CommandBlock->Hardware.NumberOfDataBlocks = 1; + CommandBlock->Hardware.TransmitFrameSize = + (USHORT)BufferDescriptor->DataLength; + + // + // Initialize the (one) data block for this transmit. + // + DataBlock->BlockLength = (USHORT)BufferDescriptor->DataLength; + DataBlock->PhysicalAddress = NdisGetPhysicalAddressLow(BufferDescriptor->PhysicalNE3200Buffer); + + Adapter->TransmitsQueued++; + + Reserved->CommandBlockIndex = CommandBlock->CommandBlockIndex; + + IF_LOG('x'); + + // + // Start the transmit. + // + NE3200SubmitCommandBlock( + Adapter, + CommandBlock + ); + + return(NDIS_STATUS_PENDING); + } + + return(NDIS_STATUS_RESOURCES); + +} + +STATIC +VOID +NE3200ConstrainPacket( + IN PNE3200_ADAPTER Adapter, + IN PNDIS_PACKET Packet + ) + +/*++ + +Routine Description: + + Given a packet and if necessary attempt to acquire adapter + buffer resources so that the packet meets NE3200 hardware/MAC.BIN + contraints. + + NOTE : MUST BE CALLED WITH NE3200BufferListHead != -1!! + +Arguments: + + Adapter - The adapter the packet is coming through. + + Packet - The packet whose buffers are to be constrained. + The packet reserved section is filled with information + detailing how the packet needs to be adjusted. + +Return Value: + + None. + +--*/ + +{ + + // + // Holds the adapter buffer index available for allocation. + // + INT NE3200BuffersIndex; + + // + // Points to a successfully allocated adapter buffer descriptor. + // + PNE3200_BUFFER_DESCRIPTOR BufferDescriptor; + + // + // Will point into the virtual address space addressed + // by the adapter buffer if one was successfully allocated. + // + PCHAR CurrentDestination; + + // + // Will hold the total amount of data copied to the + // adapter buffer. + // + UINT TotalDataMoved = 0; + + // + // Will point to the current source buffer. + // + PNDIS_BUFFER SourceBuffer; + + // + // Points to the virtual address of the source buffers data. + // + PVOID SourceData; + + // + // The number of ndis buffers in the packet. + // + UINT NdisBufferCount; + + // + // Will point to the number of bytes of data in the source + // buffer. + // + UINT SourceLength; + + // + // The total amount of data contained within the ndis packet. + // + UINT TotalVirtualLength; + + // + // Simple iteration variable. + // + INT i; + + NE3200BuffersIndex = Adapter->NE3200BufferListHead; + + BufferDescriptor = Adapter->NE3200Buffers + NE3200BuffersIndex; + Adapter->NE3200BufferListHead = BufferDescriptor->Next; + + // + // Fill in the adapter buffer with the data from the users + // buffers. + // + CurrentDestination = BufferDescriptor->VirtualNE3200Buffer; + + NdisQueryPacket( + Packet, + NULL, + &NdisBufferCount, + &SourceBuffer, + &TotalVirtualLength + ); + + NdisQueryBuffer( + SourceBuffer, + &SourceData, + &SourceLength + ); + + BufferDescriptor->DataLength = TotalVirtualLength; + + for ( + i = NdisBufferCount; + i; + i-- + ) { + + // + // Copy this buffer + // + NE3200_MOVE_MEMORY( + CurrentDestination, + SourceData, + SourceLength + ); + + // + // Update destination address + // + CurrentDestination = (PCHAR)CurrentDestination + SourceLength; + + // + // Update count of packet length. + // + TotalDataMoved += SourceLength; + + if (i > 1) { + + // + // Get the next buffers information + // + 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) { + + NdisZeroMemory( + CurrentDestination, + MINIMUM_ETHERNET_PACKET_SIZE - TotalVirtualLength + ); + + } + + // + // We need to save in the packet which adapter buffer descriptor + // it is using so that we can deallocate it later. + // + PNE3200_RESERVED_FROM_PACKET(Packet)->NE3200BuffersIndex = NE3200BuffersIndex; +} + |