/*
* MTL_RX.C - Receive side processing for MTL
*/
#include <ndis.h>
#include <ndiswan.h>
#include <mytypes.h>
#include <mydefs.h>
#include <disp.h>
#include <util.h>
#include <opcodes.h>
#include <adapter.h>
#include <idd.h>
#include <mtl.h>
#include <cm.h>
/* main handler, called when data arrives at bchannels */
VOID
mtl__rx_bchan_handler
(
MTL_CHAN *chan,
USHORT bchan,
ULONG IddRxFrameType,
IDD_XMSG *msg
)
{
MTL *mtl;
MTL_HDR hdr;
MTL_AS *as;
USHORT FragmentFlags, CopyLen;
MTL_RX_TBL *RxTable;
D_LOG(D_ENTRY, ("mtl__rx_bchan_handler: chan: 0x%lx, bchan: %d, msg: 0x%lx\n", chan, bchan, msg));
/* assigned mtl using back pointer */
mtl = chan->mtl;
//
// acquire the lock fot this mtl
//
NdisAcquireSpinLock(&mtl->lock);
/* if not connected, ignore */
if ( !mtl->is_conn )
{
D_LOG(DIGIMTL, ("mtl__rx_bchan_handler: packet on non connected mtl, ignored\n"));
goto exit_code;
}
RxTable = &mtl->rx_tbl;
D_LOG(D_ENTRY, ("mtl__rx_bchan_handler: mtl: 0x%lx, buflen: %d, bufptr: 0x%lx\n", \
mtl, msg->buflen, msg->bufptr));
//
// if we are in detect mode
//
if (!mtl->RecvFramingBits)
{
UCHAR DetectData[3];
/* extract header, check for fields */
IddGetDataFromAdapter(chan->idd,
(PUCHAR)&hdr,
(PUCHAR)msg->bufptr,
sizeof(MTL_HDR));
DigiDump( DIGIRXFRAGDATA, ("Recv Frag (detect):\n") );
DigiDumpData( DIGIRXFRAGDATA, (PUCHAR)&hdr, sizeof(MTL_HDR) );
// NdisMoveMemory ((PUCHAR)&hdr, (PUCHAR)msg->bufptr, sizeof(MTL_HDR));
//
// this is used for inband signalling - ignore it
//
if (hdr.sig_tot == 0x50)
goto exit_code;
//
// if this is dkf we need offset of zero for detection to work
//
if ( ((hdr.sig_tot & 0xF0) == 0x50) && (hdr.ofs != 0) )
goto exit_code;
//
// extract some data from the frame
//
IddGetDataFromAdapter(chan->idd,
(PUCHAR)&DetectData,
(PUCHAR)&msg->bufptr[4],
2);
DigiDump( DIGIRXFRAGDATA, ("Recv Frag (detect con't):\n") );
DigiDumpData( DIGIRXFRAGDATA, (PUCHAR)&DetectData, 2 );
// NdisMoveMemory((PUCHAR)&DetectData, (PUCHAR)&msg->bufptr[4], 2);
D_LOG(DIGIMTL, ("mtl__rx_bchan_handler: hdr: 0x%x 0x%x 0x%x\n", hdr.sig_tot, \
hdr.seq, hdr.ofs));
D_LOG(DIGIMTL, ("mtl__rx_bchan_handler: DetectData: 0x%x 0x%x\n", DetectData[0], DetectData[1]));
if ( (IddRxFrameType & IDD_FRAME_PPP) ||
((IddRxFrameType & IDD_FRAME_DKF) &&
((DetectData[0] == 0xFF) && (DetectData[1] == 0x03))))
{
mtl->RecvFramingBits = PPP_FRAMING;
mtl->SendFramingBits = PPP_FRAMING;
RxTable->NextFree = 0;
}
else
{
mtl->RecvFramingBits = RAS_FRAMING;
mtl->SendFramingBits = RAS_FRAMING;
}
D_LOG(DIGIMTL, ("mtl__rx_bchan_handler: Deteced WrapperFrameType: 0x%x\n", mtl->RecvFramingBits));
//
// don't pass up detected frame for now
//
goto exit_code;
}
if (IddRxFrameType & IDD_FRAME_DKF)
{
D_LOG(DIGIMTL, ("mtl__rx_bchan_handler: Received IddFrameType: DKF\n"));
/* size of packet has to be atleast as size of header */
if ( msg->buflen < sizeof(hdr) )
{
D_LOG(DIGIMTL, ("mtl__rx_bchan_handler: packet size too small, ignored\n"));
RxTable->DKFReceiveError1++;
goto exit_code;
}
/* extract header, check for fields */
IddGetDataFromAdapter(chan->idd,
(PUCHAR)&hdr,
(PUCHAR)msg->bufptr,
sizeof(MTL_HDR));
DigiDump( DIGIRXFRAGDATA, ("Recv Frag (DKF hdr):\n") );
DigiDumpData( DIGIRXFRAGDATA, (PUCHAR)&hdr, sizeof(MTL_HDR) );
D_LOG(DIGIMTL, ("mtl__rx_bchan_handler: hdr: 0x%x 0x%x 0x%x\n", hdr.sig_tot, \
hdr.seq, hdr.ofs));
//
// if this is not our header of if this is an inband uus
// ignore it
//
if ( (hdr.sig_tot & 0xF0) != 0x50 || hdr.sig_tot == 0x50)
{
D_LOG(DIGIMTL, ("mtl__rx_bchan_handler: bad header signature, ignored\n"));
D_LOG(DIGIMTL, ("mtl__rx_bchan_handler: mtl: 0x%lx, [0]: 0x%x", mtl, hdr.sig_tot));
RxTable->DKFReceiveError2++;
goto exit_code;
}
if ( (hdr.ofs >= MTL_MAC_MTU) || ((hdr.ofs + msg->buflen - sizeof(hdr)) > MTL_MAC_MTU) )
{
D_LOG(DIGIMTL, ("mtl__rx_bchan_handler: bad offset/buflen, ignored\n"));
D_LOG(DIGIMTL, ("mtl: 0x%lx, Offset: %d, BufferLength: %d\n", mtl, hdr.ofs, msg->buflen));
RxTable->DKFReceiveError3++;
goto exit_code;
}
NdisAcquireSpinLock(&RxTable->lock);
/* build pointer to assembly descriptor & lock it */
as = RxTable->as_tbl + (hdr.seq % MTL_RX_BUFS);
//
// if this assembly pointer is not free (queued) then
// just drop this fragment
//
if (as->Queued)
{
D_LOG(DIGIMTL, ("DKFRx: AssemblyQueue Overrun! mtl: 0x%lx, as: 0x%lx, seq: %d\n", \
mtl, as, hdr.seq));
RxTable->DKFReceiveError4++;
as->QueueOverRun++;
NdisReleaseSpinLock(&RxTable->lock);
goto exit_code;
}
/* check for new slot */
if ( !as->tot )
{
new_slot:
/* new entry, fill-up */
as->seq = hdr.seq; /* record sequence number */
as->num = 1; /* just received 1'st fragment */
as->ttl = 1000; /* time to live init val */
as->len = msg->buflen - sizeof(hdr); /* record received length */
as->tot = hdr.sig_tot & 0x0F; /* record number of expected fragments */
/* copy received data into buffer */
copy_data:
IddGetDataFromAdapter(chan->idd,
(PUCHAR)as->buf + hdr.ofs,
(PUCHAR)msg->bufptr + sizeof(hdr),
(USHORT)(msg->buflen - sizeof(hdr)));
DigiDump( DIGIRXFRAGDATA, ("Recv Frag (DKF data):\n") );
DigiDumpData( DIGIRXFRAGDATA,
(PUCHAR)as->buf + hdr.ofs,
(msg->buflen - sizeof(hdr)) );
// NdisMoveMemory (as->buf + hdr.ofs, msg->bufptr + sizeof(hdr), msg->buflen - sizeof(hdr));
}
else if ( as->seq == hdr.seq )
{
/* same_seq: */
/* same sequence number, accumulate */
as->num++;
as->len += (msg->buflen - sizeof(hdr));
goto copy_data;
}
else
{
/* bad_frag: */
/*
* if this case, an already taken slot is hit, but with a different
* sequence number. this indicates a wrap-around in as_tbl. prev
* entry is freed and then this fragment is recorded as first
*/
D_LOG(DIGIMTL, ("DKFRx: Bad Fragment! mtl: 0x%lx, as: 0x%lx, as->seq: %d, seq: %d\n", \
mtl, as, as->seq, hdr.seq));
D_LOG(DIGIMTL, ("as->tot: %d, as->num: %d\n", as->tot, as->num));
RxTable->DKFReceiveError5++;
goto new_slot;
}
/* if all fragments recieved for packet, time to mail it up */
if ( as->tot == as->num )
{
D_LOG(DIGIMTL, ("mtl__rx_bchan_handler: pkt mailed up, buf: 0x%lx, len: 0x%x\n", \
as->buf, as->len));
QueueDescriptorForRxIndication(mtl, as);
//
// mark this guy as being queued
//
as->Queued = 1;
}
/* release assembly descriptor */
NdisReleaseSpinLock(&RxTable->lock);
}
else if (IddRxFrameType & IDD_FRAME_PPP)
{
D_LOG(DIGIMTL, ("mtl__rx_bchan_handler: Received IddFrameType: PPP\n"));
NdisAcquireSpinLock(&RxTable->lock);
/* build pointer to assembly descriptor & lock it */
as = RxTable->as_tbl + (RxTable->NextFree % MTL_RX_BUFS);
//
// if this assembly pointer is not free (queued) then
// just drop this fragment
//
if (as->Queued)
{
D_LOG(DIGIMTL, ("PPPRx: AssemblyQueue Overrun! mtl: 0x%lx, as: 0x%lx, NextFree: %d\n", \
mtl, as, RxTable->NextFree));
as->QueueOverRun++;
RxTable->PPPReceiveError1++;
NdisReleaseSpinLock(&RxTable->lock);
goto exit_code;
}
FragmentFlags = msg->FragmentFlags;
D_LOG(DIGIMTL, ("mtl__rx_bchan_handler: FragmentFlags: 0x%x, CurrentRxState: 0x%x\n", FragmentFlags, as->State));
switch (as->State)
{
case RX_MIDDLE:
if (FragmentFlags & H_RX_N_BEG)
break;
as->MissCount++;
//
// missed an end buffer
//
D_LOG(DIGIMTL, ("mtl__rx_bchan_handler: mtl: 0x%lx, Miss in State: %d, FragmentFlags: 0x%x, MissCount: %d\n", \
mtl, as->State, FragmentFlags, as->MissCount));
RxTable->PPPReceiveError2++;
goto clearbuffer;
break;
case RX_BEGIN:
case RX_END:
if (FragmentFlags & H_RX_N_BEG)
{
//
// missed a begining buffer
//
as->MissCount++;
D_LOG(DIGIMTL, ("mtl__rx_bchan_handler: mtl: 0x%lx, Miss in State: %d, FragmentFlags: 0x%x, MissCount: %d\n", \
mtl, as->State, FragmentFlags, as->MissCount));
RxTable->PPPReceiveError3++;
goto done;
}
clearbuffer:
//
// clear rx buffer
//
NdisZeroMemory(as->buf, sizeof(as->buf));
//
// start data at begin of buffer
//
as->DataPtr = as->buf;
//
// new buffer
//
as->len = 0;
//
// set rx state
//
as->State = RX_MIDDLE;
//
// set time to live
//
as->ttl = 1000;
//
// there is always only one fragment with PPP
// maybe a big one but still only one
//
as->tot = 1;
break;
default:
D_LOG(DIGIMTL, ("Invalid PPP Rx State! mtl: 0x%lx, as: 0x%lx State: 0x%x\n", \
mtl, as, as->State));
as->State = RX_BEGIN;
as->tot = 0;
as->MissCount++;
goto done;
break;
}
//
// get the length to be copy
//
CopyLen = msg->buflen;
D_LOG(DIGIMTL, ("mtl__rx_bchan_handler: CopyLen: %d\n", CopyLen));
if (FragmentFlags & H_RX_N_END)
{
//
// if this is not the last buffer and length is 0
// we are done
//
if (CopyLen == 0)
goto done_copy;
}
else
{
//
// if CopyLen = 0 buffer only contains 2 CRC bytes
//
if (CopyLen == 0)
{
goto done_copy;
}
//
// buffer contains only 1 CRC byte
//
else if (CopyLen == (-1 & H_RX_LEN_MASK))
{
//
// previous buffer had a crc byte in it so remove it
//
as->len -= 1;
goto done_copy;
}
//
// buffer contains no crc or data bytes
//
else if (CopyLen == (-2 & H_RX_LEN_MASK))
{
//
// previous buffer had 2 crc bytes in it so remove them
//
as->len -= 2;
goto done_copy;
}
}
//
// if larger than max rx size throw away
//
if (CopyLen > IDP_MAX_RX_LEN)
{
//
// buffer to big so dump it
//
as->State = RX_BEGIN;
as->MissCount++;
RxTable->PPPReceiveError4++;
/* mark as free now */
as->tot = 0;
D_LOG(DIGIMTL, ("mtl__rx_bchan_handler: mtl: 0x%lx, RxToLarge: RxSize: %d, MissCount: %d\n", mtl, CopyLen, as->MissCount));
goto done;
}
as->len += CopyLen;
if (as->len > MTL_MAC_MTU)
{
//
// Frame is to big so dump it
//
as->State = RX_BEGIN;
RxTable->PPPReceiveError5++;
as->MissCount++;
/* mark as free now */
as->tot = 0;
D_LOG(DIGIMTL, ("mtl__rx_bchan_handler: AssembledRxToLarge: mtl: 0x%lx, AsRxSize: %d, MissCount: %d\n", mtl, as->len, as->MissCount));
goto done;
}
//
// copy the data to rx descriptor
//
IddGetDataFromAdapter(chan->idd,
(PUCHAR)as->DataPtr,
(PUCHAR)msg->bufptr,
CopyLen);
DigiDump( DIGIRXFRAGDATA, ("Recv Frag (PPP):\n") );
DigiDumpData( DIGIRXFRAGDATA,
(PUCHAR)as->DataPtr,
CopyLen );
// NdisMoveMemory(as->DataPtr, msg->bufptr, CopyLen);
//
// update data ptr
//
as->DataPtr += CopyLen;
done_copy:
if (!(FragmentFlags & H_RX_N_END))
{
//
// if this is the end of the frame indicate to wrapper
//
as->State = RX_END;
RxTable->NextFree++;
QueueDescriptorForRxIndication(mtl, as);
//
// mark this guy as being queued
//
as->Queued = 1;
}
done:
/* release assembly descriptor */
NdisReleaseSpinLock(&RxTable->lock);
}
else
D_LOG(DIGIMTL, ("mtl__rx_bchan_handler: Received IddFrameType: ??????!!!!!!\n"));
//
// exit code
// release spinlock and return
//
exit_code:
NdisReleaseSpinLock(&mtl->lock);
}
VOID
IndicateRxToWrapper(
MTL *mtl
)
{
UCHAR *BufferPtr;
USHORT BufferLength = 0;
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
ADAPTER *Adapter;
MTL_AS *as;
MTL_RX_TBL *RxTable;
NdisAcquireSpinLock(&mtl->lock);
Adapter = mtl->Adapter;
RxTable = &mtl->rx_tbl;
while (!IsRxIndicationFifoEmpty(mtl))
{
NdisAcquireSpinLock(&RxTable->lock);
//
// get the next completed rx assembly
//
as = GetAssemblyFromRxIndicationFifo(mtl);
if (!as)
{
D_LOG(DIGIMTL, ("IndicateRx: Got a NULL as from queue! mtl: 0x%lx\n", mtl));
RxTable->IndicateReceiveError1++;
NdisReleaseSpinLock(&RxTable->lock);
goto exit_code;
}
//
// if this is an old ras frame then we must strip off
// the mac header Dst[6] + Src[6] + Length[2]
//
if (mtl->RecvFramingBits & RAS_FRAMING)
{
//
// pass over the mac header - tommyd does not want to see this
//
BufferPtr = as->buf + 14;
//
// indicate with the size of the ethernet packet not the received size
// this takes care of the old driver that does padding on small frames
//
BufferLength = as->buf[12];
BufferLength = BufferLength << 8;
BufferLength += as->buf[13];
D_LOG(DIGIMTL, ("IndicateRxToWrapper: WrapperFrameType: RAS\n"));
D_LOG(DIGIMTL, ("IndicateRxToWrapper: BufPtr: 0x%lx, BufLen: %d\n", BufferPtr, BufferLength));
}
else if (mtl->RecvFramingBits & PPP_FRAMING)
{
//
// the received buffer is the data that needs to be inidcated
//
BufferPtr = as->buf;
//
// the received length is the length that needs to be indicated
//
BufferLength = as->len;
D_LOG(DIGIMTL, ("IndicateRxToWrapper: WrapperFrameType: PPP\n"));
D_LOG(DIGIMTL, ("IndicateRxToWrapper: BufPtr: 0x%lx, BufLen: %d\n", BufferPtr, BufferLength));
}
else
{
//
// unknown framing - what to do what to do
// throw it away
//
D_LOG(DIGIMTL, ("IndicateRxToWrapper: mtl: 0x%lx, Unknown WrapperFramming: 0x%x\n", mtl, mtl->RecvFramingBits));
RxTable->IndicateReceiveError2++;
as->tot = 0;
as->Queued = 0;
NdisReleaseSpinLock(&RxTable->lock);
goto exit_code;
}
if (BufferLength > MTL_MAC_MTU)
{
D_LOG(DIGIMTL, ("IndicateRxToWrapper: mtl: 0x%lx, ReceiveLength > MAX ALLOWED (1514): RxLength: %d\n", mtl, as->len));
RxTable->IndicateReceiveError3++;
as->tot = 0;
as->Queued = 0;
NdisReleaseSpinLock(&RxTable->lock);
goto exit_code;
}
//
// send frame up
//
if (mtl->LinkHandle)
{
/* release assembly descriptor */
NdisReleaseSpinLock(&RxTable->lock);
NdisReleaseSpinLock(&mtl->lock);
DigiDump( DIGIRXDATA, ("Indicating Recv Data:\n") );
DigiDumpData( DIGIRXDATA, BufferPtr, BufferLength );
NdisMWanIndicateReceive(&Status,
Adapter->Handle,
mtl->LinkHandle,
BufferPtr,
BufferLength);
NdisAcquireSpinLock(&mtl->lock);
NdisAcquireSpinLock(&RxTable->lock);
mtl->RecvCompleteScheduled = 1;
}
/* mark as free now */
as->tot = 0;
//
// mark this guy as being free
//
as->Queued = 0;
/* release assembly descriptor */
NdisReleaseSpinLock(&RxTable->lock);
}
//
// exit code
// release spinlock and return
//
exit_code:
NdisReleaseSpinLock(&mtl->lock);
}
//
// this function checks all of the mtl's on this adapter to see if
// the protocols need to be given a chance to do some work
//
VOID
MtlRecvCompleteFunction(
ADAPTER *Adapter
)
{
ULONG n;
for ( n = 0; n < MAX_MTL_PER_ADAPTER; n++)
{
MTL *mtl = Adapter->MtlTbl[n] ;
//
// if this is a valid mtl
//
if (mtl)
{
//
// get lock for this mtl
//
NdisAcquireSpinLock(&mtl->lock);
//
// is a receive complete scheduled on a valid link?
//
if (mtl->RecvCompleteScheduled && mtl->LinkHandle)
{
//
// release the lock
//
NdisReleaseSpinLock(&mtl->lock);
NdisMWanIndicateReceiveComplete(Adapter->Handle,
mtl->LinkHandle);
//
// reaquire the lock
//
NdisAcquireSpinLock(&mtl->lock);
//
// clear the schedule flag
//
mtl->RecvCompleteScheduled = 0;
}
//
// release the lock
//
NdisReleaseSpinLock(&mtl->lock);
}
}
}
BOOLEAN
IsRxIndicationFifoEmpty(
MTL *mtl)
{
BOOLEAN Ret = 0;
NdisAcquireSpinLock(&mtl->RxIndicationFifo.lock);
Ret = IsListEmpty(&mtl->RxIndicationFifo.head);
NdisReleaseSpinLock(&mtl->RxIndicationFifo.lock);
return(Ret);
}
MTL_AS*
GetAssemblyFromRxIndicationFifo(
MTL *mtl
)
{
MTL_AS *as = NULL;
NdisAcquireSpinLock(&mtl->RxIndicationFifo.lock);
if (!IsListEmpty(&mtl->RxIndicationFifo.head))
as = (MTL_AS*)RemoveHeadList(&mtl->RxIndicationFifo.head);
NdisReleaseSpinLock(&mtl->RxIndicationFifo.lock);
return(as);
}
VOID
QueueDescriptorForRxIndication(
MTL *mtl,
MTL_AS *as
)
{
NdisAcquireSpinLock(&mtl->RxIndicationFifo.lock);
InsertTailList(&mtl->RxIndicationFifo.head, &as->link);
NdisReleaseSpinLock(&mtl->RxIndicationFifo.lock);
}
/* do timer tick processing for rx side */
VOID
mtl__rx_tick(MTL *mtl)
{
INT n;
MTL_AS *as;
MTL_RX_TBL *RxTable = &mtl->rx_tbl;
//
// see if there are any receives to give to wrapper
//
IndicateRxToWrapper(mtl);
NdisAcquireSpinLock(&mtl->lock);
NdisAcquireSpinLock(&RxTable->lock);
/* scan assembly table */
for ( n = 0, as = RxTable->as_tbl ; n < MTL_RX_BUFS ; n++, as++ )
{
/* update ttl & check */
if ( as->tot && !(as->ttl -= 25) )
{
D_LOG(DIGIMTL, ("mtl__rx_bchan_handler: Pkt Kill ttl = 0: Slot: %d, mtl: 0x%lx\n", n, mtl));
D_LOG(DIGIMTL, ("AS Timeout! mtl: 0x%lx, as: 0x%lx, as->seq: 0x%x\n", mtl, as, as->seq));
D_LOG(DIGIMTL, ("as->tot: %d, as->num: %d", as->tot, as->num));
RxTable->TimeOutReceiveError1++;
//
// if this guy was queued for indication to wrapper
// and was not indicated within a second something is wrong
//
if (as->Queued)
{
D_LOG(DIGIMTL, ("AS Timeout while queued for indication! mtl: 0x%lx, as: 0x%lx\n", mtl, as));
#if DBG
DbgBreakPoint();
#endif
}
as->tot = 0;
//
// mark this guy as being free
//
as->Queued = 0;
}
}
NdisReleaseSpinLock(&RxTable->lock);
NdisReleaseSpinLock(&mtl->lock);
}
//
// see if there are any receives to give to wrapper
//
VOID
TryToIndicateMtlReceives(
ADAPTER *Adapter
)
{
ULONG n;
for (n = 0; n < MAX_MTL_PER_ADAPTER; n++)
{
MTL *mtl = Adapter->MtlTbl[n];
if (mtl)
IndicateRxToWrapper(mtl);
}
}