665 lines
17 KiB
C
Raw Normal View History

2001-01-01 00:00:00 +01:00
/*+
* file: send.c
*
* Copyright (C) 1992-1995 by
* Digital Equipment Corporation, Maynard, Massachusetts.
* All rights reserved.
*
* This software is furnished under a license and may be used and copied
* only in accordance of the terms of such license and with the
* inclusion of the above copyright notice. This software or any other
* copies thereof may not be provided or otherwise made available to any
* other person. No title to and ownership of the software is hereby
* transferred.
*
* The information in this software is subject to change without notice
* and should not be construed as a commitment by digital equipment
* corporation.
*
* Digital assumes no responsibility for the use or reliability of its
* software on equipment which is not supplied by digital.
*
*
* Abstract: This file is part of the NDIS 4.0 miniport driver for
* Digital Equipment's DC21X4 Ethernet adapter family.
* It contains the code for submitting a packet for
* transmission.
*
* Author: Philippe Klein
*
* Revision History:
*
* phk 28-Aug-1994 creation date
*
-*/
#include <precomp.h>
#include <crc.h>
/*+
* DC21X4Send
*
* Routine Description:
*
* The DC21X4Send request instructs a MAC to transmit a packet through
* the adapter onto the medium.
*
* Arguments:
*
* MiniportAdapterContext -
* Packet - A pointer to a descriptor for the packet to transmit
*
* Return Value:
*
* The status of the operation.
*
-*/
extern
NDIS_STATUS
DC21X4Send(
IN NDIS_HANDLE MiniportAdapterContext,
IN PNDIS_PACKET Packet,
IN UINT Flags
)
{
PDC21X4_ADAPTER Adapter;
UINT PacketSize;
UCHAR PacketType;
PNDIS_BUFFER CurrentBuffer;
UINT NdisBufferCount;
UINT PhysicalSegmentCount;
UINT MapTableIndex;
PVOID TxmBuffer;
PUCHAR Tmp;
NDIS_STATUS NdisStatus;
NDIS_PHYSICAL_ADDRESS_UNIT PhysicalSegmentArray[DC21X4_MAX_SEGMENTS];
UINT BufferPhysicalSegments;
BOOLEAN FirstSegment;
BOOLEAN FirstBuffer;
PDC21X4_TRANSMIT_DESCRIPTOR FirstSegmentDescriptor=NULL;
PDC21X4_TRANSMIT_DESCRIPTOR LastSegmentDescriptor=NULL;
PDC21X4_TRANSMIT_DESCRIPTOR CurrentDescriptor=NULL;
UINT Length;
UINT Buffer;
UINT Segment;
UCHAR SendMode;
BOOLEAN GenerateCRC=FALSE;
ULONG TxmDescriptorCount = 0;
ULONG MapRegistersCount = 0;
ULONG MaxTransmitBufferCount = 0;
ULONG MinTransmitBufferCount = 0;
ULONG GoTransmitCount = 0;
#if _DBG
DbgPrint("DC21X4Send AdapterContext =%x Packet=%08x\n",
MiniportAdapterContext,Packet);
#endif
Adapter = (PDC21X4_ADAPTER)(MiniportAdapterContext);
//Check the link status
if (Adapter->LinkStatus == LinkFail) {
return NDIS_STATUS_NO_CABLE;
}
NdisQueryPacket(
Packet,
&PhysicalSegmentCount,
&NdisBufferCount,
&CurrentBuffer,
&PacketSize
);
if (PacketSize > DC21X4_MAX_FRAME_SIZE) {
return NDIS_STATUS_INVALID_PACKET;
}
// NT BUG: Clean up the msw of the PhysicalSegmentCount
PhysicalSegmentCount &= 0xFFFF;
#if _DBG
DbgPrint(" PacketSize= %d\n BufferCount= %d\n SegmentCount= %d\n",
PacketSize,NdisBufferCount,PhysicalSegmentCount);
DbgPrint(" FreeTxmDescCount = %d\n",Adapter->FreeTransmitDescriptorCount);
#endif
ASSERT(NdisBufferCount != 0);
// DC21040 Pass1 and Pass2:
// if SoftwareCRC mode is enabled, generate the CRC by software
// for packet > Transmit threshold
if (Adapter->SoftwareCRC) {
GenerateCRC = (PacketSize > Adapter->TxmThreshold);
}
// if the Ndis Packet is too fragmented or GenerateCRC is on,
// copy the packet into a single buffer
if ( (PhysicalSegmentCount > Adapter->PhysicalSegmentThreshold) || GenerateCRC ) {
if ((Adapter->MaxTransmitBufferInUse == DC21X4_NUMBER_OF_MAX_TRANSMIT_BUFFERS) ||
(Adapter->FreeTransmitDescriptorCount <= DC21X4_NUMBER_OF_SETUP_DESCRIPTORS)
) {
// All the Txm buffer are currently allocated or
// there is no free Txm descriptor in the ring
#if _DBG
DbgPrint ("No free Txm buffer or Txm desc...\n");
#endif
return NDIS_STATUS_RESOURCES;
}
else {
SendMode = CopyMaxBuffer;
}
}
// if the Ndis Packet is smaller than DC21X4_MIN_TXM_SIZE,
// copy the packet into a preallocated Txm buffer if the resource
// is available
else if ((PacketSize <= DC21X4_MIN_TXM_SIZE)
&& (Adapter->MinTransmitBufferInUse < DC21X4_NUMBER_OF_MIN_TRANSMIT_BUFFERS)
&& !Adapter->DontUseMinTransmitBuffer)
{
SendMode = CopyMinBuffer;
}
// Check if there are enough descriptors available in the ring to load
// the packet and enough free map registers to map the whole packet
else if ( (PhysicalSegmentCount >
((Adapter->FreeTransmitDescriptorCount - DC21X4_NUMBER_OF_SETUP_DESCRIPTORS) * NUMBER_OF_SEGMENT_PER_DESC))
||(PhysicalSegmentCount > Adapter->FreeMapRegisters)
){
// not enough descriptors in the ring
// or enough map registers to load the whole packet
#if _DBG
DbgPrint("Not enough txm desc or enough map registers\n");
DbgPrint("Phys. segment count=%d FreeTxDesc=%d FreeMapReg=%d\n",
PhysicalSegmentCount,
(Adapter->FreeTransmitDescriptorCount-DC21X4_NUMBER_OF_SETUP_DESCRIPTORS) * NUMBER_OF_SEGMENT_PER_DESC,
Adapter->FreeMapRegisters);
#endif
return NDIS_STATUS_RESOURCES;
}
else {
SendMode = MappedBuffer;
}
// For now, do not separately count multicast, broadcast and
// directed packets/bytes and avoid having to map the buffer, which
// is a very expensive operation. The mapping happens when we call
// NdisQueryBuffer with a VirtualAddress argument.
#if 0
NdisQueryBuffer(
CurrentBuffer,
&TxmBuffer,
&Length
);
ASSERT(Length >= ETH_LENGTH_OF_ADDRESS);
PacketType = CHECK_PACKET_TYPE(TxmBuffer);
#else
PacketType = TXM_DIRECTED_FRAME;
#endif
// Until Send and Request are serialized
// the Enqueue pointer which can modified in both
// send and filter routines should be protected by a SpinLock
if (Adapter->FullDuplex) {
NdisDprAcquireSpinLock(&Adapter->EnqueueSpinLock);
}
switch (SendMode) {
case CopyMaxBuffer:
// Copy the Packet into a Max Txm Buffer
TxmBuffer = (PVOID)Adapter->MaxTransmitBuffer[Adapter->MaxTransmitBufferIndex].Va;
#if _DBG
DbgPrint ("Copy packet %x into a Max Txm buffer [%d]\n",Packet,Adapter->MaxTransmitBufferIndex);
#endif
CopyFromPacketToBuffer (
Packet,
0,
TxmBuffer,
PacketSize,
&Length
);
ASSERT (Length == PacketSize);
ASSERT(Length >= ETH_LENGTH_OF_ADDRESS);
CurrentDescriptor = Adapter->EnqueueTransmitDescriptor;
Adapter->EnqueueTransmitDescriptor = CurrentDescriptor->Next;
MaxTransmitBufferCount++;
TxmDescriptorCount++;
//Clear all the Descriptor Control word but the SECOND_ADDR_CHAINED flag;
CurrentDescriptor->Control &= DC21X4_TDES_SECOND_ADDR_CHAINED;
// If GenerateCRC, add the software generated CRC
// to the end of the buffer
if (GenerateCRC) {
Tmp = (PUCHAR)TxmBuffer;
*(UNALIGNED ULONG *)&Tmp[PacketSize] =
CRC32 (
&Tmp[0],
PacketSize
);
PacketSize += sizeof(UINT);
CurrentDescriptor->Control |= DC21X4_TDES_ADD_CRC_DISABLE;
#if _DBG
DbgPrint(" Software CRC = %02x %02x %02x %02x\n",
Tmp[PacketSize-4],Tmp[PacketSize-3],
Tmp[PacketSize-2],Tmp[PacketSize-1]);
#endif
}
CurrentDescriptor->Control |= (
DC21X4_TDES_FIRST_SEGMENT
| PacketSize);
FirstSegmentDescriptor = CurrentDescriptor;
LastSegmentDescriptor = FirstSegmentDescriptor;
CurrentDescriptor->FirstBufferAddress =
Adapter->MaxTransmitBuffer[Adapter->MaxTransmitBufferIndex].Pa;
NdisFlushBuffer(
Adapter->MaxTransmitBuffer[Adapter->MaxTransmitBufferIndex].FlushBuffer,
TRUE
);
Adapter->MaxTransmitBufferIndex++;
Adapter->MaxTransmitBufferIndex &= (DC21X4_NUMBER_OF_MAX_TRANSMIT_BUFFERS-1);
#if _DBG
DbgPrint (" [%08x]\n %08x\n %08x\n %08x\n",
CurrentDescriptor,
CurrentDescriptor->Status,
CurrentDescriptor->Control,
CurrentDescriptor->FirstBufferAddress);
#endif
NdisStatus = NDIS_STATUS_PENDING;
// NdisStatus = NDIS_STATUS_SUCCESS; // hapi stress test pb
break;
case CopyMinBuffer:
// Copy the Packet into a Min Txm Buffer
TxmBuffer = (PVOID)Adapter->MinTransmitBuffer[Adapter->MinTransmitBufferIndex].Va;
#if _DBG
DbgPrint ("Copy packet %x into a Min Txm buffer [%d]\n",
Packet,Adapter->MinTransmitBufferIndex);
#endif
CopyFromPacketToBuffer (
Packet,
0,
TxmBuffer,
PacketSize,
&Length
);
ASSERT (Length == PacketSize);
ASSERT(Length >= ETH_LENGTH_OF_ADDRESS);
CurrentDescriptor = Adapter->EnqueueTransmitDescriptor;
Adapter->EnqueueTransmitDescriptor = CurrentDescriptor->Next;
MinTransmitBufferCount++;
TxmDescriptorCount++;
//Clear all the Descriptor Control word but the SECOND_ADDR_CHAINED flag;
CurrentDescriptor->Control &= DC21X4_TDES_SECOND_ADDR_CHAINED;
CurrentDescriptor->Control |= (
DC21X4_TDES_FIRST_SEGMENT
| PacketSize);
FirstSegmentDescriptor = CurrentDescriptor;
LastSegmentDescriptor = FirstSegmentDescriptor;
CurrentDescriptor->FirstBufferAddress =
Adapter->MinTransmitBuffer[Adapter->MinTransmitBufferIndex].Pa;
NdisFlushBuffer(
Adapter->MinTransmitBuffer[Adapter->MinTransmitBufferIndex].FlushBuffer,
TRUE
);
Adapter->MinTransmitBufferIndex++;
Adapter->MinTransmitBufferIndex &= (DC21X4_NUMBER_OF_MIN_TRANSMIT_BUFFERS-1);
#if _DBG
DbgPrint (" [%08x]\n %08x\n %08x\n %08x\n",
CurrentDescriptor,
CurrentDescriptor->Status,
CurrentDescriptor->Control,
CurrentDescriptor->FirstBufferAddress);
#endif
NdisStatus = NDIS_STATUS_PENDING;
// NdisStatus = NDIS_STATUS_SUCCESS; // hapi stress test pb
break;
case MappedBuffer:
FirstSegment = TRUE;
FirstBuffer = TRUE;
FirstSegmentDescriptor = Adapter->EnqueueTransmitDescriptor;
LastSegmentDescriptor = FirstSegmentDescriptor;
MapTableIndex = FirstSegmentDescriptor->MapTableIndex;
FirstSegmentDescriptor->Control &= DC21X4_TDES_SECOND_ADDR_CHAINED ;
FirstSegmentDescriptor->Control |= DC21X4_TDES_FIRST_SEGMENT;
for (Buffer=0; Buffer<NdisBufferCount; Buffer++) {
// Get the mapping of the physical segments
// of the current buffer
#if _DBG
DbgPrint("NdisMStartBufferPhysicalMapping (%d)\n",MapTableIndex);
DbgPrint("MapRegisterIndex = %d\n",Adapter->MapRegisterIndex);
DbgPrint("FreeMapRegisters = %d\n",Adapter->FreeMapRegisters);
#endif
NdisMStartBufferPhysicalMapping(
Adapter->MiniportAdapterHandle,
CurrentBuffer,
Adapter->MapRegisterIndex,
TRUE,
PhysicalSegmentArray,
&BufferPhysicalSegments
);
if (BufferPhysicalSegments) {
// Save the CurrentBuffer address for NdisCompleteBufferPhysicalMapping
Adapter->PhysicalMapping[MapTableIndex].Register = Adapter->MapRegisterIndex;
Adapter->PhysicalMapping[MapTableIndex].Buffer = CurrentBuffer;
Adapter->PhysicalMapping[MapTableIndex].Valid = TRUE;
Adapter->MapRegisterIndex++;
if (Adapter->MapRegisterIndex >= Adapter->AllocMapRegisters) {
Adapter->MapRegisterIndex = 0;
}
MapRegistersCount++;
// Put the physical segments for this buffer into
// the transmit descriptors.
#if _DBG
DbgPrint(" Nb segments = %d\n",BufferPhysicalSegments);
#endif
for (Segment=0; Segment<BufferPhysicalSegments;) {
ASSERT (NdisGetPhysicalAddressHigh(PhysicalSegmentArray[Segment].PhysicalAddress) == 0);
if (FirstBuffer) {
FirstBuffer = FALSE;
CurrentDescriptor = Adapter->EnqueueTransmitDescriptor;
LastSegmentDescriptor = CurrentDescriptor;
// Point to the next descriptor in the ring;
Adapter->EnqueueTransmitDescriptor= (Adapter->EnqueueTransmitDescriptor)->Next;
TxmDescriptorCount++;
if (FirstSegment) {
// The ownership bit of the first segment descriptor will be changed
// after the entire frame is fully mapped into the transmit ring
FirstSegment = FALSE;
}
else {
//Clear all the Descriptor Control word but the SECOND_ADDR_CHAINED flag;
CurrentDescriptor->Control &= DC21X4_TDES_SECOND_ADDR_CHAINED ;
// set the ownership bit to DC21X4
CurrentDescriptor->Status = DESC_OWNED_BY_DC21X4;
}
//First BufferSize
CurrentDescriptor->Control |= PhysicalSegmentArray[Segment].Length;
//First Buffer Address
CurrentDescriptor->FirstBufferAddress =
NdisGetPhysicalAddressLow(PhysicalSegmentArray[Segment].PhysicalAddress);
MapTableIndex++;
}
else {
FirstBuffer=TRUE;
MapTableIndex = (Adapter->EnqueueTransmitDescriptor)->MapTableIndex;
if (CurrentDescriptor->Control & DC21X4_TDES_SECOND_ADDR_CHAINED) {
continue;
}
else {
// Second BufferSize
CurrentDescriptor->Control |=
(PhysicalSegmentArray[Segment].Length << TDES_SECOND_BUFFER_SIZE_BIT_NUMBER);
// Second Buffer Address
CurrentDescriptor->SecondBufferAddress =
NdisGetPhysicalAddressLow(PhysicalSegmentArray[Segment].PhysicalAddress);
}
}
Segment++;
}
}
else {
// No physical segments in this Ndis buffer
NdisMCompleteBufferPhysicalMapping(
Adapter->MiniportAdapterHandle,
CurrentBuffer,
Adapter->MapRegisterIndex
);
}
NdisFlushBuffer(
CurrentBuffer,
TRUE
);
// Get the Next Buffer;
NdisGetNextBuffer(
CurrentBuffer,
&CurrentBuffer
);
}
NdisStatus = NDIS_STATUS_PENDING;
break;
}
// Save the information needed by the Txm interrupt handler
// to complete the Send request
LastSegmentDescriptor->Packet = Packet;
LastSegmentDescriptor->PacketType = PacketType;
LastSegmentDescriptor->PacketSize = PacketSize;
LastSegmentDescriptor->SendStatus = SendMode;
LastSegmentDescriptor->Control |= DC21X4_TDES_LAST_SEGMENT;
LastSegmentDescriptor->Control |= DC21X4_TDES_INTERRUPT_ON_COMPLETION;
GoTransmitCount++;
// Desc Pointer of last segment descriptor points the first segment descriptor
LastSegmentDescriptor->DescPointer = FirstSegmentDescriptor;
// Desc Pointer of first segment descriptor points the last segment descriptor
FirstSegmentDescriptor->DescPointer = LastSegmentDescriptor;
if (Adapter->FullDuplex) {
NdisDprReleaseSpinLock(&Adapter->EnqueueSpinLock);
NdisDprAcquireSpinLock(&Adapter->FullDuplexSpinLock);
}
Adapter->FreeTransmitDescriptorCount -= TxmDescriptorCount;
Adapter->FreeMapRegisters -= MapRegistersCount;
Adapter->MaxTransmitBufferInUse += MaxTransmitBufferCount;
Adapter->MinTransmitBufferInUse += MinTransmitBufferCount;
Adapter->GeneralOptional[GO_TRANSMIT_QUEUE_LENGTH] += GoTransmitCount;
if (Adapter->FullDuplex) {
NdisDprReleaseSpinLock(&Adapter->FullDuplexSpinLock);
}
// Set the Ownership bit off the First_Segment descriptor;
FirstSegmentDescriptor->Status = DESC_OWNED_BY_DC21X4;
if (!Adapter->DisableTransmitPolling) {
// Poll Transmit the adapter
DC21X4_WRITE_PORT(
DC21X4_TXM_POLL_DEMAND,
1
);
}
return NdisStatus;
}
/*+
*
* CRC32
*
* Routine Description:
*
* Generate a CRC-32 from the data stream
*
* Arguments:
*
* Data - the data stream
* Len - the length of the stream
*
*Return Value:
*
* CRC-32
*
-*/
extern
ULONG
CRC32 (
IN PUCHAR Data,
IN UINT Len
)
{
ULONG Crc = 0xffffffff;
while (Len--) {
Crc = CrcTable[(Crc ^ *Data++) & 0xFF] ^ (Crc >> 8);
}
return ~Crc;
}
/*+
*
* NdisSendPackets
*
*
-*/
NDIS_STATUS
DC21X4SendPackets(
IN NDIS_HANDLE MiniportAdapterContext,
IN PPNDIS_PACKET PacketArray,
IN UINT NumberOfPackets
) {
return NDIS_STATUS_SUCCESS;
}