summaryrefslogtreecommitdiffstats
path: root/private/ntos/ndis/sonic/send.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/ndis/sonic/send.c')
-rw-r--r--private/ntos/ndis/sonic/send.c1305
1 files changed, 1305 insertions, 0 deletions
diff --git a/private/ntos/ndis/sonic/send.c b/private/ntos/ndis/sonic/send.c
new file mode 100644
index 000000000..7f41a8f07
--- /dev/null
+++ b/private/ntos/ndis/sonic/send.c
@@ -0,0 +1,1305 @@
+/*++
+
+Copyright (c) 1990-1992 Microsoft Corporation
+
+Module Name:
+
+ send.c
+
+Abstract:
+
+ This file contains the code for putting a packet through the
+ 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:
+
+ Adam Barr (adamba) 16-Nov-1990
+
+Environment:
+
+ Kernel Mode - Or whatever is the equivalent.
+
+Revision History:
+
+
+--*/
+
+
+#include <ndis.h>
+
+#include <sonichrd.h>
+#include <sonicsft.h>
+
+
+
+//
+// This macro will poke the sonic hardware into noticing that
+// there is a packet available for transmit.
+//
+
+#define START_TRANSMIT(_Adapter) \
+ SONIC_WRITE_PORT(_Adapter, SONIC_COMMAND, SONIC_CR_TRANSMIT_PACKETS)
+
+
+STATIC
+VOID
+AssignPacketToDescriptor(
+ IN PSONIC_ADAPTER Adapter,
+ IN PNDIS_PACKET Packet,
+ IN UINT DescriptorIndex
+ );
+
+STATIC
+VOID
+RelinquishPacket(
+ IN PSONIC_ADAPTER Adapter,
+ IN PNDIS_PACKET Packet,
+ IN UINT RingIndex
+ );
+
+STATIC
+VOID
+CalculatePacketConstraints(
+ IN PSONIC_ADAPTER Adapter,
+ IN PNDIS_PACKET Packet
+ );
+
+STATIC
+BOOLEAN
+ConstrainPacket(
+ IN PSONIC_ADAPTER Adapter,
+ IN PNDIS_PACKET Packet
+ );
+
+
+#ifdef CHECK_DUP_SENDS
+
+#define PACKET_LIST_SIZE 20
+
+PNDIS_PACKET SonicPacketList[PACKET_LIST_SIZE];
+UINT SonicPacketListSize = 0;
+
+VOID
+SonicAddPacketToList(
+ PSONIC_ADAPTER Adapter,
+ PNDIS_PACKET NewPacket
+ )
+{
+ INT i;
+
+ for (i=0; i<SonicPacketListSize; i++) {
+
+ if (SonicPacketList[i] == NewPacket) {
+
+ DbgPrint("SONIC: dup send of %lx\n", NewPacket);
+
+ }
+
+ }
+
+ SonicPacketList[SonicPacketListSize] = NewPacket;
+ ++SonicPacketListSize;
+
+}
+
+VOID
+SonicRemovePacketFromList(
+ PSONIC_ADAPTER Adapter,
+ PNDIS_PACKET OldPacket
+ )
+{
+ INT i;
+
+ for (i=0; i<SonicPacketListSize; i++) {
+
+ if (SonicPacketList[i] == OldPacket) {
+
+ break;
+
+ }
+
+ }
+
+ if (i == SonicPacketListSize) {
+
+ DbgPrint("SONIC: bad remove of %lx\n", OldPacket);
+
+ } else {
+
+ --SonicPacketListSize;
+ SonicPacketList[i] = SonicPacketList[SonicPacketListSize];
+
+ }
+
+}
+#endif // CHECK_DUP_SENDS
+
+
+extern
+NDIS_STATUS
+SonicSend(
+ IN NDIS_HANDLE MiniportAdapterContext,
+ IN PNDIS_PACKET Packet,
+ IN UINT SendFlags
+ )
+
+/*++
+
+Routine Description:
+
+ The SonicSend request instructs a driver to transmit a packet through
+ the adapter onto the medium.
+
+Arguments:
+
+ MiniportAdapterContext - Context registered with the wrapper, really
+ a pointer to the adapter.
+
+ Packet - A pointer to a descriptor for the packet that is to be
+ transmitted.
+
+ SendFlags - Optional send flags
+
+Return Value:
+
+ The function value is the status of the operation.
+
+--*/
+
+{
+
+ //
+ // Holds the status that should be returned to the caller.
+ //
+ NDIS_STATUS StatusToReturn = NDIS_STATUS_PENDING;
+
+ //
+ // Pointer to the adapter.
+ //
+ PSONIC_ADAPTER Adapter = PSONIC_ADAPTER_FROM_CONTEXT_HANDLE(MiniportAdapterContext);
+
+ PSONIC_PACKET_RESERVED Reserved;
+
+ //
+ // Holds whether the packet has been constrained
+ // to the hardware requirements.
+ //
+ BOOLEAN SuitableForHardware;
+
+ //
+ // If we successfully acquire some ring entries, this
+ // is the index of the first one.
+ //
+ UINT DescriptorIndex;
+
+#ifdef CHECK_DUP_SENDS
+ SonicAddPacketToList(Adapter, Packet);
+#endif
+
+
+ //
+ // Check to see if the packet should even make it out to
+ // the media. The primary reason this shouldn't *actually*
+ // be sent is if the destination is equal to the source
+ // address.
+ //
+
+ Reserved = PSONIC_RESERVED_FROM_PACKET(Packet);
+
+ //
+ // Determine if and how much adapter space would need to be allocated
+ // to meet hardware constraints.
+ //
+
+ CalculatePacketConstraints(
+ Adapter,
+ Packet
+ );
+
+ //
+ // We look to see if there are enough ring entries.
+ // If there aren't then fail this send.
+ //
+
+ if (Adapter->NumberOfAvailableDescriptors > 1) {
+
+ DescriptorIndex = Adapter->AllocateableDescriptor - Adapter->TransmitDescriptorArea;
+
+ if (Adapter->AllocateableDescriptor == Adapter->LastTransmitDescriptor) {
+
+ Adapter->AllocateableDescriptor = Adapter->TransmitDescriptorArea;
+
+ } else {
+
+ ++(Adapter->AllocateableDescriptor);
+
+ }
+
+ --(Adapter->NumberOfAvailableDescriptors);
+
+ if (Reserved->UsedSonicBuffer == TRUE) {
+
+ //
+ // ConstrainPacket returns FALSE if an adapter buffer
+ // is needed and none is available.
+ //
+
+ SuitableForHardware = ConstrainPacket(
+ Adapter,
+ Packet
+ );
+
+ if (!SuitableForHardware) {
+
+ //
+ // Return transmit descriptor
+ //
+
+ Adapter->AllocateableDescriptor = Adapter->TransmitDescriptorArea +
+ DescriptorIndex;
+
+
+ ++(Adapter->NumberOfAvailableDescriptors);
+
+ StatusToReturn = NDIS_STATUS_RESOURCES;
+
+ goto FinishWithSend;
+
+ }
+
+ }
+
+ //
+ // Put the packet on the finish transmit queue.
+ //
+
+ if (Adapter->FirstFinishTransmit == NULL) {
+
+ Adapter->FirstFinishTransmit = Packet;
+
+ } else {
+
+ PSONIC_RESERVED_FROM_PACKET(Adapter->LastFinishTransmit)->Next = Packet;
+
+ }
+
+ Adapter->LastFinishTransmit = Packet;
+
+ Reserved->Next = NULL;
+
+ //
+ // We have the number of buffers that we need.
+ // We assign all of the buffers to the ring entries.
+ //
+
+ AssignPacketToDescriptor(
+ Adapter,
+ Packet,
+ DescriptorIndex
+ );
+
+ RelinquishPacket(
+ Adapter,
+ Packet,
+ DescriptorIndex
+ );
+
+ } else {
+
+ StatusToReturn = NDIS_STATUS_RESOURCES;
+
+ }
+
+FinishWithSend:
+
+ return StatusToReturn;
+}
+
+STATIC
+BOOLEAN
+ConstrainPacket(
+ IN PSONIC_ADAPTER Adapter,
+ IN PNDIS_PACKET Packet
+ )
+
+/*++
+
+Routine Description:
+
+ Given a packet and necessary attempt to acquire adapter
+ buffer resources so that the packet meets sonic hardware
+ contraints. If a buffer is needed and is not available then
+ return FALSE.
+
+ The constraints are that the packet must have SONIC_MAX_FRAGMENTS
+ or fewer physical pieces and no piece may be less than
+ SONIC_MIN_PIECE_SIZE bytes. The first constraint is based on
+ the size of the SONIC_TRANSMIT_DESCRIPTOR, and the second
+ is to prevent underflow in the Silo.
+
+ If a packet violates either of the constraints then it
+ will be copied in its entirety into an adapter buffer.
+
+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:
+
+ Returns TRUE if the packet is suitable for the hardware.
+
+--*/
+
+{
+
+ //
+ // Pointer to the reserved section of the packet to be contrained.
+ //
+ PSONIC_PACKET_RESERVED Reserved = PSONIC_RESERVED_FROM_PACKET(Packet);
+
+ //
+ // Holds the adapter buffer index available for allocation.
+ //
+ INT SonicBuffersIndex;
+
+ //
+ // Points to a successfully allocated adapter buffer descriptor.
+ //
+ PSONIC_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;
+
+ //
+ // Will point to the number of bytes of data in the source
+ // buffer.
+ //
+ UINT SourceLength;
+
+ //
+ // Simple iteration variable.
+ //
+ INT i;
+
+ //
+ // Total length of the packet
+ //
+ UINT PacketLength;
+
+ NdisQueryPacket(
+ Packet,
+ NULL,
+ NULL,
+ &SourceBuffer,
+ &PacketLength
+ );
+
+ if (PacketLength <= SONIC_SMALL_BUFFER_SIZE) {
+
+ i = 1;
+
+ } else if (PacketLength <= SONIC_MEDIUM_BUFFER_SIZE) {
+
+ i = 2;
+
+ } else {
+
+ i = 3;
+
+ }
+
+
+ for (
+ ;
+ i <= 3;
+ i++
+ ) {
+
+ if ((SonicBuffersIndex = Adapter->SonicBufferListHeads[i]) != -1) {
+
+ BufferDescriptor = Adapter->SonicBuffers + SonicBuffersIndex;
+ Adapter->SonicBufferListHeads[i] = BufferDescriptor->Next;
+ break;
+
+ }
+
+ }
+
+ if (SonicBuffersIndex == -1) {
+
+ //
+ // Nothing available for the packet.
+ //
+ return FALSE;
+
+ }
+
+ //
+ // Save the list head index in the buffer descriptor
+ // to permit easy deallocation later.
+ //
+
+ BufferDescriptor->Next = i;
+
+ //
+ // Fill in the adapter buffer with the data from the users
+ // buffers.
+ //
+
+ CurrentDestination = BufferDescriptor->VirtualSonicBuffer;
+
+ while (SourceBuffer) {
+
+ NdisQueryBuffer(
+ SourceBuffer,
+ &SourceData,
+ &SourceLength
+ );
+
+ SONIC_MOVE_MEMORY(
+ CurrentDestination,
+ SourceData,
+ SourceLength
+ );
+
+ CurrentDestination = (PCHAR)CurrentDestination + SourceLength;
+
+ TotalDataMoved += SourceLength;
+
+ NdisGetNextBuffer(
+ SourceBuffer,
+ &SourceBuffer
+ );
+
+ }
+
+ //
+ // If the packet is less then the minimum size then we
+ // need to zero out the rest of the packet.
+ //
+
+ if (TotalDataMoved < SONIC_MIN_PACKET_SIZE) {
+
+ SONIC_ZERO_MEMORY(
+ CurrentDestination,
+ SONIC_MIN_PACKET_SIZE - TotalDataMoved
+ );
+
+ BufferDescriptor->DataLength = SONIC_MIN_PACKET_SIZE;
+
+ } else {
+
+ BufferDescriptor->DataLength = TotalDataMoved;
+
+ }
+
+ //
+ // We need to save in the packet which adapter buffer descriptor
+ // it is using so that we can deallocate it later.
+ //
+
+ Reserved->SonicBuffersIndex = SonicBuffersIndex;
+
+ return TRUE;
+}
+
+STATIC
+VOID
+CalculatePacketConstraints(
+ IN PSONIC_ADAPTER Adapter,
+ IN PNDIS_PACKET Packet
+ )
+
+/*++
+
+Routine Description:
+
+ Given a packet calculate how the packet will have to be
+ adjusted to meet with hardware constraints.
+
+ The constraints are that the packet must have SONIC_MAX_FRAGMENTS
+ or fewer physical pieces and no piece may be less than
+ SONIC_MIN_FRAGMENT_SIZE bytes. The first constraint is based on
+ the size of the SONIC_TRANSMIT_DESCRIPTOR, and the second
+ is to prevent underflow in the Silo (exception: the last
+ fragment in a packet may be smaller).
+
+ If the packet is found to violate the constraints, then
+ UsedSonicBuffer will be set to TRUE. This will cause the entire packet to
+ be copied into the adapter buffer (which is guaranteed
+ to be physically contiguous).
+
+Arguments:
+
+ Adapter - The adapter the packet is coming through.
+
+ Packet - The packet whose buffers are to be reallocated.
+ The packet reserved section is filled with information
+ detailing how the packet needs to be adjusted.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ //
+ // Points to the MacReserved portion of the packet.
+ //
+ PSONIC_PACKET_RESERVED Reserved = PSONIC_RESERVED_FROM_PACKET(Packet);
+
+ //
+ // The number of physical buffers in the entire packet.
+ //
+ UINT PacketPhysicalSegments;
+
+ //
+ // Points to the current ndis buffer being walked.
+ //
+ PNDIS_BUFFER CurrentBuffer;
+
+ //
+ // The virtual address of the current ndis buffer.
+ //
+ UINT BufferOffset;
+
+ //
+ // The length in bytes of the current ndis buffer.
+ //
+ UINT BufferVirtualLength;
+
+ //
+ // The total amount of data contained within the ndis packet.
+ //
+ UINT PacketVirtualLength;
+
+ //
+ // The number of physical buffers in a single buffer.
+ //
+ UINT BufferPhysicalSegments;
+
+ //
+ // TRUE once we find a constraint violation
+ //
+ BOOLEAN ViolatedConstraints = FALSE;
+
+#ifndef NO_CHIP_FIXUP
+ //
+ // Used to keep track of the total number of fragments
+ // that the packet will occupy when assigned to a
+ // transmit descriptor (may be more than PacketPhysicalSegments
+ // if we have to worry about packets starting or ending
+ // on non-longword boundaries.
+ //
+ UINT TotalTransmitSegments = 0;
+#endif
+
+
+
+ //
+ // Get the first buffer in the packet.
+ //
+
+ NdisQueryPacket(
+ Packet,
+ &PacketPhysicalSegments,
+ NULL,
+ &CurrentBuffer,
+ &PacketVirtualLength
+ );
+
+ //
+ // We only allow SONIC_MAX_FRAGMENTS physical pieces.
+ //
+
+ if (PacketPhysicalSegments > SONIC_MAX_FRAGMENTS) {
+ ViolatedConstraints = TRUE;
+ goto DoneExamining;
+ }
+
+ //
+ // For short packets we can only allow SONIC_MAX_FRAGMENTS-1
+ // (to allow for the blank padding buffer). Also, we can't
+ // allow the padding itself to be less than the minimum
+ // fragment size.
+ //
+
+ if (PacketVirtualLength < SONIC_MIN_PACKET_SIZE &&
+ ((PacketPhysicalSegments > (SONIC_MAX_FRAGMENTS-1)) ||
+ (PacketVirtualLength >
+ (SONIC_MIN_PACKET_SIZE - SONIC_MIN_FRAGMENT_SIZE)))) {
+ ViolatedConstraints = TRUE;
+ goto DoneExamining;
+ }
+
+
+ //
+ // Now loop making sure no fragment is less than
+ // SONIC_MIN_FRAGMENT_SIZE bytes unless it is the
+ // last one.
+ //
+
+ while (CurrentBuffer) {
+
+ NdisQueryBufferOffset(
+ CurrentBuffer,
+ &BufferOffset,
+ &BufferVirtualLength
+ );
+
+
+ //
+ // See if there is only one piece in the buffer.
+ //
+
+ NdisGetBufferPhysicalArraySize(
+ CurrentBuffer,
+ &BufferPhysicalSegments
+ );
+
+ if (BufferPhysicalSegments == 1) {
+
+ //
+ // Only one piece, make sure it is large enough or
+ // is the last fragment.
+ //
+
+ if ((BufferVirtualLength < SONIC_MIN_FRAGMENT_SIZE) &&
+ (NDIS_BUFFER_LINKAGE(CurrentBuffer) != NULL)) {
+ ViolatedConstraints = TRUE;
+ goto DoneExamining;
+ }
+
+#ifndef NO_CHIP_FIXUP
+
+ //
+ // See if the beginning AND end of this piece are
+ // not longword-aligned.
+ //
+
+ if (((ULONG)BufferOffset & 0x03) &&
+ (((ULONG)BufferOffset + BufferVirtualLength) & 0x03)) {
+
+ //
+ // Now see if this piece is large enough to
+ // be split into two.
+ //
+
+ if (BufferVirtualLength >
+ (UINT)(4 - ((ULONG)BufferOffset & 0x03) +
+ (2*SONIC_MIN_FRAGMENT_SIZE))) {
+
+ //
+ // Have enough to let the first fragment be
+ // SONIC_MIN_FRAGMENT_SIZE plus the extra
+ // few bytes at the beginning, and the
+ // second piece SONIC_MIN_FRAGMENT_SIZE.
+ //
+
+ TotalTransmitSegments += 2;
+
+ } else {
+
+ ViolatedConstraints = TRUE;
+ goto DoneExamining;
+
+ }
+
+ } else {
+
+ //
+ // This piece won't have to be split, so
+ // just count it as one.
+ //
+
+ TotalTransmitSegments += 1;
+
+ }
+
+#endif
+
+ } else {
+
+ //
+ // Multiple pieces. We assume that the relevant low bits
+ // will be the same in a physical and virtual address, so
+ // we can check using the virtual address whether a
+ // physical segment may be too short (we are being over-
+ // cautious here, but this allows us to avoid actually
+ // querying the physical addresses here.
+ //
+
+ //
+ // See if this buffer starts less than MIN_FRAGMENT_SIZE
+ // bytes before a page boundary.
+ //
+
+ if (PAGE_SIZE - ((ULONG)BufferOffset & (PAGE_SIZE-1)) <
+ SONIC_MIN_FRAGMENT_SIZE) {
+ ViolatedConstraints = TRUE;
+ goto DoneExamining;
+ }
+
+ //
+ // See if this buffer ends less than MIN_FRAGMENT_SIZE
+ // bytes after a page boundary.
+ //
+
+ if (((ULONG)BufferOffset + BufferVirtualLength) & (PAGE_SIZE-1) <
+ SONIC_MIN_FRAGMENT_SIZE) {
+ ViolatedConstraints = TRUE;
+ goto DoneExamining;
+ }
+
+#ifndef NO_CHIP_FIXUP
+
+ //
+ // Add the number of fragments in this piece.
+ // We assume that physical gaps will always be
+ // on at least a 4 byte boundary, so we won't
+ // need to split this piece.
+ //
+
+ TotalTransmitSegments += BufferPhysicalSegments;
+
+#endif
+
+ }
+
+
+ NdisGetNextBuffer(
+ CurrentBuffer,
+ &CurrentBuffer
+ );
+
+ }
+
+
+#ifndef NO_CHIP_FIXUP
+
+ //
+ // If the packet is short, we have to allow for the
+ // padding fragment at the end.
+ //
+
+ if (PacketVirtualLength < SONIC_MIN_PACKET_SIZE) {
+
+ TotalTransmitSegments += 1;
+
+ }
+
+#endif
+
+
+DoneExamining: ;
+
+#ifndef NO_CHIP_FIXUP
+ if (ViolatedConstraints || (TotalTransmitSegments > SONIC_MAX_FRAGMENTS)) {
+#else
+ if (ViolatedConstraints) {
+#endif
+
+ Reserved->UsedSonicBuffer = TRUE;
+
+ } else {
+
+ Reserved->UsedSonicBuffer = FALSE;
+
+ }
+
+}
+
+STATIC
+VOID
+AssignPacketToDescriptor(
+ IN PSONIC_ADAPTER Adapter,
+ IN PNDIS_PACKET Packet,
+ IN UINT DescriptorIndex
+ )
+
+/*++
+
+Routine Description:
+
+ Given a packet and a ring index, assign all of the buffers
+ in the packet to ring entries.
+
+Arguments:
+
+ Adapter - The adapter that the packets are coming through.
+
+ Packet - The packet whose buffers are to be assigned
+ ring entries.
+
+ DescriptorIndex - The index of the start of the ring entries to
+ be assigned buffers.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ //
+ // Points to the reserved portion of the packet.
+ //
+ PSONIC_PACKET_RESERVED Reserved = PSONIC_RESERVED_FROM_PACKET(Packet);
+
+ //
+ // Pointer to the ring entry to be filled with buffer information.
+ //
+ PSONIC_TRANSMIT_DESCRIPTOR TransmitDescriptor = Adapter->TransmitDescriptorArea
+ + DescriptorIndex;
+
+ //
+ // Pointer to the ring to packet entry that records the info about
+ // this packet.
+ //
+ PSONIC_DESCRIPTOR_TO_PACKET DescriptorToPacket = Adapter->DescriptorToPacket + DescriptorIndex;
+
+ //
+ // The total amount of data in the ndis packet.
+ //
+ UINT TotalDataLength;
+
+ //
+ // Points to the current ndis buffer being walked.
+ //
+ PNDIS_BUFFER CurrentBuffer;
+
+ //
+ // The number of physical segments in this buffer.
+ //
+ UINT BufferPhysicalSegments;
+
+ //
+ // An array to hold the physical segments.
+ //
+ NDIS_PHYSICAL_ADDRESS_UNIT PhysicalSegmentArray[SONIC_MAX_FRAGMENTS];
+
+ //
+ // We record the owning packet information in the ring packet packet
+ // structure.
+ //
+
+
+ DescriptorToPacket->OwningPacket = Packet;
+ DescriptorToPacket->UsedSonicBuffer = (BOOLEAN)
+ Reserved->UsedSonicBuffer;
+ DescriptorToPacket->SonicBuffersIndex =
+ Reserved->SonicBuffersIndex;
+
+
+ //
+ // First initialize the fields that don't depend on
+ // how many fragments there are in the packet.
+ //
+
+ TransmitDescriptor->TransmitStatus = 0;
+
+ //
+ // Set the programmable interrupt if it has been a long
+ // time since transmit complete interrupts were processed.
+ //
+
+ if (Adapter->PacketsSinceLastInterrupt >=
+ (SONIC_NUMBER_OF_TRANSMIT_DESCRIPTORS/2)) {
+
+ TransmitDescriptor->TransmitConfiguration = (UINT)SONIC_TCR_PROG_INTERRUPT;
+ Adapter->PacketsSinceLastInterrupt = 0;
+
+ } else {
+
+ TransmitDescriptor->TransmitConfiguration = 0;
+ ++Adapter->PacketsSinceLastInterrupt;
+
+ }
+
+
+ //
+ // Now check to see if the packet has been copied into an
+ // adapter buffer.
+ //
+
+ if (Reserved->UsedSonicBuffer) {
+
+ //
+ // Points to the adapter buffer descriptor allocated
+ // for this packet.
+ //
+ PSONIC_BUFFER_DESCRIPTOR BufferDescriptor;
+
+ BufferDescriptor = Adapter->SonicBuffers
+ + Reserved->SonicBuffersIndex;
+
+ TransmitDescriptor->FragmentCount = 1;
+ TransmitDescriptor->PacketSize = (UINT)BufferDescriptor->DataLength;
+
+ SONIC_SET_TRANSMIT_FRAGMENT_ADDRESS(
+ &(TransmitDescriptor->Fragments[0]),
+ NdisGetPhysicalAddressLow(BufferDescriptor->PhysicalSonicBuffer)
+ );
+
+ SONIC_SET_TRANSMIT_FRAGMENT_LENGTH(
+ &(TransmitDescriptor->Fragments[0]),
+ BufferDescriptor->DataLength
+ );
+
+
+ //
+ // This sets end-of-list for this descriptor.
+ //
+
+ SONIC_SET_TRANSMIT_LINK(
+ &(TransmitDescriptor->Fragments[1]),
+ TransmitDescriptor->Link
+ );
+
+ DescriptorToPacket->LinkPointer = (SONIC_PHYSICAL_ADDRESS *)
+ &(TransmitDescriptor->Fragments[1]);
+
+
+ //
+ // Flush the buffer that contains the packet.
+ //
+
+ SONIC_FLUSH_WRITE_BUFFER(BufferDescriptor->FlushBuffer);
+
+ } else {
+
+ //
+ // The total length of the packet (including padding)
+ //
+ UINT TotalPacketLength;
+
+ //
+ // Which fragment we are filling;
+ //
+ UINT CurFragment;
+
+ //
+ // Which map register we use for this buffer.
+ //
+ UINT CurMapRegister;
+
+ //
+ // Simple iteration variable.
+ //
+ UINT i;
+
+
+ CurFragment = 0;
+ CurMapRegister = DescriptorIndex * SONIC_MAX_FRAGMENTS;
+
+ NdisQueryPacket(
+ Packet,
+ NULL,
+ NULL,
+ &CurrentBuffer,
+ &TotalDataLength
+ );
+
+
+ while (CurrentBuffer) {
+
+ NdisMStartBufferPhysicalMapping(
+ Adapter->MiniportAdapterHandle,
+ CurrentBuffer,
+ CurMapRegister,
+ TRUE,
+ PhysicalSegmentArray,
+ &BufferPhysicalSegments
+ );
+
+ ++CurMapRegister;
+
+ //
+ // Put the physical segments for this buffer into
+ // the transmit descriptors.
+ //
+
+ for (i=0; i<BufferPhysicalSegments; i++) {
+
+ ASSERT (NdisGetPhysicalAddressHigh(PhysicalSegmentArray[i].PhysicalAddress) == 0);
+
+ SONIC_SET_TRANSMIT_FRAGMENT_ADDRESS(
+ &(TransmitDescriptor->Fragments[CurFragment]),
+ NdisGetPhysicalAddressLow(PhysicalSegmentArray[i].PhysicalAddress)
+ );
+
+ SONIC_SET_TRANSMIT_FRAGMENT_LENGTH(
+ &(TransmitDescriptor->Fragments[CurFragment]),
+ PhysicalSegmentArray[i].Length
+ );
+
+ ++CurFragment;
+
+#ifndef NO_CHIP_FIXUP
+
+ //
+ // If the fragment starts and ends not on a longword
+ // boundary, split it into two fragments, the first
+ // being SONIC_MIN_FRAGMENT_SIZE plus the extra bits
+ // at the beginning, the other the rest.
+ //
+
+ if ((NdisGetPhysicalAddressLow(PhysicalSegmentArray[i].PhysicalAddress) & 0x03) &&
+ ((NdisGetPhysicalAddressLow(PhysicalSegmentArray[i].PhysicalAddress) + PhysicalSegmentArray[i].Length) & 0x03)) {
+
+ UINT FirstSegmentLength;
+
+ FirstSegmentLength = (4 - ((NdisGetPhysicalAddressLow(PhysicalSegmentArray[i].PhysicalAddress) & 0x03))) +
+ SONIC_MIN_FRAGMENT_SIZE;
+
+ SONIC_SET_TRANSMIT_FRAGMENT_LENGTH(
+ &(TransmitDescriptor->Fragments[CurFragment-1]),
+ FirstSegmentLength
+ );
+
+ SONIC_SET_TRANSMIT_FRAGMENT_ADDRESS(
+ &(TransmitDescriptor->Fragments[CurFragment]),
+ SONIC_GET_TRANSMIT_FRAGMENT_ADDRESS(
+ &(TransmitDescriptor->Fragments[CurFragment-1])) +
+ FirstSegmentLength
+ );
+
+ SONIC_SET_TRANSMIT_FRAGMENT_LENGTH(
+ &(TransmitDescriptor->Fragments[CurFragment]),
+ PhysicalSegmentArray[i].Length - FirstSegmentLength
+ );
+
+ ++CurFragment;
+
+ }
+#endif
+ }
+
+
+ SONIC_FLUSH_WRITE_BUFFER (CurrentBuffer);
+
+ NdisGetNextBuffer(
+ CurrentBuffer,
+ &CurrentBuffer
+ );
+
+ }
+
+ if (TotalDataLength < SONIC_MIN_PACKET_SIZE) {
+
+ SONIC_SET_TRANSMIT_FRAGMENT_ADDRESS(
+ &(TransmitDescriptor->Fragments[CurFragment]),
+ NdisGetPhysicalAddressLow(Adapter->BlankBufferAddress)
+ );
+
+ SONIC_SET_TRANSMIT_FRAGMENT_LENGTH(
+ &(TransmitDescriptor->Fragments[CurFragment]),
+ SONIC_MIN_PACKET_SIZE - TotalDataLength
+ );
+
+ //
+ // Note that BlankBuffer has already been flushed.
+ //
+
+ ++CurFragment;
+
+ TotalPacketLength = SONIC_MIN_PACKET_SIZE;
+
+ } else {
+
+ TotalPacketLength = TotalDataLength;
+
+ }
+
+ //
+ // Make sure we didn't mess up and use up too
+ // many fragments.
+ //
+ ASSERT(CurFragment <= SONIC_MAX_FRAGMENTS);
+
+ TransmitDescriptor->FragmentCount = (UINT)CurFragment;
+ TransmitDescriptor->PacketSize = (UINT)TotalPacketLength;
+
+
+ //
+ // This sets end-of-list for this descriptor.
+ //
+
+ SONIC_SET_TRANSMIT_LINK(
+ &(TransmitDescriptor->Fragments[CurFragment]),
+ TransmitDescriptor->Link
+ );
+
+ DescriptorToPacket->LinkPointer = (SONIC_PHYSICAL_ADDRESS *)
+ &(TransmitDescriptor->Fragments[CurFragment]);
+
+ }
+
+ if (DescriptorIndex == (Adapter->NumberOfTransmitDescriptors-1)) {
+
+ Adapter->DescriptorToPacket->PrevLinkPointer = DescriptorToPacket->LinkPointer;
+
+ } else {
+
+ (DescriptorToPacket+1)->PrevLinkPointer = DescriptorToPacket->LinkPointer;
+
+ }
+
+ Reserved->DescriptorIndex = DescriptorIndex;
+
+}
+
+STATIC
+VOID
+RelinquishPacket(
+ IN PSONIC_ADAPTER Adapter,
+ IN PNDIS_PACKET Packet,
+ IN UINT RingIndex
+ )
+
+/*++
+
+Routine Description:
+
+ Relinquish the ring entries owned by the packet to the chip.
+ We also update the first uncommitted ring pointer.
+
+Arguments:
+
+ Adapter - The adapter that points to the ring entry structures.
+
+ Packet - The packet contains the ring index of the ring
+ entry for the packet.
+
+ RingIndex - Holds the index of the ring entry used
+ by this packet.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ //
+ // Holds the previous link pointer, where we turn off
+ // end-of-list.
+ //
+
+ PSONIC_PHYSICAL_ADDRESS PrevLinkPointer;
+
+#if !BINARY_COMPATIBLE
+
+ //
+ // NOTE: We have to raise the IRQL to POWER_LEVEL around the
+ // calls to SONIC_REMOVE_END_OF_LIST and START_TRANSMIT.
+ // This is to prevent a delay between these two instructions.
+ // If a delay happens right after SONIC_REMOVE_END_OF_LIST, the
+ // Sonic could transmit the packet and stop, then the call
+ // to START_TRANSMIT would cause it to retransmit all the
+ // packets in the descriptor ring.
+ //
+ // [ChuckL 8/3/94]
+ // We believe that this is no longer necessary, but we are leaving
+ // it in for RISC builds for safety's sake. We are removing in from
+ // x86 builds in order to remain binary-compatible with Chicago.
+ //
+
+ KIRQL OldIrql;
+
+#endif
+
+ PrevLinkPointer = Adapter->DescriptorToPacket[RingIndex].PrevLinkPointer;
+
+#if !BINARY_COMPATIBLE
+
+ //
+ // See NOTE above.
+ //
+
+ KeRaiseIrql(POWER_LEVEL, &OldIrql);
+
+#endif
+
+ //
+ // Turn off END_OF_LIST for the last one.
+ //
+
+ SONIC_REMOVE_END_OF_LIST(PrevLinkPointer);
+
+ //
+ // This turns on the correct bit in the SONIC_CONTROL
+ // register.
+ //
+
+ START_TRANSMIT(Adapter);
+
+#if !BINARY_COMPATIBLE
+
+ //
+ // See NOTE above.
+ //
+
+ KeLowerIrql(OldIrql);
+
+#endif
+
+ //
+ // We want FirstUncommittedDescriptor to point to right after us.
+ //
+
+ if (RingIndex == (Adapter->NumberOfTransmitDescriptors-1)) {
+
+ Adapter->FirstUncommittedDescriptor = Adapter->TransmitDescriptorArea;
+
+ } else {
+
+ Adapter->FirstUncommittedDescriptor =
+ Adapter->TransmitDescriptorArea + (RingIndex + 1);
+
+ }
+
+}
+
+