You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
887 lines
28 KiB
887 lines
28 KiB
/*
|
|
************************************************************************
|
|
*
|
|
* NSCFIR.C
|
|
*
|
|
*
|
|
* Portions Copyright (C) 1996-2001 National Semiconductor Corp.
|
|
* All rights reserved.
|
|
* Copyright (C) 1996-2001 Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
*
|
|
*
|
|
*************************************************************************
|
|
*/
|
|
|
|
|
|
#include "nsc.h"
|
|
#include "nscfir.tmh"
|
|
#include "nsctypes.h"
|
|
|
|
VOID NSC_FIR_ISR(IrDevice *thisDev, BOOLEAN *claimingInterrupt,
|
|
BOOLEAN *requireDeferredCallback)
|
|
{
|
|
LOG_FIR("==> NSC_FIR_ISR");
|
|
DEBUGFIR(DBG_ISR|DBG_OUT, ("NSC: ==> NSC_FIR_ISR(0x%x)\n", thisDev));
|
|
|
|
thisDev->InterruptMask = NSC_ReadBankReg(thisDev->portInfo.ioBase, 0, 1);
|
|
|
|
thisDev->InterruptStatus = NSC_ReadBankReg(thisDev->portInfo.ioBase, 0, 2) & thisDev->FirIntMask;
|
|
|
|
thisDev->AuxStatus = NSC_ReadBankReg(thisDev->portInfo.ioBase, 0, 7);
|
|
|
|
thisDev->LineStatus = NSC_ReadBankReg(thisDev->portInfo.ioBase, 0, 5);
|
|
|
|
|
|
LOG_FIR("InterruptMask: %02x, InterruptStatus: %02x", thisDev->InterruptMask,thisDev->InterruptStatus);
|
|
LOG_FIR("AuxStatus: %02x, LineStatus: %02x", thisDev->AuxStatus,thisDev->LineStatus);
|
|
|
|
DEBUGFIR(DBG_ISR|DBG_OUT, ("NSC: InterruptMask = 0x%x\n", thisDev->InterruptMask));
|
|
DEBUGFIR(DBG_ISR|DBG_OUT, ("NSC: InterruptStatus = 0x%x\n", thisDev->InterruptStatus));
|
|
DEBUGFIR(DBG_ISR|DBG_OUT, ("NSC: AuxStatus = 0x%x\n", thisDev->AuxStatus));
|
|
DEBUGFIR(DBG_ISR|DBG_OUT, ("NSC: LineStatus = 0x%x\n", thisDev->LineStatus));
|
|
|
|
if (thisDev->InterruptStatus) {
|
|
//
|
|
// After seeing the first packet switch the interrupt
|
|
// to timer and use the DMA counter to decide if receptions
|
|
// have stopped.
|
|
//
|
|
if (thisDev->InterruptStatus & LS_EV) {
|
|
//
|
|
// Got a link status interrupt
|
|
//
|
|
if (thisDev->LineStatus & LSR_FR_END) {
|
|
//
|
|
// The frame end status is set
|
|
//
|
|
if (!thisDev->FirTransmitPending) {
|
|
//
|
|
// we weren't tansmitting
|
|
//
|
|
}
|
|
}
|
|
if (thisDev->LineStatus & LSR_OE) {
|
|
|
|
LOG_ERROR("NSC_FIR_ISR: rx overflow");
|
|
}
|
|
}
|
|
|
|
if (thisDev->InterruptStatus & TMR_EV){
|
|
//
|
|
// Disable Timer during call to DPC bit.
|
|
//
|
|
NSC_WriteBankReg(thisDev->portInfo.ioBase, 4, 2, 0x00);
|
|
NSC_WriteBankReg(thisDev->portInfo.ioBase, 0, 7, 0x80);
|
|
}
|
|
|
|
*claimingInterrupt = TRUE;
|
|
*requireDeferredCallback = TRUE;
|
|
|
|
SetCOMInterrupts(thisDev, FALSE);
|
|
|
|
if (thisDev->UIR_ModuleId >= 0x16 )
|
|
{
|
|
// This is for the Frame stop mode when the ISR is interrupted
|
|
// after every Frame Tx
|
|
//
|
|
|
|
if ((thisDev->AuxStatus & 0x08)
|
|
&& (thisDev->InterruptStatus & 0x04)
|
|
&& (thisDev->FirTransmitPending == TRUE))
|
|
{
|
|
if (thisDev->LineStatus&0x40)
|
|
{
|
|
NSC_WriteBankReg(thisDev->portInfo.ioBase, 0, 7, 0x40);
|
|
DBGERR(("FIR: Transmit underrun\n"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
*claimingInterrupt = FALSE;
|
|
*requireDeferredCallback = FALSE;
|
|
}
|
|
|
|
LOG_FIR("<== NSC_FIR_ISR claimingInterrupt = %x, requireDeferredCallback = %x ", *claimingInterrupt,*requireDeferredCallback);
|
|
|
|
DEBUGFIR(DBG_ISR|DBG_OUT, ("NSC: <== NSC_FIR_ISR\n"));
|
|
|
|
}
|
|
|
|
typedef struct {
|
|
IrDevice *thisDev;
|
|
ULONG_PTR Offset;
|
|
ULONG_PTR Length;
|
|
} DMASPACE;
|
|
|
|
void SkipNonDmaBuffers(PLIST_ENTRY Head, PLIST_ENTRY *Entry)
|
|
{
|
|
while (Head!=*Entry)
|
|
{
|
|
rcvBuffer *rcvBuf = CONTAINING_RECORD(*Entry,
|
|
rcvBuffer,
|
|
listEntry);
|
|
|
|
if (rcvBuf->isDmaBuf)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
*Entry = (*Entry)->Flink;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// We have two lists of buffers which occupy our DMA space. We
|
|
// want to walk this list and find the largest space for putting
|
|
// new packets.
|
|
//
|
|
rcvBuffer *GetNextPacket(DMASPACE *Space,
|
|
PLIST_ENTRY *CurrFull,
|
|
PLIST_ENTRY *CurrPend)
|
|
{
|
|
rcvBuffer *Result = NULL;
|
|
|
|
SkipNonDmaBuffers(&Space->thisDev->rcvBufFull, CurrFull);
|
|
SkipNonDmaBuffers(&Space->thisDev->rcvBufPend, CurrPend);
|
|
|
|
if (*CurrFull==&Space->thisDev->rcvBufFull)
|
|
{
|
|
// Full list is finished.
|
|
if (*CurrPend!=&Space->thisDev->rcvBufPend)
|
|
{
|
|
// Any entry on the pend list is valid. Take the
|
|
// next one and advance the pointer.
|
|
|
|
Result = CONTAINING_RECORD(*CurrPend,
|
|
rcvBuffer,
|
|
listEntry);
|
|
|
|
*CurrPend = (*CurrPend)->Flink;
|
|
}
|
|
else
|
|
{
|
|
// Both lists are finished. We will return NULL.
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (*CurrPend==&Space->thisDev->rcvBufPend)
|
|
{
|
|
// Pend list is finished. Take anything from the
|
|
// Full list, advance the pointer.
|
|
Result = CONTAINING_RECORD(*CurrFull,
|
|
rcvBuffer,
|
|
listEntry);
|
|
|
|
*CurrFull = (*CurrFull)->Flink;
|
|
}
|
|
else
|
|
{
|
|
// Both list have valid entries. Compare the two and take the
|
|
// one that appears in the buffer first.
|
|
rcvBuffer *Full, *Pend;
|
|
|
|
Full = CONTAINING_RECORD(*CurrFull,
|
|
rcvBuffer,
|
|
listEntry);
|
|
Pend = CONTAINING_RECORD(*CurrPend,
|
|
rcvBuffer,
|
|
listEntry);
|
|
|
|
if (Full->dataBuf < Pend->dataBuf)
|
|
{
|
|
// Full is the winner. Take it.
|
|
|
|
Result = Full;
|
|
*CurrFull = (*CurrFull)->Flink;
|
|
}
|
|
else
|
|
{
|
|
// Pend is the winner. Take it.
|
|
|
|
Result = Pend;
|
|
*CurrPend = (*CurrPend)->Flink;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Result)
|
|
{
|
|
ASSERT(Result->isDmaBuf);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
BOOLEAN SynchronizedFindLargestSpace(IN PVOID Context)
|
|
{
|
|
DMASPACE *Space = Context;
|
|
IrDevice *thisDev = Space->thisDev;
|
|
BOOLEAN Result;
|
|
PLIST_ENTRY Full, Pend;
|
|
rcvBuffer *Current = NULL;
|
|
|
|
ASSERT(sizeof(ULONG)==sizeof(PVOID));
|
|
|
|
|
|
if (IsListEmpty(&thisDev->rcvBufFull) && IsListEmpty(&thisDev->rcvBufPend))
|
|
{
|
|
Space->Offset = (ULONG_PTR)thisDev->dmaReadBuf;
|
|
Space->Length = RCV_DMA_SIZE;
|
|
}
|
|
else
|
|
{
|
|
ULONG_PTR EndOfLast;
|
|
ULONG_PTR ThisSpace;
|
|
|
|
Full = thisDev->rcvBufFull.Flink;
|
|
Pend = thisDev->rcvBufPend.Flink;
|
|
|
|
Space->Length = 0;
|
|
|
|
EndOfLast = Space->Offset = (ULONG_PTR)thisDev->dmaReadBuf;
|
|
|
|
Current = GetNextPacket(Space, &Full, &Pend);
|
|
while (Current)
|
|
{
|
|
// It's possible to get a packet that is from SIR. If so, skip it.
|
|
|
|
if (Current->isDmaBuf)
|
|
{
|
|
ASSERT((ULONG_PTR)Current->dataBuf >= EndOfLast);
|
|
|
|
ThisSpace = (ULONG_PTR)Current->dataBuf - EndOfLast;
|
|
|
|
// ASSERT the pointer is actually in our DMA buffer.
|
|
ASSERT(Current->dataBuf >= thisDev->dmaReadBuf ||
|
|
Current->dataBuf < thisDev->dmaReadBuf+RCV_DMA_SIZE);
|
|
|
|
if (ThisSpace > Space->Length)
|
|
{
|
|
Space->Offset = EndOfLast;
|
|
Space->Length = ThisSpace;
|
|
}
|
|
|
|
EndOfLast = (ULONG_PTR)Current->dataBuf + Current->dataLen;
|
|
}
|
|
|
|
|
|
Current = GetNextPacket(Space, &Full, &Pend);
|
|
}
|
|
|
|
// Now we do one more calculation for the space after the end of the list.
|
|
|
|
ThisSpace = (ULONG_PTR)thisDev->dmaReadBuf + RCV_DMA_SIZE - (ULONG_PTR)EndOfLast;
|
|
|
|
if (ThisSpace > Space->Length)
|
|
{
|
|
Space->Offset = EndOfLast;
|
|
Space->Length = ThisSpace;
|
|
}
|
|
|
|
// Round off to start DMA on 4 byte boundary
|
|
Space->Length -= 4 - (Space->Offset & 3);
|
|
Space->Offset = (Space->Offset+3) & (~3);
|
|
}
|
|
|
|
// We want space relative to start of buffer.
|
|
Space->Offset -= (ULONG_PTR)thisDev->dmaReadBuf;
|
|
|
|
Result = (Space->Length >= MAX_RCV_DATA_SIZE + FAST_IR_FCS_SIZE);
|
|
|
|
if (!Result)
|
|
{
|
|
DEBUGFIR(DBG_ERR, ("NSC: ERROR: Not enough space to DMA full packet %x\n", thisDev));
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
BOOLEAN FindLargestSpace(IN IrDevice *thisDev,
|
|
OUT PULONG_PTR pOffset,
|
|
OUT PULONG_PTR pLength)
|
|
{
|
|
DMASPACE Space;
|
|
BOOLEAN Result;
|
|
|
|
Space.Offset = 0;
|
|
Space.Length = 0;
|
|
Space.thisDev = thisDev;
|
|
|
|
Result = NdisMSynchronizeWithInterrupt(
|
|
&thisDev->interruptObj,
|
|
SynchronizedFindLargestSpace,
|
|
&Space
|
|
);
|
|
|
|
|
|
*pOffset = Space.Offset;
|
|
*pLength = Space.Length;
|
|
|
|
return Result;
|
|
}
|
|
|
|
|
|
|
|
|
|
void FIR_DeliverFrames(IrDevice *thisDev)
|
|
{
|
|
UCHAR frameStat;
|
|
NDIS_STATUS stat;
|
|
ULONG rcvFrameSize;
|
|
PUCHAR NewFrame;
|
|
|
|
UCHAR BytesInFifo=0;
|
|
ULONG_PTR LastReadDMACount, EndOfData;
|
|
BOOLEAN resetDma = FALSE, OverflowOccurred = FALSE;
|
|
BOOLEAN Discarding = thisDev->DiscardNextPacketSet;
|
|
const UINT fcsSize = ( thisDev->currentSpeed >= MIN_FIR_SPEED) ?
|
|
FAST_IR_FCS_SIZE : MEDIUM_IR_FCS_SIZE;
|
|
|
|
LOG("==> FIR_DeliverFrames, prev dma=%d",(ULONG)thisDev->LastReadDMACount);
|
|
DEBUGFIR(DBG_RX|DBG_OUT, ("NSC: ==> FIR_DeliverFrames(0x%x)\n", thisDev));
|
|
|
|
LastReadDMACount = NdisMReadDmaCounter(thisDev->DmaHandle);
|
|
|
|
// Check for data received since the last time we were here.
|
|
// We also check for data in fifo. If there is some, we wait, as long
|
|
// as the DMA still has room to capture data.
|
|
|
|
if (LastReadDMACount > 0) {
|
|
//
|
|
// we have not transfered all of the data possible into the recieve area.
|
|
// See if we have read more since the last time we were here
|
|
//
|
|
if (((LastReadDMACount < thisDev->LastReadDMACount) || (BytesInFifo=SyncReadBankReg(&thisDev->interruptObj,thisDev->portInfo.ioBase, 2, 7) & 0x3f))) {
|
|
//
|
|
// we transfered something since
|
|
// the last interrupt or there are still bytes in the fifo, then set a timer
|
|
//
|
|
|
|
thisDev->LastReadDMACount = LastReadDMACount;
|
|
|
|
//
|
|
// Set Timer Enable bit for another Timeout.
|
|
//
|
|
thisDev->FirIntMask = 0x90;
|
|
SyncWriteBankReg(&thisDev->interruptObj,thisDev->portInfo.ioBase, 4, 2, 0x01);
|
|
LOG_FIR("<== FIR_DeliverFrames- Enable timer, fifo=%02x, LastDma=%d",BytesInFifo,(ULONG)LastReadDMACount);
|
|
DEBUGFIR(DBG_RX|DBG_OUT, ("NSC: <== FIR_DeliverFrames\n"));
|
|
return;
|
|
}
|
|
} else {
|
|
//
|
|
// The dma count has gone to zero so we have transfered all we possibly can,
|
|
// see if any thing is left in the fifo
|
|
//
|
|
BytesInFifo=SyncReadBankReg(&thisDev->interruptObj,thisDev->portInfo.ioBase, 2, 7) & 0x3f;
|
|
|
|
LOG("Receive: dma transfer complete, bytes in fifo=%d",BytesInFifo);
|
|
|
|
//
|
|
// we are stopping because the dma buffer filled up, not because the
|
|
// receiver was idle
|
|
//
|
|
thisDev->ForceTurnAroundTimeout=TRUE;
|
|
|
|
}
|
|
|
|
RegStats.RxDPC_Window++;
|
|
|
|
|
|
//
|
|
// stop the dma transfer, so the data in the buffer will be valid
|
|
//
|
|
stat=CompleteDmaTransferFromDevice(&thisDev->DmaUtil);
|
|
|
|
//
|
|
// see how was transfer now that we have stopped the dma
|
|
//
|
|
LastReadDMACount = NdisMReadDmaCounter(thisDev->DmaHandle);
|
|
|
|
if (stat != NDIS_STATUS_SUCCESS) {
|
|
DBGERR(("NdisMCompleteDmaTransfer failed: %d\n", stat));
|
|
ASSERT(0);
|
|
//
|
|
// could not complete the DMA, make it appear that zero bytes were transfered
|
|
//
|
|
LastReadDMACount=thisDev->rcvDmaSize;
|
|
}
|
|
|
|
thisDev->FirReceiveDmaActive=FALSE;
|
|
|
|
thisDev->DiscardNextPacketSet = FALSE;
|
|
|
|
EndOfData = thisDev->rcvDmaOffset + (thisDev->rcvDmaSize - LastReadDMACount);
|
|
|
|
LOG("Recieve: Total data transfered %d",(ULONG)(thisDev->rcvDmaSize - LastReadDMACount));
|
|
|
|
SyncGetFifoStatus(
|
|
&thisDev->interruptObj,
|
|
thisDev->portInfo.ioBase,
|
|
&frameStat,
|
|
&rcvFrameSize
|
|
);
|
|
|
|
if (frameStat == 0) LOG_ERROR("Receive: no frames in fifo");
|
|
|
|
LOG("frameStat: %x, size=%d ", (UINT) frameStat,rcvFrameSize);
|
|
DEBUGFIR(DBG_RX|DBG_OUT, ("NSC: frameStat = %xh\n", (UINT) frameStat));
|
|
|
|
while ((frameStat & 0x80) && thisDev->rcvPktOffset < EndOfData) {
|
|
|
|
/*
|
|
* This status byte is valid; see what else it says...
|
|
* Also mask off indeterminate bit.
|
|
*/
|
|
frameStat &= ~0xA0;
|
|
|
|
/*
|
|
* Whether the frame is good or bad, we must read the counter
|
|
* FIFO to synchronize it with the frame status FIFO.
|
|
*/
|
|
if (Discarding)
|
|
{
|
|
LOG_ERROR("receive error: disc stat=%02x, lost=%d\n",frameStat,rcvFrameSize);
|
|
// Do nothing
|
|
}
|
|
else if (frameStat != 0) {
|
|
/*
|
|
* Some rcv error occurred. Reset DMA.
|
|
*/
|
|
|
|
LOG_ERROR("Receive error: stat=%02x, lost=%d\n",frameStat,rcvFrameSize);
|
|
|
|
DEBUGFIR(DBG_RX|DBG_ERR, ("NSC: RCV ERR: frameStat=%xh FrameSize=%d \n",
|
|
(UINT)frameStat,rcvFrameSize));
|
|
|
|
if (frameStat & 0x40) {
|
|
if (frameStat & 0x01) {
|
|
RegStats.StatusFIFOOverflows++;
|
|
}
|
|
if (frameStat & 0x02) {
|
|
RegStats.ReceiveFIFOOverflows++;
|
|
}
|
|
RegStats.MissedPackets += rcvFrameSize;
|
|
}
|
|
else{
|
|
if (frameStat & 0x01) {
|
|
RegStats.StatusFIFOOverflows++;
|
|
}
|
|
if (frameStat & 0x02) {
|
|
RegStats.ReceiveFIFOOverflows++;
|
|
}
|
|
if (frameStat & 0x04) {
|
|
RegStats.ReceiveCRCErrors++;
|
|
}
|
|
if (frameStat & 0x08) {
|
|
}
|
|
if (frameStat & 0x10) {
|
|
}
|
|
LOG("Bytes Lost: %d",rcvFrameSize);
|
|
ASSERT((thisDev->rcvPktOffset + rcvFrameSize)<= RCV_DMA_SIZE);
|
|
|
|
/* Advance pointer past bad packet. */
|
|
thisDev->rcvPktOffset += rcvFrameSize;
|
|
}
|
|
}
|
|
|
|
else if (thisDev->rcvPktOffset + rcvFrameSize > EndOfData )
|
|
{
|
|
|
|
LOG_ERROR("Receive: Frame extends beyond where dma control wrote: offset=%x, Frame size=%x, EndOfData=%x",(ULONG)thisDev->rcvPktOffset,(ULONG)rcvFrameSize,(ULONG)EndOfData);
|
|
|
|
DBGERR(("Packet won't fit in received data!\n"));
|
|
DBGERR(("rcvPktOffset:%x rcvFrameSize:%x LastReadDmaCount:%x\n",
|
|
thisDev->rcvPktOffset,
|
|
rcvFrameSize,
|
|
LastReadDMACount));
|
|
DBGERR(("rcvDmaOffset:%x rcvDmaSize:%x EndOfData:%x\n",
|
|
thisDev->rcvDmaOffset, thisDev->rcvDmaSize, EndOfData));
|
|
|
|
//
|
|
// The frame seems to have extended past the end of where we dma the data.
|
|
// This should only happen if the dma space just less than the size of
|
|
// the fifo too small. The remaining data is still sitting in the fifo
|
|
// Attempt to read it out so it will be empty when we got read more frames
|
|
//
|
|
while (BytesInFifo > 0) {
|
|
|
|
SyncReadBankReg(&thisDev->interruptObj,thisDev->portInfo.ioBase, BANK_0, TXD_RXD_OFFSET);
|
|
BytesInFifo--;
|
|
}
|
|
|
|
|
|
BytesInFifo=SyncReadBankReg(&thisDev->interruptObj,thisDev->portInfo.ioBase, 2, 7) & 0x3f;
|
|
|
|
if (BytesInFifo > 0) {
|
|
//
|
|
// still bytes in the fifo after attmpting to read them out.
|
|
// Another frame has probably started showing up, Can trust the data collected
|
|
// so mark these frames to be discarded
|
|
//
|
|
LOG_ERROR("Receive: Still have bytes in the fifo after attempting to flush, %d bytes remaining",BytesInFifo);
|
|
|
|
BytesInFifo=0;
|
|
|
|
thisDev->DiscardNextPacketSet = TRUE;
|
|
}
|
|
//
|
|
// this should be the last frame to be received with out error, advance this pointer
|
|
// anyway.
|
|
//
|
|
thisDev->rcvPktOffset += rcvFrameSize;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
DEBUGFIR(DBG_RX|DBG_OUT, ("NSC: *** >>> FIR_DeliverFrames DMA offset 0x%x:\n",
|
|
thisDev->rcvDmaOffset));
|
|
|
|
//
|
|
// this is where the new frame starts
|
|
//
|
|
NewFrame = thisDev->dmaReadBuf + thisDev->rcvPktOffset;
|
|
|
|
//
|
|
// the next frame will start after the end of this frame
|
|
//
|
|
thisDev->rcvPktOffset += rcvFrameSize;
|
|
|
|
ASSERT(thisDev->rcvPktOffset < RCV_DMA_SIZE);
|
|
|
|
//
|
|
// the FCS is included in the length of the frame, remove that from the length
|
|
// we send to the protocol
|
|
//
|
|
rcvFrameSize -= fcsSize;
|
|
|
|
if (rcvFrameSize <= MAX_NDIS_DATA_SIZE &&
|
|
rcvFrameSize >= IR_ADDR_SIZE + IR_CONTROL_SIZE)
|
|
{
|
|
//
|
|
// Queue this rcv packet. Move Newframe pointer
|
|
// into RxDMA buffer.
|
|
//
|
|
RegStats.ReceivedPackets++;
|
|
RegStats.RxWindow++;
|
|
QueueReceivePacket(thisDev, NewFrame, rcvFrameSize, TRUE);
|
|
}
|
|
else {
|
|
LOG("Error: invalid packet size in FIR_DeliverFrames %d", rcvFrameSize);
|
|
|
|
DEBUGFIR(DBG_RX|DBG_ERR, ("NSC: invalid packet size in FIR_DeliverFrames; %xh > %xh\n", rcvFrameSize, MAX_RCV_DATA_SIZE));
|
|
//
|
|
// Discard the rest of the packets.
|
|
//
|
|
while (SyncReadBankReg(&thisDev->interruptObj,thisDev->portInfo.ioBase, 5, 5)&0x80)
|
|
{
|
|
SyncReadBankReg(&thisDev->interruptObj,thisDev->portInfo.ioBase, 5, 6);
|
|
SyncReadBankReg(&thisDev->interruptObj,thisDev->portInfo.ioBase, 5, 7);
|
|
}
|
|
thisDev->DiscardNextPacketSet = TRUE;
|
|
}
|
|
}
|
|
|
|
SyncGetFifoStatus(
|
|
&thisDev->interruptObj,
|
|
thisDev->portInfo.ioBase,
|
|
&frameStat,
|
|
&rcvFrameSize
|
|
);
|
|
|
|
LOG("frameStat: %x, size=%d ", (UINT) frameStat,rcvFrameSize);
|
|
DEBUGFIR(DBG_RX|DBG_OUT, ("NSC: frameStat = %xh\n", (UINT) frameStat));
|
|
|
|
//
|
|
// Clear the line status register, of any past events.
|
|
//
|
|
thisDev->LineStatus = SyncReadBankReg(&thisDev->interruptObj,thisDev->portInfo.ioBase, 0, 5);
|
|
|
|
}
|
|
|
|
thisDev->FirIntMask = 0x04;
|
|
|
|
SetupRecv(thisDev);
|
|
|
|
LOG("<== FIR_DeliverFrames");
|
|
DEBUGFIR(DBG_RX|DBG_OUT, ("NSC: <== FIR_DeliverFrames\n"));
|
|
}
|
|
|
|
BOOLEAN NSC_Setup(IrDevice *thisDev)
|
|
{
|
|
NDIS_DMA_DESCRIPTION DMAChannelDcr;
|
|
NDIS_STATUS stat;
|
|
|
|
/*
|
|
* Initialize rcv DMA channel
|
|
*/
|
|
RtlZeroMemory(&DMAChannelDcr,sizeof(DMAChannelDcr));
|
|
|
|
DMAChannelDcr.DemandMode = TRUE;
|
|
DMAChannelDcr.AutoInitialize = FALSE;
|
|
DMAChannelDcr.DmaChannelSpecified = FALSE;
|
|
DMAChannelDcr.DmaWidth = Width8Bits;
|
|
DMAChannelDcr.DmaSpeed = Compatible;
|
|
DMAChannelDcr.DmaPort = 0;
|
|
DMAChannelDcr.DmaChannel = thisDev->portInfo.DMAChannel; // 0;
|
|
|
|
stat = NdisMRegisterDmaChannel(&thisDev->DmaHandle,
|
|
thisDev->ndisAdapterHandle,
|
|
thisDev->portInfo.DMAChannel,
|
|
FALSE, &DMAChannelDcr, RCV_DMA_SIZE);
|
|
|
|
if (stat != NDIS_STATUS_SUCCESS) {
|
|
|
|
DEBUGFIR(DBG_ERR, ("NSC: NdisMRegisterDmaChannel failed\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
InitializeDmaUtil(
|
|
&thisDev->DmaUtil,
|
|
thisDev->DmaHandle
|
|
);
|
|
|
|
|
|
thisDev->rcvDmaOffset = 0;
|
|
|
|
/*
|
|
* Because we enable rcv DMA while SIR receives may still be
|
|
* going on, we need to keep a separate receive buffer for DMA.
|
|
* This buffer gets swapped with the rcvBuffer data pointer
|
|
* and must be the same size.
|
|
*/
|
|
thisDev->dmaReadBuf=NscAllocateDmaBuffer(
|
|
thisDev->ndisAdapterHandle,
|
|
RCV_DMA_SIZE,
|
|
&thisDev->ReceiveDmaBufferInfo
|
|
);
|
|
|
|
if (thisDev->dmaReadBuf == NULL) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
thisDev->TransmitDmaBuffer=NscAllocateDmaBuffer(
|
|
thisDev->ndisAdapterHandle,
|
|
MAX_IRDA_DATA_SIZE,
|
|
&thisDev->TransmitDmaBufferInfo
|
|
);
|
|
|
|
|
|
NdisAllocateBufferPool(&stat, &thisDev->dmaBufferPoolHandle, 2);
|
|
|
|
if (stat != NDIS_STATUS_SUCCESS){
|
|
LOG("Error: NdisAllocateBufferPool failed in NSC_Setup");
|
|
DEBUGFIR(DBG_ERR, ("NSC: NdisAllocateBufferPool failed in NSC_Setup\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
NdisAllocateBuffer(&stat, &thisDev->rcvDmaBuffer,
|
|
thisDev->dmaBufferPoolHandle,
|
|
thisDev->dmaReadBuf,
|
|
RCV_DMA_SIZE
|
|
);
|
|
|
|
if (stat != NDIS_STATUS_SUCCESS) {
|
|
LOG("Error: NdisAllocateBuffer failed (rcv) in NSC_Setup");
|
|
DEBUGFIR(DBG_ERR, ("NSC: NdisAllocateBuffer failed (rcv) in NSC_Setup\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
NdisAllocateBuffer(&stat, &thisDev->xmitDmaBuffer,
|
|
thisDev->dmaBufferPoolHandle,
|
|
thisDev->TransmitDmaBuffer,
|
|
MAX_IRDA_DATA_SIZE
|
|
);
|
|
|
|
if (stat != NDIS_STATUS_SUCCESS) {
|
|
LOG("NdisAllocateBuffer failed (xmit) in NSC_Setup");
|
|
DEBUGFIR(DBG_ERR, ("NSC: NdisAllocateBuffer failed (xmit) in NSC_Setup\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void NSC_Shutdown(IrDevice *thisDev)
|
|
{
|
|
|
|
if (thisDev->xmitDmaBuffer){
|
|
NdisFreeBuffer( thisDev->xmitDmaBuffer);
|
|
thisDev->xmitDmaBuffer = NULL;
|
|
}
|
|
|
|
if (thisDev->rcvDmaBuffer){
|
|
NdisFreeBuffer(thisDev->rcvDmaBuffer);
|
|
thisDev->rcvDmaBuffer = NULL;
|
|
}
|
|
|
|
if (thisDev->dmaBufferPoolHandle){
|
|
NdisFreeBufferPool(thisDev->dmaBufferPoolHandle);
|
|
thisDev->dmaBufferPoolHandle = NULL;
|
|
}
|
|
|
|
if (thisDev->dmaReadBuf){
|
|
|
|
NscFreeDmaBuffer(&thisDev->ReceiveDmaBufferInfo);
|
|
thisDev->dmaReadBuf = NULL;
|
|
}
|
|
|
|
if (thisDev->TransmitDmaBuffer){
|
|
|
|
NscFreeDmaBuffer(&thisDev->TransmitDmaBufferInfo);
|
|
thisDev->TransmitDmaBuffer = NULL;
|
|
}
|
|
|
|
|
|
if (thisDev->DmaHandle){
|
|
NdisMDeregisterDmaChannel(thisDev->DmaHandle);
|
|
thisDev->DmaHandle = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
NdisToFirPacket(
|
|
PNDIS_PACKET Packet,
|
|
UCHAR *irPacketBuf,
|
|
UINT TotalDmaBufferLength,
|
|
UINT *ActualTransferLength
|
|
)
|
|
{
|
|
PNDIS_BUFFER ndisBuf;
|
|
UINT ndisPacketBytes = 0;
|
|
UINT ndisPacketLen;
|
|
|
|
LOG("==> NdisToFirPacket");
|
|
DEBUGFIR(DBG_OUT, ("NSC: ==> NdisToFirPacket\n"));
|
|
|
|
/*
|
|
* Get the packet's entire length and its first NDIS buffer
|
|
*/
|
|
NdisQueryPacket(Packet, NULL, NULL, &ndisBuf, &ndisPacketLen);
|
|
|
|
LOG("NdisToFirPacket, number of bytes: %d", ndisPacketLen);
|
|
DEBUGFIR(DBG_OUT, ("NSC: NdisToFirPacket, number of bytes: %d\n", ndisPacketLen));
|
|
|
|
/*
|
|
* Make sure that the packet is big enough to be legal.
|
|
* It consists of an A, C, and variable-length I field.
|
|
*/
|
|
if (ndisPacketLen < IR_ADDR_SIZE + IR_CONTROL_SIZE){
|
|
LOG("Error: packet too short in %d", ndisPacketLen);
|
|
DEBUGFIR(DBG_ERR, ("NSC: packet too short in NdisToFirPacket (%d bytes)\n", ndisPacketLen));
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Make sure that we won't overwrite our contiguous buffer.
|
|
*/
|
|
if (ndisPacketLen > TotalDmaBufferLength){
|
|
/*
|
|
* The packet is too large
|
|
* Tell the caller to retry with a packet size large
|
|
* enough to get past this stage next time.
|
|
*/
|
|
LOG("Error: packet too large in %d ", ndisPacketLen);
|
|
DEBUGFIR(DBG_ERR, ("NSC: Packet too large in NdisToIrPacket (%d=%xh bytes), MAX_IRDA_DATA_SIZE=%d, TotalDmaBufferLength=%d.\n",
|
|
ndisPacketLen, ndisPacketLen, MAX_IRDA_DATA_SIZE, TotalDmaBufferLength));
|
|
*ActualTransferLength = ndisPacketLen;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
* Read the NDIS packet into a contiguous buffer.
|
|
* We have to do this in two steps so that we can compute the
|
|
* FCS BEFORE applying escape-byte transparency.
|
|
*/
|
|
while (ndisBuf) {
|
|
UCHAR *bufData;
|
|
UINT bufLen;
|
|
|
|
NdisQueryBufferSafe(ndisBuf, (PVOID *)&bufData, &bufLen,NormalPagePriority);
|
|
|
|
if (bufData==NULL || (ndisPacketBytes + bufLen > ndisPacketLen)){
|
|
/*
|
|
* Packet was corrupt -- it misreported its size.
|
|
*/
|
|
*ActualTransferLength = 0;
|
|
ASSERT(0);
|
|
return FALSE;
|
|
}
|
|
|
|
NdisMoveMemory((PVOID)(irPacketBuf+ndisPacketBytes),
|
|
(PVOID)bufData, (ULONG)bufLen);
|
|
ndisPacketBytes += bufLen;
|
|
|
|
NdisGetNextBuffer(ndisBuf, &ndisBuf);
|
|
}
|
|
|
|
if (WPP_LEVEL_ENABLED(DBG_LOG_INFO)) {
|
|
|
|
UCHAR CommandByte=*(irPacketBuf+1);
|
|
UCHAR Nr=CommandByte >> 5;
|
|
UCHAR Ns=(CommandByte >> 1) & 0x7;
|
|
UCHAR Pf=(CommandByte >> 4) & 0x1;
|
|
|
|
if ((CommandByte & 1) == 0) {
|
|
|
|
LOG("Sending - I frame, Nr=%d, Ns=%d p/f=%d",Nr,Ns,Pf);
|
|
|
|
} else {
|
|
|
|
if ((CommandByte & 0x3) == 0x1) {
|
|
|
|
LOG("Sending - S frame, Nr=%d, xx=%d, p/f=%d",Nr, (CommandByte > 2) & 0x3, Pf);
|
|
|
|
} else {
|
|
|
|
LOG("Sending - U frame, p/f=%d",Pf);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Do a sanity check on the length of the packet.
|
|
*/
|
|
if (ndisPacketBytes != ndisPacketLen){
|
|
/*
|
|
* Packet was corrupt -- it misreported its size.
|
|
*/
|
|
LOG("Error: Packet corrupt in NdisToIrPacket "
|
|
"(buffer lengths don't add up to packet length)");
|
|
DEBUGFIR(DBG_ERR, ("NSC: Packet corrupt in NdisToIrPacket (buffer lengths don't add up to packet length).\n"));
|
|
*ActualTransferLength = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef DBG_ADD_PKT_ID
|
|
if (addPktIdOn){
|
|
static USHORT uniqueId = 0;
|
|
DEBUGFIR(DBG_OUT, ("NSC: *** --> SEND PKT ID: %xh\n", (UINT)uniqueId));
|
|
LOG("ID: Send (FIR) Pkt id: %x", uniqueId);
|
|
*(USHORT *)(irPacketBuf+ndisPacketBytes) = uniqueId++;
|
|
ndisPacketBytes += sizeof(USHORT);
|
|
}
|
|
#endif
|
|
|
|
*ActualTransferLength = ndisPacketBytes;
|
|
|
|
LOG("<== NdisToFirPacket");
|
|
DEBUGFIR(DBG_OUT, ("NSC: <== NdisToFirPacket\n"));
|
|
|
|
return TRUE;
|
|
}
|