/* * MTL_RX.C - Receive side processing for MTL */ #include #include #include #include #include #include #include #include #include #include #include /* 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); } }