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.
2475 lines
74 KiB
2475 lines
74 KiB
/*
|
|
************************************************************************
|
|
*
|
|
* 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"
|
|
#include "nsc.tmh"
|
|
/*
|
|
* 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); \
|
|
NdisQueryBufferSafe(NdisBuffer, &Address, &Len,NormalPagePriority); \
|
|
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); \
|
|
NdisQueryBufferSafe(NdisBuffer, &Address, &Len,NormalPagePriority); \
|
|
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)
|
|
{
|
|
NdisQueryBufferSafe(NdisBuffer, &Address, &Len,NormalPagePriority);
|
|
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");
|
|
DBGOUT(("==> MiniportCheckForHang(0x%x)", MiniportAdapterContext));
|
|
|
|
// 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"));
|
|
LOG_ERROR("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);
|
|
SyncSetInterruptMask(thisDev, TRUE);
|
|
|
|
break;
|
|
}
|
|
thisDev->HangChk++;
|
|
}
|
|
|
|
NdisDprReleaseSpinLock(&thisDev->QueueLock);
|
|
|
|
// LOG("<== MiniportCheckForHang");
|
|
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");
|
|
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");
|
|
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.
|
|
*
|
|
*/
|
|
VOID
|
|
DeliverFullBuffers(IrDevice *thisDev)
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
|
|
LOG("==> DeliverFullBuffers");
|
|
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->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 %x", 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: %xh", 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");
|
|
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));
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
NdisAcquireSpinLock(&thisDev->QueueLock);
|
|
|
|
if (thisDev->Halting) {
|
|
//
|
|
// the adapter is being halted, stop sending packets up
|
|
//
|
|
|
|
NdisReleaseSpinLock(&thisDev->QueueLock);
|
|
|
|
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);
|
|
|
|
|
|
//
|
|
// free the buffer we chained to the packet
|
|
//
|
|
packetBuf=NULL;
|
|
|
|
NdisUnchainBufferAtFront(rcvBuf->packet, &packetBuf);
|
|
|
|
if (packetBuf){
|
|
|
|
NdisFreeBuffer(packetBuf);
|
|
}
|
|
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// increment the count of packets sent to the protocol
|
|
//
|
|
NdisInterlockedIncrement(&thisDev->PacketsSentToProtocol);
|
|
|
|
NdisReleaseSpinLock(&thisDev->QueueLock);
|
|
|
|
rcvBuf->state = STATE_PENDING;
|
|
|
|
*(rcvBuffer **)rcvBuf->packet->MiniportReserved = rcvBuf;
|
|
|
|
InterlockedInsertBufferSorted(
|
|
&thisDev->rcvBufPend,
|
|
rcvBuf,
|
|
&thisDev->QueueLock
|
|
);
|
|
|
|
VerifyNdisPacket(rcvBuf->packet, 1);
|
|
LOG_Data2(thisDev, rcvBuf->dataBuf);
|
|
|
|
|
|
NDIS_SET_PACKET_STATUS(rcvBuf->packet,STATUS_SUCCESS);
|
|
NdisMIndicateReceivePacket(thisDev->ndisAdapterHandle,
|
|
&rcvBuf->packet, 1);
|
|
|
|
/*
|
|
* 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: %d",
|
|
rcvBuf->dataLen);
|
|
DBGSTAT(("Rcv Pending. Rcvd %d packets",
|
|
++thisDev->packetsRcvd));
|
|
|
|
}
|
|
|
|
LOG("<== DeliverFullBuffers");
|
|
DBGOUT(("<== DeliverFullBuffers"));
|
|
return ;
|
|
}
|
|
|
|
|
|
/*
|
|
*************************************************************************
|
|
* 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");
|
|
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 %d",
|
|
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
|
|
//
|
|
|
|
{
|
|
ULONG CurrentDMACount;
|
|
UCHAR BytesInFifo;
|
|
ULONG LoopCount=0;
|
|
|
|
CurrentDMACount = NdisMReadDmaCounter(thisDev->DmaHandle);
|
|
|
|
if (CurrentDMACount > 0) {
|
|
|
|
LOG_ERROR("FIR send: Dma Count was not zero: %d\n\n", CurrentDMACount);
|
|
#if DBG
|
|
DbgPrint("FIR send: Count was not zero: %d\n\n", CurrentDMACount);
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// see if the fifo is empty yet
|
|
//
|
|
BytesInFifo=SyncReadBankReg(&thisDev->interruptObj,thisDev->portInfo.ioBase, BANK_2, TXFLV_OFFSET) & 0x3f;
|
|
|
|
if (BytesInFifo > 0) {
|
|
|
|
LOG_ERROR("FIR send: Bytes still in fifo: %d", BytesInFifo);
|
|
|
|
while ((BytesInFifo > 0) && (LoopCount < 64)) {
|
|
|
|
BytesInFifo=SyncReadBankReg(&thisDev->interruptObj,thisDev->portInfo.ioBase, BANK_2, TXFLV_OFFSET) & 0x3f;
|
|
LoopCount++;
|
|
}
|
|
|
|
LOG_ERROR("FIR send: Bytes still in fifo after loop: %d, loops=%d", BytesInFifo,LoopCount);
|
|
}
|
|
}
|
|
|
|
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, ASCR_OFFSET) & ASCR_TXUR) {
|
|
|
|
USHORT TransmitCurrentCount;
|
|
|
|
//
|
|
// for debugging purposes, see where we were in the frame when it stopped
|
|
//
|
|
TransmitCurrentCount = SyncReadBankReg(&thisDev->interruptObj,thisDev->portInfo.ioBase, BANK_4, TFRCCL_OFFSET);
|
|
TransmitCurrentCount |= ((USHORT)SyncReadBankReg(&thisDev->interruptObj,thisDev->portInfo.ioBase, BANK_4, TFRCCH_OFFSET)) << 8;
|
|
|
|
//
|
|
// reset the fifo's
|
|
//
|
|
SyncWriteBankReg(&thisDev->interruptObj,thisDev->portInfo.ioBase, 0, 2, 0x07);
|
|
|
|
//
|
|
// clear the tx underrun
|
|
//
|
|
SyncWriteBankReg(&thisDev->interruptObj,thisDev->portInfo.ioBase, 0, ASCR_OFFSET, ASCR_TXUR);
|
|
|
|
RegStats.TxUnderruns++;
|
|
PacketStatus = NDIS_STATUS_FAILURE;
|
|
|
|
LOG_ERROR("MiniportDpc: Transmit Underrun: tx current count=%d",TransmitCurrentCount);
|
|
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"));
|
|
LOG_ERROR("MiniportHandleInterrupt: fir: not sending and not RX state %02x",thisDev->InterruptStatus);
|
|
}
|
|
|
|
}
|
|
|
|
} 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");
|
|
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_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"));
|
|
NSC_Shutdown(thisDev);
|
|
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");
|
|
DBGOUT(("==> QueueReceivePacket(0x%x, 0x%lx, 0x%x)",
|
|
thisDev, data, dataLen));
|
|
LOG("QueueReceivePacket, len: %d ", 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");
|
|
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");
|
|
DBGOUT(("<== MiniportISR"));
|
|
}
|
|
|
|
|
|
/*
|
|
*************************************************************************
|
|
* 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 *thisDev = CONTEXT_TO_DEV(MiniportAdapterContext);
|
|
NDIS_STATUS result = NDIS_STATUS_PENDING;
|
|
|
|
LIST_ENTRY TempList;
|
|
BOOLEAN SetSpeedNow=FALSE;
|
|
PNDIS_PACKET Packet;
|
|
|
|
DBGERR(("MiniportReset(0x%x)", MiniportAdapterContext));
|
|
|
|
InitializeListHead(&TempList);
|
|
|
|
NdisAcquireSpinLock(&thisDev->QueueLock);
|
|
|
|
thisDev->hardwareStatus = NdisHardwareStatusReset;
|
|
//
|
|
// 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 );
|
|
}
|
|
|
|
thisDev->hardwareStatus = NdisHardwareStatusReady;
|
|
|
|
NdisMResetComplete(thisDev->ndisAdapterHandle,
|
|
NDIS_STATUS_SUCCESS,
|
|
TRUE); // Addressing reset
|
|
|
|
|
|
|
|
*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->dmaReadBuf ||
|
|
rcvBuf->dataBuf >= thisDev->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");
|
|
|
|
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.writeComBufferPos = 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->CurrentPacket,
|
|
(UCHAR *) thisDev->TransmitDmaBuffer,
|
|
MAX_IRDA_DATA_SIZE,
|
|
&thisDev->TransmitDmaLength
|
|
);
|
|
|
|
|
|
LOG_FIR("Sending packet %d",thisDev->TransmitDmaLength);
|
|
DEBUGFIR(DBG_PKT, ("NSC: Sending packet\n"));
|
|
DBGPRINTBUF(thisDev->TransmitDmaBuffer,thisDev->TransmitDmaLength);
|
|
|
|
{
|
|
UCHAR LsrValue;
|
|
|
|
//
|
|
// make sure the transmitter is empty before starting to send this frame
|
|
//
|
|
LsrValue=SyncReadBankReg(&thisDev->interruptObj,thisDev->portInfo.ioBase, 0, LSR_OFFSET);
|
|
|
|
if ((LsrValue & (LSR_TXRDY | LSR_TXEMP)) != (LSR_TXRDY | LSR_TXEMP)) {
|
|
|
|
ULONG LoopCount=0;
|
|
|
|
while (((LsrValue & (LSR_TXRDY | LSR_TXEMP)) != (LSR_TXRDY | LSR_TXEMP)) && (LoopCount < 16)) {
|
|
|
|
LsrValue=SyncReadBankReg(&thisDev->interruptObj,thisDev->portInfo.ioBase, 0, LSR_OFFSET);
|
|
LoopCount++;
|
|
}
|
|
|
|
LOG_ERROR("SendCurrentPacket: Looped %d times waiting for tx empty",LoopCount);
|
|
}
|
|
|
|
}
|
|
|
|
/* Setup Transmit DMA. */
|
|
StartDmaTransferToDevice(
|
|
&thisDev->DmaUtil,
|
|
thisDev->xmitDmaBuffer,
|
|
0,
|
|
thisDev->TransmitDmaLength
|
|
);
|
|
#if 0
|
|
{
|
|
ULONG CurrentDMACount;
|
|
|
|
CurrentDMACount = NdisMReadDmaCounter(thisDev->DmaHandle);
|
|
|
|
LOG("SendCurrentPacket: dma count after start %d",CurrentDMACount);
|
|
}
|
|
#endif
|
|
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->CurrentPacket,
|
|
(UCHAR *)thisDev->portInfo.writeComBuffer,
|
|
MAX_IRDA_DATA_SIZE,
|
|
&thisDev->portInfo.writeComBufferLen
|
|
);
|
|
if (Result){
|
|
|
|
LOG_SIR("Sending packet %d",thisDev->portInfo.writeComBufferLen);
|
|
|
|
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
|
|
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
|
|
|
|
LOG("Turn around timer expired");
|
|
SendCurrentPacket(thisDev);
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
//
|
|
// see if this packet is requesting a turnaround time or not
|
|
//
|
|
if (packetInfo->MinTurnAroundTime > 0) {
|
|
//
|
|
// Since we are using the timer to determine when the receive has ended,
|
|
// we must have waited atleast the timer timeout period before we indicated
|
|
// the packet to the protocol. Therefore, if that timeout was greater than
|
|
// the turn around time then we don't need to wait.
|
|
//
|
|
// this of course assumes that the protocol is not going to start sending
|
|
// in the middle of a receive. If it is, then the other station is not going to
|
|
// see the data anyway, so it does not matter much
|
|
//
|
|
// this saves us from waiting the 10ms or so for the time out since the OS timer
|
|
// resoltion is around 10ms.
|
|
//
|
|
// if the transfer ended because the DMA buffer filled up, then we will force a turnaround
|
|
//
|
|
// we don't do this SIR speeds
|
|
//
|
|
if ((packetInfo->MinTurnAroundTime > RECEIVE_TIMEOUT)
|
|
||
|
|
thisDev->ForceTurnAroundTimeout
|
|
||
|
|
(thisDev->currentSpeed <= MAX_SIR_SPEED) ) {
|
|
|
|
UINT usecToWait = packetInfo->MinTurnAroundTime;
|
|
UINT msecToWait;
|
|
|
|
msecToWait = (usecToWait<1000) ? 1 : (usecToWait+500)/1000;
|
|
#if DBG
|
|
thisDev->WaitingForTurnAroundTimer=TRUE;
|
|
#endif
|
|
NdisSetTimer(&thisDev->TurnaroundTimer, msecToWait);
|
|
|
|
thisDev->ForceTurnAroundTimeout=FALSE;
|
|
|
|
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)
|
|
{
|
|
UINT i;
|
|
|
|
IrDevice *thisDev = CONTEXT_TO_DEV(MiniportAdapterContext);
|
|
|
|
DEBUGMSG(DBG_TRACE_TX, ("M"));
|
|
LOG("==> SendPacketsHandler");
|
|
DBGOUT(("==> SendPacketsHandler(0x%x)", MiniportAdapterContext));
|
|
|
|
|
|
LOG("Number of transmit Burst %d ", 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 (thisDev->Halting) {
|
|
//
|
|
// ndis should not send us more packets after calling the halt rountine.
|
|
// just make sure here
|
|
//
|
|
NdisReleaseSpinLock(&thisDev->QueueLock);
|
|
|
|
NdisMSendComplete(thisDev->ndisAdapterHandle, PacketArray[i], NDIS_STATUS_FAILURE );
|
|
|
|
NdisAcquireSpinLock(&thisDev->QueueLock);
|
|
|
|
} else {
|
|
|
|
InsertTailList(
|
|
&thisDev->SendQueue,
|
|
(PLIST_ENTRY)PacketArray[i]->MiniportReserved
|
|
);
|
|
}
|
|
}
|
|
|
|
NdisReleaseSpinLock(&thisDev->QueueLock);
|
|
|
|
ProcessSendQueue(thisDev);
|
|
|
|
LOG("<== SendPacketsHandler");
|
|
DBGOUT(("<== SendPacketsHandler"));
|
|
|
|
return ;
|
|
}
|
|
|
|
VOID
|
|
NscUloadHandler(
|
|
PDRIVER_OBJECT DriverObject
|
|
)
|
|
|
|
{
|
|
DBGOUT(("Unloading"));
|
|
|
|
WPP_CLEANUP(DriverObject);
|
|
|
|
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;
|
|
|
|
WPP_INIT_TRACING(DriverObject,RegistryPath);
|
|
|
|
LOG("==> DriverEntry");
|
|
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;
|
|
}
|
|
|
|
NdisMRegisterUnloadHandler(
|
|
wrapperHandle,
|
|
NscUloadHandler
|
|
);
|
|
|
|
|
|
_entryDone:
|
|
DBGOUT(("DriverEntry %s", (PUCHAR)((result == NDIS_STATUS_SUCCESS) ? "succeeded" : "failed")));
|
|
|
|
LOG("<== DriverEntry");
|
|
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");
|
|
|
|
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);
|
|
LOG_ERROR("SetupRecv: 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) {
|
|
LOG("SetupRecv small dma %d\n",(ULONG)thisDev->rcvDmaSize);
|
|
}
|
|
|
|
thisDev->LastReadDMACount = thisDev->rcvDmaSize;
|
|
|
|
stat=StartDmaTransferFromDevice(
|
|
&thisDev->DmaUtil,
|
|
thisDev->rcvDmaBuffer,
|
|
(ULONG)thisDev->rcvDmaOffset,
|
|
(ULONG)thisDev->rcvDmaSize
|
|
);
|
|
|
|
LOG("SetupRecv - Begin Rcv Setup: offset=%d, size =%d",(ULONG)thisDev->rcvDmaOffset,(ULONG)thisDev->rcvDmaSize);
|
|
|
|
if (stat != NDIS_STATUS_SUCCESS) {
|
|
|
|
thisDev->FirReceiveDmaActive=FALSE;
|
|
|
|
LOG("Error: NdisMSetupDmaTransfer failed in SetupRecv %x", stat);
|
|
DBGERR(("NdisMSetupDmaTransfer failed (%xh) in SetupRecv", (UINT)stat));
|
|
ASSERT(0);
|
|
}
|
|
LOG("SetupRecv - End Rcv Setup");
|
|
}
|