windows-nt/Source/XPSP1/NT/drivers/net/irda/mknet/sys/send.c

497 lines
14 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*****************************************************************************
** **
** COPYRIGHT (C) 2000, 2001 MKNET CORPORATION **
** DEVELOPED FOR THE MK7100-BASED VFIR PCI CONTROLLER. **
** **
*****************************************************************************/
/**********************************************************************
Module Name:
SEND.C
Routines:
MKMiniportMultiSend
SendPkt
PrepareForTransmit
CopyFromPacketToBuffer
MinTurnaroundTxTimeout
[TestDataToTXBuff]
Comments:
Transmits in the NDIS env.
**********************************************************************/
#include "precomp.h"
#include "protot.h"
#pragma hdrstop
#if DBG
// for debug/test
extern VOID TestDataToTXBuff(PCHAR, UINT, PUINT);
#define TEST_PATTERN_SIZE 16
CHAR TestPattern[] = {0,1,2,3,4,5,6,7,8,9,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F};
#endif
//-----------------------------------------------------------------------------
// Procedure: [MKMiniportMultiSend]
//
// Description: This routine simply takes the pkt(s) passed down and queues
// it to the trasmit queue (FirstTxQueue) for later processing. Each
// pkt is marked NDIS_STATUS_PENDING) before returning.
//
// Arguments:
// MiniportAdapterContext (Adapter Structure pointer)
// PacketArray - an array of pointers to NDIS_PACKET structs
// PacketCount - number of packets in PacketArray
//
// Returns: (none)
//
//-----------------------------------------------------------------------------
VOID
MKMiniportMultiSend(NDIS_HANDLE MiniportAdapterContext,
PPNDIS_PACKET PacketArray,
UINT NumberOfPackets)
{
PMK7_ADAPTER Adapter;
NDIS_STATUS Status;
UINT PacketCount;
UINT i;
PNDIS_PACKET QueuePacket;
DBGFUNC("=> MKMiniportMultiSend");
DBGLOG("=> MKMiniportMultiSend", 0);
Adapter = PMK7_ADAPTER_FROM_CONTEXT_HANDLE(MiniportAdapterContext);
NdisAcquireSpinLock(&Adapter->Lock);
#if DBG
Adapter->DbgSendCallCnt++;
GDbgStat.txSndCnt++;
Adapter->DbgSentCnt++;
Adapter->DbgSentPktsCnt += NumberOfPackets;
#endif
// Q 'em up 1st
for(PacketCount=0; PacketCount < NumberOfPackets; PacketCount++) {
Adapter->NumPacketsQueued++;
EnqueuePacket( Adapter->FirstTxQueue,
Adapter->LastTxQueue,
PacketArray[PacketCount] );
NDIS_SET_PACKET_STATUS(PacketArray[PacketCount], NDIS_STATUS_PENDING);
}
if (Adapter->writePending || (Adapter->IOMode == TX_MODE)) {
// In TX mode: Meaning TX outstanding. We wait for the TX comp to kick
// off the next TX.
// Or we have writePending, which means a pkt is on q waiting for
// MinTurnaroundTimeout.
DBGLOG("<= MKMiniportMultiSend: TX_MODE", 0);
NdisReleaseSpinLock(&Adapter->Lock);
return;
}
ASSERT(Adapter->tcbUsed == 0);
QueuePacket = Adapter->FirstTxQueue;
Status = SendPkt(Adapter, QueuePacket);
DBGLOG("<= MKMiniportMultiSend", 0);
NdisReleaseSpinLock(&Adapter->Lock);
MK7EnableInterrupt(Adapter);
}
//-----------------------------------------------------------------------------
// Procedure: [SendPkt]
//
// Description: This sets up (copies) the pkt to the TX ring data buffer in
// preparation for TX. The caller then needs to Enable Int & Prompt to
// initiate the actual tx at hw level.
//
// Arguments:
// Adapter - ptr to Adapter object instance
// Packet - A pointer to a descriptor for the packet that is to be
// transmitted.
// Returns:
// NDIS_STATUS_SUCCESS - We copied the entire packet into a TRD data buff,
// so we can immediately return the packet/buffer
// back to the upper layers.
// NDIS_STATUS_RESOURCE - No resource. NDIS should re-send this to us
// at a later time. (Caller should re-Q the pkt.)
//----------------------------------------------------------------------
NDIS_STATUS
SendPkt( PMK7_ADAPTER Adapter,
PNDIS_PACKET Packet)
{
PTCB tcb;
UINT bytestosend, sndcnt, nextavail;
MK7REG mk7reg;
BOOLEAN timeout;
PNDIS_IRDA_PACKET_INFO packetInfo;
PNDIS_PACKET QueuePacket;
//****************************************
// To send a pkt we do the following:
// 1. Check Min Turnaround Time.
// 2. Check if there's avail TX resource. It not we return "resource".
// (We assume that there's outstanding TXs to trigger subseuqent TX
// completion interrupts, which will keep the ball rolling.)
// (RYM-IRDA-5+ Need to talk to Wayne about missed interrupts.)
// 3. Copy the NDIS pkt into the contiguous TX buffer.
// 4. The copied pkt could have been marked as the last pkt to go out
// at the old speed after which we change speed. We check for this.
//****************************************
DBGFUNC("=> SendPkt");
DBGLOG("=> SendPkt", 0);
packetInfo = GetPacketInfo(Packet);
if (packetInfo->MinTurnAroundTime) {
UINT usecToWait = packetInfo->MinTurnAroundTime;
UINT msecToWait;
packetInfo->MinTurnAroundTime = 0;
DBGLOG("<= SendPkt: Delay TX", 0);
// Need to set IOMode = TX so if a multisend comes down before
// the delayed TX timer goes off we just q.
// Ndis timer has a 1ms granularity (in theory). Let's round off.
msecToWait = (usecToWait<1000) ? 1 : (usecToWait+500)/1000;
NdisMSetTimer(&Adapter->MinTurnaroundTxTimer, msecToWait);
// 4.0.1 BOC
MK7SwitchToTXMode(Adapter);
// 4.0.1 EOC
Adapter->writePending = TRUE;
return (NDIS_STATUS_PENDING); // Say we're successful. We'll come back here.
}
// Avail TX resource
if (Adapter->tcbUsed >= Adapter->NumTcb) {
#if DBG
GDbgStat.txNoTcb++;
#endif
DBGSTR(("STATUS (SendPkt): No avail TCB\n"));
return (NDIS_STATUS_RESOURCES);
}
tcb = Adapter->pTcbArray[Adapter->nextAvailTcbIdx];
bytestosend = PrepareForTransmit(Adapter, Packet, tcb);
if (Adapter->changeSpeedAfterThisPkt == Packet) {
Adapter->changeSpeedAfterThisPkt = NULL;
Adapter->changeSpeedPending = CHANGESPEED_ON_DONE;
}
#if DBG
if (bytestosend > GDbgStat.txLargestPkt) {
GDbgStat.txLargestPkt = bytestosend;
}
#endif
// 1.0.0
if (bytestosend == 0) {
#if DBG
DbgPrint ("==> OB \n\r");
#endif
// Simplified change speed
if (Adapter->changeSpeedPending == CHANGESPEED_ON_DONE) {
// Note: We're changing speed in TX mode.
MK7ChangeSpeedNow(Adapter);
Adapter->changeSpeedPending = 0;
}
// For each completing TX there's a corresponding q'd pkt.
// We release it here.
QueuePacket = Adapter->FirstTxQueue;
DequeuePacket(Adapter->FirstTxQueue, Adapter->LastTxQueue);
Adapter->NumPacketsQueued--;
NDIS_SET_PACKET_STATUS(QueuePacket, NDIS_STATUS_RESOURCES);
NdisMSendComplete( Adapter->MK7AdapterHandle,
QueuePacket,
NDIS_STATUS_RESOURCES);
return(NDIS_STATUS_RESOURCES);
}
// Take care of ring wrap when incrementing.
Adapter->nextAvailTcbIdx++;
Adapter->nextAvailTcbIdx %= Adapter->NumTcb;
Adapter->tcbUsed++;
tcb->trd->count = bytestosend;
GrantTrdToHw(tcb->trd);
MK7SwitchToTXMode(Adapter);
#if DBG
NdisGetCurrentSystemTime((PLARGE_INTEGER)&GDbgTACmdTime[GDbgTATimeIdx]);
#endif
DBGLOG("<= SendPkt", 0);
return (NDIS_STATUS_SUCCESS);
}
//-----------------------------------------------------------------------------
// Procedure: [PrepareForTransmit]
//
// Description: When we come here we know there's an available TCB for the next
// TX. We move the Packet data into the tx buff associated w/ the TCB.
//
// Arguments:
// Adapter - ptr to Adapter object instance
// Packet - A pointer to a descriptor for the packet that is to be
// transmitted.
// SwTcb - Pointer to a software structure that represents a hardware TCB.
//
// Returns:
// TRUE If we were able to acquire the necessary TRD's or Coalesce buffer
// for the packet in we are attempting to prepare for transmission.
// FALSE If we needed a coalesce buffer, and we didn't have any available.
//-----------------------------------------------------------------------------
UINT PrepareForTransmit(PMK7_ADAPTER Adapter,
PNDIS_PACKET Packet,
PTCB tcb)
{
UINT BytesCopied;
if (Adapter->CurrentSpeed <= MAX_SIR_SPEED) {
// SIR needs additional software process
if ( NdisToSirPacket(Adapter,
Packet,
(UCHAR *)tcb->buff,
MK7_MAXIMUM_PACKET_SIZE,
&BytesCopied) ) {
return(BytesCopied);
}
return(0);
}
#if DBG
if (Adapter->DbgTestDataCnt > 0) {
TestDataToTXBuff(tcb->buff, Adapter->DbgTestDataCnt, &BytesCopied);
return(BytesCopied);
}
#endif
tcb->Packet = Packet;
NdisQueryPacket(tcb->Packet,
&tcb->NumPhysDesc,
&tcb->BufferCount,
&tcb->FirstBuffer,
&tcb->PacketLength);
// Alignment??
//
// Copy from packet to TCB data buffer
CopyFromPacketToBuffer( Adapter,
tcb->Packet,
tcb->PacketLength,
tcb->buff,
tcb->FirstBuffer,
&BytesCopied );
// ASSERT(BytesCopied == tcb->PacketLength);
if (BytesCopied != tcb->PacketLength) {
#if DBG
DbgPrint (" ==> BytesCopied Unmatched\n\r");
#endif
return(0);
}
else
return(BytesCopied);
}
//-----------------------------------------------------------------------------
// Procedure: [CopyFromPacketToBuffer]
//
// Description: This routine will copy a packet to a the passed buffer (which
// in this case will be a coalesce buffer).
//
// Arguments:
// Adapter - ptr to Adapter object instance
// Packet - The packet to copy from.
// BytesToCopy - The number of bytes to copy from the packet.
// DestBuffer - The destination of the copy.
// FirstBuffer - The first buffer of the packet that we are copying from.
//
// Result:
// BytesCopied - The number of bytes actually copied
//
// Returns: (none)
//-----------------------------------------------------------------------------
VOID
CopyFromPacketToBuffer( PMK7_ADAPTER Adapter,
PNDIS_PACKET Packet,
UINT BytesToCopy,
PCHAR DestBuffer,
PNDIS_BUFFER FirstBuffer,
PUINT BytesCopied)
{
PNDIS_BUFFER CurrentBuffer, NextBuffer;
PVOID VirtualAddress;
UINT CurrentLength;
UINT AmountToMove;
*BytesCopied = 0;
if (!BytesToCopy)
return;
if (FirstBuffer == NULL)
return;
CurrentBuffer = FirstBuffer;
while (CurrentBuffer != NULL) {
NdisQueryBufferSafe(CurrentBuffer, &VirtualAddress, &CurrentLength, 16);
if (!VirtualAddress) {
#if DBG
DbgPrint("==> Throw Away Failed Packet\n\r");
#endif
return;
}
NdisGetNextBuffer(CurrentBuffer, &NextBuffer);
CurrentBuffer = NextBuffer;
}
CurrentBuffer = FirstBuffer;
// NDIS requirement
// NdisQueryBuffer(CurrentBuffer,&VirtualAddress,&CurrentLength);
NdisQueryBufferSafe(CurrentBuffer, &VirtualAddress, &CurrentLength, 16);
while (BytesToCopy) {
while (!CurrentLength) {
NdisGetNextBuffer(CurrentBuffer, &CurrentBuffer);
// If we've reached the end of the packet. We return with what
// we've done so far (which must be shorter than requested).
if (!CurrentBuffer)
return;
// NDIS requirement
// NdisQueryBuffer(CurrentBuffer,&VirtualAddress,&CurrentLength);
NdisQueryBufferSafe(CurrentBuffer, &VirtualAddress, &CurrentLength, 16);
}
// Compute how much data to move from this fragment
if (CurrentLength > BytesToCopy)
AmountToMove = BytesToCopy;
else
AmountToMove = CurrentLength;
// Copy the data.
NdisMoveMemory(DestBuffer, VirtualAddress, AmountToMove);
// Update destination pointer
DestBuffer = (PCHAR) DestBuffer + AmountToMove;
// Update counters
*BytesCopied +=AmountToMove;
BytesToCopy -=AmountToMove;
CurrentLength = 0;
}
DBGLOG(" CopyFromPacketToBuffer: Bytes to Copy = ", BytesToCopy);
DBGLOG(" CopyFromPacketToBuffer: Bytes Copied = ", *BytesCopied);
}
//-----------------------------------------------------------------------------
// Procedure: [MinTurnaroundTxTimeout] RYM-2K-1TX
//
// Description: Delayed write because of Min Turnaround requirement. Just
// do send.
//-----------------------------------------------------------------------------
VOID MinTurnaroundTxTimeout(PVOID sysspiff1,
NDIS_HANDLE MiniportAdapterContext,
PVOID sysspiff2,
PVOID sysspiff3)
{
PMK7_ADAPTER Adapter;
PNDIS_PACKET QueuePacket;
NDIS_STATUS Status;
Adapter = PMK7_ADAPTER_FROM_CONTEXT_HANDLE(MiniportAdapterContext);
DBGLOG("=> MinTurnaroundTxTimeout", 0);
NdisAcquireSpinLock(&Adapter->Lock);
QueuePacket = Adapter->FirstTxQueue;
if (!QueuePacket) {
NdisReleaseSpinLock(&Adapter->Lock);
return;
}
Status = SendPkt(Adapter, QueuePacket);
// Note: We set false here because we just processed a q'd TX pkt
// that was waiting for MinTurnaround. However, we may still stay
// in TX mode based on other pkts on the q. This is determined in
// TX comp. Either writePending or IOMode will prevent new pkts
// from above to get thru out of sequence.
Adapter->writePending = FALSE;
NdisReleaseSpinLock(&Adapter->Lock);
MK7EnableInterrupt(Adapter);
}
#if DBG
//--------------------------------------------------------------------------------
// Procedure: [TestDataToTXBuff]
//
// Description: Put test data in tx buff instead of data that came down.
//--------------------------------------------------------------------------------
VOID TestDataToTXBuff( PCHAR DestBuffer,
UINT BytesToCopy,
PUINT BytesCopied)
{
UINT i, j;
for(i=0,j=0; j<BytesToCopy; j++) {
DestBuffer[j] = TestPattern[i];
i++;
i %= TEST_PATTERN_SIZE;
}
*BytesCopied = BytesToCopy;
}
#endif