|
|
/*
************************************************************************ * * NSC.c * * * Portions Copyright (C) 1996-2001 National Semiconductor Corp. * All rights reserved. * Copyright (C) 1996-2001 Microsoft Corporation. All Rights Reserved. * * * ************************************************************************* */
#include "nsc.h"
/*
* We keep a linked list of device objects */
/* This fuction sets up the device for Recv */ void SetupRecv(IrDevice *thisDev);
//
// Debug Counters
//
DebugCounters RegStats = {0,0,0,0,0,0,0,0,0};
ULONG DebugSpeed=0;
#ifdef RECEIVE_PACKET_LOGGING
typedef struct { UCHAR Data[12]; } DATA_BITS;
typedef struct { USHORT Tag; USHORT Line; union { struct { PNDIS_PACKET Packet; PVOID DmaBuffer; ULONG Length; } Packet; struct { PLIST_ENTRY Head; PLIST_ENTRY Entry; } List; struct { PVOID Start; ULONG Offset; ULONG Length; } Dma; struct { ULONG Length; } Discard; DATA_BITS Data; }; } RCV_LOG;
#define CHAIN_PACKET_TAG 'CP'
#define UNCHAIN_PACKET_TAG 'UP'
#define ADD_HEAD_LIST_TAG 'HA'
#define ADD_TAIL_LIST_TAG 'TA'
#define REMOVE_HEAD_LIST_TAG 'HR'
#define REMOVE_ENTRY_TAG 'ER'
#define DMA_TAG 'MD'
#define DATA_TAG 'AD'
#define DATA2_TAG '2D'
#define DISCARD_TAG 'XX'
#define NUM_RCV_LOG 256
ULONG RcvLogIndex = 0; RCV_LOG RcvLog[NUM_RCV_LOG];
BOOLEAN SyncGetRcvLogEntry(PVOID Context) { *(ULONG*)Context = RcvLogIndex++; RcvLogIndex &= NUM_RCV_LOG-1; return TRUE; }
ULONG GetRcvLogEntry(IrDevice *thisDev) { ULONG Entry;
NdisAcquireSpinLock(&thisDev->QueueLock); NdisMSynchronizeWithInterrupt(&thisDev->interruptObj, SyncGetRcvLogEntry, &Entry); NdisReleaseSpinLock(&thisDev->QueueLock); return Entry; }
#define LOG_InsertHeadList(d, h, e) \
{ \ ULONG i = GetRcvLogEntry(d); \ RcvLog[i].Tag = ADD_HEAD_LIST_TAG; \ RcvLog[i].Line = __LINE__; \ RcvLog[i].List.Head = (h); \ RcvLog[i].List.Entry = (PLIST_ENTRY)(e); \ }
#define LOG_InsertTailList(d, h, e) \
{ \ ULONG i = GetRcvLogEntry(d); \ RcvLog[i].Tag = ADD_TAIL_LIST_TAG; \ RcvLog[i].Line = __LINE__; \ RcvLog[i].List.Head = (h); \ RcvLog[i].List.Entry = (PLIST_ENTRY)(e); \ }
#define LOG_RemoveHeadList(d, h, e) \
{ \ ULONG i = GetRcvLogEntry(d); \ RcvLog[i].Tag = REMOVE_HEAD_LIST_TAG; \ RcvLog[i].Line = __LINE__; \ RcvLog[i].List.Head = (h); \ RcvLog[i].List.Entry = (PLIST_ENTRY)(e); \ }
#define LOG_RemoveEntryList(d, e) \
{ \ ULONG i = GetRcvLogEntry(d); \ RcvLog[i].Tag = REMOVE_ENTRY_TAG; \ RcvLog[i].Line = __LINE__; \ RcvLog[i].List.Head = NULL; \ RcvLog[i].List.Entry = (PLIST_ENTRY)(e); \ }
#define LOG_PacketChain(d, p) \
{ \ PNDIS_BUFFER NdisBuffer; \ PVOID Address; \ ULONG Len; \ ULONG i = GetRcvLogEntry(d); \ RcvLog[i].Tag = CHAIN_PACKET_TAG; \ RcvLog[i].Line = __LINE__; \ NdisQueryPacket((p), NULL, NULL, &NdisBuffer, NULL); \ NdisQueryBuffer(NdisBuffer, &Address, &Len); \ RcvLog[i].Packet.Packet = (p); \ RcvLog[i].Packet.DmaBuffer = Address; \ RcvLog[i].Packet.Length = Len; \ }
#define LOG_PacketUnchain(d, p) \
{ \ PNDIS_BUFFER NdisBuffer; \ PVOID Address; \ ULONG Len; \ ULONG i = GetRcvLogEntry(d); \ RcvLog[i].Tag = UNCHAIN_PACKET_TAG; \ RcvLog[i].Line = __LINE__; \ NdisQueryPacket((p), NULL, NULL, &NdisBuffer, NULL); \ NdisQueryBuffer(NdisBuffer, &Address, &Len); \ RcvLog[i].Packet.Packet = (p); \ RcvLog[i].Packet.DmaBuffer = Address; \ RcvLog[i].Packet.Length = Len; \ }
#define LOG_Dma(d) \
{ \ ULONG i = GetRcvLogEntry(d); \ RcvLog[i].Tag = DMA_TAG; \ RcvLog[i].Line = __LINE__; \ RcvLog[i].Dma.Start = (d)->rcvDmaBuffer; \ RcvLog[i].Dma.Offset = (d)->rcvDmaOffset; \ RcvLog[i].Dma.Length = (d)->rcvDmaSize; \ }
#define LOG_Data(d,s) \
{ \ ULONG i = GetRcvLogEntry(d); \ RcvLog[i].Tag = DATA_TAG; \ RcvLog[i].Line = ((USHORT)(s))&0xffff; \ RcvLog[i].Data = *(DATA_BITS*)(s); \ }
#define LOG_Data2(d,s) \
{ \ ULONG i = GetRcvLogEntry(d); \ RcvLog[i].Tag = DATA2_TAG; \ RcvLog[i].Line = ((USHORT)(s))&0xffff; \ RcvLog[i].Data = *(DATA_BITS*)(s); \ }
#define LOG_Discard(d,s) \
{ \ ULONG i = GetRcvLogEntry(d); \ RcvLog[i].Tag = DISCARD_TAG; \ RcvLog[i].Line = __LINE__; \ RcvLog[i].Discard.Length = (s); \ }
void DumpNdisPacket(PNDIS_PACKET Packet, UINT Line) { UINT PhysBufCnt, BufCnt, TotLen, Len; PNDIS_BUFFER NdisBuffer; PVOID Address;
DbgPrint("Badly formed NDIS packet at line %d\n", Line);
NdisQueryPacket(Packet, &PhysBufCnt, &BufCnt, &NdisBuffer, &TotLen); DbgPrint("Packet:%08X PhysBufCnt:%d BufCnt:%d TotLen:%d\n", Packet, PhysBufCnt, BufCnt, TotLen); while (NdisBuffer) { NdisQueryBuffer(NdisBuffer, &Address, &Len); DbgPrint(" Buffer:%08X Address:%08X Length:%d\n", NdisBuffer, Address, Len); NdisGetNextBuffer(NdisBuffer, &NdisBuffer); } ASSERT(0); }
#define VerifyNdisPacket(p, b) \
{ \ UINT BufCnt; \ \ NdisQueryPacket((p), NULL, &BufCnt, NULL, NULL); \ if (BufCnt>(b)) \ { \ DumpNdisPacket((p), __LINE__); \ } \ } #else
#define VerifyNdisPacket(p,b)
#define LOG_InsertHeadList(d, h, e)
#define LOG_InsertTailList(d, h, e)
#define LOG_RemoveHeadList(d, h, e)
#define LOG_RemoveEntryList(d, e)
#define LOG_PacketChain(d, p)
#define LOG_PacketUnchain(d, p)
#define LOG_Dma(d)
#define LOG_Data(d,s)
#define LOG_Data2(d,s)
#define LOG_Discard(d,s)
#endif
BOOLEAN VerifyHardware( IrDevice *thisDev );
/*
************************************************************************* * MiniportCheckForHang ************************************************************************* * * Reports the state of the network interface card. * */ BOOLEAN MiniportCheckForHang(NDIS_HANDLE MiniportAdapterContext) { IrDevice *thisDev = CONTEXT_TO_DEV(MiniportAdapterContext); LOG("==> MiniportCheckForHang", 0); DBGOUT(("==> MiniportCheckForHang(0x%x)", MiniportAdapterContext)); #if 1
// We have seen cases where we hang sending at high speeds. This occurs only
// on very old revisions of the NSC hardware.
// This is an attempt to kick us off again.
NdisDprAcquireSpinLock(&thisDev->QueueLock);
if (thisDev->FirTransmitPending) {
switch (thisDev->HangChk) { case 0: break;
default: DBGERR(("NSCIRDA: CheckForHang--we appear hung\n"));
// Issue a soft reset to the transmitter & receiver.
SyncWriteBankReg(&thisDev->interruptObj,thisDev->portInfo.ioBase, 0, 2, 0x06);
//
// turn the timer on and let it gnerate an interrupt
//
thisDev->FirIntMask = 0x90; SyncWriteBankReg(&thisDev->interruptObj,thisDev->portInfo.ioBase, 4, 2, 0x01);
break; } thisDev->HangChk++; }
NdisDprReleaseSpinLock(&thisDev->QueueLock); #endif
LOG("<== MiniportCheckForHang", 1); DBGOUT(("<== MiniportCheckForHang(0x%x)", MiniportAdapterContext)); return FALSE; }
/*
************************************************************************* * MiniportHalt ************************************************************************* * * Halts the network interface card. * */ VOID MiniportHalt(IN NDIS_HANDLE MiniportAdapterContext) { IrDevice *thisDev = CONTEXT_TO_DEV(MiniportAdapterContext);
LOG("==> MiniportHalt", 0); DBGOUT(("==> MiniportHalt(0x%x)", MiniportAdapterContext));
thisDev->hardwareStatus = NdisHardwareStatusClosing;
NdisAcquireSpinLock(&thisDev->QueueLock);
thisDev->Halting=TRUE;
if (thisDev->PacketsSentToProtocol > 0) { //
// wait for all the packets to come back from the protocol
//
NdisReleaseSpinLock(&thisDev->QueueLock);
NdisWaitEvent(&thisDev->ReceiveStopped, 1*60*1000);
NdisAcquireSpinLock(&thisDev->QueueLock);
}
if (!thisDev->TransmitIsIdle) { //
// wait for all the packets to be transmitted
//
NdisReleaseSpinLock(&thisDev->QueueLock);
NdisWaitEvent(&thisDev->SendStoppedOnHalt,1*60*1000);
NdisAcquireSpinLock(&thisDev->QueueLock);
}
if (thisDev->FirReceiveDmaActive) {
thisDev->FirReceiveDmaActive=FALSE; //
// receive dma is running, stop it
//
CompleteDmaTransferFromDevice( &thisDev->DmaUtil );
}
//
// which back to SIR mode
//
CloseCOM(thisDev);
SyncSetInterruptMask(thisDev, FALSE);
NdisReleaseSpinLock(&thisDev->QueueLock);
//
// release the interrupt
//
NdisMDeregisterInterrupt(&thisDev->interruptObj);
#if DBG
NdisZeroMemory(&thisDev->interruptObj,sizeof(thisDev->interruptObj)); #endif
//
// release fir related resources including dma channel
//
NSC_Shutdown(thisDev);
//
// release sir related buffers
//
DoClose(thisDev);
if (thisDev->portInfo.ConfigIoBasePhysAddr) {
NdisMDeregisterIoPortRange(thisDev->ndisAdapterHandle, thisDev->portInfo.ConfigIoBasePhysAddr, 2, (PVOID)thisDev->portInfo.ConfigIoBaseAddr); }
NdisMDeregisterIoPortRange(thisDev->ndisAdapterHandle, thisDev->portInfo.ioBasePhys, ((thisDev->CardType==PUMA108)?16:8), (PVOID)thisDev->portInfo.ioBase);
//
// free the device block
//
FreeDevice(thisDev); LOG("<== MiniportHalt", 1); DBGOUT(("<== MiniportHalt(0x%x)", MiniportAdapterContext)); }
void InterlockedInsertBufferSorted(PLIST_ENTRY Head, rcvBuffer *rcvBuf, PNDIS_SPIN_LOCK Lock) { PLIST_ENTRY ListEntry;
NdisAcquireSpinLock(Lock); if (IsListEmpty(Head)) { InsertHeadList(Head, &rcvBuf->listEntry); } else { BOOLEAN EntryInserted = FALSE; for (ListEntry = Head->Flink; ListEntry != Head; ListEntry = ListEntry->Flink) { rcvBuffer *temp = CONTAINING_RECORD(ListEntry, rcvBuffer, listEntry); if (temp->dataBuf > rcvBuf->dataBuf) { // We found one that comes after ours.
// We need to insert before it
InsertTailList(ListEntry, &rcvBuf->listEntry); EntryInserted = TRUE; break; } } if (!EntryInserted) { // We didn't find an entry on the last who's address was later
// than our buffer. We go at the end.
InsertTailList(Head, &rcvBuf->listEntry); } } NdisReleaseSpinLock(Lock); }
/*
************************************************************************* * DeliverFullBuffers ************************************************************************* * * Deliver received packets to the protocol. * Return TRUE if delivered at least one frame. * */ BOOLEAN DeliverFullBuffers(IrDevice *thisDev) { BOOLEAN result = FALSE; PLIST_ENTRY ListEntry;
LOG("==> DeliverFullBuffers", 0); DBGOUT(("==> DeliverFullBuffers(0x%x)", thisDev));
/*
* Deliver all full rcv buffers */
for ( ListEntry = NDISSynchronizedRemoveHeadList(&thisDev->rcvBufFull, &thisDev->interruptObj); ListEntry;
ListEntry = NDISSynchronizedRemoveHeadList(&thisDev->rcvBufFull, &thisDev->interruptObj) ) { rcvBuffer *rcvBuf = CONTAINING_RECORD(ListEntry, rcvBuffer, listEntry); NDIS_STATUS stat; PNDIS_BUFFER packetBuf; SLOW_IR_FCS_TYPE fcs;
VerifyNdisPacket(rcvBuf->packet, 0);
if (thisDev->Halting) { //
// the adapter is being halted, stop sending packets up
//
if (!rcvBuf->isDmaBuf) {
NDISSynchronizedInsertTailList(&thisDev->rcvBufBuf, RCV_BUF_TO_LIST_ENTRY(rcvBuf->dataBuf), &thisDev->interruptObj); } rcvBuf->dataBuf = NULL; rcvBuf->isDmaBuf = FALSE;
VerifyNdisPacket(rcvBuf->packet, 0); NDISSynchronizedInsertHeadList(&thisDev->rcvBufFree, &rcvBuf->listEntry, &thisDev->interruptObj);
continue; }
if (thisDev->currentSpeed <= MAX_SIR_SPEED) { /*
* The packet we have already has had BOFs, * EOF, and * escape-sequences removed. It * contains an FCS code at the end, which we * need to verify and then remove before * delivering the frame. We compute the FCS * on the packet with the packet FCS attached; * this should produce the constant value * GOOD_FCS. */ fcs = ComputeFCS(rcvBuf->dataBuf, rcvBuf->dataLen);
if (fcs != GOOD_FCS) { /*
* FCS Error. Drop this frame. */ LOG("Error: Bad FCS in DeliverFullBuffers", fcs); DBGERR(("Bad FCS in DeliverFullBuffers 0x%x!=0x%x.", (UINT)fcs, (UINT) GOOD_FCS)); rcvBuf->state = STATE_FREE;
DBGSTAT(("Dropped %d/%d pkts; BAD FCS (%xh!=%xh):", ++thisDev->packetsDropped, thisDev->packetsDropped + thisDev->packetsRcvd, fcs, GOOD_FCS));
DBGPRINTBUF(rcvBuf->dataBuf, rcvBuf->dataLen);
if (!rcvBuf->isDmaBuf) { NDISSynchronizedInsertTailList(&thisDev->rcvBufBuf, RCV_BUF_TO_LIST_ENTRY(rcvBuf->dataBuf), &thisDev->interruptObj); } rcvBuf->dataBuf = NULL; rcvBuf->isDmaBuf = FALSE;
VerifyNdisPacket(rcvBuf->packet, 0); NDISSynchronizedInsertHeadList(&thisDev->rcvBufFree, &rcvBuf->listEntry, &thisDev->interruptObj);
//break;
continue; }
/* Remove the FCS from the end of the packet. */ rcvBuf->dataLen -= SLOW_IR_FCS_SIZE; } #ifdef DBG_ADD_PKT_ID
if (addPktIdOn) {
/* Remove dbg packet id. */ USHORT uniqueId; rcvBuf->dataLen -= sizeof(USHORT); uniqueId = *(USHORT *)(rcvBuf->dataBuf+ rcvBuf->dataLen); DBGOUT(("ID: RCVing packet %xh **", (UINT)uniqueId)); LOG("ID: Rcv Pkt id:", uniqueId); } #endif
/*
* The packet array is set up with its NDIS_PACKET. * Now we need to allocate a single NDIS_BUFFER for * the NDIS_PACKET and set the NDIS_BUFFER to the * part of dataBuf that we want to deliver. */ NdisAllocateBuffer(&stat, &packetBuf, thisDev->bufferPoolHandle, (PVOID)rcvBuf->dataBuf, rcvBuf->dataLen);
if (stat != NDIS_STATUS_SUCCESS){ LOG("Error: NdisAllocateBuffer failed", 0); DBGERR(("NdisAllocateBuffer failed")); ASSERT(0); break; }
VerifyNdisPacket(rcvBuf->packet, 0); NdisChainBufferAtFront(rcvBuf->packet, packetBuf); LOG_PacketChain(thisDev, rcvBuf->packet); VerifyNdisPacket(rcvBuf->packet, 1);
/*
* Fix up some other packet fields. */ NDIS_SET_PACKET_HEADER_SIZE(rcvBuf->packet, IR_ADDR_SIZE+IR_CONTROL_SIZE);
DBGPKT(("Indicating rcv packet 0x%x.", rcvBuf->packet)); //DBGPRINTBUF(rcvBuf->dataBuf, rcvBuf->dataLen);
/*
* Indicate to the protocol that another packet is * ready. Set the rcv buffer's state to PENDING first * to avoid a race condition with NDIS's call to the * return packet handler. */ rcvBuf->state = STATE_PENDING;
*(rcvBuffer **)rcvBuf->packet->MiniportReserved = rcvBuf; InterlockedInsertBufferSorted(&thisDev->rcvBufPend, rcvBuf, &thisDev->QueueLock);
VerifyNdisPacket(rcvBuf->packet, 1); LOG_Data2(thisDev, rcvBuf->dataBuf);
NdisInterlockedIncrement(&thisDev->PacketsSentToProtocol); NDIS_SET_PACKET_STATUS(rcvBuf->packet,STATUS_SUCCESS); NdisMIndicateReceivePacket(thisDev->ndisAdapterHandle, &rcvBuf->packet, 1); result = TRUE;
/*
* The packet is being delivered asynchronously. * Leave the rcv buffer's state as PENDING; * we'll get a callback when the transfer is */
LOG("Indicated rcv complete (Async) bytes:", rcvBuf->dataLen); DBGSTAT(("Rcv Pending. Rcvd %d packets", ++thisDev->packetsRcvd));
}
LOG("<== DeliverFullBuffers", 1); DBGOUT(("<== DeliverFullBuffers")); return result; }
/*
************************************************************************* * MiniportHandleInterrupt ************************************************************************* * * * This is the deferred interrupt processing routine (DPC) which is * optionally called following an interrupt serviced by MiniportISR. * */ VOID MiniportHandleInterrupt(NDIS_HANDLE MiniportAdapterContext) { IrDevice *thisDev = CONTEXT_TO_DEV( MiniportAdapterContext); PNDIS_PACKET PacketToComplete=NULL; NDIS_STATUS PacketStatus=NDIS_STATUS_SUCCESS; BOOLEAN SpeedChange=FALSE;
LOG("==> MiniportHandleInterrupt", 0); DBGOUT(("==> MiniportHandleInterrupt(0x%x)", MiniportAdapterContext));
/*
* If we have just started receiving a packet, indicate media-busy * to the protocol. */ if (thisDev->mediaBusy && !thisDev->haveIndicatedMediaBusy) {
if (thisDev->currentSpeed > MAX_SIR_SPEED) { LOG("Error: MiniportHandleInterrupt is in wrong state", thisDev->currentSpeed); DBGERR(("MiniportHandleInterrupt is in wrong state: speed is 0x%x", thisDev->currentSpeed)); ASSERT(0); }
NdisMIndicateStatus(thisDev->ndisAdapterHandle, NDIS_STATUS_MEDIA_BUSY, NULL, 0); NdisMIndicateStatusComplete(thisDev->ndisAdapterHandle);
thisDev->haveIndicatedMediaBusy = TRUE; }
NdisDprAcquireSpinLock(&thisDev->QueueLock);
if (thisDev->currentSpeed > MAX_SIR_SPEED) { //
// fir speed
//
//
// disable any other
//
thisDev->FirIntMask = 0x00;
if (thisDev->FirTransmitPending) {
ASSERT(thisDev->CurrentPacket != NULL);
thisDev->FirTransmitPending=FALSE;
//
// we seem to be transmitting now
//
#if DBG
{ ULONG CurrentDMACount;
CurrentDMACount = NdisMReadDmaCounter(thisDev->DmaHandle);
if (CurrentDMACount > 0) {
DbgPrint("FIR send: Count was not zero: %d\n\n", CurrentDMACount); } } #endif
PacketStatus=CompleteDmaTransferToDevice( &thisDev->DmaUtil );
if (PacketStatus != NDIS_STATUS_SUCCESS) { DBGERR(("NdisMCompleteDmaTransfer failed: %d\n", PacketStatus)); #if DBG
DbgBreakPoint(); #endif
}
/*
* Check for Tx underrun. */ if (SyncReadBankReg(&thisDev->interruptObj,thisDev->portInfo.ioBase, 0, 7) & 0x40) {
SyncWriteBankReg(&thisDev->interruptObj,thisDev->portInfo.ioBase, 0, 7, 0x40);
RegStats.TxUnderruns++; PacketStatus = NDIS_STATUS_FAILURE;
LOG("FIR_MegaSendComplete: Transmit Underrun", 0); DEBUGFIR(DBG_TX|DBG_ERR, ("NSC: FIR_MegaSendComplete: Transmit Underrun\n")); }
PacketToComplete=thisDev->CurrentPacket; thisDev->CurrentPacket=NULL;
} else {
if (thisDev->FirReceiveDmaActive) {
FIR_DeliverFrames(thisDev);
} else {
DBGERR(("MiniportHandleInterrupt: fir: not sending and not RX state")); // DbgPrint("MiniportHandleInterrupt: fir: not sending and not RX state %02x\n",thisDev->InterruptStatus);
// ASSERT(0);
}
}
} else { //
// in SIR mode
//
if (thisDev->CurrentPacket != NULL) { //
//
UINT TransmitComplete=InterlockedExchange(&thisDev->portInfo.IsrDoneWithPacket,0);
if (TransmitComplete) {
PacketToComplete=thisDev->CurrentPacket; thisDev->CurrentPacket=NULL; } } }
thisDev->setSpeedAfterCurrentSendPacket = FALSE;
if (PacketToComplete != NULL) {
if (thisDev->lastPacketAtOldSpeed == PacketToComplete) {
thisDev->lastPacketAtOldSpeed=NULL;
SpeedChange=TRUE;
DBGERR(("defered set speed\n"));
SetSpeed(thisDev); } }
NdisDprReleaseSpinLock(&thisDev->QueueLock);
if (PacketToComplete != NULL) {
ProcessSendQueue(thisDev); NdisMSendComplete(thisDev->ndisAdapterHandle, PacketToComplete, PacketStatus);
} //
// send any received packets to irda.sys
//
DeliverFullBuffers(thisDev);
SyncSetInterruptMask(thisDev, TRUE);
LOG("<== MiniportHandleInterrupt", 1); DBGOUT(("<== MiniportHandleInterrupt"));
}
/*
************************************************************************* * GetPnPResources ************************************************************************* * * */ BOOLEAN GetPnPResources(IrDevice *thisDev, NDIS_HANDLE WrapperConfigurationContext) { NDIS_STATUS stat; BOOLEAN result = FALSE;
/*
* We should only need 2 adapter resources (2 IO and 1 interrupt), * but I've seen devices get extra resources. * So give the NdisMQueryAdapterResources call room for 10 resources. */ #define RESOURCE_LIST_BUF_SIZE (sizeof(NDIS_RESOURCE_LIST) + (10*sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR)))
UCHAR buf[RESOURCE_LIST_BUF_SIZE]; PNDIS_RESOURCE_LIST resList = (PNDIS_RESOURCE_LIST)buf; UINT bufSize = RESOURCE_LIST_BUF_SIZE;
NdisMQueryAdapterResources(&stat, WrapperConfigurationContext, resList, &bufSize); if (stat == NDIS_STATUS_SUCCESS){ PCM_PARTIAL_RESOURCE_DESCRIPTOR resDesc; BOOLEAN haveIRQ = FALSE, haveIOAddr = FALSE, haveDma = FALSE; UINT i;
for (resDesc = resList->PartialDescriptors, i = 0; i < resList->Count; resDesc++, i++){
switch (resDesc->Type){ case CmResourceTypePort: if (thisDev->CardType==PC87108 && (resDesc->u.Port.Start.LowPart==0xEA || resDesc->u.Port.Start.LowPart==0x398 || resDesc->u.Port.Start.LowPart==0x150)) { // This is an eval board and this is the config io base address
thisDev->portInfo.ConfigIoBasePhysAddr = resDesc->u.Port.Start.LowPart; } else if (thisDev->CardType==PC87308 && (resDesc->u.Port.Start.LowPart==0x2E || resDesc->u.Port.Start.LowPart==0x15C)) { // This is an eval board and this is the config io base address
thisDev->portInfo.ConfigIoBasePhysAddr = resDesc->u.Port.Start.LowPart; } else if (thisDev->CardType==PC87338 && (resDesc->u.Port.Start.LowPart==0x2E || resDesc->u.Port.Start.LowPart==0x398 || resDesc->u.Port.Start.LowPart==0x15C)) { // This is an eval board and this is the config io base address
thisDev->portInfo.ConfigIoBasePhysAddr = resDesc->u.Port.Start.LowPart; } else { if (haveIOAddr){ /*
* The *PNP0510 chip on the IBM ThinkPad 760EL * gets an extra IO range assigned to it. * So only pick up the first IO port range; * ignore this subsequent one. */ DBGERR(("Ignoring extra PnP IO base %xh because already using %xh.", (UINT)resDesc->u.Port.Start.LowPart, (UINT)thisDev->portInfo.ioBasePhys)); } else { thisDev->portInfo.ioBasePhys = resDesc->u.Port.Start.LowPart; haveIOAddr = TRUE; DBGOUT(("Got UART IO addr: %xh.", thisDev->portInfo.ioBasePhys)); } } break;
case CmResourceTypeInterrupt: if (haveIRQ){ DBGERR(("Ignoring second PnP IRQ %xh because already using %xh.", (UINT)resDesc->u.Interrupt.Level, thisDev->portInfo.irq)); } else { thisDev->portInfo.irq = resDesc->u.Interrupt.Level; haveIRQ = TRUE; DBGOUT(("Got PnP IRQ: %d.", thisDev->portInfo.irq)); } break;
case CmResourceTypeDma: if (haveDma){ DBGERR(("Ignoring second DMA address %d because already using %d.", (UINT)resDesc->u.Dma.Channel, (UINT)thisDev->portInfo.DMAChannel)); } else { ASSERT(!(resDesc->u.Dma.Channel&0xffffff00)); thisDev->portInfo.DMAChannel = (UCHAR)resDesc->u.Dma.Channel; haveDma = TRUE; DBGOUT(("Got DMA channel: %d.", thisDev->portInfo.DMAChannel)); } break; } }
result = (haveIOAddr && haveIRQ && haveDma); }
return result; }
/*
************************************************************************* * Configure ************************************************************************* * * Read configurable parameters out of the system registry. * */ BOOLEAN Configure( IrDevice *thisDev, NDIS_HANDLE WrapperConfigurationContext ) { //
// Status of Ndis calls.
//
NDIS_STATUS Status;
//
// The handle for reading from the registry.
//
NDIS_HANDLE ConfigHandle;
//
// The value read from the registry.
//
PNDIS_CONFIGURATION_PARAMETER ReturnedValue;
//
// String names of all the parameters that will be read.
//
NDIS_STRING CardTypeStr = CARDTYPE; NDIS_STRING Dongle_A_TypeStr = DONGLE_A_TYPE; NDIS_STRING Dongle_B_TypeStr = DONGLE_B_TYPE; NDIS_STRING MaxConnectRateStr = MAXCONNECTRATE;
UINT Valid_DongleTypes[] = VALID_DONGLETYPES;
DBGOUT(("Configure(0x%x)", thisDev)); NdisOpenConfiguration(&Status, &ConfigHandle, WrapperConfigurationContext);
if (Status != NDIS_STATUS_SUCCESS){ DBGERR(("NdisOpenConfiguration failed in Configure()")); return FALSE; } //
// Read Ir108 Configuration I/O base Address
//
//DbgBreakPoint();
NdisReadConfiguration( &Status, &ReturnedValue, ConfigHandle, &CardTypeStr, NdisParameterHexInteger ); if (Status != NDIS_STATUS_SUCCESS){ DBGERR(("NdisReadConfiguration failed in accessing CardType.")); NdisCloseConfiguration(ConfigHandle); return FALSE; } thisDev->CardType = (UCHAR)ReturnedValue->ParameterData.IntegerData;
if (!GetPnPResources(thisDev, WrapperConfigurationContext)){
DBGERR(("GetPnPResources failed\n"));
NdisCloseConfiguration(ConfigHandle); return FALSE; }
// Read Dongle type constant Number.
//
NdisReadConfiguration(&Status, &ReturnedValue, ConfigHandle, &Dongle_A_TypeStr, NdisParameterInteger);
if (Status != NDIS_STATUS_SUCCESS){ DBGERR(("NdisReadConfiguration failed in accessing DongleType (0x%x).",Status)); } thisDev->DonglesSupported = 1; thisDev->DongleTypes[0] = (UCHAR)Valid_DongleTypes[(UCHAR)ReturnedValue->ParameterData.IntegerData];
// Read Dongle type constant Number.
//
NdisReadConfiguration(&Status, &ReturnedValue, ConfigHandle, &Dongle_B_TypeStr, NdisParameterInteger);
if (Status != NDIS_STATUS_SUCCESS){ DBGERR(("NdisReadConfiguration failed in accessing DongleType (0x%x).", Status)); } thisDev->DongleTypes[1] = (UCHAR)Valid_DongleTypes[(UCHAR)ReturnedValue->ParameterData.IntegerData]; thisDev->DonglesSupported++;
// Read MaxConnectRate.
//
NdisReadConfiguration(&Status, &ReturnedValue, ConfigHandle, &MaxConnectRateStr, NdisParameterInteger);
if (Status != NDIS_STATUS_SUCCESS){ DBGERR(("NdisReadConfiguration failed in accessing MaxConnectRate (0x%x).",Status)); thisDev->AllowedSpeedMask = ALL_IRDA_SPEEDS; } else { thisDev->AllowedSpeedMask = 0;
switch (ReturnedValue->ParameterData.IntegerData) { default: case 4000000: thisDev->AllowedSpeedMask |= NDIS_IRDA_SPEED_4M; case 1152000: thisDev->AllowedSpeedMask |= NDIS_IRDA_SPEED_1152K; case 115200: thisDev->AllowedSpeedMask |= NDIS_IRDA_SPEED_115200; case 57600: thisDev->AllowedSpeedMask |= NDIS_IRDA_SPEED_57600; case 38400: thisDev->AllowedSpeedMask |= NDIS_IRDA_SPEED_38400; case 19200: thisDev->AllowedSpeedMask |= NDIS_IRDA_SPEED_19200; case 9600: thisDev->AllowedSpeedMask |= NDIS_IRDA_SPEED_9600 | NDIS_IRDA_SPEED_2400; break; }
}
NdisCloseConfiguration(ConfigHandle);
DBGOUT(("Configure done: ConfigIO=0x%x UartIO=0x%x irq=%d DMA=%d", thisDev->portInfo.ConfigIoBaseAddr,thisDev->portInfo.ioBase, thisDev->portInfo.irq,thisDev->portInfo.DMAChannel));
return TRUE; }
/*
************************************************************************* * MiniportInitialize ************************************************************************* * * * Initializes the network interface card. * * * */ NDIS_STATUS MiniportInitialize ( PNDIS_STATUS OpenErrorStatus, PUINT SelectedMediumIndex, PNDIS_MEDIUM MediumArray, UINT MediumArraySize, NDIS_HANDLE NdisAdapterHandle, NDIS_HANDLE WrapperConfigurationContext ) { UINT mediumIndex; IrDevice *thisDev = NULL; NDIS_STATUS retStat, result = NDIS_STATUS_SUCCESS;
DBGOUT(("MiniportInitialize()"));
/*
* Search the passed-in array of supported media for the IrDA medium. */ for (mediumIndex = 0; mediumIndex < MediumArraySize; mediumIndex++){ if (MediumArray[mediumIndex] == NdisMediumIrda){ break; } } if (mediumIndex < MediumArraySize){ *SelectedMediumIndex = mediumIndex; } else { /*
* Didn't see the IrDA medium */ DBGERR(("Didn't see the IRDA medium in MiniportInitialize")); result = NDIS_STATUS_UNSUPPORTED_MEDIA; return result; }
/*
* Allocate a new device object to represent this connection. */ thisDev = NewDevice(); if (!thisDev){ return NDIS_STATUS_NOT_ACCEPTED; }
thisDev->hardwareStatus = NdisHardwareStatusInitializing; /*
* Allocate resources for this connection. */ if (!OpenDevice(thisDev)){ DBGERR(("OpenDevice failed")); result = NDIS_STATUS_FAILURE; goto _initDone; }
/*
* Record the NDIS wrapper's handle for this adapter, which we use * when we call up to the wrapper. * (This miniport's adapter handle is just thisDev, the pointer to the device object.). */ DBGOUT(("NDIS handle: %xh <-> IRMINI handle: %xh", NdisAdapterHandle, thisDev)); thisDev->ndisAdapterHandle = NdisAdapterHandle;
/*
* Read the system registry to get parameters like COM port number, etc. */ if (!Configure(thisDev, WrapperConfigurationContext)){ DBGERR(("Configure failed")); result = NDIS_STATUS_FAILURE; goto _initDone; }
/*
* This call will associate our adapter handle with the wrapper's * adapter handle. The wrapper will then always use our handle * when calling us. We use a pointer to the device object as the context. */ NdisMSetAttributesEx(NdisAdapterHandle, (NDIS_HANDLE)thisDev, 0, NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT | NDIS_ATTRIBUTE_DESERIALIZE, NdisInterfaceInternal);
/*
* Tell NDIS about the range of IO space that we'll be using. * Puma uses Chip-select mode, so the ConfigIOBase address actually * follows the regular io, so get both in one shot. */ retStat = NdisMRegisterIoPortRange( (PVOID)&thisDev->portInfo.ioBase, NdisAdapterHandle, thisDev->portInfo.ioBasePhys, ((thisDev->CardType==PUMA108)?16:8));
if (retStat != NDIS_STATUS_SUCCESS){ DBGERR(("NdisMRegisterIoPortRange failed")); thisDev->portInfo.ioBase=NULL; result = NDIS_STATUS_FAILURE; goto _initDone; }
if (thisDev->portInfo.ConfigIoBasePhysAddr) { /*
* Eval boards require a second IO range. * */ retStat = NdisMRegisterIoPortRange( (PVOID)&thisDev->portInfo.ConfigIoBaseAddr, NdisAdapterHandle, thisDev->portInfo.ConfigIoBasePhysAddr, 2); if (retStat != NDIS_STATUS_SUCCESS){
DBGERR(("NdisMRegisterIoPortRange config failed")); thisDev->portInfo.ConfigIoBaseAddr=NULL; result = NDIS_STATUS_FAILURE; goto _initDone; } }
NdisMSleep(20); //
// Set to Non-Extended mode
//
NSC_WriteBankReg(thisDev->portInfo.ioBase, 2, 2, 0x02);
//
// set to bank 0
//
NdisRawWritePortUchar(thisDev->portInfo.ioBase+LCR_BSR_OFFSET, 03);
//
// mask all ints, before attaching interrupt
//
NdisRawWritePortUchar(thisDev->portInfo.ioBase+INT_ENABLE_REG_OFFSET, 0);
/*
* Register an interrupt with NDIS. */ retStat = NdisMRegisterInterrupt( (PNDIS_MINIPORT_INTERRUPT)&thisDev->interruptObj, NdisAdapterHandle, thisDev->portInfo.irq, thisDev->portInfo.irq, TRUE, // want ISR
TRUE, // MUST share interrupts
NdisInterruptLatched ); if (retStat != NDIS_STATUS_SUCCESS){ DBGERR(("NdisMRegisterInterrupt failed")); result = NDIS_STATUS_FAILURE; goto _initDone; }
thisDev->InterruptRegistered=TRUE;
{ LONG VerifyTries=5;
while (VerifyTries > 0) {
if (VerifyHardware(thisDev)) {
break; } #if DBG
DbgPrint("NSCIRDA: VerifiyHardware() failed, trying again, tries left=%d\n",VerifyTries); #endif
VerifyTries--; }
if ( VerifyTries == 0) {
result = NDIS_STATUS_FAILURE; goto _initDone; } }
/*
* Open COMM communication channel. * This will let the dongle driver update its capabilities from their default values. */ if (!DoOpen(thisDev)){ DBGERR(("DoOpen failed")); result = NDIS_STATUS_FAILURE; goto _initDone; }
/*
* Do special NSC setup * (do this after comport resources, like read buf, have been allocated). */ if (!NSC_Setup(thisDev)){ DBGERR(("NSC_Setup failed")); result = NDIS_STATUS_FAILURE; goto _initDone; }
_initDone:
if (result == NDIS_STATUS_SUCCESS){ /*
* Add this device object to the beginning of our global list. */ thisDev->hardwareStatus = NdisHardwareStatusReady; DBGOUT(("MiniportInitialize succeeded")); return result;
}
//
// init failed, release the resources
//
if (thisDev->InterruptRegistered) {
NdisMDeregisterInterrupt(&thisDev->interruptObj); thisDev->InterruptRegistered=FALSE; }
if (thisDev->portInfo.ioBase != NULL) {
NdisMDeregisterIoPortRange( thisDev->ndisAdapterHandle, thisDev->portInfo.ioBasePhys, ((thisDev->CardType==PUMA108)?16:8), (PVOID)thisDev->portInfo.ioBase );
thisDev->portInfo.ioBase=NULL;
}
if (thisDev->portInfo.ConfigIoBaseAddr != NULL) {
NdisMDeregisterIoPortRange( thisDev->ndisAdapterHandle, thisDev->portInfo.ConfigIoBasePhysAddr, 2, (PVOID)thisDev->portInfo.ConfigIoBaseAddr );
thisDev->portInfo.ConfigIoBaseAddr=NULL;
}
if (thisDev){
FreeDevice(thisDev); }
DBGOUT(("MiniportInitialize failed"));
return result;
}
BOOLEAN VerifyHardware( IrDevice *thisDev )
{ UCHAR TempValue; LONG MilliSecondsToWait=500;
NdisMSleep(20); //
// set to bank 0
//
NdisRawWritePortUchar(thisDev->portInfo.ioBase+LCR_BSR_OFFSET, 03);
//
// mask all ints, before attaching interrupt
//
NdisRawWritePortUchar(thisDev->portInfo.ioBase+INT_ENABLE_REG_OFFSET, 0);
NdisRawReadPortUchar(thisDev->portInfo.ioBase+INT_ENABLE_REG_OFFSET,&TempValue);
if (TempValue != 0) { #if DBG
DbgPrint("NSCIRDA: After masking interrupts IER was not zero %x, base= %x\n",TempValue,thisDev->portInfo.ioBase); #endif
return FALSE; }
//
// reset the fifo's and enable the fifos
//
NdisRawWritePortUchar(thisDev->portInfo.ioBase+INT_ID_AND_FIFO_CNTRL_REG_OFFSET, 0x7);
//
// read the interrupt ident reg, to see if the fifo's enabled
//
NdisRawReadPortUchar(thisDev->portInfo.ioBase+INT_ID_AND_FIFO_CNTRL_REG_OFFSET,&TempValue);
if ((TempValue & 0xc0) != 0xc0) {
#if DBG
DbgPrint("NSCIRDA: Fifo's not enabled in iir %x, base= %x\n",TempValue,thisDev->portInfo.ioBase); #endif
return FALSE; }
//
// bring up DTR and RTS, turn on the out pins
//
NdisRawWritePortUchar(thisDev->portInfo.ioBase+MODEM_CONTROL_REG_OFFSET, 0xf);
thisDev->GotTestInterrupt=FALSE; thisDev->TestingInterrupt=TRUE;
//
// unmask the transmit holding register so an interrupt will be generated
//
NdisRawWritePortUchar(thisDev->portInfo.ioBase+INT_ENABLE_REG_OFFSET, 2);
while ((thisDev->GotTestInterrupt == FALSE) && (MilliSecondsToWait > 0)) {
NdisMSleep(1000); MilliSecondsToWait--; }
#if DBG
if (!thisDev->GotTestInterrupt) {
NdisRawReadPortUchar(thisDev->portInfo.ioBase+INT_ID_AND_FIFO_CNTRL_REG_OFFSET,&TempValue);
DbgPrint("NSCIRDA: Did not get interrupt while initializing, ier-%x, base= %x\n",TempValue,thisDev->portInfo.ioBase); } #endif
//
// mask all ints again;
//
NdisRawWritePortUchar(thisDev->portInfo.ioBase+INT_ENABLE_REG_OFFSET, 0);
thisDev->TestingInterrupt=FALSE;
return thisDev->GotTestInterrupt; }
/*
************************************************************************* * QueueReceivePacket ************************************************************************* * * * * */ VOID QueueReceivePacket(IrDevice *thisDev, PUCHAR data, UINT dataLen, BOOLEAN IsFIR) { rcvBuffer *rcvBuf = NULL; PLIST_ENTRY ListEntry;
/*
* Note: We cannot use a spinlock to protect the rcv buffer structures * in an ISR. This is ok, since we used a sync-with-isr function * the the deferred callback routine to access the rcv buffers. */
LOG("==> QueueReceivePacket", 0); DBGOUT(("==> QueueReceivePacket(0x%x, 0x%lx, 0x%x)", thisDev, data, dataLen)); LOG("QueueReceivePacket, len: ", dataLen);
if (!IsFIR) { // This function is called inside the ISR during SIR mode.
if (IsListEmpty(&thisDev->rcvBufFree)) { ListEntry = NULL; } else { ListEntry = RemoveHeadList(&thisDev->rcvBufFree); } } else { ListEntry = NDISSynchronizedRemoveHeadList(&thisDev->rcvBufFree, &thisDev->interruptObj); } if (ListEntry) { rcvBuf = CONTAINING_RECORD(ListEntry, rcvBuffer, listEntry); if (IsFIR) { LOG_Data(thisDev, data); } }
if (rcvBuf){ rcvBuf->dataBuf = data;
VerifyNdisPacket(rcvBuf->packet, 0); rcvBuf->state = STATE_FULL; rcvBuf->dataLen = dataLen;
if (!IsFIR) { rcvBuf->isDmaBuf = FALSE; InsertTailList(&thisDev->rcvBufFull, ListEntry); } else { rcvBuf->isDmaBuf = TRUE; LOG_InsertTailList(thisDev, &thisDev->rcvBufFull, rcvBuf); NDISSynchronizedInsertTailList(&thisDev->rcvBufFull, ListEntry, &thisDev->interruptObj); } } LOG("<== QueueReceivePacket", 1); DBGOUT(("<== QueueReceivePacket")); }
/*
************************************************************************* * MiniportISR ************************************************************************* * * * This is the miniport's interrupt service routine (ISR). * * */ VOID MiniportISR(PBOOLEAN InterruptRecognized, PBOOLEAN QueueMiniportHandleInterrupt, NDIS_HANDLE MiniportAdapterContext) { IrDevice *thisDev = CONTEXT_TO_DEV(MiniportAdapterContext);
if (thisDev->TestingInterrupt) { //
// we are testing to make sure the interrupt works
//
UCHAR TempValue;
//
// Read the interrupt identification register
//
NdisRawReadPortUchar(thisDev->portInfo.ioBase+INT_ID_AND_FIFO_CNTRL_REG_OFFSET,&TempValue);
//
// if the low bit is clear then an interrupt is pending
//
if ((TempValue & 1) == 0) {
//
// inform the test code that we got the interrupt
//
thisDev->GotTestInterrupt=TRUE; thisDev->TestingInterrupt=FALSE;
//
// mask all ints again
//
NdisRawWritePortUchar(thisDev->portInfo.ioBase+INT_ENABLE_REG_OFFSET, 0);
DBGOUT(("NSCIRDA: Got test interrupt %x\n",TempValue))
*InterruptRecognized=TRUE; *QueueMiniportHandleInterrupt=FALSE;
return; }
//
// seems that our uart did not generate this interrupt
//
*InterruptRecognized=FALSE; *QueueMiniportHandleInterrupt=FALSE;
return;
}
//LOG("==> MiniportISR", ++thisDev->interruptCount);
//DBGOUT(("==> MiniportISR(0x%x), interrupt #%d)", (UINT)thisDev,
// thisDev->interruptCount));
#if DBG
{ UCHAR TempVal; //
// This code assumes that bank 0 is current, we will make sure of that
//
NdisRawReadPortUchar(thisDev->portInfo.ioBase+LCR_BSR_OFFSET, &TempVal);
ASSERT((TempVal & BKSE) == 0); } #endif
/*
* Service the interrupt. */ if (thisDev->currentSpeed > MAX_SIR_SPEED){ NSC_FIR_ISR(thisDev, InterruptRecognized, QueueMiniportHandleInterrupt); } else { COM_ISR(thisDev, InterruptRecognized, QueueMiniportHandleInterrupt); }
LOG("<== MiniportISR", 1); DBGOUT(("<== MiniportISR")); }
VOID MiniportResetCallback(PNDIS_WORK_ITEM pWorkItem, PVOID pVoid) { IrDevice *thisDev = pWorkItem->Context; LIST_ENTRY TempList; BOOLEAN SetSpeedNow=FALSE; PNDIS_PACKET Packet;
InitializeListHead(&TempList);
NdisFreeMemory(pWorkItem, sizeof(NDIS_WORK_ITEM), 0);
NdisAcquireSpinLock(&thisDev->QueueLock);
//
// un-queue all the send packets and put them on a temp list
//
while (!IsListEmpty(&thisDev->SendQueue)) {
PLIST_ENTRY ListEntry;
ListEntry=RemoveHeadList(&thisDev->SendQueue);
InsertTailList(&TempList,ListEntry); }
//
// if there is a current send packet, then request a speed change after it completes
//
if (thisDev->CurrentPacket != NULL) { //
// the current packet is now the last, chage speed after it is done
//
thisDev->lastPacketAtOldSpeed=thisDev->CurrentPacket;
} else { //
// no current packet, change speed now
//
SetSpeedNow=TRUE;
}
//
// back to 9600
//
thisDev->linkSpeedInfo = &supportedBaudRateTable[BAUDRATE_9600];
if (SetSpeedNow) { //
// there are no packets being transmitted now, switch speeds now.
// otherwise the dpc will do it when the current send completes
//
SetSpeed(thisDev); thisDev->TransmitIsIdle=FALSE; }
NdisReleaseSpinLock(&thisDev->QueueLock);
if (SetSpeedNow) { //
// the transmit was idle, but we change this to get the receive going again
//
ProcessSendQueue(thisDev); }
//
// return all of these back to the protocol
//
while (!IsListEmpty(&TempList)) {
PLIST_ENTRY ListEntry;
ListEntry=RemoveHeadList(&TempList);
Packet= CONTAINING_RECORD( ListEntry, NDIS_PACKET, MiniportReserved ); NdisMSendComplete(thisDev->ndisAdapterHandle, Packet, NDIS_STATUS_RESET_IN_PROGRESS ); } #if 0
CloseCOM(thisDev); OpenCOM(thisDev);
#endif
thisDev->hardwareStatus = NdisHardwareStatusReady;
NdisMResetComplete(thisDev->ndisAdapterHandle, NDIS_STATUS_SUCCESS, TRUE); // Addressing reset
}
/*
************************************************************************* * MiniportReset ************************************************************************* * * * MiniportReset issues a hardware reset to the network interface card. * The miniport driver also resets its software state. * * */ NDIS_STATUS MiniportReset(PBOOLEAN AddressingReset, NDIS_HANDLE MiniportAdapterContext) { IrDevice *dev, *thisDev = CONTEXT_TO_DEV(MiniportAdapterContext); NDIS_STATUS result = NDIS_STATUS_PENDING; PNDIS_WORK_ITEM pWorkItem; NDIS_PHYSICAL_ADDRESS noMaxAddr = NDIS_PHYSICAL_ADDRESS_CONST(-1,-1);
DBGERR(("MiniportReset(0x%x)", MiniportAdapterContext));
thisDev->hardwareStatus = NdisHardwareStatusReset;
result = NdisAllocateMemory(&pWorkItem, sizeof(NDIS_WORK_ITEM), 0, noMaxAddr); if (!pWorkItem) { thisDev->hardwareStatus = NdisHardwareStatusReady; return result; }
NdisInitializeWorkItem(pWorkItem, MiniportResetCallback, thisDev); result = NdisScheduleWorkItem(pWorkItem);
if (result!=NDIS_STATUS_SUCCESS) { NdisFreeMemory(pWorkItem, sizeof(NDIS_WORK_ITEM), 0); thisDev->hardwareStatus = NdisHardwareStatusReady; return result; }
*AddressingReset = TRUE;
DBGOUT(("MiniportReset done.")); return NDIS_STATUS_PENDING; }
/*
************************************************************************* * ReturnPacketHandler ************************************************************************* * * When NdisMIndicateReceivePacket returns asynchronously, * the protocol returns ownership of the packet to the miniport via this function. * */ VOID ReturnPacketHandler(NDIS_HANDLE MiniportAdapterContext, PNDIS_PACKET Packet) { IrDevice *thisDev = CONTEXT_TO_DEV(MiniportAdapterContext); rcvBuffer *rcvBuf; LONG PacketsLeft;
DBGOUT(("ReturnPacketHandler(0x%x)", MiniportAdapterContext)); RegStats.ReturnPacketHandlerCalled++;
//
// MiniportReserved contains the pointer to our rcvBuffer
//
rcvBuf = *(rcvBuffer**) Packet->MiniportReserved;
VerifyNdisPacket(Packet, 1);
if (rcvBuf->state == STATE_PENDING){ PNDIS_BUFFER ndisBuf;
DBGPKT(("Reclaimed rcv packet 0x%x.", Packet));
LOG_RemoveEntryList(thisDev, rcvBuf); NDISSynchronizedRemoveEntryList(&rcvBuf->listEntry, &thisDev->interruptObj);
LOG_PacketUnchain(thisDev, rcvBuf->packet); NdisUnchainBufferAtFront(Packet, &ndisBuf); if (ndisBuf){ NdisFreeBuffer(ndisBuf); }
if (!rcvBuf->isDmaBuf) { NDISSynchronizedInsertTailList(&thisDev->rcvBufBuf, RCV_BUF_TO_LIST_ENTRY(rcvBuf->dataBuf), &thisDev->interruptObj); // ASSERT the pointer is actually outside our FIR DMA buffer
ASSERT(rcvBuf->dataBuf < thisDev->portInfo.dmaReadBuf || rcvBuf->dataBuf >= thisDev->portInfo.dmaReadBuf+RCV_DMA_SIZE); } rcvBuf->dataBuf = NULL;
rcvBuf->state = STATE_FREE;
VerifyNdisPacket(rcvBuf->packet, 0); NDISSynchronizedInsertHeadList(&thisDev->rcvBufFree, &rcvBuf->listEntry, &thisDev->interruptObj); } else { LOG("Error: Packet in ReturnPacketHandler was " "not PENDING", 0); DBGERR(("Packet in ReturnPacketHandler was not PENDING.")); }
NdisAcquireSpinLock(&thisDev->QueueLock);
PacketsLeft=NdisInterlockedDecrement(&thisDev->PacketsSentToProtocol);
if (thisDev->Halting && (PacketsLeft == 0)) {
NdisSetEvent(&thisDev->ReceiveStopped); }
NdisReleaseSpinLock(&thisDev->QueueLock);
VerifyNdisPacket(rcvBuf->packet, 1);
}
VOID GivePacketToSirISR( IrDevice *thisDev )
{
thisDev->portInfo.writeBufPos = 0; thisDev->portInfo.SirWritePending = TRUE; thisDev->nowReceiving = FALSE;
SetCOMPort(thisDev->portInfo.ioBase, INT_ENABLE_REG_OFFSET, XMIT_MODE_INTS_ENABLE);
}
VOID SendCurrentPacket( IrDevice *thisDev )
{ BOOLEAN Result; PNDIS_PACKET FailedPacket=NULL;
NdisAcquireSpinLock(&thisDev->QueueLock);
thisDev->TransmitIsIdle=FALSE;
if (thisDev->CurrentPacket == thisDev->lastPacketAtOldSpeed){ thisDev->setSpeedAfterCurrentSendPacket = TRUE; }
if (thisDev->currentSpeed > MAX_SIR_SPEED) { //
// send via FIR
//
if (thisDev->FirReceiveDmaActive) {
thisDev->FirReceiveDmaActive=FALSE; //
// receive dma is running, stop it
//
CompleteDmaTransferFromDevice( &thisDev->DmaUtil );
}
thisDev->HangChk=0;
thisDev->FirTransmitPending = TRUE; //
// Use DMA swap bit to switch to DMA to Transmit.
//
SyncWriteBankReg(&thisDev->interruptObj,thisDev->portInfo.ioBase, 2, 2, 0x0B);
//
// Switch on the DMA interrupt to decide when
// transmission is complete.
//
thisDev->FirIntMask = 0x14;
SyncSetInterruptMask(thisDev, TRUE); //
// Kick off the first transmit.
//
NdisToFirPacket(thisDev, thisDev->CurrentPacket, (UCHAR *) thisDev->portInfo.writeBuf, MAX_IRDA_DATA_SIZE, &thisDev->portInfo.writeBufLen);
DEBUGFIR(DBG_PKT, ("NSC: Sending packet\n")); DBGPRINTBUF(thisDev->portInfo.writeBuf, thisDev->portInfo.writeBufLen);
/* Setup Transmit DMA. */ StartDmaTransferToDevice( &thisDev->DmaUtil, thisDev->xmitDmaBuffer, 0, thisDev->portInfo.writeBufLen );
SyncSetInterruptMask(thisDev, TRUE);
} else { //
// SIR mode transfer
//
/*
* See if this was the last packet before we need to change * speed. */
/*
* Send one packet to the COMM port. */ DBGPKT(("Sending packet 0x%x (0x%x).", thisDev->packetsSent++, thisDev->CurrentPacket));
/*
* Convert the NDIS packet to an IRDA packet. */ Result = NdisToIrPacket( thisDev, thisDev->CurrentPacket, (UCHAR *)thisDev->portInfo.writeBuf, MAX_IRDA_DATA_SIZE, &thisDev->portInfo.writeBufLen ); if (Result){
NdisMSynchronizeWithInterrupt( &thisDev->interruptObj, GivePacketToSirISR, thisDev );
} else {
ASSERT(0); FailedPacket=thisDev->CurrentPacket; thisDev->CurrentPacket=NULL;
} }
NdisReleaseSpinLock(&thisDev->QueueLock);
if (FailedPacket != NULL) {
NdisMSendComplete(thisDev->ndisAdapterHandle, FailedPacket, NDIS_STATUS_FAILURE ); ProcessSendQueue(thisDev); }
return; }
VOID ProcessSendQueue( IrDevice *thisDev )
{
PNDIS_PACKET Packet=NULL; PLIST_ENTRY ListEntry;
NdisAcquireSpinLock(&thisDev->QueueLock);
if (thisDev->CurrentPacket == NULL) { //
// not currently processing a send
//
if (!IsListEmpty(&thisDev->SendQueue)) { //
// we have some queue packets to process
//
ListEntry=RemoveHeadList(&thisDev->SendQueue);
Packet= CONTAINING_RECORD( ListEntry, NDIS_PACKET, MiniportReserved );
thisDev->CurrentPacket=Packet;
NdisReleaseSpinLock(&thisDev->QueueLock);
{
PNDIS_IRDA_PACKET_INFO packetInfo;
/*
* Enforce the minimum turnaround time that must transpire * after the last receive. */ packetInfo = GetPacketInfo(Packet);
if (packetInfo->MinTurnAroundTime > 0 ){
UINT usecToWait = packetInfo->MinTurnAroundTime; UINT msecToWait;
msecToWait = (usecToWait<1000) ? 1 : (usecToWait+500)/1000; #if DBG
thisDev->WaitingForTurnAroundTimer=TRUE; #endif
NdisSetTimer(&thisDev->TurnaroundTimer, msecToWait);
return; } }
SendCurrentPacket(thisDev);
NdisAcquireSpinLock(&thisDev->QueueLock); } }
if ((thisDev->CurrentPacket == NULL) && IsListEmpty(&thisDev->SendQueue)) { //
// not currently processing a send
//
if (!thisDev->TransmitIsIdle) { //
// We were not idle before
//
thisDev->TransmitIsIdle=TRUE;
if (thisDev->Halting) {
NdisSetEvent(&thisDev->SendStoppedOnHalt); }
//
// restart receive
//
if (thisDev->currentSpeed > MAX_SIR_SPEED) { //
// Start receive
//
thisDev->FirIntMask = 0x04; SetupRecv(thisDev); SyncSetInterruptMask(thisDev, TRUE);
} else { //
// For SIR, the ISR switches back to receive for us
//
} } }
NdisReleaseSpinLock(&thisDev->QueueLock);
return; }
/*
************************************************************************* * SendPacketsHandler ************************************************************************* * * Send an array of packets simultaneously. * */ VOID SendPacketsHandler(NDIS_HANDLE MiniportAdapterContext, PPNDIS_PACKET PacketArray, UINT NumberofPackets) { NDIS_STATUS stat; BOOLEAN TxWasActive; UINT i;
IrDevice *thisDev = CONTEXT_TO_DEV(MiniportAdapterContext);
DEBUGMSG(DBG_TRACE_TX, ("M")); LOG("==> SendPacketsHandler", 0); DBGOUT(("==> SendPacketsHandler(0x%x)", MiniportAdapterContext));
LOG("Number of transmit Burst ", NumberofPackets);
ASSERT(!thisDev->Halting); //
// NDIS gives us the PacketArray, but it is not ours to keep, so we have to take
// the packets out and store them elsewhere.
//
NdisAcquireSpinLock(&thisDev->QueueLock); //
// all packets get queued
//
for (i = 0; i < NumberofPackets; i++) {
if (i > 0) { //
// Only the first packet needs to respect the turnaround time.
//
PNDIS_IRDA_PACKET_INFO packetInfo;
packetInfo = GetPacketInfo(PacketArray[i]);
packetInfo->MinTurnAroundTime=0; }
InsertTailList( &thisDev->SendQueue, (PLIST_ENTRY)PacketArray[i]->MiniportReserved); }
NdisReleaseSpinLock(&thisDev->QueueLock);
ProcessSendQueue(thisDev);
LOG("<== SendPacketsHandler", 1); DBGOUT(("<== SendPacketsHandler"));
return ; }
BOOLEAN AbortLoad = FALSE; /*
************************************************************************* * DriverEntry ************************************************************************* * * Only include if IRMINI is a stand-alone driver. * */ NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath); #pragma NDIS_INIT_FUNCTION(DriverEntry)
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { NTSTATUS result = STATUS_SUCCESS, stat; NDIS_HANDLE wrapperHandle; NDIS50_MINIPORT_CHARACTERISTICS info;
LOG("==> DriverEntry", 0); DBGOUT(("==> DriverEntry"));
//DbgBreakPoint();
if (AbortLoad) { return STATUS_CANCELLED; }
NdisZeroMemory(&info, sizeof(info));
NdisMInitializeWrapper( (PNDIS_HANDLE)&wrapperHandle, DriverObject, RegistryPath, NULL ); DBGOUT(("Wrapper handle is %xh", wrapperHandle));
info.MajorNdisVersion = (UCHAR)NDIS_MAJOR_VERSION; info.MinorNdisVersion = (UCHAR)NDIS_MINOR_VERSION; //info.Flags = 0;
info.CheckForHangHandler = MiniportCheckForHang; info.HaltHandler = MiniportHalt; info.InitializeHandler = MiniportInitialize; info.QueryInformationHandler = MiniportQueryInformation; info.ReconfigureHandler = NULL; info.ResetHandler = MiniportReset; info.SendHandler = NULL; //MiniportSend;
info.SetInformationHandler = MiniportSetInformation; info.TransferDataHandler = NULL;
info.HandleInterruptHandler = MiniportHandleInterrupt; info.ISRHandler = MiniportISR; info.DisableInterruptHandler = NULL; info.EnableInterruptHandler = NULL; //MiniportEnableInterrupt;
/*
* New NDIS 4.0 fields */ info.ReturnPacketHandler = ReturnPacketHandler; info.SendPacketsHandler = SendPacketsHandler; info.AllocateCompleteHandler = NULL;
stat = NdisMRegisterMiniport( wrapperHandle, (PNDIS_MINIPORT_CHARACTERISTICS)&info, sizeof(info)); if (stat != NDIS_STATUS_SUCCESS){ DBGERR(("NdisMRegisterMiniport failed in DriverEntry")); result = STATUS_UNSUCCESSFUL; goto _entryDone; }
_entryDone: DBGOUT(("DriverEntry %s", (PUCHAR)((result == NDIS_STATUS_SUCCESS) ? "succeeded" : "failed")));
LOG("<== DriverEntry", 1); DBGOUT(("<== DriverEntry")); return result; }
PNDIS_IRDA_PACKET_INFO GetPacketInfo(PNDIS_PACKET packet) { MEDIA_SPECIFIC_INFORMATION *mediaInfo; UINT size; NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO(packet, &mediaInfo, &size); return (PNDIS_IRDA_PACKET_INFO)mediaInfo->ClassInformation; }
/* Setup for Recv */ // This function is always called at MIR & FIR speeds
void SetupRecv(IrDevice *thisDev) { NDIS_STATUS stat; UINT FifoClear = 8; UCHAR FifoStatus;
LOG("SetupRecv - Begin Rcv Setup", 0);
FindLargestSpace(thisDev, &thisDev->rcvDmaOffset, &thisDev->rcvDmaSize);
// Drain the status fifo of any pending packets
//
FifoStatus=SyncReadBankReg(&thisDev->interruptObj,thisDev->portInfo.ioBase, 5, FRM_ST);
while ((FifoStatus & 0x80) && FifoClear--) {
ULONG Size;
Size = SyncReadBankReg(&thisDev->interruptObj,thisDev->portInfo.ioBase, 5, RFRL_L); Size |= SyncReadBankReg(&thisDev->interruptObj,thisDev->portInfo.ioBase, 5, RFRL_H);
LOG_Discard(thisDev, Size); DBGERR(("nsc: sr fifo %02x, %d",FifoStatus,Size));
FifoStatus=SyncReadBankReg(&thisDev->interruptObj,thisDev->portInfo.ioBase, 5, FRM_ST); thisDev->DiscardNextPacketSet = TRUE; }
thisDev->rcvPktOffset = thisDev->rcvDmaOffset;
//
// Use DMA swap bit to switch to DMA to Receive.
//
SyncWriteBankReg(&thisDev->interruptObj,thisDev->portInfo.ioBase, 2, 2, 0x03);
LOG_Dma(thisDev);
thisDev->FirReceiveDmaActive=TRUE;
if (thisDev->rcvDmaSize < 8192) { DBGERR(("sr small dma %d\n",thisDev->rcvDmaSize)); }
thisDev->LastReadDMACount = thisDev->rcvDmaSize;
stat=StartDmaTransferFromDevice( &thisDev->DmaUtil, thisDev->rcvDmaBuffer, (ULONG)thisDev->rcvDmaOffset, (ULONG)thisDev->rcvDmaSize );
if (stat != NDIS_STATUS_SUCCESS) {
thisDev->FirReceiveDmaActive=FALSE;
LOG("Error: NdisMSetupDmaTransfer failed in SetupRecv", stat); DBGERR(("NdisMSetupDmaTransfer failed (%xh) in SetupRecv", (UINT)stat)); ASSERT(0); } LOG("SetupRecv - End Rcv Setup", 0); }
VOID DelayedWrite(IN PVOID SystemSpecific1, IN PVOID FunctionContext, IN PVOID SystemSpecific2, IN PVOID SystemSpecific3) { IrDevice *thisDev = FunctionContext; #if DBG
ASSERT(thisDev->WaitingForTurnAroundTimer);
thisDev->WaitingForTurnAroundTimer=FALSE; #endif
SendCurrentPacket(thisDev); }
|