456 lines
8.0 KiB
C
456 lines
8.0 KiB
C
/*++
|
||
|
||
Copyright (c) 1990 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
send.c
|
||
|
||
Abstract:
|
||
|
||
|
||
Author:
|
||
|
||
12/20/94 kyleb Created.
|
||
|
||
Environment:
|
||
|
||
Kernel Mode - Or whatever is the equivalent on OS/2 and DOS.
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include <ndis.h>
|
||
#include "elnkhrd.h"
|
||
#include "elnksft.h"
|
||
|
||
#if DBG
|
||
#define STATIC
|
||
#else
|
||
#define STATIC static
|
||
#endif
|
||
|
||
|
||
//
|
||
// This is used to pad short packets.
|
||
//
|
||
|
||
static UCHAR BlankBuffer[60] = " ";
|
||
|
||
#if DBG
|
||
|
||
#define ELNKII_LOG_SIZE 256
|
||
extern UCHAR ElnkiiLogSaveBuffer[ELNKII_LOG_SIZE];
|
||
extern BOOLEAN ElnkiiLogSave;
|
||
extern UINT ElnkiiLogSaveLoc;
|
||
extern UINT ElnkiiLogSaveLeft;
|
||
|
||
extern VOID ElnkiiLog(UCHAR c);
|
||
|
||
#endif
|
||
|
||
|
||
|
||
VOID ElnkiiDoNextSend(
|
||
IN PELNKII_ADAPTER pAdapter
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Copies packets from the transmit queue to the board and starts
|
||
transmission as long as there is data waiting. Must be called
|
||
with Lock held.
|
||
|
||
Arguments:
|
||
|
||
pAdapter - pointer to the adapter block
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PNDIS_PACKET Packet; // Packet to process.
|
||
XMIT_BUF TmpBuf; // Current destination transmit buffer.
|
||
|
||
IF_LOG(ElnkiiLog('s');)
|
||
|
||
//
|
||
// Check if we have enough resources and a packet to process.
|
||
//
|
||
while
|
||
(
|
||
(pAdapter->XmitQueue != NULL) &&
|
||
(EMPTY == pAdapter->BufferStatus[pAdapter->NextBufToFill])
|
||
)
|
||
{
|
||
//
|
||
// Take a packet off of the transmit queue.
|
||
//
|
||
IF_VERY_LOUD(DbgPrint("ELNKII: Removing 0x%x, New Head is 0x%x\n", pAdapter->XmitQueue, RESERVED(pAdapter->XmitQueue)->NextPacket);)
|
||
|
||
//
|
||
// Set starting location
|
||
//
|
||
TmpBuf = pAdapter->NextBufToFill;
|
||
|
||
//
|
||
// Remove the packet from the queue.
|
||
//
|
||
Packet = pAdapter->XmitQueue;
|
||
pAdapter->XmitQueue = RESERVED(Packet)->NextPacket;
|
||
if (Packet == pAdapter->XmitQTail)
|
||
pAdapter->XmitQTail = NULL;
|
||
|
||
//
|
||
// Store the packet in the packet list.
|
||
//
|
||
pAdapter->Packets[TmpBuf] = Packet;
|
||
|
||
//
|
||
// Update the next free buffer.
|
||
//
|
||
pAdapter->NextBufToFill = NextBuf(pAdapter, TmpBuf);
|
||
|
||
//
|
||
// Copy down the data.
|
||
//
|
||
CardCopyDownPacket(
|
||
pAdapter,
|
||
Packet,
|
||
TmpBuf,
|
||
&pAdapter->PacketLens[TmpBuf]
|
||
);
|
||
|
||
//
|
||
// Pad short packets with blanks.
|
||
//
|
||
if (pAdapter->PacketLens[TmpBuf] < 60)
|
||
{
|
||
CardCopyDownBuffer(
|
||
pAdapter,
|
||
BlankBuffer,
|
||
TmpBuf,
|
||
pAdapter->PacketLens[TmpBuf],
|
||
60 - pAdapter->PacketLens[TmpBuf]
|
||
);
|
||
}
|
||
|
||
//
|
||
// Set the buffer status.
|
||
//
|
||
pAdapter->BufferStatus[TmpBuf] = FULL;
|
||
|
||
//
|
||
// See whether to start the transmission.
|
||
//
|
||
if (pAdapter->CurBufXmitting != -1)
|
||
continue;
|
||
|
||
if (pAdapter->NextBufToXmit != TmpBuf)
|
||
continue;
|
||
|
||
//
|
||
// Start transmission.
|
||
//
|
||
pAdapter->CurBufXmitting = pAdapter->NextBufToXmit;
|
||
|
||
//
|
||
// If we are handling an overflow, then we need to let
|
||
// the overflow handler send this packet....
|
||
//
|
||
if (pAdapter->BufferOverflow)
|
||
{
|
||
pAdapter->OverflowRestartXmitDpc = TRUE;
|
||
|
||
IF_LOUD(DbgPrint("O\n");)
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// This is used to check if stopping the chip prevented
|
||
// a transmit complete interrupt from coming through
|
||
// (it is cleared in the ISR if a transmit DPC is queued).
|
||
//
|
||
pAdapter->TransmitInterruptPending = TRUE;
|
||
|
||
CardStartXmit(pAdapter);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
VOID OctogmetusceratorRevisited(
|
||
IN PELNKII_ADAPTER pAdapter
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Recovers the card from a transmit error.
|
||
|
||
Arguments:
|
||
|
||
ppAdapter - pointer to the pAdapter block
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
IF_LOUD(DbgPrint("ELNKII: Octogmetuscerator called!");)
|
||
|
||
//
|
||
// Ack the interrupt, if needed.
|
||
//
|
||
NdisRawWritePortUchar(
|
||
pAdapter->MappedIoBaseAddr + NIC_INTR_STATUS,
|
||
ISR_XMIT_ERR
|
||
);
|
||
|
||
//
|
||
// Stop the card.
|
||
//
|
||
SyncCardStop(pAdapter);
|
||
|
||
//
|
||
// Wait up to 1.6 msfor any receives to finish.
|
||
//
|
||
NdisStallExecution(2000);
|
||
|
||
//
|
||
// Place the card in loopback mode.
|
||
//
|
||
NdisRawWritePortUchar(
|
||
pAdapter->MappedIoBaseAddr + NIC_XMIT_CONFIG,
|
||
TCR_LOOPBACK
|
||
);
|
||
|
||
//
|
||
// Start the card in loopback mode.
|
||
//
|
||
NdisRawWritePortUchar(
|
||
pAdapter->MappedIoBaseAddr + NIC_COMMAND,
|
||
CR_START | CR_NO_DMA
|
||
);
|
||
|
||
//
|
||
// Get out of loopback mode and start the card.
|
||
//
|
||
CardStart(pAdapter);
|
||
|
||
//
|
||
// If there was a packet waiting to get sent, send it.
|
||
//
|
||
if (pAdapter->CurBufXmitting != -1)
|
||
{
|
||
pAdapter->TransmitInterruptPending = TRUE;
|
||
CardStartXmit(pAdapter);
|
||
}
|
||
}
|
||
|
||
|
||
NDIS_STATUS ElnkiiSend(
|
||
IN NDIS_HANDLE MiniportAdapterContext,
|
||
IN PNDIS_PACKET Packet,
|
||
IN UINT Flags
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
The ElnkiiSend 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 block.
|
||
|
||
Packet - A pointer to a descriptor for the packet that
|
||
is to be transmitted.
|
||
|
||
Flags - Optional send flags.
|
||
|
||
Notes:
|
||
|
||
--*/
|
||
|
||
{
|
||
PELNKII_ADAPTER pAdapter = (PELNKII_ADAPTER)MiniportAdapterContext;
|
||
|
||
IF_LOG( ElnkiiLog('D');)
|
||
|
||
//
|
||
// Put Packet on queue to hit the pipe.
|
||
//
|
||
IF_VERY_LOUD( DbgPrint("Putting 0x%x on, after 0x%x\n", Packet, pAdapter->XmitQTail); )
|
||
|
||
if (pAdapter->XmitQueue == NULL)
|
||
pAdapter->XmitQueue = Packet;
|
||
else
|
||
RESERVED(pAdapter->XmitQTail)->NextPacket = Packet;
|
||
|
||
RESERVED(Packet)->NextPacket = NULL;
|
||
pAdapter->XmitQTail = Packet;
|
||
|
||
//
|
||
// Process the next send.
|
||
//
|
||
ElnkiiDoNextSend(pAdapter);
|
||
|
||
return(NDIS_STATUS_PENDING);
|
||
}
|
||
|
||
|
||
VOID ElnkiiXmitDpc(
|
||
IN PELNKII_ADAPTER pAdapter
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the real interrupt handler for a transmit complete interrupt.
|
||
ElnkiiInterrupt queues a call to it. It calls ElnkiiHandleXmitComplete.
|
||
|
||
NOTE : Called with the spinlock held!! and returns with it released!!!
|
||
|
||
Arguments:
|
||
|
||
pAdapter - A pointer to the adapter block.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
XMIT_BUF TmpBuf; // Buffer to transmit.
|
||
NDIS_STATUS Status; // Completion status.
|
||
PNDIS_PACKET Packet; // Packet that was transmitted.
|
||
UINT c; // Counter variable.
|
||
|
||
IF_VERY_LOUD(DbgPrint("ELNKII: ElnkiiXmitDpc entered\n");)
|
||
IF_LOG(ElnkiiLog('C');)
|
||
|
||
pAdapter->TransmitTimeOut = FALSE;
|
||
|
||
//
|
||
// Are we actually transmitting a packet?
|
||
//
|
||
if (pAdapter->CurBufXmitting == -1)
|
||
{
|
||
IF_LOUD(DbgPrint("ELNKII: ElnkiiXmitDpc called with nothing to transmit!\n");)
|
||
|
||
NdisWriteErrorLogEntry(
|
||
pAdapter->MiniportAdapterHandle,
|
||
NDIS_ERROR_CODE_DRIVER_FAILURE,
|
||
1,
|
||
ELNKII_ERRMSG_HANDLE_XMIT_COMPLETE
|
||
);
|
||
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Get the status of the transmit.
|
||
//
|
||
SyncCardGetXmitStatus(pAdapter);
|
||
|
||
//
|
||
// Statistics
|
||
//
|
||
if (pAdapter->XmitStatus & TSR_XMIT_OK)
|
||
{
|
||
pAdapter->FramesXmitGood++;
|
||
|
||
Status = NDIS_STATUS_SUCCESS;
|
||
}
|
||
else
|
||
{
|
||
pAdapter->FramesXmitBad++;
|
||
|
||
Status = NDIS_STATUS_FAILURE;
|
||
}
|
||
|
||
//
|
||
// Ack the send.
|
||
//
|
||
NdisMSendComplete(
|
||
pAdapter->MiniportAdapterHandle,
|
||
pAdapter->Packets[pAdapter->CurBufXmitting],
|
||
Status
|
||
);
|
||
|
||
//
|
||
// Mark the current transmit as done.
|
||
//
|
||
pAdapter->Packets[pAdapter->CurBufXmitting] = (PNDIS_PACKET)NULL;
|
||
pAdapter->BufferStatus[pAdapter->CurBufXmitting] = EMPTY;
|
||
TmpBuf = NextBuf(pAdapter, pAdapter->CurBufXmitting);
|
||
pAdapter->NextBufToXmit = TmpBuf;
|
||
|
||
//
|
||
// See what to do next.
|
||
//
|
||
switch (pAdapter->BufferStatus[TmpBuf])
|
||
{
|
||
case FULL:
|
||
//
|
||
// The next packet is ready to go -- only happens with
|
||
// more than one transmit buffer.
|
||
//
|
||
IF_VERY_LOUD(DbgPrint("ELNKII: Next packet ready to go\n");)
|
||
|
||
//
|
||
// Start the transmission and check for more.
|
||
//
|
||
pAdapter->CurBufXmitting = TmpBuf;
|
||
|
||
IF_LOG(ElnkiiLog('2');)
|
||
|
||
//
|
||
// This is used to check if stopping the chip prevented
|
||
// a transmit complete interrupt from coming through
|
||
// (it is cleared in the ISR if a transmit DPC is queued).
|
||
//
|
||
pAdapter->TransmitInterruptPending = TRUE;
|
||
|
||
IF_LOG(ElnkiiLog('6');)
|
||
|
||
CardStartXmit(pAdapter);
|
||
|
||
break;
|
||
|
||
case EMPTY:
|
||
//
|
||
// No packet is read to transmit.
|
||
//
|
||
IF_VERY_LOUD(DbgPrint("ELNKII: Next packet empty\n");)
|
||
|
||
pAdapter->CurBufXmitting = (XMIT_BUF)-1;
|
||
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Start next send.
|
||
//
|
||
ElnkiiDoNextSend(pAdapter);
|
||
|
||
IF_VERY_LOUD(DbgPrint("ELNKII: ElnkiiXmitDpc exiting\n");)
|
||
}
|
||
|