/* * ReactOS AMD PCNet Driver * * Copyright (C) 2003 Vizzini * Copyright (C) 2004 Filip Navara * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * REVISIONS: * 09-Sep-2003 vizzini - Created * 10-Oct-2004 navaraf - Fix receive to work on VMware adapters ( * need to set busmaster bit on PCI). * - Indicate receive completion. * - Implement packet transmitting. * - Don't read slot number from registry and * report itself as NDIS 5.0 miniport. * 11-Oct-2004 navaraf - Fix nasty bugs in halt code path. * 17-Oct-2004 navaraf - Add multicast support. * - Add media state detection support. * - Protect the adapter context with spinlock * and move code talking to card to inside * NdisMSynchronizeWithInterrupt calls where * necessary. * * NOTES: * - this assumes a 32-bit machine */ #include "pcnet.h" #define NDEBUG #include NTSTATUS NTAPI DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath); static VOID NTAPI MiniportHandleInterrupt( IN NDIS_HANDLE MiniportAdapterContext) /* * FUNCTION: Handle an interrupt if told to by MiniportISR * ARGUMENTS: * MiniportAdapterContext: context specified to NdisMSetAttributes * NOTES: * - Called by NDIS at DISPATCH_LEVEL */ { PADAPTER Adapter = (PADAPTER)MiniportAdapterContext; USHORT Data; UINT i = 0; DPRINT("Called\n"); ASSERT_IRQL_EQUAL(DISPATCH_LEVEL); NdisDprAcquireSpinLock(&Adapter->Lock); NdisRawWritePortUshort(Adapter->PortOffset + RAP, CSR0); NdisRawReadPortUshort(Adapter->PortOffset + RDP, &Data); DPRINT("CSR0 is 0x%x\n", Data); while((Data & CSR0_INTR) && i++ < INTERRUPT_LIMIT) { /* Clear interrupt flags early to avoid race conditions. */ NdisRawWritePortUshort(Adapter->PortOffset + RDP, Data); if(Data & CSR0_ERR) { DPRINT("error: %x\n", Data & (CSR0_MERR|CSR0_BABL|CSR0_CERR|CSR0_MISS)); if (Data & CSR0_CERR) Adapter->Statistics.XmtCollisions++; } if(Data & CSR0_IDON) { DPRINT("IDON\n"); } if(Data & CSR0_RINT) { BOOLEAN IndicatedData = FALSE; DPRINT("receive interrupt\n"); while(1) { PRECEIVE_DESCRIPTOR Descriptor = Adapter->ReceiveDescriptorRingVirt + Adapter->CurrentReceiveDescriptorIndex; PCHAR Buffer; ULONG ByteCount; if(Descriptor->FLAGS & RD_OWN) { DPRINT("no more receive descriptors to process\n"); break; } if(Descriptor->FLAGS & RD_ERR) { DPRINT("receive descriptor error: 0x%x\n", Descriptor->FLAGS); if (Descriptor->FLAGS & RD_BUFF) Adapter->Statistics.RcvBufferErrors++; if (Descriptor->FLAGS & RD_CRC) Adapter->Statistics.RcvCrcErrors++; if (Descriptor->FLAGS & RD_OFLO) Adapter->Statistics.RcvOverflowErrors++; if (Descriptor->FLAGS & RD_FRAM) Adapter->Statistics.RcvFramingErrors++; break; } if(!((Descriptor->FLAGS & RD_STP) && (Descriptor->FLAGS & RD_ENP))) { DPRINT("receive descriptor not start&end: 0x%x\n", Descriptor->FLAGS); break; } Buffer = Adapter->ReceiveBufferPtrVirt + Adapter->CurrentReceiveDescriptorIndex * BUFFER_SIZE; ByteCount = Descriptor->MCNT & 0xfff; DPRINT("Indicating a %d-byte packet (index %d)\n", ByteCount, Adapter->CurrentReceiveDescriptorIndex); NdisMEthIndicateReceive(Adapter->MiniportAdapterHandle, 0, Buffer, 14, Buffer+14, ByteCount-14, ByteCount-14); IndicatedData = TRUE; RtlZeroMemory(Descriptor, sizeof(RECEIVE_DESCRIPTOR)); Descriptor->RBADR = Adapter->ReceiveBufferPtrPhys.QuadPart + (Adapter->CurrentReceiveDescriptorIndex * BUFFER_SIZE); Descriptor->BCNT = (-BUFFER_SIZE) | 0xf000; Descriptor->FLAGS |= RD_OWN; Adapter->CurrentReceiveDescriptorIndex++; Adapter->CurrentReceiveDescriptorIndex %= Adapter->BufferCount; Adapter->Statistics.RcvGoodFrames++; } if (IndicatedData) NdisMEthIndicateReceiveComplete(Adapter->MiniportAdapterHandle); } if(Data & CSR0_TINT) { PTRANSMIT_DESCRIPTOR Descriptor; DPRINT("transmit interrupt\n"); while (Adapter->CurrentTransmitStartIndex != Adapter->CurrentTransmitEndIndex) { Descriptor = Adapter->TransmitDescriptorRingVirt + Adapter->CurrentTransmitStartIndex; DPRINT("buffer %d flags %x flags2 %x\n", Adapter->CurrentTransmitStartIndex, Descriptor->FLAGS, Descriptor->FLAGS2); if (Descriptor->FLAGS & TD1_OWN) { DPRINT("non-TXed buffer\n"); break; } if (Descriptor->FLAGS & TD1_STP) { if (Descriptor->FLAGS & TD1_ONE) Adapter->Statistics.XmtOneRetry++; else if (Descriptor->FLAGS & TD1_MORE) Adapter->Statistics.XmtMoreThanOneRetry++; } if (Descriptor->FLAGS & TD1_ERR) { DPRINT("major error: %x\n", Descriptor->FLAGS2); if (Descriptor->FLAGS2 & TD2_RTRY) Adapter->Statistics.XmtRetryErrors++; if (Descriptor->FLAGS2 & TD2_LCAR) Adapter->Statistics.XmtLossesOfCarrier++; if (Descriptor->FLAGS2 & TD2_LCOL) Adapter->Statistics.XmtLateCollisions++; if (Descriptor->FLAGS2 & TD2_EXDEF) Adapter->Statistics.XmtExcessiveDeferrals++; if (Descriptor->FLAGS2 & TD2_UFLO) Adapter->Statistics.XmtBufferUnderflows++; if (Descriptor->FLAGS2 & TD2_BUFF) Adapter->Statistics.XmtBufferErrors++; break; } Adapter->CurrentTransmitStartIndex++; Adapter->CurrentTransmitStartIndex %= Adapter->BufferCount; Adapter->Statistics.XmtGoodFrames++; } NdisMSendResourcesAvailable(Adapter->MiniportAdapterHandle); } if(Data & ~(CSR0_ERR | CSR0_IDON | CSR0_RINT | CSR0_TINT)) { DPRINT("UNHANDLED INTERRUPT CSR0 0x%x\n", Data); } NdisRawReadPortUshort(Adapter->PortOffset + RDP, &Data); } /* re-enable interrupts */ NdisRawWritePortUshort(Adapter->PortOffset + RDP, CSR0_IENA); NdisRawReadPortUshort(Adapter->PortOffset + RDP, &Data); DPRINT("CSR0 is now 0x%x\n", Data); NdisDprReleaseSpinLock(&Adapter->Lock); } static NDIS_STATUS MiQueryCard( IN PADAPTER Adapter) /* * FUNCTION: Detect the PCNET NIC in the configured slot and query its I/O address and interrupt vector * ARGUMENTS: * MiniportAdapterContext: context supplied to NdisMSetAttributes * RETURNS: * NDIS_STATUS_FAILURE on a general error * NDIS_STATUS_ADAPTER_NOT_FOUND on not finding the adapter * NDIS_STATUS_SUCCESS on succes */ { ULONG buf32 = 0; UCHAR buf8 = 0; NDIS_STATUS Status; /* Detect the card in the configured slot */ Status = NdisReadPciSlotInformation(Adapter->MiniportAdapterHandle, 0, PCI_PCIID, &buf32, 4); if(Status != 4) { Status = NDIS_STATUS_FAILURE; DPRINT1("NdisReadPciSlotInformation failed\n"); return Status; } if(buf32 != PCI_ID) { Status = NDIS_STATUS_ADAPTER_NOT_FOUND; DPRINT1("card in slot isn't our: 0x%x\n", 0, buf32); return Status; } /* set busmaster and io space enable bits */ buf32 = PCI_BMEN | PCI_IOEN; NdisWritePciSlotInformation(Adapter->MiniportAdapterHandle, 0, PCI_COMMAND, &buf32, 4); /* get IO base physical address */ buf32 = 0; Status = NdisReadPciSlotInformation(Adapter->MiniportAdapterHandle, 0, PCI_IOBAR, &buf32, 4); if(Status != 4) { Status = NDIS_STATUS_FAILURE; DPRINT1("NdisReadPciSlotInformation failed\n"); return Status; } if(!buf32) { DPRINT1("No base i/o address set\n"); return NDIS_STATUS_FAILURE; } buf32 &= ~1; /* even up address - comes out odd for some reason */ DPRINT("detected io address 0x%x\n", buf32); Adapter->IoBaseAddress = buf32; /* get interrupt vector */ Status = NdisReadPciSlotInformation(Adapter->MiniportAdapterHandle, 0, PCI_ILR, &buf8, 1); if(Status != 1) { Status = NDIS_STATUS_FAILURE; DPRINT1("NdisReadPciSlotInformation failed\n"); return Status; } DPRINT("interrupt: 0x%x\n", buf8); Adapter->InterruptVector = buf8; return NDIS_STATUS_SUCCESS; } static VOID MiFreeSharedMemory( PADAPTER Adapter) /* * FUNCTION: Free all allocated shared memory * ARGUMENTS: * Adapter: pointer to the miniport's adapter struct */ { NDIS_PHYSICAL_ADDRESS PhysicalAddress; if(Adapter->InitializationBlockVirt) { PhysicalAddress = Adapter->InitializationBlockPhys; NdisMFreeSharedMemory(Adapter->MiniportAdapterHandle, Adapter->InitializationBlockLength, FALSE, Adapter->InitializationBlockVirt, PhysicalAddress); Adapter->InitializationBlockVirt = NULL; } if(Adapter->TransmitDescriptorRingVirt) { PhysicalAddress = Adapter->TransmitDescriptorRingPhys; NdisMFreeSharedMemory(Adapter->MiniportAdapterHandle, Adapter->TransmitDescriptorRingLength, FALSE, Adapter->TransmitDescriptorRingVirt, PhysicalAddress); Adapter->TransmitDescriptorRingVirt = NULL; } if(Adapter->ReceiveDescriptorRingVirt) { PhysicalAddress = Adapter->ReceiveDescriptorRingPhys; NdisMFreeSharedMemory(Adapter->MiniportAdapterHandle, Adapter->ReceiveDescriptorRingLength, FALSE, Adapter->ReceiveDescriptorRingVirt, PhysicalAddress); Adapter->ReceiveDescriptorRingVirt = NULL; } if(Adapter->TransmitBufferPtrVirt) { PhysicalAddress = Adapter->TransmitBufferPtrPhys; NdisMFreeSharedMemory(Adapter->MiniportAdapterHandle, Adapter->TransmitBufferLength, TRUE, Adapter->TransmitBufferPtrVirt, PhysicalAddress); Adapter->TransmitBufferPtrVirt = NULL; } if(Adapter->ReceiveBufferPtrVirt) { PhysicalAddress = Adapter->ReceiveBufferPtrPhys; NdisMFreeSharedMemory(Adapter->MiniportAdapterHandle, Adapter->ReceiveBufferLength, TRUE, Adapter->ReceiveBufferPtrVirt, PhysicalAddress); Adapter->ReceiveBufferPtrVirt = NULL; } } static NDIS_STATUS MiAllocateSharedMemory( PADAPTER Adapter) /* * FUNCTION: Allocate all shared memory used by the miniport * ARGUMENTS: * Adapter: Pointer to the miniport's adapter object * RETURNS: * NDIS_STATUS_RESOURCES on insufficient memory * NDIS_STATUS_SUCCESS on success */ { PTRANSMIT_DESCRIPTOR TransmitDescriptor; PRECEIVE_DESCRIPTOR ReceiveDescriptor; NDIS_PHYSICAL_ADDRESS PhysicalAddress; ULONG i; ULONG BufferCount = NUMBER_OF_BUFFERS; ULONG LogBufferCount = LOG_NUMBER_OF_BUFFERS; while (BufferCount != 0) { /* allocate the initialization block (we have this in the loop so we can use MiFreeSharedMemory) */ Adapter->InitializationBlockLength = sizeof(INITIALIZATION_BLOCK); NdisMAllocateSharedMemory(Adapter->MiniportAdapterHandle, Adapter->InitializationBlockLength, FALSE, (PVOID *)&Adapter->InitializationBlockVirt, &PhysicalAddress); if(!Adapter->InitializationBlockVirt) { /* Buffer backoff won't help us here */ DPRINT1("insufficient resources\n"); return NDIS_STATUS_RESOURCES; } if (((ULONG_PTR)Adapter->InitializationBlockVirt & 0x00000003) != 0) { DPRINT1("address 0x%x not dword-aligned\n", Adapter->InitializationBlockVirt); return NDIS_STATUS_RESOURCES; } Adapter->InitializationBlockPhys = PhysicalAddress; /* allocate the transport descriptor ring */ Adapter->TransmitDescriptorRingLength = sizeof(TRANSMIT_DESCRIPTOR) * BufferCount; NdisMAllocateSharedMemory(Adapter->MiniportAdapterHandle, Adapter->TransmitDescriptorRingLength, FALSE, (PVOID *)&Adapter->TransmitDescriptorRingVirt, &PhysicalAddress); if (!Adapter->TransmitDescriptorRingVirt) { DPRINT1("Backing off buffer count by %d buffers due to allocation failure\n", (BufferCount >> 1)); BufferCount = BufferCount >> 1; LogBufferCount--; MiFreeSharedMemory(Adapter); continue; } if (((ULONG_PTR)Adapter->TransmitDescriptorRingVirt & 0x00000003) != 0) { DPRINT1("address 0x%x not dword-aligned\n", Adapter->TransmitDescriptorRingVirt); return NDIS_STATUS_RESOURCES; } Adapter->TransmitDescriptorRingPhys = PhysicalAddress; RtlZeroMemory(Adapter->TransmitDescriptorRingVirt, sizeof(TRANSMIT_DESCRIPTOR) * BufferCount); /* allocate the receive descriptor ring */ Adapter->ReceiveDescriptorRingLength = sizeof(RECEIVE_DESCRIPTOR) * BufferCount; NdisMAllocateSharedMemory(Adapter->MiniportAdapterHandle, Adapter->ReceiveDescriptorRingLength, FALSE, (PVOID *)&Adapter->ReceiveDescriptorRingVirt, &PhysicalAddress); if (!Adapter->ReceiveDescriptorRingVirt) { DPRINT1("Backing off buffer count by %d buffers due to allocation failure\n", (BufferCount >> 1)); BufferCount = BufferCount >> 1; LogBufferCount--; MiFreeSharedMemory(Adapter); continue; } if (((ULONG_PTR)Adapter->ReceiveDescriptorRingVirt & 0x00000003) != 0) { DPRINT1("address 0x%x not dword-aligned\n", Adapter->ReceiveDescriptorRingVirt); return NDIS_STATUS_RESOURCES; } Adapter->ReceiveDescriptorRingPhys = PhysicalAddress; RtlZeroMemory(Adapter->ReceiveDescriptorRingVirt, sizeof(RECEIVE_DESCRIPTOR) * BufferCount); /* allocate transmit buffers */ Adapter->TransmitBufferLength = BUFFER_SIZE * BufferCount; NdisMAllocateSharedMemory(Adapter->MiniportAdapterHandle, Adapter->TransmitBufferLength, TRUE, (PVOID *)&Adapter->TransmitBufferPtrVirt, &PhysicalAddress); if(!Adapter->TransmitBufferPtrVirt) { DPRINT1("Backing off buffer count by %d buffers due to allocation failure\n", (BufferCount >> 1)); BufferCount = BufferCount >> 1; LogBufferCount--; MiFreeSharedMemory(Adapter); continue; } if(((ULONG_PTR)Adapter->TransmitBufferPtrVirt & 0x00000003) != 0) { DPRINT1("address 0x%x not dword-aligned\n", Adapter->TransmitBufferPtrVirt); return NDIS_STATUS_RESOURCES; } Adapter->TransmitBufferPtrPhys = PhysicalAddress; RtlZeroMemory(Adapter->TransmitBufferPtrVirt, BUFFER_SIZE * BufferCount); /* allocate receive buffers */ Adapter->ReceiveBufferLength = BUFFER_SIZE * BufferCount; NdisMAllocateSharedMemory(Adapter->MiniportAdapterHandle, Adapter->ReceiveBufferLength, TRUE, (PVOID *)&Adapter->ReceiveBufferPtrVirt, &PhysicalAddress); if(!Adapter->ReceiveBufferPtrVirt) { DPRINT1("Backing off buffer count by %d buffers due to allocation failure\n", (BufferCount >> 1)); BufferCount = BufferCount >> 1; LogBufferCount--; MiFreeSharedMemory(Adapter); continue; } if (((ULONG_PTR)Adapter->ReceiveBufferPtrVirt & 0x00000003) != 0) { DPRINT1("address 0x%x not dword-aligned\n", Adapter->ReceiveBufferPtrVirt); return NDIS_STATUS_RESOURCES; } Adapter->ReceiveBufferPtrPhys = PhysicalAddress; RtlZeroMemory(Adapter->ReceiveBufferPtrVirt, BUFFER_SIZE * BufferCount); break; } if (!BufferCount) { DPRINT1("Failed to allocate adapter buffers\n"); return NDIS_STATUS_RESOURCES; } Adapter->BufferCount = BufferCount; Adapter->LogBufferCount = LogBufferCount; /* initialize tx descriptors */ TransmitDescriptor = Adapter->TransmitDescriptorRingVirt; for(i = 0; i < BufferCount; i++) { (TransmitDescriptor+i)->TBADR = Adapter->TransmitBufferPtrPhys.QuadPart + i * BUFFER_SIZE; (TransmitDescriptor+i)->BCNT = 0xf000 | -BUFFER_SIZE; /* 2's compliment + set top 4 bits */ (TransmitDescriptor+i)->FLAGS = TD1_STP | TD1_ENP; } DPRINT("transmit ring initialized\n"); /* initialize rx */ ReceiveDescriptor = Adapter->ReceiveDescriptorRingVirt; for(i = 0; i < BufferCount; i++) { (ReceiveDescriptor+i)->RBADR = Adapter->ReceiveBufferPtrPhys.QuadPart + i * BUFFER_SIZE; (ReceiveDescriptor+i)->BCNT = 0xf000 | -BUFFER_SIZE; /* 2's compliment + set top 4 bits */ (ReceiveDescriptor+i)->FLAGS = RD_OWN; } DPRINT("receive ring initialized\n"); return NDIS_STATUS_SUCCESS; } static VOID MiPrepareInitializationBlock( PADAPTER Adapter) /* * FUNCTION: Initialize the initialization block * ARGUMENTS: * Adapter: pointer to the miniport's adapter object */ { ULONG i = 0; RtlZeroMemory(Adapter->InitializationBlockVirt, sizeof(INITIALIZATION_BLOCK)); /* read burned-in address from card */ for(i = 0; i < 6; i++) NdisRawReadPortUchar(Adapter->PortOffset + i, Adapter->InitializationBlockVirt->PADR + i); DPRINT("MAC address: %02x-%02x-%02x-%02x-%02x-%02x\n", Adapter->InitializationBlockVirt->PADR[0], Adapter->InitializationBlockVirt->PADR[1], Adapter->InitializationBlockVirt->PADR[2], Adapter->InitializationBlockVirt->PADR[3], Adapter->InitializationBlockVirt->PADR[4], Adapter->InitializationBlockVirt->PADR[5]); /* set up receive ring */ DPRINT("Receive ring physical address: 0x%x\n", Adapter->ReceiveDescriptorRingPhys); Adapter->InitializationBlockVirt->RDRA = Adapter->ReceiveDescriptorRingPhys.QuadPart; Adapter->InitializationBlockVirt->RLEN = (Adapter->LogBufferCount << 4) & 0xf0; /* set up transmit ring */ DPRINT("Transmit ring physical address: 0x%x\n", Adapter->TransmitDescriptorRingPhys); Adapter->InitializationBlockVirt->TDRA = Adapter->TransmitDescriptorRingPhys.QuadPart; Adapter->InitializationBlockVirt->TLEN = (Adapter->LogBufferCount << 4) & 0xf0; } static BOOLEAN NTAPI MiSyncStop( IN PVOID SynchronizeContext) /* * FUNCTION: Stop the adapter * ARGUMENTS: * SynchronizeContext: Adapter context */ { PADAPTER Adapter = (PADAPTER)SynchronizeContext; NdisRawWritePortUshort(Adapter->PortOffset + RAP, CSR0); NdisRawWritePortUshort(Adapter->PortOffset + RDP, CSR0_STOP); return TRUE; } static VOID NTAPI MiniportHalt( IN NDIS_HANDLE MiniportAdapterContext) /* * FUNCTION: Stop the adapter and release any per-adapter resources * ARGUMENTS: * MiniportAdapterContext: context specified to NdisMSetAttributes * NOTES: * - Called by NDIS at PASSIVE_LEVEL */ { PADAPTER Adapter = (PADAPTER)MiniportAdapterContext; BOOLEAN TimerCancelled; DPRINT("Called\n"); ASSERT(Adapter); /* stop the media detection timer */ NdisMCancelTimer(&Adapter->MediaDetectionTimer, &TimerCancelled); /* stop the chip */ NdisMSynchronizeWithInterrupt(&Adapter->InterruptObject, MiSyncStop, Adapter); /* deregister the interrupt */ NdisMDeregisterInterrupt(&Adapter->InterruptObject); /* deregister i/o port range */ NdisMDeregisterIoPortRange(Adapter->MiniportAdapterHandle, Adapter->IoBaseAddress, NUMBER_OF_PORTS, (PVOID)Adapter->PortOffset); /* deregister the shutdown routine */ NdisMDeregisterAdapterShutdownHandler(Adapter->MiniportAdapterHandle); /* free shared memory */ MiFreeSharedMemory(Adapter); /* free map registers */ NdisMFreeMapRegisters(Adapter->MiniportAdapterHandle); /* free the lock */ NdisFreeSpinLock(&Adapter->Lock); /* free the adapter */ NdisFreeMemory(Adapter, 0, 0); } static BOOLEAN NTAPI MiSyncMediaDetection( IN PVOID SynchronizeContext) /* * FUNCTION: Stop the adapter * ARGUMENTS: * SynchronizeContext: Adapter context */ { PADAPTER Adapter = (PADAPTER)SynchronizeContext; NDIS_MEDIA_STATE MediaState = MiGetMediaState(Adapter); UINT MediaSpeed = MiGetMediaSpeed(Adapter); BOOLEAN FullDuplex = MiGetMediaDuplex(Adapter); DPRINT("Called\n"); DPRINT("MediaState: %d\n", MediaState); if (MediaState != Adapter->MediaState || MediaSpeed != Adapter->MediaSpeed || FullDuplex != Adapter->FullDuplex) { Adapter->MediaState = MediaState; Adapter->MediaSpeed = MediaSpeed; Adapter->FullDuplex = FullDuplex; return TRUE; } return FALSE; } static VOID NTAPI MiniportMediaDetectionTimer( IN PVOID SystemSpecific1, IN PVOID FunctionContext, IN PVOID SystemSpecific2, IN PVOID SystemSpecific3) /* * FUNCTION: Periodically query media state * ARGUMENTS: * FunctionContext: Adapter context * NOTES: * - Called by NDIS at DISPATCH_LEVEL */ { PADAPTER Adapter = (PADAPTER)FunctionContext; ASSERT_IRQL_EQUAL(DISPATCH_LEVEL); if (NdisMSynchronizeWithInterrupt(&Adapter->InterruptObject, MiSyncMediaDetection, FunctionContext)) { NdisMIndicateStatus(Adapter->MiniportAdapterHandle, Adapter->MediaState == NdisMediaStateConnected ? NDIS_STATUS_MEDIA_CONNECT : NDIS_STATUS_MEDIA_DISCONNECT, (PVOID)0, 0); NdisMIndicateStatusComplete(Adapter->MiniportAdapterHandle); } } static VOID MiInitChip( PADAPTER Adapter) /* * FUNCTION: Initialize and start the PCNET chip * ARGUMENTS: * Adapter: pointer to the miniport's adapter struct * NOTES: * - should be coded to detect failure and return an error * - the vmware virtual lance chip doesn't support 32-bit i/o so don't do that. */ { USHORT Data = 0; DPRINT("Called\n"); /* * first reset the chip - 32-bit reset followed by 16-bit reset. if it's in 32-bit mode, it'll reset * twice. if it's in 16-bit mode, the first read will be nonsense and the second will be a reset. the * card is reset by reading from the reset register. on reset it's in 16-bit i/o mode. */ NdisRawReadPortUshort(Adapter->PortOffset + RESET32, &Data); NdisRawReadPortUshort(Adapter->PortOffset + RESET16, &Data); /* stop the chip */ NdisRawWritePortUshort(Adapter->PortOffset + RAP, CSR0); NdisRawWritePortUshort(Adapter->PortOffset + RDP, CSR0_STOP); /* pause for 1ms so the chip will have time to reset */ NdisStallExecution(1); DPRINT("chip stopped\n"); /* set the software style to 2 (32 bits) */ NdisRawWritePortUshort(Adapter->PortOffset + RAP, CSR58); NdisRawReadPortUshort(Adapter->PortOffset + RDP, &Data); Data |= SW_STYLE_2; NdisRawWritePortUshort(Adapter->PortOffset + RDP, Data); /* set up csr4: auto transmit pad, disable polling, disable transmit interrupt, dmaplus */ NdisRawWritePortUshort(Adapter->PortOffset + RAP, CSR4); NdisRawReadPortUshort(Adapter->PortOffset + RDP, &Data); Data |= CSR4_APAD_XMT | /* CSR4_DPOLL |*/ CSR4_TXSTRTM | CSR4_DMAPLUS; NdisRawWritePortUshort(Adapter->PortOffset + RDP, Data); /* set up bcr18: burst read/write enable */ NdisRawWritePortUshort(Adapter->PortOffset + RAP, BCR18); NdisRawReadPortUshort(Adapter->PortOffset + BDP, &Data); Data |= BCR18_BREADE | BCR18_BWRITE ; NdisRawWritePortUshort(Adapter->PortOffset + BDP, Data); /* set up csr1 and csr2 with init block */ NdisRawWritePortUshort(Adapter->PortOffset + RAP, CSR1); NdisRawWritePortUshort(Adapter->PortOffset + RDP, (USHORT)(Adapter->InitializationBlockPhys.LowPart & 0xffff)); NdisRawWritePortUshort(Adapter->PortOffset + RAP, CSR2); NdisRawWritePortUshort(Adapter->PortOffset + RDP, (USHORT)(Adapter->InitializationBlockPhys.LowPart >> 16) & 0xffff); DPRINT("programmed with init block\n"); /* Set mode to 0 */ Data = 0; NdisRawWritePortUshort(Adapter->PortOffset + RAP, CSR15); NdisRawWritePortUshort(Adapter->PortOffset + RDP, Data); /* load init block and start the card */ NdisRawWritePortUshort(Adapter->PortOffset + RAP, CSR0); NdisRawWritePortUshort(Adapter->PortOffset + RDP, CSR0_STRT|CSR0_INIT|CSR0_IENA); /* Allow LED programming */ NdisRawWritePortUshort(Adapter->PortOffset + RAP, BCR2); NdisRawWritePortUshort(Adapter->PortOffset + BDP, BCR2_LEDPE); /* LED0 is configured for link status (on = up, off = down) */ NdisRawWritePortUshort(Adapter->PortOffset + RAP, BCR4); NdisRawWritePortUshort(Adapter->PortOffset + BDP, BCR4_LNKSTE | BCR4_PSE); /* LED1 is configured for link duplex (on = full, off = half) */ NdisRawWritePortUshort(Adapter->PortOffset + RAP, BCR5); NdisRawWritePortUshort(Adapter->PortOffset + BDP, BCR5_FDLSE | BCR5_PSE); /* LED2 is configured for link speed (on = 100M, off = 10M) */ NdisRawWritePortUshort(Adapter->PortOffset + RAP, BCR6); NdisRawWritePortUshort(Adapter->PortOffset + BDP, BCR6_E100 | BCR6_PSE); /* LED3 is configured for trasmit/receive activity */ NdisRawWritePortUshort(Adapter->PortOffset + RAP, BCR7); NdisRawWritePortUshort(Adapter->PortOffset + BDP, BCR7_XMTE | BCR7_RCVE | BCR7_PSE); Adapter->MediaState = MiGetMediaState(Adapter); Adapter->FullDuplex = MiGetMediaDuplex(Adapter); Adapter->MediaSpeed = MiGetMediaSpeed(Adapter); DPRINT("card started\n"); Adapter->Flags &= ~RESET_IN_PROGRESS; } #if DBG static BOOLEAN MiTestCard( PADAPTER Adapter) /* * FUNCTION: Test the NIC * ARGUMENTS: * Adapter: pointer to the miniport's adapter struct * RETURNS: * TRUE if the test succeeds * FALSE otherwise * NOTES: * - this is where to add diagnostics. This is called * at the very end of initialization. */ { int i = 0; UCHAR address[6]; USHORT Data = 0; /* see if we can read/write now */ NdisRawWritePortUshort(Adapter->PortOffset + RAP, CSR0); NdisRawReadPortUshort(Adapter->PortOffset + RDP, &Data); DPRINT("Port 0x%x RAP 0x%x CSR0 0x%x RDP 0x%x, Interrupt status register is 0x%x\n", Adapter->PortOffset, RAP, CSR0, RDP, Data); /* read the BIA */ for(i = 0; i < 6; i++) NdisRawReadPortUchar(Adapter->PortOffset + i, &address[i]); DPRINT("burned-in address: %02x:%02x:%02x:%02x:%02x:%02x\n", address[0], address[1], address[2], address[3], address[4], address[5]); /* Read status flags from CSR0 */ NdisRawWritePortUshort(Adapter->PortOffset + RAP, CSR0); NdisRawReadPortUshort(Adapter->PortOffset + RDP, &Data); DPRINT("CSR0: 0x%x\n", Data); /* Read status flags from CSR3 */ NdisRawWritePortUshort(Adapter->PortOffset + RAP, CSR3); NdisRawReadPortUshort(Adapter->PortOffset + RDP, &Data); DPRINT("CSR3: 0x%x\n", Data); /* Read status flags from CSR4 */ NdisRawWritePortUshort(Adapter->PortOffset + RAP, CSR4); NdisRawReadPortUshort(Adapter->PortOffset + RDP, &Data); DPRINT("CSR4: 0x%x\n", Data); /* Read status flags from CSR5 */ NdisRawWritePortUshort(Adapter->PortOffset + RAP, CSR5); NdisRawReadPortUshort(Adapter->PortOffset + RDP, &Data); DPRINT("CSR5: 0x%x\n", Data); /* Read status flags from CSR6 */ NdisRawWritePortUshort(Adapter->PortOffset + RAP, CSR6); NdisRawReadPortUshort(Adapter->PortOffset + RDP, &Data); DPRINT("CSR6: 0x%x\n", Data); /* Read status flags from BCR4 */ NdisRawWritePortUshort(Adapter->PortOffset + RAP, BCR4); NdisRawReadPortUshort(Adapter->PortOffset + BDP, &Data); DPRINT("BCR4: 0x%x\n", Data); return TRUE; } #endif VOID NTAPI MiniportShutdown( PVOID Context ) { PADAPTER Adapter = Context; DPRINT("Stopping the chip\n"); NdisRawWritePortUshort(Adapter->PortOffset + RAP, CSR0); NdisRawWritePortUshort(Adapter->PortOffset + RDP, CSR0_STOP); } static NDIS_STATUS NTAPI MiniportInitialize( OUT PNDIS_STATUS OpenErrorStatus, OUT PUINT SelectedMediumIndex, IN PNDIS_MEDIUM MediumArray, IN UINT MediumArraySize, IN NDIS_HANDLE MiniportAdapterHandle, IN NDIS_HANDLE WrapperConfigurationContext) /* * FUNCTION: Initialize a new miniport * ARGUMENTS: * OpenErrorStatus: pointer to a var to return status info in * SelectedMediumIndex: index of the selected medium (will be NdisMedium802_3) * MediumArray: array of media that we can pick from * MediumArraySize: size of MediumArray * MiniportAdapterHandle: NDIS-assigned handle for this miniport instance * WrapperConfigurationContext: temporary NDIS-assigned handle for passing * to configuration APIs * RETURNS: * NDIS_STATUS_SUCCESS on success * NDIS_STATUS_FAILURE on general failure * NDIS_STATUS_UNSUPPORTED_MEDIA on not finding 802_3 in the MediaArray * NDIS_STATUS_RESOURCES on insufficient system resources * NDIS_STATUS_ADAPTER_NOT_FOUND on not finding the adapter * NOTES: * - Called by NDIS at PASSIVE_LEVEL, once per detected card * - Will int 3 on failure of MiTestCard if DBG=1 */ { UINT i = 0; PADAPTER Adapter = 0; NDIS_STATUS Status = NDIS_STATUS_FAILURE; BOOLEAN InterruptRegistered = FALSE, MapRegistersAllocated = FALSE; NDIS_HANDLE ConfigurationHandle; UINT *RegNetworkAddress = 0; UINT RegNetworkAddressLength = 0; ASSERT_IRQL_EQUAL(PASSIVE_LEVEL); /* Pick a medium */ for(i = 0; i < MediumArraySize; i++) if(MediumArray[i] == NdisMedium802_3) break; if(i == MediumArraySize) { Status = NDIS_STATUS_UNSUPPORTED_MEDIA; DPRINT1("unsupported media\n"); *OpenErrorStatus = Status; return Status; } *SelectedMediumIndex = i; /* allocate our adapter struct */ Status = NdisAllocateMemoryWithTag((PVOID *)&Adapter, sizeof(ADAPTER), PCNET_TAG); if(Status != NDIS_STATUS_SUCCESS) { Status = NDIS_STATUS_RESOURCES; DPRINT1("Insufficient resources\n"); *OpenErrorStatus = Status; return Status; } RtlZeroMemory(Adapter, sizeof(ADAPTER)); Adapter->MiniportAdapterHandle = MiniportAdapterHandle; /* register our adapter structwith ndis */ NdisMSetAttributesEx(Adapter->MiniportAdapterHandle, Adapter, 0, NDIS_ATTRIBUTE_BUS_MASTER, NdisInterfacePci); do { /* Card-specific detection and setup */ Status = MiQueryCard(Adapter); if(Status != NDIS_STATUS_SUCCESS) { DPRINT1("MiQueryCard failed\n"); Status = NDIS_STATUS_ADAPTER_NOT_FOUND; break; } /* register an IO port range */ Status = NdisMRegisterIoPortRange((PVOID*)&Adapter->PortOffset, Adapter->MiniportAdapterHandle, (UINT)Adapter->IoBaseAddress, NUMBER_OF_PORTS); if(Status != NDIS_STATUS_SUCCESS) { DPRINT1("NdisMRegisterIoPortRange failed: 0x%x\n", Status); break; } /* Allocate map registers */ Status = NdisMAllocateMapRegisters(Adapter->MiniportAdapterHandle, 0, NDIS_DMA_32BITS, 8, BUFFER_SIZE); if(Status != NDIS_STATUS_SUCCESS) { DPRINT1("NdisMAllocateMapRegisters failed: 0x%x\n", Status); break; } MapRegistersAllocated = TRUE; /* set up the interrupt */ Status = NdisMRegisterInterrupt(&Adapter->InterruptObject, Adapter->MiniportAdapterHandle, Adapter->InterruptVector, Adapter->InterruptVector, TRUE, TRUE, NdisInterruptLevelSensitive); if(Status != NDIS_STATUS_SUCCESS) { DPRINT1("NdisMRegisterInterrupt failed: 0x%x\n", Status); break; } InterruptRegistered = TRUE; /* Allocate and initialize shared data structures */ Status = MiAllocateSharedMemory(Adapter); if(Status != NDIS_STATUS_SUCCESS) { Status = NDIS_STATUS_RESOURCES; DPRINT1("MiAllocateSharedMemory failed\n", Status); break; } /* set up the initialization block */ MiPrepareInitializationBlock(Adapter); /* see if someone set a network address manually */ NdisOpenConfiguration(&Status, &ConfigurationHandle, WrapperConfigurationContext); if (Status == NDIS_STATUS_SUCCESS) { NdisReadNetworkAddress(&Status, (PVOID *)&RegNetworkAddress, &RegNetworkAddressLength, ConfigurationHandle); if(Status == NDIS_STATUS_SUCCESS && RegNetworkAddressLength == 6) { int i; DPRINT("NdisReadNetworkAddress returned successfully, address %x:%x:%x:%x:%x:%x\n", RegNetworkAddress[0], RegNetworkAddress[1], RegNetworkAddress[2], RegNetworkAddress[3], RegNetworkAddress[4], RegNetworkAddress[5]); for(i = 0; i < 6; i++) Adapter->InitializationBlockVirt->PADR[i] = RegNetworkAddress[i]; } NdisCloseConfiguration(ConfigurationHandle); } DPRINT("Interrupt registered successfully\n"); /* Initialize and start the chip */ MiInitChip(Adapter); NdisAllocateSpinLock(&Adapter->Lock); Status = NDIS_STATUS_SUCCESS; } while(0); if(Status != NDIS_STATUS_SUCCESS && Adapter) { DPRINT("Error; freeing stuff\n"); MiFreeSharedMemory(Adapter); if(MapRegistersAllocated) NdisMFreeMapRegisters(Adapter->MiniportAdapterHandle); if(Adapter->PortOffset) NdisMDeregisterIoPortRange(Adapter->MiniportAdapterHandle, Adapter->IoBaseAddress, NUMBER_OF_PORTS, (PVOID)Adapter->PortOffset); if(InterruptRegistered) NdisMDeregisterInterrupt(&Adapter->InterruptObject); NdisFreeMemory(Adapter, 0, 0); } if(Status == NDIS_STATUS_SUCCESS) { NdisMInitializeTimer(&Adapter->MediaDetectionTimer, Adapter->MiniportAdapterHandle, MiniportMediaDetectionTimer, Adapter); NdisMSetPeriodicTimer(&Adapter->MediaDetectionTimer, MEDIA_DETECTION_INTERVAL); NdisMRegisterAdapterShutdownHandler(Adapter->MiniportAdapterHandle, Adapter, MiniportShutdown); } #if DBG if(!MiTestCard(Adapter)) ASSERT(0); #endif DPRINT("returning 0x%x\n", Status); *OpenErrorStatus = Status; return Status; } static VOID NTAPI MiniportISR( OUT PBOOLEAN InterruptRecognized, OUT PBOOLEAN QueueMiniportHandleInterrupt, IN NDIS_HANDLE MiniportAdapterContext) /* * FUNCTION: Miniport interrupt service routine * ARGUMENTS: * InterruptRecognized: the interrupt was ours * QueueMiniportHandleInterrupt: whether to queue a DPC to handle this interrupt * MiniportAdapterContext: the context originally passed to NdisMSetAttributes * NOTES: * - called by NDIS at DIRQL * - by setting QueueMiniportHandleInterrupt to TRUE, MiniportHandleInterrupt * will be called */ { USHORT Data; USHORT Rap; PADAPTER Adapter = (PADAPTER)MiniportAdapterContext; DPRINT("Called\n"); /* save the old RAP value */ NdisRawReadPortUshort(Adapter->PortOffset + RAP, &Rap); /* is this ours? */ NdisRawWritePortUshort(Adapter->PortOffset + RAP, CSR0); NdisRawReadPortUshort(Adapter->PortOffset + RDP, &Data); if(!(Data & CSR0_INTR)) { DPRINT("not our interrupt.\n"); *InterruptRecognized = FALSE; *QueueMiniportHandleInterrupt = FALSE; } else { DPRINT("detected our interrupt\n"); /* disable interrupts */ NdisRawWritePortUshort(Adapter->PortOffset + RAP, CSR0); NdisRawWritePortUshort(Adapter->PortOffset + RDP, 0); *InterruptRecognized = TRUE; *QueueMiniportHandleInterrupt = TRUE; } /* restore the rap */ NdisRawWritePortUshort(Adapter->PortOffset + RAP, Rap); } static NDIS_STATUS NTAPI MiniportReset( OUT PBOOLEAN AddressingReset, IN NDIS_HANDLE MiniportAdapterContext) /* * FUNCTION: Reset the miniport * ARGUMENTS: * AddressingReset: Whether or not we want NDIS to subsequently call MiniportSetInformation * to reset our addresses and filters * MiniportAdapterContext: context originally passed to NdisMSetAttributes * RETURNS: * NDIS_STATUS_SUCCESS on all requests * Notes: * - Called by NDIS at PASSIVE_LEVEL when it thinks we need a reset */ { DPRINT("Called\n"); /* MiniportReset doesn't do anything at the moment... perhaps this should be fixed. */ *AddressingReset = FALSE; return NDIS_STATUS_SUCCESS; } static BOOLEAN NTAPI MiSyncStartTransmit( IN PVOID SynchronizeContext) /* * FUNCTION: Stop the adapter * ARGUMENTS: * SynchronizeContext: Adapter context */ { PADAPTER Adapter = (PADAPTER)SynchronizeContext; NdisRawWritePortUshort(Adapter->PortOffset + RAP, CSR0); NdisRawWritePortUshort(Adapter->PortOffset + RDP, CSR0_IENA | CSR0_TDMD); return TRUE; } static NDIS_STATUS NTAPI MiniportSend( IN NDIS_HANDLE MiniportAdapterContext, IN PNDIS_PACKET Packet, IN UINT Flags) /* * FUNCTION: Called by NDIS when it has a packet for the NIC to send out * ARGUMENTS: * MiniportAdapterContext: context originally input to NdisMSetAttributes * Packet: The NDIS_PACKET to be sent * Flags: Flags associated with Packet * RETURNS: * NDIS_STATUS_SUCCESS on processed requests * NDIS_STATUS_RESOURCES if there's no place in buffer ring * NOTES: * - Called by NDIS at DISPATCH_LEVEL */ { PADAPTER Adapter = (PADAPTER)MiniportAdapterContext; PTRANSMIT_DESCRIPTOR Desc; PNDIS_BUFFER NdisBuffer; PVOID SourceBuffer; UINT TotalPacketLength, SourceLength, Position = 0; DPRINT("Called\n"); ASSERT_IRQL_EQUAL(DISPATCH_LEVEL); NdisDprAcquireSpinLock(&Adapter->Lock); /* Check if we have free entry in our circular buffer. */ if ((Adapter->CurrentTransmitEndIndex + 1 == Adapter->CurrentTransmitStartIndex) || (Adapter->CurrentTransmitEndIndex == Adapter->BufferCount - 1 && Adapter->CurrentTransmitStartIndex == 0)) { DPRINT1("No free space in circular buffer\n"); NdisDprReleaseSpinLock(&Adapter->Lock); return NDIS_STATUS_RESOURCES; } Desc = Adapter->TransmitDescriptorRingVirt + Adapter->CurrentTransmitEndIndex; NdisQueryPacket(Packet, NULL, NULL, &NdisBuffer, &TotalPacketLength); ASSERT(TotalPacketLength <= BUFFER_SIZE); DPRINT("TotalPacketLength: %x\n", TotalPacketLength); while (NdisBuffer) { NdisQueryBuffer(NdisBuffer, &SourceBuffer, &SourceLength); DPRINT("Buffer: %x Length: %x\n", SourceBuffer, SourceLength); RtlCopyMemory(Adapter->TransmitBufferPtrVirt + Adapter->CurrentTransmitEndIndex * BUFFER_SIZE + Position, SourceBuffer, SourceLength); Position += SourceLength; NdisGetNextBuffer(NdisBuffer, &NdisBuffer); } #if DBG && 0 { PUCHAR Ptr = Adapter->TransmitBufferPtrVirt + Adapter->CurrentTransmitEndIndex * BUFFER_SIZE; for (Position = 0; Position < TotalPacketLength; Position++) { if (Position % 16 == 0) DbgPrint("\n"); DbgPrint("%x ", *Ptr++); } } DbgPrint("\n"); #endif Adapter->CurrentTransmitEndIndex++; Adapter->CurrentTransmitEndIndex %= Adapter->BufferCount; Desc->FLAGS = TD1_OWN | TD1_STP | TD1_ENP; Desc->BCNT = 0xf000 | -(INT)TotalPacketLength; NdisMSynchronizeWithInterrupt(&Adapter->InterruptObject, MiSyncStartTransmit, Adapter); NdisDprReleaseSpinLock(&Adapter->Lock); return NDIS_STATUS_SUCCESS; } static ULONG NTAPI MiEthernetCrc(UCHAR *Address) /* * FUNCTION: Calculate Ethernet CRC32 * ARGUMENTS: * Address: 6-byte ethernet address * RETURNS: * The calculated CRC32 value. */ { UINT Counter, Length; ULONG Value = ~0; for (Length = 0; Length < 6; Length++) { Value ^= *Address++; for (Counter = 0; Counter < 8; Counter++) { Value >>= 1; Value ^= (Value & 1) * 0xedb88320; } } return Value; } NDIS_STATUS NTAPI MiSetMulticast( PADAPTER Adapter, UCHAR *Addresses, UINT AddressCount) { UINT Index; ULONG CrcIndex; NdisZeroMemory(Adapter->InitializationBlockVirt->LADR, 8); for (Index = 0; Index < AddressCount; Index++) { CrcIndex = MiEthernetCrc(Addresses) >> 26; Adapter->InitializationBlockVirt->LADR[CrcIndex >> 3] |= 1 << (CrcIndex & 15); Addresses += 6; } /* FIXME: The specification mentions we need to reload the init block here. */ return NDIS_STATUS_SUCCESS; } BOOLEAN NTAPI MiGetMediaDuplex(PADAPTER Adapter) { ULONG Data; NdisRawWritePortUshort(Adapter->PortOffset + RAP, BCR5); NdisRawReadPortUshort(Adapter->PortOffset + BDP, &Data); return (Data & BCR5_LEDOUT) != 0; } UINT NTAPI MiGetMediaSpeed(PADAPTER Adapter) { ULONG Data; NdisRawWritePortUshort(Adapter->PortOffset + RAP, BCR6); NdisRawReadPortUshort(Adapter->PortOffset + BDP, &Data); return Data & BCR6_LEDOUT ? 100 : 10; } NDIS_MEDIA_STATE NTAPI MiGetMediaState(PADAPTER Adapter) /* * FUNCTION: Determine the link state * ARGUMENTS: * Adapter: Adapter context * RETURNS: * NdisMediaStateConnected if the cable is connected * NdisMediaStateDisconnected if the cable is disconnected */ { ULONG Data; NdisRawWritePortUshort(Adapter->PortOffset + RAP, BCR4); NdisRawReadPortUshort(Adapter->PortOffset + BDP, &Data); return Data & BCR4_LEDOUT ? NdisMediaStateConnected : NdisMediaStateDisconnected; } NTSTATUS NTAPI DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) /* * FUNCTION: Start this driver * ARGUMENTS: * DriverObject: Pointer to the system-allocated driver object * RegistryPath: Pointer to our SCM database entry * RETURNS: * NDIS_STATUS_SUCCESS on success * NDIS_STATUS_FAILURE on failure * NOTES: * - Called by the I/O manager when the driver starts at PASSIVE_LEVEL * - TODO: convert this to NTSTATUS return values */ { NDIS_HANDLE WrapperHandle; NDIS_MINIPORT_CHARACTERISTICS Characteristics; NDIS_STATUS Status; RtlZeroMemory(&Characteristics, sizeof(Characteristics)); Characteristics.MajorNdisVersion = NDIS_MINIPORT_MAJOR_VERSION; Characteristics.MinorNdisVersion = NDIS_MINIPORT_MINOR_VERSION; Characteristics.HaltHandler = MiniportHalt; Characteristics.HandleInterruptHandler = MiniportHandleInterrupt; Characteristics.InitializeHandler = MiniportInitialize; Characteristics.ISRHandler = MiniportISR; Characteristics.QueryInformationHandler = MiniportQueryInformation; Characteristics.ResetHandler = MiniportReset; Characteristics.SetInformationHandler = MiniportSetInformation; Characteristics.SendHandler = MiniportSend; NdisMInitializeWrapper(&WrapperHandle, DriverObject, RegistryPath, 0); if (!WrapperHandle) return NDIS_STATUS_FAILURE; Status = NdisMRegisterMiniport(WrapperHandle, &Characteristics, sizeof(Characteristics)); if(Status != NDIS_STATUS_SUCCESS) { NdisTerminateWrapper(WrapperHandle, 0); return NDIS_STATUS_FAILURE; } return NDIS_STATUS_SUCCESS; }