NT4/private/ntos/ndis/netflex/transmit.c
2020-09-30 17:12:29 +02:00

803 lines
21 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//**********************************************************************
//**********************************************************************
//
// File Name: TRANSMIT.C
//
// Program Name: NetFlex NDIS 3.0 Miniport Driver
//
// Companion Files: None
//
// Function: This module contains the NetFlex Miniport Driver
// interface routines called by the Wrapper and the
// configuration manager.
//
// (c) Compaq Computer Corporation, 1992,1993,1994
//
// This file is licensed by Compaq Computer Corporation to Microsoft
// Corporation pursuant to the letter of August 20, 1992 from
// Gary Stimac to Mark Baber.
//
// History:
//
// 04/15/94 Robert Van Cleve - Converted from NDIS Mac Driver
//
//**********************************************************************
//**********************************************************************
//-------------------------------------
// Include all general companion files
//-------------------------------------
#include <ndis.h>
#include "tmsstrct.h"
#include "macstrct.h"
#include "adapter.h"
#include "protos.h"
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// Routine Name: NetFlexProcessXmit
//
// Description: This routine looks through the tranmit lists
// and calls the send complete routines of the
// bindings whose sends have completed.
//
// Input: acb - Pointer to the Adapter's acb
//
// Output: None
//
// Calls: NetFlexDequeue_TwoPtrQ,
// NetFlexEnqueue_TwoPtrQ_Tail
//
// Called_By: NetFlexDPR
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
VOID
FASTCALL
NetFlexProcessXmit(
PACB acb
)
{
PXMIT xmitptr;
UINT curmap;
PNDIS_PACKET packet;
NDIS_STATUS status = NDIS_STATUS_SUCCESS;
PNDIS_BUFFER SourceBuffer;
ULONG XmitedOk = 0;
if (acb->FullDuplexEnabled)
{
NdisAcquireSpinLock(&acb->XmitLock);
}
xmitptr = acb->acb_xmit_ahead;
if ((xmitptr == NULL) ||
!(xmitptr->XMIT_CSTAT & XCSTAT_COMPLETE))
{
if (acb->FullDuplexEnabled)
{
NdisReleaseSpinLock(&acb->XmitLock);
}
return;
}
//
// Increment the interrupt count.
//
acb->acb_int_count++;
//
// For each completed frame issue a NdisMSendComplete.
// Before completing the send, release the mapping of
// the phyical buffers if we are using the protocol's buffers.
//
while (xmitptr->XMIT_CSTAT & XCSTAT_COMPLETE)
{
XmitedOk++;
//
// Check the status of the transmit and update the
// counter accordingly.
//
if (xmitptr->XMIT_CSTAT & XCSTAT_ERROR)
{
// Transmit error
//
DebugPrint(1,("NF(%d): Xmit Error CSTAT = 0x%x\n",acb->anum,xmitptr->XMIT_CSTAT));
acb->acb_gen_objs.frames_xmitd_err++;
XmitedOk--;
status = NDIS_STATUS_FAILURE;
}
else if (( xmitptr->XMIT_CSTAT & 0xff00) &&
((xmitptr->XMIT_CSTAT & 0xff00) != 0xcc00))
{
// FS indicates something happened
//
DebugPrint(1,("NF(%d): Xmit: FS = 0x%x\n",acb->anum,xmitptr->XMIT_CSTAT));
status = ((xmitptr->XMIT_CSTAT & XCSTAT_GOODFS) != XCSTAT_GOODFS)
? NDIS_STATUS_NOT_RECOGNIZED : NDIS_STATUS_NOT_COPIED;
}
//
// Get the info we need from the sof.
//
curmap = xmitptr->XMIT_MapReg;
packet = xmitptr->XMIT_Packet;
//
// Clean up the transmit lists and the transmit queues.
//
xmitptr->XMIT_CSTAT = 0;
xmitptr->XMIT_Packet = NULL;
if (xmitptr->XMIT_OurBufferPtr == NULL)
{
// Normal Xmit Packet
//
NdisQueryPacket(
packet,
NULL,
NULL,
(PNDIS_BUFFER *)&SourceBuffer,
NULL);
while (SourceBuffer)
{
NdisMCompleteBufferPhysicalMapping(
acb->acb_handle,
(PNDIS_BUFFER)SourceBuffer,
curmap);
curmap++;
if (curmap == acb->acb_maxmaps)
{
curmap = 0;
}
NdisGetNextBuffer(SourceBuffer, &SourceBuffer);
}
}
else
{
// We've used one of our adapter buffers, so put the adapter
// buffer back on the free list.
//
if (xmitptr->XMIT_OurBufferPtr->BufferSize != acb->acb_smallbufsz)
{
xmitptr->XMIT_OurBufferPtr->Next = acb->OurBuffersListHead;
acb->OurBuffersListHead = xmitptr->XMIT_OurBufferPtr;
}
else
{
//
// small buffer
//
xmitptr->XMIT_OurBufferPtr->Next = acb->SmallBuffersListHead;
acb->SmallBuffersListHead = xmitptr->XMIT_OurBufferPtr;
}
xmitptr->XMIT_OurBufferPtr = NULL;
}
//
// Point to next xmit
//
if (xmitptr == acb->acb_xmit_atail)
{
// Set the list to null, also have to
// the ahead pointer, since if we had run
// out of xmit buffers, the wrapper can call
// our sendhandler during the completion.
//
xmitptr = acb->acb_xmit_ahead = acb->acb_xmit_atail = NULL;
}
else
{
// Point to the next xmit list
//
xmitptr = xmitptr->XMIT_Next;
}
//
// Increase the number of available xmit lists
//
acb->acb_avail_xmit++;
//
// Complete the request
//
if (acb->FullDuplexEnabled)
{
NdisReleaseSpinLock(&acb->XmitLock);
}
if (packet != NULL)
{
NdisMSendComplete(acb->acb_handle, packet, status);
}
else
{
NdisMSendResourcesAvailable(acb->acb_handle);
}
if (acb->FullDuplexEnabled)
{
NdisAcquireSpinLock(&acb->XmitLock);
}
if (xmitptr == NULL)
break;
}
//
// Update the head of the active lists if we ran into a non-completed
// list.
//
if (xmitptr)
{
acb->acb_xmit_ahead = xmitptr;
}
if (acb->acb_xmit_ahead)
{
//
// Issue a xmit valid adapter interrupt
//
NdisRawWritePortUshort(acb->SifIntPort, (USHORT) SIFINT_XMTVALID);
}
acb->acb_gen_objs.frames_xmitd_ok += XmitedOk;
if (acb->FullDuplexEnabled)
{
NdisReleaseSpinLock(&acb->XmitLock);
}
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// Routine Name: NetFlexTransmitStatus
//
// Description: This routine detemined the action to take
// depending on the reason for the xmit interrupt
//
// Input: acb - Pointer to the Adapter's acb
//
// Output: None
//
// Calls: NdisRawWritePortUshort,
// NetFlexDequeue_TwoPtrQ,
// NetFlexSendNextSCB,
// NetFlexEnqueue_TwoPtrQ_Tail
//
// Called_By: NetFlexDPR
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
VOID
NetFlexTransmitStatus(
PACB acb
)
{
PXMIT xmitptr;
UINT curmap;
PNDIS_PACKET packet;
NDIS_STATUS status = NDIS_STATUS_SUCCESS;
PNDIS_BUFFER SourceBuffer;
if (acb->FullDuplexEnabled)
{
NdisAcquireSpinLock(&acb->XmitLock);
}
if (acb->acb_xmit_ahead == NULL)
{
if (acb->FullDuplexEnabled)
{
NdisReleaseSpinLock(&acb->XmitLock);
}
return;
}
//
// We have received a list error. Determine the type of list error
// in order to tell the protocol what happened.
//
acb->acb_gen_objs.frames_xmitd_err++;
xmitptr = acb->acb_xmit_ahead;
DebugPrint(1,("NF(%d): xmitptr = %x, Cstat = %x\n",acb->anum,xmitptr,xmitptr->XMIT_CSTAT));
switch (acb->acb_ssb_virtptr->SSB_Status & 0xff00)
{
case XSTAT_FRAME_SIZE_ERROR:
case XSTAT_ILLEGAL_FRAME_FORMAT:
case XSTAT_ACCESS_PRIORITY_ERR:
DebugPrint(1,("NF(%d): Frame sz err, illegal format or access priority\n",acb->anum));
status = NDIS_STATUS_INVALID_PACKET;
break;
case XSTAT_XMIT_THRESHOLD:
case XSTAT_ODD_ADDRESS:
case XSTAT_FRAME_ERROR:
case XSTAT_UNENABLE_MAC_FRAME:
acb->acb_gen_objs.frames_xmitd_err++;
DebugPrint(1,("NF(%d): threshold, frame error or unenable\n",acb->anum));
status = NDIS_STATUS_FAILURE;
break;
default:
acb->acb_gen_objs.frames_xmitd_err++;
DebugPrint(1,("NF(%d): Unknown error\n",acb->anum));
status = NDIS_STATUS_SUCCESS;
break;
}
//
// Get the info we need from the sof.
//
curmap = xmitptr->XMIT_MapReg;
packet = xmitptr->XMIT_Packet;
//
// Clean up the transmit lists and the transmit queues.
//
xmitptr->XMIT_CSTAT = 0;
xmitptr->XMIT_Packet = NULL;
//
// Take the error list off the active list. Set up the waiting list
// to either point to the next list of the active queue or the next
// available list from transmission.
//
if (acb->acb_state == AS_OPENED)
{
if (acb->acb_xmit_atail == xmitptr)
{
acb->acb_xmit_whead = acb->acb_xmit_wtail = xmitptr->XMIT_Next;
}
else
{
acb->acb_xmit_whead = xmitptr->XMIT_Next;
acb->acb_xmit_wtail = acb->acb_xmit_atail;
}
acb->acb_xmit_atail = acb->acb_xmit_ahead = NULL;
//
// Send off the transmit command to the adapter since the transmit
// command completes when a list error is encountered.
//
if (acb->acb_scb_virtptr->SCB_Cmd == 0)
{
NetFlexSendNextSCB(acb);
}
else if (!acb->acb_scbclearout)
{
acb->acb_scbclearout = TRUE;
NdisRawWritePortUshort(acb->SifIntPort, (USHORT) SIFINT_SCBREQST);
}
}
else
{
acb->acb_xmit_atail = acb->acb_xmit_ahead = NULL;
}
acb->acb_avail_xmit++;
if (xmitptr->XMIT_OurBufferPtr != NULL)
{
// We've used one of our adapter buffers, so put the adapter
// buffer back on the free list.
//
if (xmitptr->XMIT_OurBufferPtr->BufferSize != acb->acb_smallbufsz)
{
xmitptr->XMIT_OurBufferPtr->Next = acb->OurBuffersListHead;
acb->OurBuffersListHead = xmitptr->XMIT_OurBufferPtr;
}
else
{
//
// small buffer
//
xmitptr->XMIT_OurBufferPtr->Next = acb->SmallBuffersListHead;
acb->SmallBuffersListHead = xmitptr->XMIT_OurBufferPtr;
}
xmitptr->XMIT_OurBufferPtr = NULL;
}
else
{
NdisQueryPacket(
packet,
NULL,
NULL,
(PNDIS_BUFFER *)&SourceBuffer,
NULL);
while (SourceBuffer)
{
NdisMCompleteBufferPhysicalMapping(
acb->acb_handle,
(PNDIS_BUFFER)SourceBuffer,
curmap);
curmap++;
if (curmap == acb->acb_maxmaps)
{
curmap = 0;
}
NdisGetNextBuffer(SourceBuffer, &SourceBuffer);
}
}
//
// Complete the request
//
if (acb->FullDuplexEnabled)
{
NdisReleaseSpinLock(&acb->XmitLock);
}
if (packet)
{
NdisMSendComplete(acb->acb_handle, packet, status);
}
else
{
NdisMSendResourcesAvailable(acb->acb_handle);
}
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// Routine Name: NetFlexSend
//
// Description: This routine places the given packet on the
// adapter's transmit list.
//
// Input:
// MiniportAdapterContext - The context value
// returned by the Miniport when the adapter was
// initialized. In reality, it is a pointer to ACB
//
// Packet - A pointer to a descriptor for the packet
// that is to be transmitted.
//
// Flags - The send options to use.
//
// Output: Returns NDIS_STATUS_SUCCESS for a successful
// completion. Otherwise, an error code is
// returned.
//
// Calls: NdisQueryPacket,NdisQueryBuffer,NdisMoveMemory
// NdisGetNextBuffer,NdisGetBufferPhysicalAddress
// NdisWritePortUshort,NetFlexEnqueue_TwoPtrQ_Tail
// NetFlexDequeue_OnePtrQ_Head,SWAPL,SWAPS
//
// Called_By: Wrapper
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
NDIS_STATUS NetFlexSend(
IN NDIS_HANDLE MiniportAdapterContext,
IN PNDIS_PACKET Packet,
IN UINT Flags
)
{
PACB acb = (PACB) MiniportAdapterContext;
PXMIT xmitptr;
UINT PhysicalBufferCount, BufferCount;
UINT TotalPacketLength;
PNDIS_BUFFER SourceBuffer;
PUSHORT avail_xmits;
UINT curmap,j,i;
UINT arraysize;
ULONG physbufptr;
NDIS_STATUS status = NDIS_STATUS_PENDING;
NDIS_PHYSICAL_ADDRESS_UNIT physaddrarray[MAX_BUFS_PER_XMIT];
//
// if we are in full duplex mode then acquire the xmit spin lock.
//
if (acb->FullDuplexEnabled)
{
NdisAcquireSpinLock(&acb->XmitLock);
}
avail_xmits = &acb->acb_avail_xmit;
//
// Do we have at least one available xmit list?
//
if (*avail_xmits)
{
// Yes, See if we can process this send request
//
NdisQueryPacket(
Packet,
(PUINT)&PhysicalBufferCount,
(PUINT)&BufferCount,
(PNDIS_BUFFER *)(&SourceBuffer),
(PUINT)(&TotalPacketLength));
//
// Point to the head of the xmit list
//
xmitptr = acb->acb_xmit_head;
//
// Do we need to use our own buffer?
//
if ((PhysicalBufferCount <= MAX_BUFS_PER_XMIT) &&
(TotalPacketLength > acb->acb_smallbufsz ||
acb->SmallBuffersListHead == NULL))
{
// Clean the Data fields
//
NdisZeroMemory(xmitptr->XMIT_Data, SIZE_XMIT_DATA);
// With the new fpa mac code we can only use 1
// xmit list per xmit. Point the head pointer to the next
// available list. At this point we are guaranteed less than
// MAX_BUFS_PER_XMIT buffers per xmit = 1 xmit list.
//
curmap = acb->acb_curmap;
acb->acb_curmap += BufferCount;
if (acb->acb_curmap >= acb->acb_maxmaps)
{
acb->acb_curmap -= acb->acb_maxmaps;
}
xmitptr->XMIT_MapReg = curmap;
i=0;
while (SourceBuffer != NULL)
{
NdisMStartBufferPhysicalMapping(
acb->acb_handle,
SourceBuffer,
curmap,
TRUE,
physaddrarray,
&arraysize);
curmap++;
if (curmap == acb->acb_maxmaps)
{
curmap = 0;
}
for (j=0; j < arraysize; j++)
{
physbufptr = SWAPL(NdisGetPhysicalAddressLow(physaddrarray[j].PhysicalAddress));
xmitptr->XMIT_Data[i].DataCount = (USHORT)(SWAPS(physaddrarray[j].Length)) | DATA_NOT_LAST;
xmitptr->XMIT_Data[i].DataHi = (USHORT)physbufptr;
xmitptr->XMIT_Data[i].DataLo = (USHORT)(physbufptr >> 16);
PhysicalBufferCount--;
i++;
}
NdisFlushBuffer(SourceBuffer, TRUE);
NdisGetNextBuffer(SourceBuffer, &SourceBuffer);
}
xmitptr->XMIT_Data[i-1].DataCount &= DATA_LAST;
xmitptr->XMIT_Fsize = (SHORT)(SWAPS((USHORT)TotalPacketLength));
xmitptr->XMIT_Packet = Packet;
xmitptr->XMIT_OurBufferPtr = NULL;
}
else
{
// We need to constrain the packet into our own buffer
//
if (((PhysicalBufferCount > MAX_BUFS_PER_XMIT) &&
(acb->OurBuffersListHead != NULL)) ||
((acb->SmallBuffersListHead != NULL) &&
(TotalPacketLength <= acb->acb_smallbufsz)))
{
status = NetFlexConstrainPacket(
acb,
xmitptr,
Packet,
PhysicalBufferCount,
SourceBuffer,
TotalPacketLength);
if (status != NDIS_STATUS_SUCCESS)
{
if (acb->FullDuplexEnabled)
{
NdisReleaseSpinLock(&acb->XmitLock);
}
return(status);
}
}
else
{
// we don't have any buffers at this time...
// See if we can process any transmits, freeing up any that are completed...
//
DebugPrint(1,("NF(%d): No empty Xmit Buffers to transfer into\n",acb->anum));
if (acb->FullDuplexEnabled)
{
NdisReleaseSpinLock(&acb->XmitLock);
}
return(NDIS_STATUS_RESOURCES);
}
}
//
// Update all the pointers...
//
acb->acb_xmit_head = xmitptr->XMIT_Next;
xmitptr->XMIT_Timeout = 0;
#ifdef XMIT_INTS
//
// Leave the original FInt setting
//
xmitptr->XMIT_CSTAT =
((xmitptr->XMIT_Number % acb->XmitIntRatio) == 0) ? XCSTAT_GO_INT : XCSTAT_GO;
#else
xmitptr->XMIT_CSTAT = XCSTAT_GO;
#endif
//
// Update Tail Pointer
//
acb->acb_xmit_atail = xmitptr;
//
// Update the head if this is the first one...
//
if (acb->acb_xmit_ahead == NULL)
{
acb->acb_xmit_ahead = xmitptr;
}
//
// If the transmitter had stalled because it ran out of
// valid lists, issue an adapter int to pickup this new valid one.
//
NdisRawWritePortUshort(acb->SifIntPort, (USHORT) SIFINT_XMTVALID);
//
// Indicate we've taken one of the ints
//
(*avail_xmits)--;
if (acb->FullDuplexEnabled)
{
NdisReleaseSpinLock(&acb->XmitLock);
}
return(status);
}
// No, We don't have any transmits at this time...
//
DebugPrint(2,("NF(%d): Send, Out of Xmit Lists...\n",acb->anum));
if (acb->FullDuplexEnabled)
{
NdisReleaseSpinLock(&acb->XmitLock);
}
return(NDIS_STATUS_RESOURCES);
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// Routine Name: NetFlexConstrainPacket
//
// Description: This routine combines the packet fragments
// into our own buffer for transmition.
//
// Called_By: NetFlexSend
//
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
NDIS_STATUS
NetFlexConstrainPacket(
PACB acb,
PXMIT xmitptr,
PNDIS_PACKET Packet,
UINT PhysicalBufferCount,
PNDIS_BUFFER SourceBuffer,
UINT TotalPacketLength
)
{
PVOID SourceData; // Points to the virtual address of the source buffers data.
UINT SourceLength; // Number of bytes of data in the source buffer.
PCHAR CurrentDestination; // Pointer to virtual address for the adapter buffer
UINT TotalDataMoved = 0;
ULONG AdapterPhysicalBufferPtr;
PBUFFER_DESCRIPTOR BufferDescriptor;
if (TotalPacketLength > acb->acb_smallbufsz)
{
BufferDescriptor = acb->OurBuffersListHead;
if (!BufferDescriptor)
{
return(NDIS_STATUS_RESOURCES);
}
acb->OurBuffersListHead = BufferDescriptor->Next;
BufferDescriptor->Next = NULL;
}
else
{
BufferDescriptor = acb->SmallBuffersListHead;
if (!BufferDescriptor)
{
return(NDIS_STATUS_RESOURCES);
}
acb->SmallBuffersListHead = BufferDescriptor->Next;
BufferDescriptor->Next = NULL;
}
//
// Clear out the data fields in the xmit list
//
NdisZeroMemory(xmitptr->XMIT_Data, SIZE_XMIT_DATA);
//
// Copy the packet's buffers into our buffer
//
CurrentDestination = BufferDescriptor->VirtualBuffer;
BufferDescriptor->DataLength = TotalPacketLength;
do
{
// Get Buffer info
//
NdisQueryBuffer(SourceBuffer, &SourceData, &SourceLength);
// Copy this buffer
//
NdisMoveMemory(CurrentDestination, SourceData, SourceLength);
//
// Update destination address
//
CurrentDestination = (PCHAR)CurrentDestination + SourceLength;
//
// Update count of packet length.
//
TotalDataMoved += SourceLength;
//
// Get the next buffers information
//
NdisGetNextBuffer(SourceBuffer, &SourceBuffer);
} while (SourceBuffer != NULL);
NdisFlushBuffer(BufferDescriptor->FlushBuffer, TRUE);
AdapterPhysicalBufferPtr =
SWAPL(NdisGetPhysicalAddressLow(BufferDescriptor->PhysicalBuffer));
xmitptr->XMIT_OurBufferPtr = BufferDescriptor;
xmitptr->XMIT_Data[0].DataCount = (USHORT)(SWAPS((USHORT)TotalPacketLength)) & DATA_LAST;
xmitptr->XMIT_Data[0].DataHi = (USHORT) AdapterPhysicalBufferPtr;
xmitptr->XMIT_Data[0].DataLo = (USHORT)(AdapterPhysicalBufferPtr >> 16);
xmitptr->XMIT_Fsize = (SHORT)(SWAPS((USHORT)TotalPacketLength));
xmitptr->XMIT_Packet = NULL;
DebugPrint(2,("NF(%d): Using internal buffer\n",acb->anum));
return NDIS_STATUS_SUCCESS;
}