497 lines
14 KiB
C
497 lines
14 KiB
C
|
/*****************************************************************************
|
||
|
** **
|
||
|
** 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
|
||
|
|
||
|
|
||
|
|
||
|
|