mirror of https://github.com/lianthony/NT4.0
1222 lines
31 KiB
1222 lines
31 KiB
/*
|
|
************************************************************************
|
|
*
|
|
* IRMINI.c
|
|
*
|
|
* IRMINI Infrared Serial NDIS Miniport driver.
|
|
*
|
|
* (C) Copyright 1996 Microsoft Corp.
|
|
*
|
|
*
|
|
* (ep)
|
|
*
|
|
*************************************************************************
|
|
*/
|
|
|
|
|
|
#include "irmini.h"
|
|
|
|
|
|
/*
|
|
* We keep a linked list of device objects
|
|
*/
|
|
IrDevice *firstIrDevice = NULL;
|
|
|
|
|
|
|
|
/*
|
|
*************************************************************************
|
|
* MiniportCheckForHang
|
|
*************************************************************************
|
|
*
|
|
* Reports the state of the network interface card.
|
|
*
|
|
*/
|
|
BOOLEAN MiniportCheckForHang(NDIS_HANDLE MiniportAdapterContext)
|
|
{
|
|
DBGOUT(("MiniportCheckForHang(0x%x)", (UINT)MiniportAdapterContext));
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
*************************************************************************
|
|
* MiniportDisableInterrupt
|
|
*************************************************************************
|
|
*
|
|
* Disables the NIC from generating interrupts.
|
|
*
|
|
*/
|
|
VOID MiniportDisableInterrupt(NDIS_HANDLE MiniportAdapterContext)
|
|
{
|
|
IrDevice *thisDev = CONTEXT_TO_DEV(MiniportAdapterContext);
|
|
DBGOUT(("MiniportDisableInterrupt(0x%x)", (UINT)MiniportAdapterContext));
|
|
SetCOMInterrupts(thisDev, FALSE);
|
|
}
|
|
|
|
|
|
/*
|
|
*************************************************************************
|
|
* MiniportEnableInterrupt
|
|
*************************************************************************
|
|
*
|
|
* Enables the NIC to generate interrupts.
|
|
*
|
|
*/
|
|
VOID MiniportEnableInterrupt(IN NDIS_HANDLE MiniportAdapterContext)
|
|
{
|
|
IrDevice *thisDev = CONTEXT_TO_DEV(MiniportAdapterContext);
|
|
DBGOUT(("MiniportEnableInterrupt(0x%x)", (UINT)MiniportAdapterContext));
|
|
SetCOMInterrupts(thisDev, TRUE);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
*************************************************************************
|
|
* MiniportHalt
|
|
*************************************************************************
|
|
*
|
|
* Halts the network interface card.
|
|
*
|
|
*/
|
|
VOID MiniportHalt(IN NDIS_HANDLE MiniportAdapterContext)
|
|
{
|
|
IrDevice *thisDev = CONTEXT_TO_DEV(MiniportAdapterContext);
|
|
|
|
DBGOUT(("MiniportHalt(0x%x)", (UINT)MiniportAdapterContext));
|
|
|
|
/*
|
|
* Remove this device from our global list
|
|
*/
|
|
|
|
if (thisDev == firstIrDevice){
|
|
firstIrDevice = firstIrDevice->next;
|
|
}
|
|
else {
|
|
IrDevice *dev;
|
|
for (dev = firstIrDevice; dev && (dev->next != thisDev); dev = dev->next){ }
|
|
if (dev){
|
|
dev->next = dev->next->next;
|
|
}
|
|
else {
|
|
/*
|
|
* Don't omit this error check. I've seen NDIS call MiniportHalt with
|
|
* a bogus context when the system gets corrupted.
|
|
*/
|
|
DBGERR(("Bad context in MiniportHalt"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Now destroy the device object.
|
|
*/
|
|
DoClose(thisDev);
|
|
|
|
NdisMDeregisterIoPortRange( thisDev->ndisAdapterHandle,
|
|
thisDev->portInfo.ioBase,
|
|
8,
|
|
(PVOID)thisDev->mappedPortRange);
|
|
FreeDevice(thisDev);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
*************************************************************************
|
|
* MiniportSyncHandleInterrupt
|
|
*************************************************************************
|
|
*
|
|
* This function is called from MiniportHandleInterrupt
|
|
* via NdisMSynchronizeWithInterrupt to synchronize with MiniportISR.
|
|
* This is required because the deferred procedure call (MiniportHandleInterrupt)
|
|
* shares data with MiniportISR but cannot achieve mutual exclusion with a spinlock
|
|
* because ISR's are not allowed to acquire spinlocks.
|
|
*
|
|
* This function should be called WITH DEVICE LOCK HELD, however, to synchronize
|
|
* with the rest of the miniport code (besides the ISR).
|
|
*
|
|
* The device's IRQ is masked out in the PIC while this function executes,
|
|
* so don't make calls up the stack.
|
|
*
|
|
*/
|
|
BOOLEAN MiniportSyncHandleInterrupt(PVOID MiniportAdapterContext)
|
|
{
|
|
IrDevice *thisDev = CONTEXT_TO_DEV(MiniportAdapterContext);
|
|
|
|
DBGOUT(("==> MiniportSyncHandleInterrupt"));
|
|
|
|
/*
|
|
* Update .firstRcvBufIndex and .lastRcvBufIndex.
|
|
*/
|
|
while ((thisDev->firstRcvBufIndex != NO_BUF_INDEX) &&
|
|
(thisDev->rcvBufs[thisDev->firstRcvBufIndex].state == STATE_FREE)){
|
|
|
|
if (thisDev->firstRcvBufIndex == thisDev->lastRcvBufIndex){
|
|
thisDev->firstRcvBufIndex = thisDev->lastRcvBufIndex = NO_BUF_INDEX;
|
|
}
|
|
else {
|
|
thisDev->firstRcvBufIndex = NEXT_RCV_BUF_INDEX(thisDev->firstRcvBufIndex);
|
|
}
|
|
}
|
|
|
|
DBGOUT(("<== MiniportSyncHandleInterrupt"));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
*************************************************************************
|
|
* DeliverFullBuffers
|
|
*************************************************************************
|
|
*
|
|
* Deliver received packets to the protocol.
|
|
*
|
|
*/
|
|
VOID DeliverFullBuffers(IrDevice *thisDev)
|
|
{
|
|
int rcvBufIndex = thisDev->firstRcvBufIndex;
|
|
|
|
DBGOUT(("==> DeliverFullBuffers"));
|
|
|
|
|
|
|
|
/*
|
|
* Deliver all full rcv buffers
|
|
*/
|
|
while (rcvBufIndex != NO_BUF_INDEX){
|
|
rcvBuffer *rcvBuf = &thisDev->rcvBufs[rcvBufIndex];
|
|
NDIS_STATUS stat;
|
|
PNDIS_BUFFER packetBuf;
|
|
SLOW_IR_FCS_TYPE fcs;
|
|
|
|
switch (rcvBuf->state){
|
|
|
|
case STATE_FREE:
|
|
case STATE_PENDING:
|
|
/*
|
|
* This frame was already delivered. Just go to the next one.
|
|
*/
|
|
break;
|
|
|
|
case STATE_FULL:
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
DBGERR(("Bad FCS in DeliverFullBuffers 0x%x!=0x%x.", (UINT)fcs, (UINT)GOOD_FCS));
|
|
rcvBuf->state = STATE_FREE;
|
|
|
|
DBGSTAT(("Dropped %d/%d packets; packet with BAD FCS (%xh!=%xh):",
|
|
++thisDev->packetsDropped, thisDev->packetsDropped + thisDev->packetsRcvd, fcs, GOOD_FCS));
|
|
DBGPRINTBUF(rcvBuf->dataBuf, rcvBuf->dataLen);
|
|
break;
|
|
}
|
|
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
rcvBuf->dataLen -= sizeof(USHORT);
|
|
DBGOUT((" RCVing packet %xh **", (UINT)*(USHORT *)(rcvBuf->dataBuf+rcvBuf->dataLen)));
|
|
}
|
|
#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){
|
|
DBGERR(("NdisAllocateBuffer failed"));
|
|
break;
|
|
}
|
|
NdisChainBufferAtFront(rcvBuf->packet, packetBuf);
|
|
|
|
/*
|
|
* Fix up some other packet fields.
|
|
*/
|
|
NDIS_SET_PACKET_HEADER_SIZE(rcvBuf->packet, SLOW_IR_ADDR_SIZE + SLOW_IR_CONTROL_SIZE);
|
|
|
|
DBGPKT(("Indicating rcv packet 0x%x.", (UINT)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;
|
|
NdisMIndicateReceivePacket(thisDev->ndisAdapterHandle, &rcvBuf->packet, 1);
|
|
stat = NDIS_GET_PACKET_STATUS(rcvBuf->packet);
|
|
if (stat == NDIS_STATUS_PENDING){
|
|
/*
|
|
* The packet is being delivered asynchronously.
|
|
* Leave the rcv buffer's state as PENDING; we'll
|
|
* get a callback when the transfer is complete.
|
|
*
|
|
* Do NOT step firstRcvBufIndex.
|
|
* We don't really need to break out here,
|
|
* but we will anyways just to make things simple.
|
|
* This is ok since we get this deferred interrupt callback
|
|
* for each packet anyway. It'll give the protocol a chance
|
|
* to catch up.
|
|
*/
|
|
DBGSTAT(("Rcv Pending. Rcvd %d packets", ++thisDev->packetsRcvd));
|
|
}
|
|
else {
|
|
/*
|
|
* If there was an error, we are dropping this packet;
|
|
* otherwise, this packet was delivered synchronously.
|
|
* We can free the packet buffer and make this rcv frame
|
|
* available.
|
|
*/
|
|
NdisUnchainBufferAtFront(rcvBuf->packet, &packetBuf);
|
|
if (packetBuf){
|
|
NdisFreeBuffer(packetBuf);
|
|
}
|
|
rcvBuf->state = STATE_FREE;
|
|
|
|
if (stat == NDIS_STATUS_SUCCESS){
|
|
DBGSTAT(("Rcvd %d packets", ++thisDev->packetsRcvd));
|
|
}
|
|
else {
|
|
DBGSTAT(("Dropped %d/%d rcv packets. ", thisDev->packetsDropped++, thisDev->packetsDropped+thisDev->packetsRcvd));
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
/*
|
|
* This should never happen.
|
|
*/
|
|
DBGERR(("Bad rcv buffer state in DPC"));
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Step the buffer index
|
|
*/
|
|
if (rcvBufIndex == thisDev->lastRcvBufIndex){
|
|
rcvBufIndex = NO_BUF_INDEX;
|
|
}
|
|
else {
|
|
rcvBufIndex = NEXT_RCV_BUF_INDEX(rcvBufIndex);
|
|
}
|
|
}
|
|
|
|
DBGOUT(("<== DeliverFullBuffers"));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
*************************************************************************
|
|
* 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);
|
|
|
|
DBGOUT(("==> MiniportHandleInterrupt(0x%x)", (UINT)MiniportAdapterContext));
|
|
|
|
|
|
/*
|
|
* If we have just started receiving a packet,
|
|
* indicate media-busy to the protocol.
|
|
*/
|
|
if (thisDev->mediaBusy && !thisDev->haveIndicatedMediaBusy){
|
|
NdisMIndicateStatus(thisDev->ndisAdapterHandle, NDIS_STATUS_MEDIA_BUSY, NULL, 0);
|
|
NdisMIndicateStatusComplete(thisDev->ndisAdapterHandle);
|
|
thisDev->haveIndicatedMediaBusy = TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* Deliver all undelivered receive packets to the protocol.
|
|
*/
|
|
DeliverFullBuffers(thisDev);
|
|
|
|
|
|
/*
|
|
* Update the rcv queue 'first' and 'last' pointers.
|
|
*
|
|
* We cannot use a spinlock to coordinate accesses to the rcv buffers
|
|
* with the ISR, since ISR's are not allowed to acquire spinlocks.
|
|
* So instead, we synchronize with the ISR using this special mechanism.
|
|
* MiniportSyncHandleInterrupt will do our work for us with the IRQ
|
|
* masked out in the PIC.
|
|
*/
|
|
NdisMSynchronizeWithInterrupt( &thisDev->interruptObj,
|
|
MiniportSyncHandleInterrupt,
|
|
(PVOID)MiniportAdapterContext);
|
|
|
|
|
|
/*
|
|
* Send any pending write packets if possible.
|
|
*/
|
|
if (IsCommReadyForTransmit(thisDev)){
|
|
PortReadyForWrite(thisDev, FALSE);
|
|
}
|
|
|
|
DBGOUT(("<== MiniportHandleInterrupt"));
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
*************************************************************************
|
|
* Configure
|
|
*************************************************************************
|
|
*
|
|
* Read configurable parameters out of the system registry.
|
|
*
|
|
*/
|
|
BOOLEAN Configure(IrDevice *thisDev, NDIS_HANDLE WrapperConfigurationContext)
|
|
{
|
|
NDIS_STATUS result = NDIS_STATUS_SUCCESS, stat;
|
|
NDIS_HANDLE configHandle;
|
|
PNDIS_CONFIGURATION_PARAMETER configParamPtr;
|
|
NDIS_STRING regKeyIRQString = NDIS_STRING_CONST("INTERRUPT");
|
|
NDIS_STRING regKeyIOString = NDIS_STRING_CONST("IOADDRESS");
|
|
NDIS_STRING regKeyIRTransceiverString = NDIS_STRING_CONST("InfraredTransceiverType");
|
|
|
|
DBGOUT(("Configure(0x%x)", (UINT)thisDev));
|
|
|
|
/*
|
|
* Set default values for configurable parameters. Default to COM1.
|
|
*/
|
|
thisDev->portInfo.irq = comPortIRQ[1];
|
|
thisDev->portInfo.ioBase = comPortIOBase[1];
|
|
thisDev->transceiverType = STANDARD_UART;
|
|
|
|
NdisOpenConfiguration(&stat, &configHandle, WrapperConfigurationContext);
|
|
if (stat != NDIS_STATUS_SUCCESS){
|
|
DBGERR(("NdisOpenConfiguration failed in Configure()"));
|
|
return FALSE;
|
|
}
|
|
|
|
#if 1
|
|
// BUGBUG REMOVE !!!
|
|
// (this here because reserving system resources causes problems for UART driver)
|
|
{
|
|
NDIS_STRING regKeyPortString = NDIS_STRING_CONST("PORT");
|
|
int comPort = 1;
|
|
|
|
/*
|
|
* Get infrared transceiver type for this connection.
|
|
*/
|
|
NdisReadConfiguration( &stat,
|
|
&configParamPtr,
|
|
configHandle,
|
|
®KeyPortString,
|
|
NdisParameterInteger);
|
|
if (stat == NDIS_STATUS_SUCCESS){
|
|
|
|
comPort = (irTransceiverType)configParamPtr->ParameterData.IntegerData;
|
|
thisDev->portInfo.irq = comPortIRQ[comPort];
|
|
thisDev->portInfo.ioBase = comPortIOBase[comPort];
|
|
}
|
|
else {
|
|
DBGERR(("Couldn't read Com# from registry"));
|
|
}
|
|
}
|
|
#else
|
|
|
|
/*
|
|
* Get IRQ level for this connection.
|
|
*/
|
|
NdisReadConfiguration( &stat,
|
|
&configParamPtr,
|
|
configHandle,
|
|
®KeyIRQString,
|
|
NdisParameterInteger);
|
|
if (stat == NDIS_STATUS_SUCCESS){
|
|
thisDev->portInfo.irq = (UINT)configParamPtr->ParameterData.IntegerData;
|
|
}
|
|
else {
|
|
DBGERR(("Couldn't read IRQ value from registry"));
|
|
}
|
|
|
|
/*
|
|
* Get IO base address for this connection.
|
|
*/
|
|
NdisReadConfiguration( &stat,
|
|
&configParamPtr,
|
|
configHandle,
|
|
®KeyIOString,
|
|
NdisParameterHexInteger);
|
|
if (stat == NDIS_STATUS_SUCCESS){
|
|
thisDev->portInfo.ioBase = (UINT)configParamPtr->ParameterData.IntegerData;
|
|
}
|
|
else {
|
|
DBGERR(("Couldn't read IO value from registry"));
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Get infrared transceiver type for this connection.
|
|
*/
|
|
NdisReadConfiguration( &stat,
|
|
&configParamPtr,
|
|
configHandle,
|
|
®KeyIRTransceiverString,
|
|
NdisParameterInteger);
|
|
if ((stat == NDIS_STATUS_SUCCESS) &&
|
|
((UINT)configParamPtr->ParameterData.IntegerData < NUM_TRANSCEIVER_TYPES)){
|
|
|
|
thisDev->transceiverType = (irTransceiverType)configParamPtr->ParameterData.IntegerData;
|
|
}
|
|
else {
|
|
DBGERR(("Couldn't read IR transceiver type from registry"));
|
|
}
|
|
|
|
|
|
NdisCloseConfiguration(configHandle);
|
|
|
|
DBGOUT(("Configure done: irq=%d IO=%xh", thisDev->portInfo.irq, thisDev->portInfo.ioBase));
|
|
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;
|
|
goto _initDone;
|
|
}
|
|
|
|
/*
|
|
* Allocate a new device object to represent this connection.
|
|
*/
|
|
thisDev = NewDevice();
|
|
if (!thisDev){
|
|
return NDIS_STATUS_NOT_ACCEPTED;
|
|
}
|
|
|
|
/*
|
|
* Allocate resources for this connection.
|
|
*/
|
|
if (!OpenDevice(thisDev)){
|
|
DBGERR(("OpenDevice failed"));
|
|
result = NDIS_STATUS_FAILURE;
|
|
goto _initDone;
|
|
}
|
|
|
|
|
|
/*
|
|
* Read the system registry to get parameters like COM port number, etc.
|
|
*/
|
|
if (!Configure(thisDev, WrapperConfigurationContext)){
|
|
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.
|
|
*/
|
|
NdisMSetAttributes ( NdisAdapterHandle,
|
|
(NDIS_HANDLE)thisDev,
|
|
FALSE,
|
|
NdisInterfaceInternal
|
|
);
|
|
|
|
|
|
/*
|
|
* Tell NDIS about the range of IO space that we'll be using.
|
|
*/
|
|
retStat = NdisMRegisterIoPortRange( (PVOID)thisDev->mappedPortRange,
|
|
NdisAdapterHandle,
|
|
thisDev->portInfo.ioBase,
|
|
8);
|
|
if (retStat != NDIS_STATUS_SUCCESS){
|
|
DBGERR(("NdisMRegisterIoPortRange 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", (UINT)NdisAdapterHandle, (UINT)thisDev));
|
|
thisDev->ndisAdapterHandle = NdisAdapterHandle;
|
|
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
|
|
/*
|
|
* 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
|
|
NdisInterruptLevelSensitive
|
|
);
|
|
if (retStat != NDIS_STATUS_SUCCESS){
|
|
DBGERR(("NdisMRegisterInterrupt 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->next = firstIrDevice;
|
|
firstIrDevice = thisDev;
|
|
|
|
DBGOUT(("MiniportInitialize succeeded"));
|
|
}
|
|
else {
|
|
if (thisDev){
|
|
FreeDevice(thisDev);
|
|
}
|
|
DBGOUT(("MiniportInitialize failed"));
|
|
}
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
*************************************************************************
|
|
* QueueReceivePacket
|
|
*************************************************************************
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
VOID QueueReceivePacket(IrDevice *thisDev, PUCHAR *data, UINT dataLen)
|
|
{
|
|
rcvBuffer *rcvBuf;
|
|
int nextRcvBufIndex;
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
if (thisDev->firstRcvBufIndex == NO_BUF_INDEX){
|
|
nextRcvBufIndex = 0;
|
|
rcvBuf = &thisDev->rcvBufs[0];
|
|
}
|
|
else {
|
|
nextRcvBufIndex = NEXT_RCV_BUF_INDEX(thisDev->lastRcvBufIndex);
|
|
|
|
if (nextRcvBufIndex == thisDev->firstRcvBufIndex){
|
|
/*
|
|
* Buffers are all full.
|
|
*/
|
|
rcvBuf = NULL;
|
|
}
|
|
else {
|
|
rcvBuf = &thisDev->rcvBufs[nextRcvBufIndex];
|
|
}
|
|
}
|
|
|
|
if (rcvBuf){
|
|
/*
|
|
* Get the COM port data into the receive buffer.
|
|
* For efficiency, we just swap pointers.
|
|
*/
|
|
PVOID tmpptr = *data;
|
|
*data = rcvBuf->dataBuf;
|
|
rcvBuf->dataBuf = tmpptr;
|
|
|
|
rcvBuf->state = STATE_FULL;
|
|
rcvBuf->dataLen = dataLen;
|
|
|
|
/*
|
|
* Update rcv queue pointers only if DoRcv succeeded
|
|
*/
|
|
if (thisDev->firstRcvBufIndex == NO_BUF_INDEX){
|
|
thisDev->firstRcvBufIndex = thisDev->lastRcvBufIndex = 0;
|
|
}
|
|
else {
|
|
thisDev->lastRcvBufIndex = nextRcvBufIndex;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
*************************************************************************
|
|
* 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);
|
|
|
|
DBGOUT(("MiniportISR(0x%x, interrupt #%d)", (UINT)thisDev, ++thisDev->interruptCount));
|
|
|
|
/*
|
|
* Service the interrupt.
|
|
*/
|
|
COM_ISR(thisDev, InterruptRecognized, QueueMiniportHandleInterrupt);
|
|
|
|
DBGOUT(("... MiniportISR done."));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
*************************************************************************
|
|
* MiniportReconfigure
|
|
*************************************************************************
|
|
*
|
|
*
|
|
* Reconfigures the network interface card to new parameters available
|
|
* in the NDIS library configuration functions.
|
|
*
|
|
*
|
|
*/
|
|
NDIS_STATUS MiniportReconfigure ( OUT PNDIS_STATUS OpenErrorStatus,
|
|
IN NDIS_HANDLE MiniportAdapterContext,
|
|
IN NDIS_HANDLE WrapperConfigurationContext
|
|
)
|
|
{
|
|
IrDevice *thisDev = CONTEXT_TO_DEV(MiniportAdapterContext);
|
|
NDIS_STATUS result;
|
|
|
|
DBGOUT(("MiniportReconfigure(0x%x)", (UINT)MiniportAdapterContext));
|
|
|
|
MiniportHalt(MiniportAdapterContext);
|
|
|
|
if (Configure(thisDev, WrapperConfigurationContext)){
|
|
result = NDIS_STATUS_SUCCESS;
|
|
}
|
|
else {
|
|
result = NDIS_STATUS_FAILURE;
|
|
}
|
|
|
|
DBGOUT(("MiniportReconfigure"));
|
|
*OpenErrorStatus = result;
|
|
return result;
|
|
}
|
|
|
|
|
|
/*
|
|
*************************************************************************
|
|
* MiniportReset
|
|
*************************************************************************
|
|
*
|
|
*
|
|
* MiniportReset issues a hardware reset to the network interface card.
|
|
* The miniport driver also resets its software state.
|
|
*
|
|
*
|
|
*/
|
|
// BUGBUG: Arguments are reversed from as documented in April '96 MSDN!
|
|
NDIS_STATUS MiniportReset(PBOOLEAN AddressingReset, NDIS_HANDLE MiniportAdapterContext)
|
|
{
|
|
IrDevice *dev, *thisDev = CONTEXT_TO_DEV(MiniportAdapterContext);
|
|
NDIS_STATUS result = NDIS_STATUS_SUCCESS;
|
|
|
|
DBGOUT(("MiniportReset(0x%x)", (UINT)MiniportAdapterContext));
|
|
|
|
/* BUGBUG: fixed; REMOVE ???
|
|
* Verify that the context is not bogus.
|
|
* I've seen bad contexts getting passed in when the system gets corrupted.
|
|
*/
|
|
for (dev = firstIrDevice; dev && (dev != thisDev); dev = dev->next){ }
|
|
if (!dev){
|
|
DBGERR(("Bad context in MiniportReset"));
|
|
return NDIS_STATUS_FAILURE;
|
|
}
|
|
|
|
DoClose(thisDev);
|
|
CloseDevice(thisDev);
|
|
OpenDevice(thisDev);
|
|
DoOpen(thisDev);
|
|
|
|
*AddressingReset = TRUE;
|
|
|
|
DBGOUT(("MiniportReset done."));
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
*************************************************************************
|
|
* MiniportSend
|
|
*************************************************************************
|
|
*
|
|
*
|
|
* Transmits a packet through the network interface card onto the medium.
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
NDIS_STATUS MiniportSend(
|
|
IN NDIS_HANDLE MiniportAdapterContext,
|
|
IN PNDIS_PACKET Packet,
|
|
IN UINT Flags
|
|
)
|
|
{
|
|
IrDevice *thisDev = CONTEXT_TO_DEV(MiniportAdapterContext);
|
|
NDIS_STATUS result;
|
|
|
|
DBGOUT(("MiniportSend(thisDev=0x%x)", (UINT)thisDev));
|
|
|
|
|
|
|
|
/*
|
|
* Put this packet at the end of our send queue.
|
|
* We use the packet's MiniportReserved field as the
|
|
* 'next' pointer.
|
|
*/
|
|
DBGPKT(("Queueing send packet 0x%x.", (UINT)Packet));
|
|
if (thisDev->firstSendPacket){
|
|
*(PNDIS_PACKET *)thisDev->lastSendPacket->MiniportReserved = Packet;
|
|
}
|
|
else {
|
|
thisDev->firstSendPacket = Packet;
|
|
}
|
|
thisDev->lastSendPacket = Packet;
|
|
*(PNDIS_PACKET *)Packet->MiniportReserved = NULL;
|
|
|
|
|
|
/*
|
|
* Try to send the first queued send packet.
|
|
*/
|
|
if (IsCommReadyForTransmit(thisDev)){
|
|
BOOLEAN isSynchronousSend, xmitSucceeded;
|
|
|
|
isSynchronousSend = (BOOLEAN)(Packet == thisDev->firstSendPacket);
|
|
|
|
xmitSucceeded = PortReadyForWrite(thisDev, isSynchronousSend);
|
|
|
|
if (isSynchronousSend){
|
|
result = xmitSucceeded ? NDIS_STATUS_SUCCESS : NDIS_STATUS_FAILURE;
|
|
}
|
|
else {
|
|
result = NDIS_STATUS_PENDING;
|
|
}
|
|
}
|
|
else {
|
|
result = NDIS_STATUS_PENDING;
|
|
}
|
|
|
|
DBGOUT(("MiniportSend returning %s", DBG_NDIS_RESULT_STR(result)));
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
*************************************************************************
|
|
* MiniportTransferData
|
|
*************************************************************************
|
|
*
|
|
*
|
|
* Copies the contents of the received packet to a specified packet buffer.
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
NDIS_STATUS MiniportTransferData (
|
|
OUT PNDIS_PACKET Packet,
|
|
OUT PUINT BytesTransferred,
|
|
IN NDIS_HANDLE MiniportAdapterContext,
|
|
IN NDIS_HANDLE MiniportReceiveContext,
|
|
IN UINT ByteOffset,
|
|
IN UINT BytesToTransfer
|
|
)
|
|
{
|
|
|
|
DBGERR(("MiniportTransferData - should not get called."));
|
|
|
|
/*
|
|
* We always pass the entire packet up in the indicate-receive call,
|
|
* so we will never get this callback.
|
|
* (We can't do anything but return failure anyway,
|
|
* since NdisMIndicateReceivePacket does not pass up a packet context).
|
|
*/
|
|
*BytesTransferred = 0;
|
|
return NDIS_STATUS_FAILURE;
|
|
}
|
|
|
|
|
|
/*
|
|
*************************************************************************
|
|
* 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);
|
|
UINT rcvBufIndex;
|
|
|
|
DBGOUT(("ReturnPacketHandler(0x%x)", (UINT)MiniportAdapterContext));
|
|
|
|
/*
|
|
* The rcv buffer index is cached in the MiniportReserved field of the packet.
|
|
*/
|
|
rcvBufIndex = *(UINT *)Packet->MiniportReserved;
|
|
|
|
if (rcvBufIndex < NUM_RCV_BUFS){
|
|
if (thisDev->rcvBufs[rcvBufIndex].state == STATE_PENDING){
|
|
PNDIS_BUFFER ndisBuf;
|
|
|
|
DBGPKT(("Reclaimed rcv packet 0x%x.", (UINT)Packet));
|
|
|
|
NdisUnchainBufferAtFront(Packet, &ndisBuf);
|
|
if (ndisBuf){
|
|
NdisFreeBuffer(ndisBuf);
|
|
}
|
|
|
|
thisDev->rcvBufs[rcvBufIndex].state = STATE_FREE;
|
|
}
|
|
else {
|
|
DBGERR(("Packet in ReturnPacketHandler was not PENDING."));
|
|
}
|
|
}
|
|
else {
|
|
DBGERR(("Bad rcvBufIndex (from corrupted MiniportReserved) in ReturnPacketHandler."));
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
*************************************************************************
|
|
* SendPacketsHandler
|
|
*************************************************************************
|
|
*
|
|
* Send an array of packets simultaneously.
|
|
*
|
|
*/
|
|
VOID SendPacketsHandler(NDIS_HANDLE MiniportAdapterContext,
|
|
PPNDIS_PACKET PacketArray,
|
|
UINT NumberofPackets)
|
|
{
|
|
NDIS_STATUS stat;
|
|
UINT i;
|
|
|
|
DBGOUT(("==> SendPacketsHandler(0x%x)", (UINT)MiniportAdapterContext));
|
|
|
|
/*
|
|
* This is a great opportunity to be lazy.
|
|
* Just call MiniportSend with each packet in sequence and
|
|
* set the result in the packet array object.
|
|
*/
|
|
for (i = 0; i < NumberofPackets; i++){
|
|
stat = MiniportSend(MiniportAdapterContext, PacketArray[i], 0);
|
|
NDIS_SET_PACKET_STATUS(PacketArray[i], stat);
|
|
}
|
|
|
|
DBGOUT(("<== SendPacketsHandler"));
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
*************************************************************************
|
|
* AllocateCompleteHandler
|
|
*************************************************************************
|
|
*
|
|
* Indicate completion of an NdisMAllocateSharedMemoryAsync call.
|
|
* We never call that function, so we should never get entered here.
|
|
*
|
|
*/
|
|
VOID AllocateCompleteHandler( NDIS_HANDLE MiniportAdapterContext,
|
|
PVOID VirtualAddress,
|
|
PNDIS_PHYSICAL_ADDRESS PhysicalAddress,
|
|
ULONG Length,
|
|
PVOID Context)
|
|
{
|
|
DBGERR(("AllocateCompleteHandler - should not get called"));
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
*************************************************************************
|
|
* PortReadyForWrite
|
|
*************************************************************************
|
|
*
|
|
* Called when COM port is ready for another write packet.
|
|
* Send the first frame in the send queue.
|
|
*
|
|
* Return TRUE iff send succeeded.
|
|
*
|
|
* NOTE: Do not call inside of interrupt context.
|
|
*
|
|
*/
|
|
BOOLEAN PortReadyForWrite(IrDevice *thisDev, BOOLEAN isSynchronousSend)
|
|
{
|
|
BOOLEAN sendSucceeded = FALSE;
|
|
|
|
DBGOUT(("PortReadyForWrite(dev=0x%x, %xh, %s)",
|
|
(UINT)thisDev,
|
|
thisDev->portInfo.ioBase,
|
|
(CHAR *)(isSynchronousSend ? "sync" : "async")));
|
|
|
|
|
|
if (thisDev->firstSendPacket){
|
|
PNDIS_PACKET packetToSend;
|
|
PNDIS_IRDA_PACKET_INFO packetInfo;
|
|
|
|
/*
|
|
* Dequeue the first send packet and step the send queue.
|
|
* (We use the packet's MiniportReserved field is a 'next' pointer).
|
|
*/
|
|
packetToSend = thisDev->firstSendPacket;
|
|
thisDev->firstSendPacket = *(PNDIS_PACKET *)thisDev->firstSendPacket->MiniportReserved;
|
|
if (!thisDev->firstSendPacket){
|
|
thisDev->lastSendPacket = NULL;
|
|
}
|
|
*(PNDIS_PACKET *)packetToSend->MiniportReserved = NULL;
|
|
|
|
/*
|
|
* Enforce the minimum turnaround time that must transpire
|
|
* after the last receive.
|
|
*/
|
|
packetInfo = GetPacketInfo(packetToSend);
|
|
|
|
if (packetInfo->MinTurnAroundTime){
|
|
/*
|
|
* Don't want to call NdisStallExecution with more than
|
|
* 8 msec or it will cause a task switch.
|
|
* Make a series of calls with 8 msec or less.
|
|
*/
|
|
UINT usecToWait = packetInfo->MinTurnAroundTime;
|
|
do {
|
|
UINT usec = (usecToWait > 8000) ? 8000 : usecToWait;
|
|
NdisStallExecution(usec);
|
|
usecToWait -= usec;
|
|
} while (usecToWait > 0);
|
|
}
|
|
|
|
/*
|
|
* See if this was the last packet before we need to change speed.
|
|
*/
|
|
if (packetToSend == thisDev->lastPacketAtOldSpeed){
|
|
thisDev->lastPacketAtOldSpeed = NULL;
|
|
thisDev->setSpeedAfterCurrentSendPacket = TRUE;
|
|
}
|
|
|
|
/*
|
|
* Send one packet to the COMM port.
|
|
*/
|
|
DBGPKT(("Sending packet 0x%x (0x%x).", thisDev->packetsSent++, (UINT)packetToSend));
|
|
sendSucceeded = DoSend(thisDev, packetToSend);
|
|
|
|
/*
|
|
* If the buffer we just sent was pending
|
|
* (i.e. we returned NDIS_STATUS_PENDING for it in MiniportSend),
|
|
* then hand the sent packet back to the protocol.
|
|
* Otherwise, we're just delivering it synchronously from MiniportSend.
|
|
*/
|
|
if (!isSynchronousSend){
|
|
DBGOUT(("Calling NdisMSendComplete"));
|
|
NdisMSendComplete(thisDev->ndisAdapterHandle, packetToSend,
|
|
(NDIS_STATUS)(sendSucceeded ? NDIS_STATUS_SUCCESS : NDIS_STATUS_FAILURE));
|
|
}
|
|
|
|
}
|
|
|
|
|
|
DBGOUT(("PortReadyForWrite done."));
|
|
|
|
return sendSucceeded;
|
|
}
|
|
|
|
|
|
/*
|
|
*************************************************************************
|
|
* 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;
|
|
NDIS40_MINIPORT_CHARACTERISTICS info;
|
|
|
|
DBGOUT(("==> IRMINI_Entry()"));
|
|
|
|
NdisMInitializeWrapper( (PNDIS_HANDLE)&wrapperHandle,
|
|
DriverObject,
|
|
RegistryPath,
|
|
NULL
|
|
);
|
|
DBGOUT(("Wrapper handle is %xh", (UINT)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 = MiniportReconfigure;
|
|
info.ResetHandler = MiniportReset;
|
|
info.SendHandler = MiniportSend;
|
|
info.SetInformationHandler = MiniportSetInformation;
|
|
info.TransferDataHandler = MiniportTransferData;
|
|
|
|
info.HandleInterruptHandler = MiniportHandleInterrupt;
|
|
info.ISRHandler = MiniportISR;
|
|
info.DisableInterruptHandler = MiniportDisableInterrupt;
|
|
info.EnableInterruptHandler = MiniportEnableInterrupt;
|
|
|
|
|
|
/*
|
|
* New NDIS 4.0 fields
|
|
*/
|
|
info.ReturnPacketHandler = ReturnPacketHandler;
|
|
info.SendPacketsHandler = SendPacketsHandler;
|
|
info.AllocateCompleteHandler = AllocateCompleteHandler;
|
|
|
|
|
|
stat = NdisMRegisterMiniport( wrapperHandle,
|
|
(PNDIS_MINIPORT_CHARACTERISTICS)&info,
|
|
sizeof(NDIS_MINIPORT_CHARACTERISTICS));
|
|
if (stat != NDIS_STATUS_SUCCESS){
|
|
DBGERR(("NdisMRegisterMiniport failed in DriverEntry"));
|
|
result = STATUS_UNSUCCESSFUL;
|
|
goto _entryDone;
|
|
}
|
|
|
|
_entryDone:
|
|
DBGOUT(("<== IRMINI_Entry %s", (PUCHAR)((result == NDIS_STATUS_SUCCESS) ? "succeeded" : "failed")));
|
|
return result;
|
|
|
|
}
|
|
|
|
|