reactos/drivers/network/dd/dc21x4/init.c
Dmitry Borisov 59d8a77df6
[DC21X4] Add driver for DECchip 21x4-compatible network adapters (#5614)
These adapters were common in DEC Alpha boxes and they are really rare
nowadays. The 21140 chip is emulated in Connectix / Microsoft Virtual PC
and Hyper-V Gen 1 VM.

This is an experimental driver, not yet tested on real hardware.

CORE-8724
2023-10-18 20:12:36 +03:00

1322 lines
38 KiB
C

/*
* PROJECT: ReactOS DC21x4 Driver
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: Miniport initialization helper routines
* COPYRIGHT: Copyright 2023 Dmitry Borisov <di.sean@protonmail.com>
*/
/* INCLUDES *******************************************************************/
#include "dc21x4.h"
#include <debug.h>
/* GLOBALS ********************************************************************/
/*
* The driver must align the buffers on a 4 byte boundary to meet the hardware requirement.
* We stick with cache alignment to get better performance.
*/
C_ASSERT((SYSTEM_CACHE_ALIGNMENT_SIZE % DC_RECEIVE_BUFFER_ALIGNMENT) == 0);
C_ASSERT((SYSTEM_CACHE_ALIGNMENT_SIZE % DC_DESCRIPTOR_ALIGNMENT) == 0);
C_ASSERT((SYSTEM_CACHE_ALIGNMENT_SIZE % DC_SETUP_FRAME_ALIGNMENT) == 0);
/* Errata: The end of receive buffer must not fall on a cache boundary */
#define DC_RECEIVE_BUFFER_SIZE (DC_RECEIVE_BLOCK_SIZE - 4)
C_ASSERT((DC_RECEIVE_BUFFER_SIZE % 32) != 0);
#define DC_MEM_BLOCK_SIZE_RCB \
(DC_RECEIVE_BLOCK_SIZE + SYSTEM_CACHE_ALIGNMENT_SIZE - 1)
#define DC_MEM_BLOCK_SIZE_RBD \
(sizeof(DC_RBD) * DC_RECEIVE_BUFFERS_DEFAULT + SYSTEM_CACHE_ALIGNMENT_SIZE - 1)
#define DC_MEM_BLOCK_SIZE_TBD_AUX \
(sizeof(DC_TBD) * DC_TRANSMIT_DESCRIPTORS + SYSTEM_CACHE_ALIGNMENT_SIZE - 1 + \
DC_SETUP_FRAME_SIZE + SYSTEM_CACHE_ALIGNMENT_SIZE - 1 + \
DC_LOOPBACK_FRAME_SIZE * DC_LOOPBACK_FRAMES + SYSTEM_CACHE_ALIGNMENT_SIZE - 1)
#define DC_MEM_BLOCK_SIZE_TX_BUFFER \
(DC_TRANSMIT_BLOCK_SIZE + SYSTEM_CACHE_ALIGNMENT_SIZE - 1)
/* FUNCTIONS ******************************************************************/
static
CODE_SEG("PAGE")
VOID
DcConfigQueryInteger(
_In_ NDIS_HANDLE ConfigurationHandle,
_In_ PCWSTR EntryName,
_Out_ PULONG EntryContext,
_In_ ULONG DefaultValue,
_In_ ULONG Minimum,
_In_ ULONG Maximum)
{
NDIS_STATUS Status;
UNICODE_STRING Keyword;
PNDIS_CONFIGURATION_PARAMETER ConfigurationParameter;
PAGED_CODE();
NdisInitUnicodeString(&Keyword, EntryName);
NdisReadConfiguration(&Status,
&ConfigurationParameter,
ConfigurationHandle,
&Keyword,
NdisParameterInteger);
if (Status != NDIS_STATUS_SUCCESS)
{
TRACE("'%S' request failed, default value %u\n", EntryName, DefaultValue);
*EntryContext = DefaultValue;
return;
}
if (ConfigurationParameter->ParameterData.IntegerData >= Minimum &&
ConfigurationParameter->ParameterData.IntegerData <= Maximum)
{
*EntryContext = ConfigurationParameter->ParameterData.IntegerData;
}
else
{
WARN("'%S' value out of range\n", EntryName);
*EntryContext = DefaultValue;
}
TRACE("Set '%S' to %u\n", EntryName, *EntryContext);
}
static
CODE_SEG("PAGE")
NDIS_STATUS
DcReadConfiguration(
_In_ PDC21X4_ADAPTER Adapter)
{
NDIS_STATUS Status;
NDIS_HANDLE ConfigurationHandle;
PUCHAR NetworkAddress;
UINT Length;
ULONG GenericUlong;
PAGED_CODE();
NdisOpenConfiguration(&Status,
&ConfigurationHandle,
Adapter->WrapperConfigurationHandle);
if (Status != NDIS_STATUS_SUCCESS)
return Status;
DcConfigQueryInteger(ConfigurationHandle,
L"SpeedDuplex",
&GenericUlong,
MEDIA_AUTO,
MEDIA_10T,
MEDIA_HMR);
Adapter->DefaultMedia = GenericUlong;
NdisReadNetworkAddress(&Status,
(PVOID*)&NetworkAddress,
&Length,
ConfigurationHandle);
if ((Status == NDIS_STATUS_SUCCESS) && (Length == ETH_LENGTH_OF_ADDRESS))
{
if (ETH_IS_MULTICAST(NetworkAddress) ||
ETH_IS_EMPTY(NetworkAddress) ||
ETH_IS_BROADCAST(NetworkAddress) ||
!ETH_IS_LOCALLY_ADMINISTERED(NetworkAddress))
{
ERR("Invalid software MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n",
NetworkAddress[0],
NetworkAddress[1],
NetworkAddress[2],
NetworkAddress[3],
NetworkAddress[4],
NetworkAddress[5]);
}
else
{
INFO("Using software MAC address\n");
/* Override the MAC address */
NdisMoveMemory(Adapter->CurrentMacAddress, NetworkAddress, ETH_LENGTH_OF_ADDRESS);
}
}
NdisCloseConfiguration(ConfigurationHandle);
return NDIS_STATUS_SUCCESS;
}
static
CODE_SEG("PAGE")
VOID
DcFreeRcb(
_In_ PDC21X4_ADAPTER Adapter,
_In_ __drv_freesMem(Mem) PDC_RCB Rcb)
{
PAGED_CODE();
if (Rcb->VirtualAddressOriginal)
{
NdisMFreeSharedMemory(Adapter->AdapterHandle,
DC_MEM_BLOCK_SIZE_RCB,
TRUE, /* Cached */
Rcb->VirtualAddressOriginal,
Rcb->PhysicalAddressOriginal);
}
if (Rcb->NdisBuffer)
NdisFreeBuffer(Rcb->NdisBuffer);
if (Rcb->Packet)
NdisFreePacket(Rcb->Packet);
NdisFreeMemory(Rcb, sizeof(*Rcb), 0);
}
static
CODE_SEG("PAGE")
PDC_RCB
DcAllocateRcb(
_In_ PDC21X4_ADAPTER Adapter)
{
NDIS_STATUS Status;
PDC_RCB Rcb;
PVOID VirtualAddress;
NDIS_PHYSICAL_ADDRESS PhysicalAddress;
PAGED_CODE();
Status = NdisAllocateMemoryWithTag((PVOID*)&Rcb, sizeof(*Rcb), DC21X4_TAG);
if (Status != NDIS_STATUS_SUCCESS)
return NULL;
NdisZeroMemory(Rcb, sizeof(*Rcb));
NdisMAllocateSharedMemory(Adapter->AdapterHandle,
DC_MEM_BLOCK_SIZE_RCB,
TRUE, /* Cached */
&VirtualAddress,
&PhysicalAddress);
if (!VirtualAddress)
goto Failure;
/* 32-bit DMA */
ASSERT(PhysicalAddress.HighPart == 0);
Rcb->VirtualAddress = ALIGN_UP_POINTER_BY(VirtualAddress, SYSTEM_CACHE_ALIGNMENT_SIZE);
Rcb->PhysicalAddress = ALIGN_UP_BY(PhysicalAddress.LowPart, SYSTEM_CACHE_ALIGNMENT_SIZE);
ASSERT((Rcb->PhysicalAddress % DC_RECEIVE_BUFFER_ALIGNMENT) == 0);
Rcb->VirtualAddressOriginal = VirtualAddress;
Rcb->PhysicalAddressOriginal.QuadPart = PhysicalAddress.QuadPart;
NdisAllocatePacket(&Status, &Rcb->Packet, Adapter->PacketPool);
if (Status != NDIS_STATUS_SUCCESS)
goto Failure;
*DC_RCB_FROM_PACKET(Rcb->Packet) = Rcb;
NdisAllocateBuffer(&Status,
&Rcb->NdisBuffer,
Adapter->BufferPool,
Rcb->VirtualAddress,
DC_RECEIVE_BLOCK_SIZE);
if (Status != NDIS_STATUS_SUCCESS)
goto Failure;
NDIS_SET_PACKET_HEADER_SIZE(Rcb->Packet, DC_ETHERNET_HEADER_SIZE);
NdisChainBufferAtFront(Rcb->Packet, Rcb->NdisBuffer);
PushEntryList(&Adapter->AllocRcbList, &Rcb->AllocListEntry);
return Rcb;
Failure:
DcFreeRcb(Adapter, Rcb);
return NULL;
}
static
CODE_SEG("PAGE")
NDIS_STATUS
DcAllocateReceiveBuffers(
_In_ PDC21X4_ADAPTER Adapter)
{
ULONG i;
NDIS_STATUS Status;
PDC_RCB Rcb;
PAGED_CODE();
NdisAllocatePacketPool(&Status,
&Adapter->PacketPool,
DC_RECEIVE_BUFFERS_DEFAULT + DC_RECEIVE_BUFFERS_EXTRA,
PROTOCOL_RESERVED_SIZE_IN_PACKET);
if (Status != NDIS_STATUS_SUCCESS)
return Status;
NdisAllocateBufferPool(&Status,
&Adapter->BufferPool,
DC_RECEIVE_BUFFERS_DEFAULT + DC_RECEIVE_BUFFERS_EXTRA);
if (Status != NDIS_STATUS_SUCCESS)
return Status;
/* Allocate RCBs */
for (i = 0; i < DC_RECEIVE_BUFFERS_DEFAULT; ++i)
{
Rcb = DcAllocateRcb(Adapter);
if (!Rcb)
{
WARN("RCB allocation failed, total buffers %u\n", Adapter->RcbCount);
break;
}
PushEntryList(&Adapter->UsedRcbList, &Rcb->ListEntry);
++Adapter->RcbCount;
}
if (Adapter->RcbCount < DC_RECEIVE_BUFFERS_MIN)
return NDIS_STATUS_RESOURCES;
Adapter->RcbFree = Adapter->RcbCount;
/* Fix up the ring size */
Adapter->TailRbd = Adapter->HeadRbd + Adapter->RcbCount - 1;
/* Allocate extra RCBs for receive indication */
for (i = 0; i < DC_RECEIVE_BUFFERS_EXTRA; ++i)
{
Rcb = DcAllocateRcb(Adapter);
if (!Rcb)
{
WARN("Extra RCB allocation failed\n");
break;
}
PushEntryList(&Adapter->FreeRcbList, &Rcb->ListEntry);
}
Status = NdisAllocateMemoryWithTag((PVOID*)&Adapter->RcbArray,
sizeof(PVOID) * Adapter->RcbCount,
DC21X4_TAG);
if (Status != NDIS_STATUS_SUCCESS)
return Status;
return NDIS_STATUS_SUCCESS;
}
static
CODE_SEG("PAGE")
NDIS_STATUS
DcAllocateReceiveDescriptors(
_In_ PDC21X4_ADAPTER Adapter)
{
PDC_RBD Rbd;
PAGED_CODE();
NdisMAllocateSharedMemory(Adapter->AdapterHandle,
DC_MEM_BLOCK_SIZE_RBD,
FALSE, /* Non-cached */
&Adapter->RbdOriginal,
&Adapter->RbdPhysOriginal);
if (!Adapter->RbdOriginal)
return NDIS_STATUS_RESOURCES;
/* 32-bit DMA */
ASSERT(Adapter->RbdPhysOriginal.HighPart == 0);
Adapter->RbdPhys = ALIGN_UP_BY(Adapter->RbdPhysOriginal.LowPart, SYSTEM_CACHE_ALIGNMENT_SIZE);
ASSERT((Adapter->RbdPhys % DC_DESCRIPTOR_ALIGNMENT) == 0);
Rbd = ALIGN_UP_POINTER_BY(Adapter->RbdOriginal, SYSTEM_CACHE_ALIGNMENT_SIZE);
Adapter->HeadRbd = Rbd;
return NDIS_STATUS_SUCCESS;
}
static
CODE_SEG("PAGE")
NDIS_STATUS
DcAllocateTransmitBlocks(
_In_ PDC21X4_ADAPTER Adapter)
{
PDC_TCB Tcb;
NDIS_STATUS Status;
PAGED_CODE();
Status = NdisAllocateMemoryWithTag((PVOID*)&Tcb,
DC_TRANSMIT_BLOCKS * sizeof(*Tcb),
DC21X4_TAG);
if (Status != NDIS_STATUS_SUCCESS)
return Status;
NdisZeroMemory(Tcb, DC_TRANSMIT_BLOCKS * sizeof(*Tcb));
Adapter->HeadTcb = Tcb;
Adapter->TailTcb = Tcb + (DC_TRANSMIT_BLOCKS - 1);
return NDIS_STATUS_SUCCESS;
}
static
CODE_SEG("PAGE")
NDIS_STATUS
DcAllocateTransmitDescriptorsAndBuffers(
_In_ PDC21X4_ADAPTER Adapter)
{
ULONG_PTR BufferVa, BufferPa;
NDIS_STATUS Status;
ULONG i;
PAGED_CODE();
NdisMAllocateSharedMemory(Adapter->AdapterHandle,
DC_MEM_BLOCK_SIZE_TBD_AUX,
FALSE, /* Non-cached */
&Adapter->TbdOriginal,
&Adapter->TbdPhysOriginal);
if (!Adapter->TbdOriginal)
return NDIS_STATUS_RESOURCES;
/* 32-bit DMA */
ASSERT(Adapter->TbdPhysOriginal.HighPart == 0);
BufferVa = (ULONG_PTR)Adapter->TbdOriginal;
BufferPa = Adapter->TbdPhysOriginal.LowPart;
BufferVa = ALIGN_UP_BY(BufferVa, SYSTEM_CACHE_ALIGNMENT_SIZE);
BufferPa = ALIGN_UP_BY(BufferPa, SYSTEM_CACHE_ALIGNMENT_SIZE);
ASSERT((BufferPa % DC_DESCRIPTOR_ALIGNMENT) == 0);
Adapter->TbdPhys = (ULONG)BufferPa;
Adapter->HeadTbd = (PDC_TBD)BufferVa;
Adapter->TailTbd = (PDC_TBD)BufferVa + DC_TRANSMIT_DESCRIPTORS - 1;
BufferVa += sizeof(DC_TBD) * DC_TRANSMIT_DESCRIPTORS;
BufferPa += sizeof(DC_TBD) * DC_TRANSMIT_DESCRIPTORS;
BufferVa = ALIGN_UP_BY(BufferVa, SYSTEM_CACHE_ALIGNMENT_SIZE);
BufferPa = ALIGN_UP_BY(BufferPa, SYSTEM_CACHE_ALIGNMENT_SIZE);
ASSERT((BufferPa % DC_SETUP_FRAME_ALIGNMENT) == 0);
Adapter->SetupFrame = (PVOID)BufferVa;
Adapter->SetupFramePhys = BufferPa;
BufferVa += DC_SETUP_FRAME_SIZE;
BufferPa += DC_SETUP_FRAME_SIZE;
BufferVa = ALIGN_UP_BY(BufferVa, SYSTEM_CACHE_ALIGNMENT_SIZE);
BufferPa = ALIGN_UP_BY(BufferPa, SYSTEM_CACHE_ALIGNMENT_SIZE);
for (i = 0; i < DC_LOOPBACK_FRAMES; ++i)
{
Adapter->LoopbackFrame[i] = (PVOID)BufferVa;
Adapter->LoopbackFramePhys[i] = BufferPa;
BufferVa += DC_LOOPBACK_FRAME_SIZE;
BufferPa += DC_LOOPBACK_FRAME_SIZE;
}
if (Adapter->Features & DC_HAS_POWER_MANAGEMENT)
{
Status = NdisAllocateMemoryWithTag((PVOID*)&Adapter->SetupFrameSaved,
DC_SETUP_FRAME_SIZE,
DC21X4_TAG);
if (Status != NDIS_STATUS_SUCCESS)
return Status;
}
return NDIS_STATUS_SUCCESS;
}
static
CODE_SEG("PAGE")
NDIS_STATUS
DcAllocateTransmitBuffers(
_In_ PDC21X4_ADAPTER Adapter)
{
PDC_COALESCE_BUFFER CoalesceBuffer;
NDIS_STATUS Status;
ULONG i;
PAGED_CODE();
Status = NdisAllocateMemoryWithTag((PVOID*)&CoalesceBuffer,
DC_TRANSMIT_BUFFERS * sizeof(*CoalesceBuffer),
DC21X4_TAG);
if (Status != NDIS_STATUS_SUCCESS)
return Status;
NdisZeroMemory(CoalesceBuffer, DC_TRANSMIT_BUFFERS * sizeof(*CoalesceBuffer));
Adapter->CoalesceBuffer = CoalesceBuffer;
for (i = 0; i < DC_TRANSMIT_BUFFERS; ++i)
{
PVOID VirtualAddress;
NDIS_PHYSICAL_ADDRESS PhysicalAddress;
NdisMAllocateSharedMemory(Adapter->AdapterHandle,
DC_MEM_BLOCK_SIZE_TX_BUFFER,
FALSE, /* Non-cached */
&VirtualAddress,
&PhysicalAddress);
if (!VirtualAddress)
continue;
ASSERT(PhysicalAddress.HighPart == 0);
CoalesceBuffer->VirtualAddress =
ALIGN_UP_POINTER_BY(VirtualAddress, SYSTEM_CACHE_ALIGNMENT_SIZE);
CoalesceBuffer->PhysicalAddress =
ALIGN_UP_BY(PhysicalAddress.LowPart, SYSTEM_CACHE_ALIGNMENT_SIZE);
Adapter->SendBufferData[i].PhysicalAddress.QuadPart = PhysicalAddress.QuadPart;
Adapter->SendBufferData[i].VirtualAddress = VirtualAddress;
PushEntryList(&Adapter->SendBufferList, &CoalesceBuffer->ListEntry);
++CoalesceBuffer;
}
if (!Adapter->SendBufferList.Next)
return NDIS_STATUS_RESOURCES;
return NDIS_STATUS_SUCCESS;
}
CODE_SEG("PAGE")
VOID
DcInitTxRing(
_In_ PDC21X4_ADAPTER Adapter)
{
PDC_TCB Tcb;
PDC_TBD Tbd;
PAGED_CODE();
InitializeListHead(&Adapter->SendQueueList);
Tcb = Adapter->HeadTcb;
Adapter->CurrentTcb = Tcb;
Adapter->LastTcb = Tcb;
Adapter->TcbSlots = DC_TRANSMIT_BLOCKS - DC_TCB_RESERVE;
Adapter->TbdSlots = DC_TRANSMIT_DESCRIPTORS - DC_TBD_RESERVE;
Adapter->LoopbackFrameSlots = DC_LOOPBACK_FRAMES;
Adapter->TcbCompleted = 0;
Tbd = Adapter->HeadTbd;
Adapter->CurrentTbd = Tbd;
NdisZeroMemory(Tbd, sizeof(*Tbd) * DC_TRANSMIT_DESCRIPTORS);
Adapter->TailTbd->Control |= DC_TBD_CONTROL_END_OF_RING;
}
static
CODE_SEG("PAGE")
VOID
DcCreateRxRing(
_In_ PDC21X4_ADAPTER Adapter)
{
PDC_RCB* RcbSlot;
PDC_RCB Rcb;
PDC_RBD Rbd;
PSINGLE_LIST_ENTRY Entry;
PAGED_CODE();
Rbd = Adapter->HeadRbd;
Adapter->CurrentRbd = Rbd;
RcbSlot = DC_GET_RCB_SLOT(Adapter, Rbd);
Rcb = (PDC_RCB)Adapter->UsedRcbList.Next;
for (Entry = Adapter->UsedRcbList.Next;
Entry != NULL;
Entry = Entry->Next)
{
Rcb = (PDC_RCB)Entry;
C_ASSERT((DC_RECEIVE_BUFFER_SIZE % DC_RECEIVE_BUFFER_SIZE_MULTIPLE) == 0);
Rbd->Address1 = Rcb->PhysicalAddress;
Rbd->Address2 = 0;
Rbd->Control = DC_RECEIVE_BUFFER_SIZE;
Rbd->Status = DC_RBD_STATUS_OWNED;
*RcbSlot = Rcb;
++RcbSlot;
++Rbd;
Rcb = (PDC_RCB)Rcb->ListEntry.Next;
}
Rbd = Rbd - 1;
Rbd->Control |= DC_RBD_CONTROL_CHAINED;
Rbd->Address2 = Adapter->RbdPhys;
ASSERT(Rbd == Adapter->TailRbd);
}
CODE_SEG("PAGE")
VOID
DcInitRxRing(
_In_ PDC21X4_ADAPTER Adapter)
{
PDC_RBD Rbd;
ULONG i;
PAGED_CODE();
Rbd = Adapter->HeadRbd;
Adapter->CurrentRbd = Rbd;
for (i = 0; i < Adapter->RcbCount; ++i)
{
Rbd->Control = DC_RECEIVE_BUFFER_SIZE;
Rbd->Status = DC_RBD_STATUS_OWNED;
++Rbd;
}
Rbd = Rbd - 1;
Rbd->Control |= DC_RBD_CONTROL_CHAINED;
ASSERT(Rbd == Adapter->TailRbd);
}
static
CODE_SEG("PAGE")
NDIS_STATUS
DcAllocateMemory(
_In_ PDC21X4_ADAPTER Adapter)
{
NDIS_STATUS Status;
PAGED_CODE();
Status = NdisMInitializeScatterGatherDma(Adapter->AdapterHandle,
FALSE, /* 32-bit DMA */
DC_TRANSMIT_BLOCK_SIZE);
if (Status != NDIS_STATUS_SUCCESS)
return Status;
Status = DcAllocateReceiveDescriptors(Adapter);
if (Status != NDIS_STATUS_SUCCESS)
return Status;
Status = DcAllocateTransmitBlocks(Adapter);
if (Status != NDIS_STATUS_SUCCESS)
return Status;
Status = DcAllocateTransmitBuffers(Adapter);
if (Status != NDIS_STATUS_SUCCESS)
return Status;
Status = DcAllocateTransmitDescriptorsAndBuffers(Adapter);
if (Status != NDIS_STATUS_SUCCESS)
return Status;
Status = DcAllocateReceiveBuffers(Adapter);
if (Status != NDIS_STATUS_SUCCESS)
return Status;
NdisAllocateSpinLock(&Adapter->SendLock);
NdisAllocateSpinLock(&Adapter->ReceiveLock);
NdisAllocateSpinLock(&Adapter->ModeLock);
return NDIS_STATUS_SUCCESS;
}
static
CODE_SEG("PAGE")
VOID
DcInitTestPacket(
_In_ PDC21X4_ADAPTER Adapter)
{
ULONG i;
PAGED_CODE();
for (i = 0; i < DC_LOOPBACK_FRAMES; ++i)
{
PETH_HEADER PacketBuffer = Adapter->LoopbackFrame[i];
NdisZeroMemory(PacketBuffer, DC_LOOPBACK_FRAME_SIZE);
/* Destination MAC address */
NdisMoveMemory(PacketBuffer->Destination,
Adapter->CurrentMacAddress,
ETH_LENGTH_OF_ADDRESS);
/* Source MAC address */
NdisMoveMemory(PacketBuffer->Source,
Adapter->CurrentMacAddress,
ETH_LENGTH_OF_ADDRESS);
++PacketBuffer;
}
}
static
CODE_SEG("PAGE")
NDIS_STATUS
DcInitializeAdapterResources(
_In_ PDC21X4_ADAPTER Adapter)
{
NDIS_STATUS Status;
PNDIS_RESOURCE_LIST AssignedResources = NULL;
PCM_PARTIAL_RESOURCE_DESCRIPTOR IoDescriptor = NULL;
PCM_PARTIAL_RESOURCE_DESCRIPTOR InterruptDescriptor = NULL;
UINT i, ResourceListSize = 0;
PAGED_CODE();
NdisMQueryAdapterResources(&Status,
Adapter->WrapperConfigurationHandle,
AssignedResources,
&ResourceListSize);
if (Status != NDIS_STATUS_RESOURCES)
return NDIS_STATUS_FAILURE;
Status = NdisAllocateMemoryWithTag((PVOID*)&AssignedResources,
ResourceListSize,
DC21X4_TAG);
if (Status != NDIS_STATUS_SUCCESS)
return Status;
NdisMQueryAdapterResources(&Status,
Adapter->WrapperConfigurationHandle,
AssignedResources,
&ResourceListSize);
if (Status != NDIS_STATUS_SUCCESS)
goto Cleanup;
for (i = 0; i < AssignedResources->Count; ++i)
{
PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor;
Descriptor = &AssignedResources->PartialDescriptors[i];
switch (Descriptor->Type)
{
case CmResourceTypePort:
case CmResourceTypeMemory:
{
if (!IoDescriptor && (Descriptor->u.Port.Length == DC_IO_LENGTH))
IoDescriptor = Descriptor;
break;
}
case CmResourceTypeInterrupt:
{
if (!InterruptDescriptor)
InterruptDescriptor = Descriptor;
break;
}
default:
break;
}
}
if (!IoDescriptor || !InterruptDescriptor)
{
Status = NDIS_STATUS_RESOURCES;
goto Cleanup;
}
Adapter->InterruptVector = InterruptDescriptor->u.Interrupt.Vector;
Adapter->InterruptLevel = InterruptDescriptor->u.Interrupt.Level;
Adapter->InterruptFlags = InterruptDescriptor->Flags;
if (InterruptDescriptor->ShareDisposition == CmResourceShareShared)
Adapter->Flags |= DC_IRQ_SHARED;
Adapter->IoBaseAddress = IoDescriptor->u.Port.Start;
if ((IoDescriptor->Type == CmResourceTypePort) &&
(IoDescriptor->Flags & CM_RESOURCE_PORT_IO))
{
Status = NdisMRegisterIoPortRange((PVOID*)&Adapter->IoBase,
Adapter->AdapterHandle,
Adapter->IoBaseAddress.LowPart,
DC_IO_LENGTH);
}
else
{
Status = NdisMMapIoSpace((PVOID*)&Adapter->IoBase,
Adapter->AdapterHandle,
Adapter->IoBaseAddress,
DC_IO_LENGTH);
Adapter->Flags |= DC_IO_MAPPED;
}
if (Status != NDIS_STATUS_SUCCESS)
goto Cleanup;
INFO("IO Base %p\n", Adapter->IoBase);
INFO("IRQ Level %u, Vector %u\n",
Adapter->InterruptLevel,
Adapter->InterruptVector);
INFO("IRQ ShareDisposition %u, InterruptFlags %lx\n",
InterruptDescriptor->ShareDisposition,
InterruptDescriptor->Flags);
Cleanup:
NdisFreeMemory(AssignedResources, ResourceListSize, 0);
return Status;
}
static
CODE_SEG("PAGE")
NDIS_STATUS
DcInitializeAdapterLocation(
_In_ PDC21X4_ADAPTER Adapter)
{
PDEVICE_OBJECT Pdo;
NTSTATUS Status;
ULONG PropertyValue, Length;
PAGED_CODE();
NdisMGetDeviceProperty(Adapter->AdapterHandle,
&Pdo,
NULL,
NULL,
NULL,
NULL);
Status = IoGetDeviceProperty(Pdo,
DevicePropertyAddress,
sizeof(PropertyValue),
&PropertyValue,
&Length);
if (!NT_SUCCESS(Status))
return NDIS_STATUS_FAILURE;
/* We need this for PCI devices only ((DeviceNumber << 16) | Function) */
Adapter->DeviceNumber = (PropertyValue >> 16) & 0x000000FF;
Status = IoGetDeviceProperty(Pdo,
DevicePropertyBusNumber,
sizeof(PropertyValue),
&Adapter->BusNumber,
&Length);
if (!NT_SUCCESS(Status))
return NDIS_STATUS_FAILURE;
return NDIS_STATUS_SUCCESS;
}
static
CODE_SEG("PAGE")
ULONG
DcGetBusModeParameters(
_In_ PDC21X4_ADAPTER Adapter,
_In_ PPCI_COMMON_CONFIG PciData)
{
ULONG DefaultMode, NewMode;
PAGED_CODE();
/* TODO: Other architectures than x86 */
DefaultMode = DC_BUS_MODE_CACHE_ALIGNMENT_16 | DC_BUS_MODE_BURST_LENGTH_NO_LIMIT;
if (!(Adapter->Features & DC_ENABLE_PCI_COMMANDS))
return DefaultMode;
INFO("PCI Cache Line Size %u\n", PciData->CacheLineSize * 4);
INFO("PCI Command %04lx\n", PciData->Command);
/* Use the cache line size if it was set up by firmware */
switch (PciData->CacheLineSize)
{
case 8:
NewMode = DC_BUS_MODE_CACHE_ALIGNMENT_8 | DC_BUS_MODE_BURST_LENGTH_8;
break;
case 16:
NewMode = DC_BUS_MODE_CACHE_ALIGNMENT_16 | DC_BUS_MODE_BURST_LENGTH_16;
break;
case 32:
NewMode = DC_BUS_MODE_CACHE_ALIGNMENT_32 | DC_BUS_MODE_BURST_LENGTH_32;
break;
default:
return DefaultMode;
}
/* Enable one of those commands */
if (PciData->Command & PCI_ENABLE_WRITE_AND_INVALIDATE)
{
NewMode |= DC_BUS_MODE_WRITE_INVALIDATE;
}
else
{
NewMode |= DC_BUS_MODE_READ_LINE;
}
return NewMode;
}
static
CODE_SEG("PAGE")
NDIS_STATUS
DcRecognizeHardware(
_In_ PDC21X4_ADAPTER Adapter)
{
UCHAR Buffer[RTL_SIZEOF_THROUGH_FIELD(PCI_COMMON_CONFIG, CacheLineSize)];
PPCI_COMMON_CONFIG PciConfig = (PPCI_COMMON_CONFIG)Buffer; // Partial PCI header
PNDIS_TIMER_FUNCTION MediaMonitorRoutine;
ULONG Bytes;
PAGED_CODE();
Bytes = NdisReadPciSlotInformation(Adapter->AdapterHandle,
0,
FIELD_OFFSET(PCI_COMMON_CONFIG, VendorID),
Buffer,
sizeof(Buffer));
if (Bytes != sizeof(Buffer))
return NDIS_STATUS_FAILURE;
Adapter->DeviceId = PciConfig->DeviceID;
Adapter->RevisionId = PciConfig->RevisionID;
switch ((PciConfig->DeviceID << 16) | PciConfig->VendorID)
{
case DC_DEV_DECCHIP_21040:
{
Adapter->ChipType = DC21040;
Adapter->InterruptMask = DC_GENERIC_IRQ_MASK | DC_IRQ_LINK_FAIL;
Adapter->LinkStateChangeMask = DC_IRQ_LINK_FAIL;
Adapter->HandleLinkStateChange = MediaLinkStateChange21040;
MediaMonitorRoutine = MediaMonitor21040Dpc;
break;
}
case DC_DEV_DECCHIP_21041:
{
Adapter->ChipType = DC21041;
Adapter->Features |= DC_HAS_POWER_SAVING | DC_HAS_TIMER;
Adapter->InterruptMask = DC_GENERIC_IRQ_MASK | DC_IRQ_LINK_PASS | DC_IRQ_LINK_FAIL;
Adapter->LinkStateChangeMask = DC_IRQ_LINK_PASS | DC_IRQ_LINK_FAIL;
Adapter->HandleLinkStateChange = MediaLinkStateChange21041;
MediaMonitorRoutine = MediaMonitor21041Dpc;
break;
}
case DC_DEV_DECCHIP_21140:
{
Adapter->ChipType = DC21140;
Adapter->Features |= DC_HAS_TIMER;
if ((PciConfig->RevisionID & 0xF0) < 0x20)
{
/* 21140 */
Adapter->Features |= DC_PERFECT_FILTERING_ONLY;
}
else
{
/* 21140A */
Adapter->Features |= DC_NEED_RX_OVERFLOW_WORKAROUND | DC_ENABLE_PCI_COMMANDS |
DC_HAS_POWER_SAVING;
}
Adapter->OpMode |= DC_OPMODE_PORT_ALWAYS;
Adapter->InterruptMask = DC_GENERIC_IRQ_MASK;
MediaMonitorRoutine = MediaMonitor21140Dpc;
break;
}
case DC_DEV_INTEL_21143:
{
Adapter->Features |= DC_NEED_RX_OVERFLOW_WORKAROUND | DC_SIA_GPIO |
DC_HAS_POWER_SAVING | DC_MII_AUTOSENSE | DC_HAS_TIMER;
Adapter->InterruptMask = DC_GENERIC_IRQ_MASK | DC_IRQ_LINK_PASS | DC_IRQ_LINK_FAIL;
Adapter->LinkStateChangeMask = DC_IRQ_LINK_PASS | DC_IRQ_LINK_FAIL;
Adapter->ChipType = DC21143;
if ((PciConfig->RevisionID & 0xF0) < 0x20)
{
/* 21142 */
}
else
{
/* 21143 */
Adapter->Features |= DC_ENABLE_PCI_COMMANDS;
Adapter->OpMode |= DC_OPMODE_PORT_ALWAYS;
Adapter->InterruptMask |= DC_IRQ_LINK_CHANGED;
Adapter->LinkStateChangeMask |= DC_IRQ_LINK_CHANGED;
}
/* 21143 -PD and -TD */
if ((PciConfig->RevisionID & 0xF0) > 0x30)
Adapter->Features |= DC_HAS_POWER_MANAGEMENT;
Adapter->HandleLinkStateChange = MediaLinkStateChange21143;
MediaMonitorRoutine = MediaMonitor21143Dpc;
break;
}
case DC_DEV_INTEL_21145:
{
Adapter->ChipType = DC21145;
Adapter->Features |= DC_NEED_RX_OVERFLOW_WORKAROUND | DC_SIA_GPIO |
DC_HAS_POWER_MANAGEMENT | DC_HAS_POWER_SAVING |
DC_SIA_ANALOG_CONTROL | DC_ENABLE_PCI_COMMANDS |
DC_MII_AUTOSENSE | DC_HAS_TIMER;
Adapter->OpMode |= DC_OPMODE_PORT_ALWAYS;
Adapter->InterruptMask = DC_GENERIC_IRQ_MASK | DC_IRQ_LINK_CHANGED |
DC_IRQ_LINK_PASS | DC_IRQ_LINK_FAIL;
Adapter->LinkStateChangeMask = DC_IRQ_LINK_CHANGED |
DC_IRQ_LINK_PASS | DC_IRQ_LINK_FAIL;
Adapter->HandleLinkStateChange = MediaLinkStateChange21143;
MediaMonitorRoutine = MediaMonitor21143Dpc;
Adapter->AnalogControl = DC_HPNA_ANALOG_CTRL;
/* Workaround for internal RX errata */
Adapter->HpnaRegister[HPNA_NOISE_FLOOR] = 0x10;
Adapter->HpnaInitBitmap = (1 << HPNA_NOISE_FLOOR);
break;
}
default:
return NDIS_STATUS_NOT_RECOGNIZED;
}
Adapter->BusMode = DcGetBusModeParameters(Adapter, PciConfig);
INFO("Bus Mode %08lx\n", Adapter->BusMode);
/* Errata: hash filtering is broken on some chips */
if (Adapter->Features & DC_PERFECT_FILTERING_ONLY)
Adapter->MulticastMaxEntries = DC_SETUP_FRAME_ADDRESSES;
else
Adapter->MulticastMaxEntries = DC_MULTICAST_LIST_SIZE;
NdisMInitializeTimer(&Adapter->MediaMonitorTimer,
Adapter->AdapterHandle,
MediaMonitorRoutine,
Adapter);
return NDIS_STATUS_SUCCESS;
}
CODE_SEG("PAGE")
VOID
DcFreeAdapter(
_In_ __drv_freesMem(Mem) PDC21X4_ADAPTER Adapter)
{
ULONG i;
PAGED_CODE();
DcFreeEeprom(Adapter);
if (Adapter->Interrupt.InterruptObject)
{
NdisMDeregisterInterrupt(&Adapter->Interrupt);
}
if (Adapter->IoBase)
{
if (Adapter->Flags & DC_IO_MAPPED)
{
NdisMUnmapIoSpace(Adapter->AdapterHandle,
Adapter->IoBase,
DC_IO_LENGTH);
}
else
{
NdisMDeregisterIoPortRange(Adapter->AdapterHandle,
Adapter->IoBaseAddress.LowPart,
DC_IO_LENGTH,
Adapter->IoBase);
}
}
if (Adapter->HeadTcb)
{
NdisFreeMemory(Adapter->HeadTcb, sizeof(DC_TCB) * DC_TRANSMIT_BLOCKS, 0);
}
if (Adapter->RcbArray)
{
NdisFreeMemory(Adapter->RcbArray, sizeof(PVOID) * Adapter->RcbCount, 0);
}
if (Adapter->SetupFrameSaved)
{
NdisFreeMemory(Adapter->SetupFrameSaved, DC_SETUP_FRAME_SIZE, 0);
}
while (Adapter->AllocRcbList.Next)
{
PSINGLE_LIST_ENTRY Entry = PopEntryList(&Adapter->AllocRcbList);
PDC_RCB Rcb = CONTAINING_RECORD(Entry, DC_RCB, AllocListEntry);
DcFreeRcb(Adapter, Rcb);
}
if (Adapter->RbdOriginal)
{
NdisMFreeSharedMemory(Adapter->AdapterHandle,
DC_MEM_BLOCK_SIZE_RBD,
FALSE, /* Non-cached */
Adapter->RbdOriginal,
Adapter->RbdPhysOriginal);
}
if (Adapter->TbdOriginal)
{
NdisMFreeSharedMemory(Adapter->AdapterHandle,
DC_MEM_BLOCK_SIZE_TBD_AUX,
FALSE, /* Non-cached */
Adapter->TbdOriginal,
Adapter->TbdPhysOriginal);
}
if (Adapter->CoalesceBuffer)
{
for (i = 0; i < DC_TRANSMIT_BUFFERS; ++i)
{
PDC_TX_BUFFER_DATA SendBufferData = &Adapter->SendBufferData[i];
if (!SendBufferData->VirtualAddress)
continue;
NdisMFreeSharedMemory(Adapter->AdapterHandle,
DC_MEM_BLOCK_SIZE_TX_BUFFER,
FALSE, /* Non-cached */
SendBufferData->VirtualAddress,
SendBufferData->PhysicalAddress);
}
}
if (Adapter->PacketPool)
NdisFreePacketPool(Adapter->PacketPool);
if (Adapter->BufferPool)
NdisFreeBufferPool(Adapter->BufferPool);
if (Adapter->SendLock.SpinLock)
NdisFreeSpinLock(&Adapter->SendLock);
if (Adapter->ReceiveLock.SpinLock)
NdisFreeSpinLock(&Adapter->ReceiveLock);
if (Adapter->ModeLock.SpinLock)
NdisFreeSpinLock(&Adapter->ModeLock);
NdisFreeMemory(Adapter->AdapterOriginal, sizeof(*Adapter), 0);
}
CODE_SEG("PAGE")
NDIS_STATUS
NTAPI
DcInitialize(
_Out_ PNDIS_STATUS OpenErrorStatus,
_Out_ PUINT SelectedMediumIndex,
_In_ PNDIS_MEDIUM MediumArray,
_In_ UINT MediumArraySize,
_In_ NDIS_HANDLE MiniportAdapterHandle,
_In_ NDIS_HANDLE WrapperConfigurationContext)
{
PDC21X4_ADAPTER Adapter;
PVOID UnalignedAdapter;
NDIS_STATUS Status;
ULONG Alignment, AdapterSize, OpMode;
BOOLEAN Success;
UINT i;
INFO("Called\n");
PAGED_CODE();
for (i = 0; i < MediumArraySize; ++i)
{
if (MediumArray[i] == NdisMedium802_3)
{
*SelectedMediumIndex = i;
break;
}
}
if (i == MediumArraySize)
{
ERR("No supported media\n");
return NDIS_STATUS_UNSUPPORTED_MEDIA;
}
Alignment = NdisGetSharedDataAlignment();
AdapterSize = sizeof(*Adapter) + Alignment;
Status = NdisAllocateMemoryWithTag((PVOID*)&UnalignedAdapter, AdapterSize, DC21X4_TAG);
if (Status != NDIS_STATUS_SUCCESS)
{
ERR("Failed to allocate adapter context\n");
return NDIS_STATUS_RESOURCES;
}
NdisZeroMemory(UnalignedAdapter, AdapterSize);
Adapter = ALIGN_UP_POINTER_BY(UnalignedAdapter, Alignment);
Adapter->AdapterOriginal = UnalignedAdapter;
Adapter->AdapterSize = AdapterSize;
Adapter->AdapterHandle = MiniportAdapterHandle;
Adapter->WrapperConfigurationHandle = WrapperConfigurationContext;
NdisInitializeWorkItem(&Adapter->ResetWorkItem, DcResetWorker, Adapter);
NdisInitializeWorkItem(&Adapter->PowerWorkItem, DcPowerWorker, Adapter);
NdisInitializeWorkItem(&Adapter->TxRecoveryWorkItem, DcTransmitTimeoutRecoveryWorker, Adapter);
NdisMSetAttributesEx(MiniportAdapterHandle,
Adapter,
2, /* CheckForHangTimeInSeconds */
NDIS_ATTRIBUTE_BUS_MASTER |
NDIS_ATTRIBUTE_DESERIALIZE |
NDIS_ATTRIBUTE_USES_SAFE_BUFFER_APIS,
NdisInterfacePci);
Status = DcRecognizeHardware(Adapter);
if (Status != NDIS_STATUS_SUCCESS)
{
return Status;
}
Status = DcInitializeAdapterResources(Adapter);
if (Status != NDIS_STATUS_SUCCESS)
{
goto Failure;
}
Status = DcInitializeAdapterLocation(Adapter);
if (Status != NDIS_STATUS_SUCCESS)
{
goto Failure;
}
/* Bring the chip out of sleep mode */
DcPowerSave(Adapter, FALSE);
OpMode = DC_READ(Adapter, DcCsr6_OpMode);
OpMode &= ~(DC_OPMODE_RX_ENABLE | DC_OPMODE_TX_ENABLE);
DC_WRITE(Adapter, DcCsr6_OpMode, OpMode);
MediaInitMediaList(Adapter);
Status = DcReadEeprom(Adapter);
if (Status != NDIS_STATUS_SUCCESS)
{
goto Failure;
}
NdisMoveMemory(Adapter->CurrentMacAddress, Adapter->PermanentMacAddress, ETH_LENGTH_OF_ADDRESS);
Status = DcReadConfiguration(Adapter);
if (Status != NDIS_STATUS_SUCCESS)
{
goto Failure;
}
Status = DcAllocateMemory(Adapter);
if (Status != NDIS_STATUS_SUCCESS)
{
goto Failure;
}
DcInitTestPacket(Adapter);
DcCreateRxRing(Adapter);
/* Execute the reset sequence */
if (Adapter->ResetStreamLength)
{
for (i = 0; i < Adapter->ResetStreamLength; ++i)
{
DcWriteGpio(Adapter, Adapter->ResetStream[i]);
NdisMSleep(100);
}
/* Give the PHY some time to reset */
NdisMSleep(5000);
}
/* Perform a software reset */
DC_WRITE(Adapter, DcCsr0_BusMode, DC_BUS_MODE_SOFT_RESET);
NdisMSleep(100);
DC_WRITE(Adapter, DcCsr0_BusMode, Adapter->BusMode);
/* Try to detect a MII PHY */
if (Adapter->Features & DC_HAS_MII)
{
MediaSelectMiiPort(Adapter, TRUE);
Success = DcFindMiiPhy(Adapter);
if (Success)
{
/* Disable all link interrupts when the MII PHY media is found */
Adapter->InterruptMask &= ~(DC_IRQ_LINK_CHANGED | DC_IRQ_LINK_FAIL | DC_IRQ_LINK_PASS);
Adapter->LinkStateChangeMask = 0;
}
else
{
/* Incorrect EEPROM table or PHY is not connected, switch to a serial transceiver */
WARN("No PHY devices found\n");
Adapter->Features &= ~DC_HAS_MII;
}
}
MediaInitDefaultMedia(Adapter, Adapter->DefaultMedia);
/* Set the MAC address */
DcSetupFrameInitialize(Adapter);
/* Clear statistics */
DC_READ(Adapter, DcCsr8_RxCounters);
Adapter->Flags |= DC_FIRST_SETUP;
Status = DcSetupAdapter(Adapter);
if (Status != NDIS_STATUS_SUCCESS)
{
ERR("Failed to initialize the NIC\n");
goto Disable;
}
Adapter->Flags &= ~DC_FIRST_SETUP;
Status = NdisMRegisterInterrupt(&Adapter->Interrupt,
Adapter->AdapterHandle,
Adapter->InterruptVector,
Adapter->InterruptLevel,
TRUE, /* Request ISR calls */
!!(Adapter->Flags & DC_IRQ_SHARED),
(Adapter->InterruptFlags & CM_RESOURCE_INTERRUPT_LATCHED) ?
NdisInterruptLatched : NdisInterruptLevelSensitive);
if (Status != NDIS_STATUS_SUCCESS)
{
ERR("Unable to register interrupt\n");
goto Disable;
}
DcStartAdapter(Adapter);
return NDIS_STATUS_SUCCESS;
Disable:
DcDisableHw(Adapter);
Failure:
ERR("Initialization failed with status %08lx\n", Status);
DcFreeAdapter(Adapter);
return Status;
}