diff --git a/boot/bootdata/packages/reactos.dff.in b/boot/bootdata/packages/reactos.dff.in index dbf0737ff83..04ad4dc83a0 100644 --- a/boot/bootdata/packages/reactos.dff.in +++ b/boot/bootdata/packages/reactos.dff.in @@ -138,8 +138,6 @@ Signature = "$Windows NT$" "modules/optional/bcmwl5.sys" 3 optional "modules/optional/alcxwdm.inf" 6 optional "modules/optional/alcxwdm.sys" 3 optional -"modules/optional/net21x4.inf" 6 optional -"modules/optional/dc21x4.sys" 3 optional "modules/optional/mfc42.dll" 2 optional "modules/optional/mfc42u.dll" 2 optional "modules/optional/mfc71.dll" 2 optional diff --git a/drivers/network/dd/CMakeLists.txt b/drivers/network/dd/CMakeLists.txt index f8b967412a3..122b82823f7 100644 --- a/drivers/network/dd/CMakeLists.txt +++ b/drivers/network/dd/CMakeLists.txt @@ -1,4 +1,5 @@ +add_subdirectory(dc21x4) add_subdirectory(e1000) add_subdirectory(ne2000) add_subdirectory(netkvm) diff --git a/drivers/network/dd/dc21x4/CMakeLists.txt b/drivers/network/dd/dc21x4/CMakeLists.txt new file mode 100644 index 00000000000..74f6927076f --- /dev/null +++ b/drivers/network/dd/dc21x4/CMakeLists.txt @@ -0,0 +1,40 @@ + +add_definitions( + -DNDIS_MINIPORT_DRIVER + -DNDIS51_MINIPORT=1) + +list(APPEND SOURCE + dc21x4.c + dc21x4.h + dc21x4hw.h + debug.h + eeprom.c + eeprom.h + eeprom_data.c + hardware.c + init.c + interrupt.c + media.c + media040.c + media041.c + media140.c + media143.c + phy.c + power.c + requests.c + send.c + util.h) + +if(DBG) + list(APPEND SOURCE debug.c) +endif() + +add_library(dc21x4 MODULE ${SOURCE} dc21x4.rc) +if(DBG) + target_link_libraries(dc21x4 memcmp) +endif() +add_pch(dc21x4 dc21x4.h SOURCE) +set_module_type(dc21x4 kernelmodedriver) +add_importlibs(dc21x4 ndis ntoskrnl hal) +add_cd_file(TARGET dc21x4 DESTINATION reactos/system32/drivers FOR all) +add_driver_inf(dc21x4 net21x4.inf) diff --git a/drivers/network/dd/dc21x4/dc21x4.c b/drivers/network/dd/dc21x4/dc21x4.c new file mode 100644 index 00000000000..f061c7f5fd5 --- /dev/null +++ b/drivers/network/dd/dc21x4/dc21x4.c @@ -0,0 +1,401 @@ +/* + * PROJECT: ReactOS DC21x4 Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Miniport driver entry + * COPYRIGHT: Copyright 2023 Dmitry Borisov + */ + +/* INCLUDES *******************************************************************/ + +#include "dc21x4.h" + +#include + +/* FUNCTIONS ******************************************************************/ + +ULONG +DcEthernetCrc( + _In_reads_bytes_(Size) const VOID* Buffer, + _In_ ULONG Size) +{ + ULONG i, j, Crc; + const UCHAR* Data = Buffer; + + Crc = 0xFFFFFFFF; + for (i = 0; i < Size; ++i) + { + Crc ^= Data[i]; + for (j = 8; j > 0; j--) + { + /* CRC-32 polynomial little-endian */ + Crc = (Crc >> 1) ^ (-(LONG)(Crc & 1) & 0xEDB88320); + } + } + + return Crc; +} + +static +VOID +DcFlushTransmitQueue( + _In_ PDC21X4_ADAPTER Adapter) +{ + LIST_ENTRY DoneList; + PLIST_ENTRY Entry; + PNDIS_PACKET Packet; + PDC_TCB Tcb; + + InitializeListHead(&DoneList); + + NdisAcquireSpinLock(&Adapter->SendLock); + + /* Remove pending transmissions from the transmit ring */ + for (Tcb = Adapter->LastTcb; + Tcb != Adapter->CurrentTcb; + Tcb = DC_NEXT_TCB(Adapter, Tcb)) + { + Packet = Tcb->Packet; + + if (!Packet) + continue; + + InsertTailList(&DoneList, DC_LIST_ENTRY_FROM_PACKET(Packet)); + + DC_RELEASE_TCB(Adapter, Tcb); + } + Adapter->CurrentTcb = Tcb; + + /* Remove pending transmissions from the internal queue */ + while (!IsListEmpty(&Adapter->SendQueueList)) + { + Entry = RemoveHeadList(&Adapter->SendQueueList); + + InsertTailList(&DoneList, Entry); + } + + NdisReleaseSpinLock(&Adapter->SendLock); + + while (!IsListEmpty(&DoneList)) + { + Entry = RemoveHeadList(&DoneList); + + NdisMSendComplete(Adapter->AdapterHandle, + DC_PACKET_FROM_LIST_ENTRY(Entry), + NDIS_STATUS_FAILURE); + } +} + +static +VOID +DcStopReceivePath( + _In_ PDC21X4_ADAPTER Adapter) +{ + BOOLEAN RxStopped; + + ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); + +#if DBG + NdisAcquireSpinLock(&Adapter->ReceiveLock); + if (Adapter->RcbFree != Adapter->RcbCount) + { + INFO("RX packets: %u/%u\n", Adapter->RcbFree, Adapter->RcbCount); + } + NdisReleaseSpinLock(&Adapter->ReceiveLock); +#endif + + while (TRUE) + { + NdisAcquireSpinLock(&Adapter->ReceiveLock); + + RxStopped = (Adapter->RcbFree == Adapter->RcbCount); + + NdisReleaseSpinLock(&Adapter->ReceiveLock); + + if (RxStopped) + break; + + NdisMSleep(10); + } +} + +DECLSPEC_NOINLINE /* Called from pageable code */ +VOID +DcStopAdapter( + _In_ PDC21X4_ADAPTER Adapter, + _In_ BOOLEAN WaitForPackets) +{ + BOOLEAN TimerCancelled; + + /* Attempt to disable interrupts to complete more quickly */ + DC_WRITE(Adapter, DcCsr7_IrqMask, 0); + + /* Prevent DPCs from executing and stop accepting incoming packets */ + NdisAcquireSpinLock(&Adapter->SendLock); + Adapter->Flags &= ~DC_ACTIVE; + NdisReleaseSpinLock(&Adapter->SendLock); + + NdisMCancelTimer(&Adapter->MediaMonitorTimer, &TimerCancelled); + + /* Wait for any DPCs to complete */ + KeFlushQueuedDpcs(); + + /* Disable interrupts */ + DC_WRITE(Adapter, DcCsr7_IrqMask, 0); + + /* Wait for completion of TX/RX and stop the DMA engine inside the NIC */ + DcStopTxRxProcess(Adapter); + Adapter->OpMode &= ~(DC_OPMODE_RX_ENABLE | DC_OPMODE_TX_ENABLE); + + DcFlushTransmitQueue(Adapter); + + /* Wait for the packets to be returned to the driver */ + if (WaitForPackets) + { + DcStopReceivePath(Adapter); + } + + /* Make sure there is no pending OID request */ + if (Adapter->OidPending) + { + NdisMSetInformationComplete(Adapter->AdapterHandle, NDIS_STATUS_SUCCESS); + + Adapter->OidPending = FALSE; + } +} + +CODE_SEG("PAGE") +VOID +DcStartAdapter( + _In_ PDC21X4_ADAPTER Adapter) +{ + PAGED_CODE(); + + /* Enable interrupts */ + _InterlockedExchange((PLONG)&Adapter->CurrentInterruptMask, Adapter->InterruptMask); + DC_WRITE(Adapter, DcCsr7_IrqMask, Adapter->InterruptMask); + + Adapter->Flags |= DC_ACTIVE; + + /* Start the RX process */ + Adapter->OpMode |= DC_OPMODE_RX_ENABLE; + DC_WRITE(Adapter, DcCsr6_OpMode, Adapter->OpMode); + + /* Start the media monitor, wait the selected media to become ready */ + NdisMSetTimer(&Adapter->MediaMonitorTimer, 2400); +} + +CODE_SEG("PAGE") +VOID +NTAPI +DcResetWorker( + _In_ PNDIS_WORK_ITEM WorkItem, + _In_opt_ PVOID Context) +{ + PDC21X4_ADAPTER Adapter = Context; + NDIS_STATUS Status; + ULONG InterruptStatus; + LONG ResetReason; + + UNREFERENCED_PARAMETER(WorkItem); + + PAGED_CODE(); + + Status = NDIS_STATUS_SUCCESS; + + /* Check if the device is present */ + InterruptStatus = DC_READ(Adapter, DcCsr5_Status); + if (InterruptStatus == 0xFFFFFFFF) + { + ERR("Hardware is gone...\n"); + + /* Remove this adapter */ + NdisMRemoveMiniport(Adapter->AdapterHandle); + + Status = NDIS_STATUS_HARD_ERRORS; + goto Done; + } + + DcStopAdapter(Adapter, FALSE); + + if (Adapter->LinkUp) + { + Adapter->LinkUp = FALSE; + + NdisMIndicateStatus(Adapter->AdapterHandle, + NDIS_STATUS_MEDIA_DISCONNECT, + NULL, + 0); + NdisMIndicateStatusComplete(Adapter->AdapterHandle); + } + + DcSetupAdapter(Adapter); + + DcStartAdapter(Adapter); + +Done: + ResetReason = _InterlockedExchange(&Adapter->ResetLock, 0); + + /* Complete the pending reset request */ + if (ResetReason == 1) + { + NdisMResetComplete(Adapter->AdapterHandle, Status, FALSE); + } +} + +VOID +NTAPI +DcTransmitTimeoutRecoveryWorker( + _In_ PNDIS_WORK_ITEM WorkItem, + _In_opt_ PVOID Context) +{ + PDC21X4_ADAPTER Adapter = Context; + + UNREFERENCED_PARAMETER(WorkItem); + + NdisAcquireSpinLock(&Adapter->ModeLock); + + DcStopTxRxProcess(Adapter); + DC_WRITE(Adapter, DcCsr6_OpMode, Adapter->OpMode); + + NdisDprAcquireSpinLock(&Adapter->SendLock); + + DC_WRITE(Adapter, DcCsr1_TxPoll, DC_TX_POLL_DOORBELL); + + NdisDprReleaseSpinLock(&Adapter->SendLock); + + NdisReleaseSpinLock(&Adapter->ModeLock); +} + +static +BOOLEAN +NTAPI +DcCheckForHang( + _In_ NDIS_HANDLE MiniportAdapterContext) +{ + PDC21X4_ADAPTER Adapter = (PDC21X4_ADAPTER)MiniportAdapterContext; + ULONG TcbCompleted; + BOOLEAN TxHang = FALSE; + + if (!(Adapter->Flags & DC_ACTIVE)) + return FALSE; + + NdisDprAcquireSpinLock(&Adapter->SendLock); + + if (Adapter->TcbSlots != (DC_TRANSMIT_BLOCKS - DC_TCB_RESERVE)) + { + TcbCompleted = Adapter->TcbCompleted; + TxHang = (TcbCompleted == Adapter->LastTcbCompleted); + Adapter->LastTcbCompleted = TcbCompleted; + } + + NdisDprReleaseSpinLock(&Adapter->SendLock); + + if (TxHang) + { + WARN("Transmit timeout, CSR12 %08lx, CSR5 %08lx\n", + DC_READ(Adapter, DcCsr12_SiaStatus), + DC_READ(Adapter, DcCsr5_Status)); + + NdisScheduleWorkItem(&Adapter->TxRecoveryWorkItem); + } + + return FALSE; +} + +static +NDIS_STATUS +NTAPI +DcReset( + _Out_ PBOOLEAN AddressingReset, + _In_ NDIS_HANDLE MiniportAdapterContext) +{ + PDC21X4_ADAPTER Adapter = (PDC21X4_ADAPTER)MiniportAdapterContext; + + WARN("Called\n"); + + if (_InterlockedCompareExchange(&Adapter->ResetLock, 1, 0)) + { + return NDIS_STATUS_RESET_IN_PROGRESS; + } + + NdisScheduleWorkItem(&Adapter->ResetWorkItem); + + return NDIS_STATUS_PENDING; +} + +static +CODE_SEG("PAGE") +VOID +NTAPI +DcHalt( + _In_ NDIS_HANDLE MiniportAdapterContext) +{ + PDC21X4_ADAPTER Adapter = (PDC21X4_ADAPTER)MiniportAdapterContext; + + PAGED_CODE(); + + INFO("Called\n"); + + DcStopAdapter(Adapter, TRUE); + + DcDisableHw(Adapter); + + DcFreeAdapter(Adapter); +} + +static +VOID +NTAPI +DcShutdown( + _In_ NDIS_HANDLE MiniportAdapterContext) +{ + PDC21X4_ADAPTER Adapter = (PDC21X4_ADAPTER)MiniportAdapterContext; + + INFO("Called\n"); + + DcDisableHw(Adapter); +} + +CODE_SEG("INIT") +NTSTATUS +NTAPI +DriverEntry( + _In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath) +{ + NDIS_HANDLE WrapperHandle; + NDIS_STATUS Status; + NDIS_MINIPORT_CHARACTERISTICS Characteristics = { 0 }; + + INFO("Called\n"); + + NdisMInitializeWrapper(&WrapperHandle, DriverObject, RegistryPath, NULL); + if (!WrapperHandle) + return NDIS_STATUS_FAILURE; + + Characteristics.MajorNdisVersion = NDIS_MINIPORT_MAJOR_VERSION; + Characteristics.MinorNdisVersion = NDIS_MINIPORT_MINOR_VERSION; + Characteristics.CheckForHangHandler = DcCheckForHang; + Characteristics.HaltHandler = DcHalt; + Characteristics.HandleInterruptHandler = DcHandleInterrupt; + Characteristics.InitializeHandler = DcInitialize; + Characteristics.ISRHandler = DcIsr; + Characteristics.QueryInformationHandler = DcQueryInformation; + Characteristics.ResetHandler = DcReset; + Characteristics.SetInformationHandler = DcSetInformation; + Characteristics.ReturnPacketHandler = DcReturnPacket; + Characteristics.SendPacketsHandler = DcSendPackets; + Characteristics.CancelSendPacketsHandler = DcCancelSendPackets; + Characteristics.AdapterShutdownHandler = DcShutdown; + + Status = NdisMRegisterMiniport(WrapperHandle, &Characteristics, sizeof(Characteristics)); + if (Status != NDIS_STATUS_SUCCESS) + { + NdisTerminateWrapper(WrapperHandle, NULL); + return Status; + } + + InitializeListHead(&SRompAdapterList); + + return NDIS_STATUS_SUCCESS; +} diff --git a/drivers/network/dd/dc21x4/dc21x4.h b/drivers/network/dd/dc21x4/dc21x4.h new file mode 100644 index 00000000000..20d62fc15ae --- /dev/null +++ b/drivers/network/dd/dc21x4/dc21x4.h @@ -0,0 +1,541 @@ +/* + * PROJECT: ReactOS DC21x4 Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Main header file + * COPYRIGHT: Copyright 2023 Dmitry Borisov + */ + +#pragma once + +#if !DBG +#define NO_KERNEL_LIST_ENTRY_CHECKS +#endif +#include +#include + +#include "dc21x4hw.h" +#include "eeprom.h" +#include "media.h" +#include "util.h" + +#define DC21X4_TAG '4x12' + +#define DC_TRANSMIT_DESCRIPTORS 64 +#define DC_TRANSMIT_BLOCKS 48 +#define DC_TRANSMIT_BUFFERS 4 +#define DC_LOOPBACK_FRAMES 4 + +#define DC_RECEIVE_BUFFERS_DEFAULT 64 +#define DC_RECEIVE_BUFFERS_MIN 8 +#define DC_RECEIVE_BUFFERS_EXTRA 16 + +#define DC_RECEIVE_ARRAY_SIZE 16 + +#define DC_MULTICAST_LIST_SIZE 36 + +#define DC_MAXIMUM_FRAME_SIZE 1514 +#define DC_TRANSMIT_BLOCK_SIZE 1536 +#define DC_RECEIVE_BLOCK_SIZE 1536 +#define DC_ETHERNET_HEADER_SIZE 14 + +#define DC_TX_UNDERRUN_LIMIT 5 +#define DC_INTERRUPT_PROCESSING_LIMIT 8 + +#define DC_FRAGMENTATION_THRESHOLD 32 + +#define DC_PACKET_FILTERS ( \ + NDIS_PACKET_TYPE_DIRECTED | \ + NDIS_PACKET_TYPE_MULTICAST | \ + NDIS_PACKET_TYPE_BROADCAST | \ + NDIS_PACKET_TYPE_PROMISCUOUS | \ + NDIS_PACKET_TYPE_ALL_MULTICAST) + +#define DC_LOOPBACK_FRAME_SIZE 64 + +/* Transmit descriptors reserved for internal use */ +#define DC_TBD_RESERVE (2 + DC_LOOPBACK_FRAMES) /* (+2 for setup frame) */ +#define DC_TCB_RESERVE (1 + DC_LOOPBACK_FRAMES) /* (+1 for setup frame) */ + +#define DC_EVENT_SETUP_FRAME_COMPLETED 0x00000001 + +typedef struct _DC21X4_ADAPTER DC21X4_ADAPTER, *PDC21X4_ADAPTER; +typedef struct _DC_TCB DC_TCB, *PDC_TCB; +typedef struct _DC_RCB DC_RCB, *PDC_RCB; +typedef struct _DC_COALESCE_BUFFER DC_COALESCE_BUFFER, *PDC_COALESCE_BUFFER; + +typedef VOID +(MEDIA_HANDLE_LINK_STATE_CHANGE)( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG InterruptStatus); +typedef MEDIA_HANDLE_LINK_STATE_CHANGE *PMEDIA_HANDLE_LINK_STATE_CHANGE; + +typedef struct _DC_TX_BUFFER_DATA +{ + PVOID VirtualAddress; + NDIS_PHYSICAL_ADDRESS PhysicalAddress; +} DC_TX_BUFFER_DATA, *PDC_TX_BUFFER_DATA; + +typedef struct _DC_STATISTICS +{ + ULONG64 TransmitOk; + ULONG64 TransmitDeferred; + ULONG64 TransmitHeartbeatErrors; + ULONG64 TransmitOneRetry; + ULONG64 TransmitMoreCollisions; + ULONG64 TransmitErrors; + ULONG64 TransmitExcessiveCollisions; + ULONG64 TransmitUnderrunErrors; + ULONG64 TransmitLostCarrierSense; + ULONG64 TransmitLateCollisions; + ULONG64 ReceiveOk; + ULONG64 ReceiveBroadcast; + ULONG64 ReceiveMulticast; + ULONG64 ReceiveUnicast; + ULONG64 ReceiveErrors; + ULONG64 ReceiveOverrunErrors; + ULONG64 ReceiveNoBuffers; + ULONG64 ReceiveCrcErrors; + ULONG64 ReceiveAlignmentErrors; +} DC_STATISTICS, *PDC_STATISTICS; + +typedef struct _DC21X4_ADAPTER +{ + PUCHAR IoBase; + ULONG InterruptMask; + ULONG CurrentInterruptMask; + + ULONG Features; +#define DC_NEED_RX_OVERFLOW_WORKAROUND 0x80000000 +#define DC_SIA_GPIO 0x00000001 +#define DC_SIA_ANALOG_CONTROL 0x00000002 +#define DC_HAS_POWER_MANAGEMENT 0x00000004 +#define DC_HAS_POWER_SAVING 0x00000008 +#define DC_HAS_MII 0x00000010 +#define DC_PERFECT_FILTERING_ONLY 0x00000020 +#define DC_ENABLE_PCI_COMMANDS 0x00000040 +#define DC_MII_AUTOSENSE 0x00000080 +#define DC_HAS_TIMER 0x00000100 + + ULONG Flags; +#define DC_ACTIVE 0x80000000 +#define DC_IO_MAPPED 0x00000001 +#define DC_IRQ_SHARED 0x00000002 +#define DC_FIRST_SETUP 0x00000004 +#define DC_AUTOSENSE 0x00000008 + + ULONG InterruptStatus; + PMEDIA_HANDLE_LINK_STATE_CHANGE HandleLinkStateChange; + + DECLSPEC_CACHEALIGN NDIS_SPIN_LOCK SendLock; + PDC_TCB TailTcb; + PDC_TCB LastTcb; + PDC_TCB CurrentTcb; + PDC_TBD CurrentTbd; + PDC_TBD HeadTbd; + PDC_TBD TailTbd; + LIST_ENTRY SendQueueList; + ULONG TcbSlots; + ULONG TbdSlots; + ULONG TcbCompleted; + ULONG LastTcbCompleted; + PDC_TCB HeadTcb; + SINGLE_LIST_ENTRY SendBufferList; + SCATTER_GATHER_LIST LocalSgList; + + DECLSPEC_CACHEALIGN NDIS_SPIN_LOCK ReceiveLock; + PDC_RCB* RcbArray; + PDC_RBD CurrentRbd; + PDC_RBD HeadRbd; + PDC_RBD TailRbd; + SINGLE_LIST_ENTRY FreeRcbList; + ULONG RcbFree; + + ULONG TransmitUnderruns; + ULONG PacketFilter; + + DC_STATISTICS Statistics; + + NDIS_HANDLE AdapterHandle; + NDIS_HANDLE WrapperConfigurationHandle; + + DECLSPEC_CACHEALIGN NDIS_SPIN_LOCK ModeLock; + ULONG ModeFlags; +#define DC_MODE_AUTONEG_MASK 0x0000000F +#define DC_MODE_PORT_AUTOSENSE 0x00000010 +#define DC_MODE_TEST_PACKET 0x00000020 +#define DC_MODE_AUI_FAILED 0x00000040 +#define DC_MODE_BNC_FAILED 0x00000080 + +#define DC_MODE_AUTONEG_NONE 0x00000000 +#define DC_MODE_AUTONEG_WAIT_INTERRUPT 0x00000001 +#define DC_MODE_AUTONEG_LINK_STATUS_CHECK 0x00000002 + + ULONG OpMode; + ULONG MediaNumber; + ULONG MediaBitmap; + BOOLEAN LinkUp; + ULONG PhyAddress; + ULONG SiaSetting; + ULONG LastReceiveActivity; + volatile LONG MediaTestStatus; + NDIS_MINIPORT_TIMER MediaMonitorTimer; + DC_MII_MEDIA MiiMedia; + DC_MEDIA Media[MEDIA_LIST_MAX]; + + ULONG AnalogControl; + ULONG SymAdvertising; + ULONG MiiAdvertising; + ULONG MiiControl; + DC_CHIP_TYPE ChipType; + ULONG LinkStateChangeMask; + + ULONG WakeUpFlags; + NDIS_DEVICE_POWER_STATE PowerState; + NDIS_DEVICE_POWER_STATE PrevPowerState; + + ULONG HpnaInitBitmap; + UCHAR HpnaRegister[32]; + + UCHAR PermanentMacAddress[ETH_LENGTH_OF_ADDRESS]; + UCHAR CurrentMacAddress[ETH_LENGTH_OF_ADDRESS]; + + ULONG MulticastMaxEntries; + _Field_range_(0, MulticastMaxEntries) + ULONG MulticastCount; + struct + { + UCHAR MacAddress[ETH_LENGTH_OF_ADDRESS]; + } MulticastList[DC_MULTICAST_LIST_SIZE]; + + ULONG LinkSpeedMbps; + ULONG BusMode; + ULONG DefaultMedia; + ULONG RcbCount; + BOOLEAN OidPending; + BOOLEAN ProgramHashPerfectFilter; + PULONG SetupFrame; + PULONG SetupFrameSaved; + ULONG SetupFramePhys; + ULONG LoopbackFrameSlots; + ULONG LoopbackFrameNumber; + ULONG LoopbackFramePhys[DC_LOOPBACK_FRAMES]; + ULONG BusNumber; + UCHAR DeviceNumber; + UCHAR RevisionId; + UCHAR ControllerIndex; + UCHAR ResetStreamLength; + USHORT ResetStream[SROM_MAX_STREAM_REGS]; + USHORT DeviceId; + SINGLE_LIST_ENTRY AllocRcbList; + SINGLE_LIST_ENTRY UsedRcbList; + NDIS_MINIPORT_INTERRUPT Interrupt; + ULONG InterruptVector; + ULONG InterruptLevel; + ULONG InterruptFlags; + ULONG AdapterSize; + NDIS_WORK_ITEM PowerWorkItem; + NDIS_WORK_ITEM ResetWorkItem; + NDIS_WORK_ITEM TxRecoveryWorkItem; + _Interlocked_ volatile LONG ResetLock; + NDIS_PHYSICAL_ADDRESS IoBaseAddress; + PDC_SROM_ENTRY SRomEntry; + PVOID AdapterOriginal; + PVOID TbdOriginal; + PVOID RbdOriginal; + ULONG TbdPhys; + ULONG RbdPhys; + NDIS_HANDLE BufferPool; + NDIS_HANDLE PacketPool; + NDIS_PHYSICAL_ADDRESS TbdPhysOriginal; + NDIS_PHYSICAL_ADDRESS RbdPhysOriginal; + PVOID LoopbackFrame[DC_LOOPBACK_FRAMES]; + PDC_COALESCE_BUFFER CoalesceBuffer; + DC_TX_BUFFER_DATA SendBufferData[DC_TRANSMIT_BUFFERS]; +} DC21X4_ADAPTER, *PDC21X4_ADAPTER; + +#include "sendrcv.h" + +extern LIST_ENTRY SRompAdapterList; + +FORCEINLINE +ULONG +DC_READ( + _In_ PDC21X4_ADAPTER Adapter, + _In_ DC_CSR Register) +{ + ULONG Value; + + NdisRawReadPortUlong((PULONG)(Adapter->IoBase + Register), &Value); + return Value; +} + +#define DC_WRITE(Adapter, Register, Value) \ + NdisRawWritePortUlong((PULONG)((Adapter)->IoBase + (Register)), (Value)); + +CODE_SEG("INIT") +DRIVER_INITIALIZE DriverEntry; + +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); + +VOID +NTAPI +DcSendPackets( + _In_ NDIS_HANDLE MiniportAdapterContext, + _In_ PPNDIS_PACKET PacketArray, + _In_ UINT NumberOfPackets); + +VOID +NTAPI +DcCancelSendPackets( + _In_ NDIS_HANDLE MiniportAdapterContext, + _In_ PVOID CancelId); + +VOID +DcProcessPendingPackets( + _In_ PDC21X4_ADAPTER Adapter); + +VOID +NTAPI +DcReturnPacket( + _In_ NDIS_HANDLE MiniportAdapterContext, + _In_ PNDIS_PACKET Packet); + +NDIS_STATUS +NTAPI +DcQueryInformation( + _In_ NDIS_HANDLE MiniportAdapterContext, + _In_ NDIS_OID Oid, + _In_ PVOID InformationBuffer, + _In_ ULONG InformationBufferLength, + _Out_ PULONG BytesWritten, + _Out_ PULONG BytesNeeded); + +NDIS_STATUS +NTAPI +DcSetInformation( + _In_ NDIS_HANDLE MiniportAdapterContext, + _In_ NDIS_OID Oid, + _In_ PVOID InformationBuffer, + _In_ ULONG InformationBufferLength, + _Out_ PULONG BytesRead, + _Out_ PULONG BytesNeeded); + +VOID +NTAPI +DcIsr( + _Out_ PBOOLEAN InterruptRecognized, + _Out_ PBOOLEAN QueueMiniportHandleInterrupt, + _In_ NDIS_HANDLE MiniportAdapterContext); + +VOID +NTAPI +DcHandleInterrupt( + _In_ NDIS_HANDLE MiniportAdapterContext); + +CODE_SEG("PAGE") +VOID +NTAPI +DcPowerWorker( + _In_ PNDIS_WORK_ITEM WorkItem, + _In_opt_ PVOID Context); + +NDIS_STATUS +DcSetPower( + _In_ PDC21X4_ADAPTER Adapter, + _In_ NDIS_DEVICE_POWER_STATE PowerState); + +NDIS_STATUS +DcAddWakeUpPattern( + _In_ PDC21X4_ADAPTER Adapter, + _In_ PNDIS_PM_PACKET_PATTERN PmPattern); + +NDIS_STATUS +DcRemoveWakeUpPattern( + _In_ PDC21X4_ADAPTER Adapter, + _In_ PNDIS_PM_PACKET_PATTERN PmPattern); + +CODE_SEG("PAGE") +VOID +DcFreeAdapter( + _In_ __drv_freesMem(Mem) PDC21X4_ADAPTER Adapter); + +CODE_SEG("PAGE") +VOID +NTAPI +DcResetWorker( + _In_ PNDIS_WORK_ITEM WorkItem, + _In_opt_ PVOID Context); + +DECLSPEC_NOINLINE +VOID +DcStopAdapter( + _In_ PDC21X4_ADAPTER Adapter, + _In_ BOOLEAN WaitForPackets); + +CODE_SEG("PAGE") +VOID +DcStartAdapter( + _In_ PDC21X4_ADAPTER Adapter); + +CODE_SEG("PAGE") +NDIS_STATUS +DcSetupAdapter( + _In_ PDC21X4_ADAPTER Adapter); + +CODE_SEG("PAGE") +NDIS_STATUS +DcReadEeprom( + _In_ PDC21X4_ADAPTER Adapter); + +CODE_SEG("PAGE") +VOID +DcFreeEeprom( + _In_ PDC21X4_ADAPTER Adapter); + +CODE_SEG("PAGE") +VOID +DcInitTxRing( + _In_ PDC21X4_ADAPTER Adapter); + +CODE_SEG("PAGE") +VOID +DcInitRxRing( + _In_ PDC21X4_ADAPTER Adapter); + +ULONG +DcEthernetCrc( + _In_reads_bytes_(Size) const VOID* Buffer, + _In_ ULONG Size); + +VOID +DcDisableHw( + _In_ PDC21X4_ADAPTER Adapter); + +VOID +DcStopTxRxProcess( + _In_ PDC21X4_ADAPTER Adapter); + +VOID +DcWriteGpio( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG Value); + +VOID +DcWriteSia( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG Csr13, + _In_ ULONG Csr14, + _In_ ULONG Csr15); + +VOID +DcTestPacket( + _In_ PDC21X4_ADAPTER Adapter); + +CODE_SEG("PAGE") +VOID +DcSetupFrameInitialize( + _In_ PDC21X4_ADAPTER Adapter); + +BOOLEAN +DcSetupFrameDownload( + _In_ PDC21X4_ADAPTER Adapter, + _In_ BOOLEAN WaitForCompletion); + +NDIS_STATUS +DcApplyPacketFilter( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG PacketFilter); + +NDIS_STATUS +DcUpdateMulticastList( + _In_ PDC21X4_ADAPTER Adapter); + +VOID +DcPowerSave( + _In_ PDC21X4_ADAPTER Adapter, + _In_ BOOLEAN Enable); + +CODE_SEG("PAGE") +BOOLEAN +DcFindMiiPhy( + _In_ PDC21X4_ADAPTER Adapter); + +BOOLEAN +MiiWrite( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG PhyAddress, + _In_ ULONG RegAddress, + _In_ ULONG Data); + +BOOLEAN +MiiRead( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG PhyAddress, + _In_ ULONG RegAddress, + _Out_ PULONG Data); + +CODE_SEG("PAGE") +VOID +HpnaPhyInit( + _In_ PDC21X4_ADAPTER Adapter); + +VOID +NTAPI +DcTransmitTimeoutRecoveryWorker( + _In_ PNDIS_WORK_ITEM WorkItem, + _In_opt_ PVOID Context); + +NDIS_TIMER_FUNCTION MediaMonitor21040Dpc; +NDIS_TIMER_FUNCTION MediaMonitor21041Dpc; +NDIS_TIMER_FUNCTION MediaMonitor21140Dpc; +NDIS_TIMER_FUNCTION MediaMonitor21143Dpc; + +MEDIA_HANDLE_LINK_STATE_CHANGE MediaLinkStateChange21040; +MEDIA_HANDLE_LINK_STATE_CHANGE MediaLinkStateChange21041; +MEDIA_HANDLE_LINK_STATE_CHANGE MediaLinkStateChange21143; + +CODE_SEG("PAGE") +VOID +MediaInitMediaList( + _In_ PDC21X4_ADAPTER Adapter); + +CODE_SEG("PAGE") +VOID +MediaInitDefaultMedia( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG MediaNumber); + +VOID +MediaIndicateConnect( + _In_ PDC21X4_ADAPTER Adapter, + _In_ BOOLEAN LinkUp); + +VOID +MediaSelectMiiPort( + _In_ PDC21X4_ADAPTER Adapter, + _In_ BOOLEAN ResetPhy); + +VOID +MediaMiiSelect( + _In_ PDC21X4_ADAPTER Adapter); + +BOOLEAN +MediaMiiCheckLink( + _In_ PDC21X4_ADAPTER Adapter); + +VOID +MediaSiaSelect( + _In_ PDC21X4_ADAPTER Adapter); + +VOID +MediaGprSelect( + _In_ PDC21X4_ADAPTER Adapter); diff --git a/drivers/network/dd/dc21x4/dc21x4.rc b/drivers/network/dd/dc21x4/dc21x4.rc new file mode 100644 index 00000000000..1ecd5c02f7d --- /dev/null +++ b/drivers/network/dd/dc21x4/dc21x4.rc @@ -0,0 +1,5 @@ +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "DC21x4 Ethernet Adapter Driver" +#define REACTOS_STR_INTERNAL_NAME "dc21x4" +#define REACTOS_STR_ORIGINAL_FILENAME "dc21x4.sys" +#include diff --git a/drivers/network/dd/dc21x4/dc21x4hw.h b/drivers/network/dd/dc21x4/dc21x4hw.h new file mode 100644 index 00000000000..fc53adc1b9d --- /dev/null +++ b/drivers/network/dd/dc21x4/dc21x4hw.h @@ -0,0 +1,597 @@ +/* + * PROJECT: ReactOS DC21x4 Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Hardware specific definitions + * COPYRIGHT: Copyright 2023 Dmitry Borisov + */ + +#pragma once + +typedef enum _DC_CHIP_TYPE +{ + DC21040, + DC21041, + DC21140, + DC21143, + DC21145, +} DC_CHIP_TYPE; + +/* + * PCI Vendor and Device IDs + */ +#define DC_DEV_DECCHIP_21040 0x00021011 +#define DC_DEV_DECCHIP_21041 0x00141011 +#define DC_DEV_DECCHIP_21140 0x00091011 +#define DC_DEV_INTEL_21143 0x00191011 +#define DC_DEV_INTEL_21145 0x00398086 + +#define DC_DESCRIPTOR_ALIGNMENT 4 +#define DC_SETUP_FRAME_ALIGNMENT 4 +#define DC_RECEIVE_BUFFER_ALIGNMENT 4 +#define DC_RECEIVE_BUFFER_SIZE_MULTIPLE 4 + +#define DC_IO_LENGTH 128 + +#define DC_SETUP_FRAME_SIZE 192 + +/* Multicast perfect filter */ +#define DC_SETUP_FRAME_PERFECT_FILTER_ADDRESSES 16 + +/* -1 for physical address and -1 for broadcast address */ +#define DC_SETUP_FRAME_ADDRESSES (16 - 2) + +/* Computed hash of FF:FF:FF:FF:FF:FF */ +#define DC_SETUP_FRAME_BROADCAST_HASH 0xFF + +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +#define DC_SETUP_FRAME_ENTRY(Value) (Value) +#else +#define DC_SETUP_FRAME_ENTRY(Value) ((Value) << 16) +#endif + +#include + +/* + * Transmit Buffer Descriptor + */ +typedef struct _DC_TBD +{ + ULONG Status; +#define DC_TBD_STATUS_DEFFERED 0x00000001 +#define DC_TBD_STATUS_UNDERFLOW 0x00000002 +#define DC_TBD_STATUS_LINK_FAIL 0x00000004 +#define DC_TBD_STATUS_COLLISIONS_MASK 0x00000078 +#define DC_TBD_STATUS_HEARTBEAT_FAIL 0x00000080 +#define DC_TBD_STATUS_RETRY_ERROR 0x00000100 +#define DC_TBD_STATUS_LATE_COLLISION 0x00000200 +#define DC_TBD_STATUS_NO_CARRIER 0x00000400 +#define DC_TBD_STATUS_CARRIER_LOST 0x00000800 +#define DC_TBD_STATUS_JABBER_TIMEOUT 0x00004000 +#define DC_TBD_STATUS_ERROR_SUMMARY 0x00008000 +#define DC_TBD_STATUS_OWNED 0x80000000 + +#define DC_TBD_STATUS_SETUP_FRAME 0x7FFFFFFF + +#define DC_TBD_STATUS_COLLISIONS_SHIFT 3 + + ULONG Control; +#define DC_TBD_CONTROL_LENGTH_MASK_1 0x000007FF +#define DC_TBD_CONTROL_LENGTH_MASK_2 0x003FF800 +#define DC_TBD_CONTROL_NO_PAD 0x00800000 +#define DC_TBD_CONTROL_CHAINED 0x01000000 +#define DC_TBD_CONTROL_END_OF_RING 0x02000000 +#define DC_TBD_CONTROL_NO_CRC 0x04000000 +#define DC_TBD_CONTROL_SETUP_FRAME 0x08000000 +#define DC_TBD_CONTROL_FIRST_FRAGMENT 0x20000000 +#define DC_TBD_CONTROL_LAST_FRAGMENT 0x40000000 +#define DC_TBD_CONTROL_REQUEST_INTERRUPT 0x80000000 +#define DC_TBD_CONTROL_PERFECT_FILTER 0x00000000 +#define DC_TBD_CONTROL_HASH_PERFECT_FILTER 0x00400000 +#define DC_TBD_CONTROL_INVERSE_FILTER 0x10000000 +#define DC_TBD_CONTROL_IMPERFECT_FILTER 0x10400000 + +#define DC_TBD_CONTROL_LENGTH_2_SHIFT 11 + + ULONG Address1; + ULONG Address2; +} DC_TBD, *PDC_TBD; + +C_ASSERT(sizeof(DC_TBD) == 16); + +/* + * Receive Buffer Descriptor + */ +typedef struct _DC_RBD +{ + ULONG Status; +#define DC_RBD_STATUS_OVERRUN 0x00000001 +#define DC_RBD_STATUS_CRC_ERROR 0x00000002 +#define DC_RBD_STATUS_DRIBBLE 0x00000004 +#define DC_RBD_STATUS_MII_ERROR 0x00000008 +#define DC_RBD_STATUS_WDT_EXPIRED 0x00000010 +#define DC_RBD_STATUS_FRAME_TYPE 0x00000020 +#define DC_RBD_STATUS_COLLISION_SEEN 0x00000040 +#define DC_RBD_STATUS_TOO_LONG 0x00000080 +#define DC_RBD_STATUS_LAST_DESCRIPTOR 0x00000100 +#define DC_RBD_STATUS_FIRST_DESCRIPTOR 0x00000200 +#define DC_RBD_STATUS_MULTICAST 0x00000400 +#define DC_RBD_STATUS_RUNT 0x00000800 +#define DC_RBD_STATUS_DATA_TYPE_MASK 0x00003000 +#define DC_RBD_STATUS_LENGTH_ERROR 0x00004000 +#define DC_RBD_STATUS_ERROR_SUMMARY 0x00008000 +#define DC_RBD_STATUS_FRAME_LENGTH_MASK 0x3FFF0000 +#define DC_RBD_STATUS_FILTERING_FAIL 0x40000000 +#define DC_RBD_STATUS_OWNED 0x80000000 + +#define DC_RBD_STATUS_FRAME_LENGTH_SHIFT 16 + + ULONG Control; +#define DC_RBD_CONTROL_BUFFER_LENGTH_MASK_1 0x000007FF +#define DC_RBD_CONTROL_BUFFER_LENGTH_MASK_2 0x003FF800 +#define DC_RBD_CONTROL_CHAINED 0x01000000 +#define DC_RBD_CONTROL_END_OF_RING 0x02000000 + + ULONG Address1; + ULONG Address2; +} DC_RBD, *PDC_RBD; + +C_ASSERT(sizeof(DC_RBD) == 16); + +#define DC_PATTERN_FILTERS 4 + +/* + * Wake-Up Filter Register Block + */ +typedef union _DC_PATTERN_FILTER_BLOCK +{ + struct + { + ULONG Mask[DC_PATTERN_FILTERS]; + + UCHAR Command[DC_PATTERN_FILTERS]; +#define DC_PATTERN_FILTER_CMD_ENABLE 0x01 +#define DC_PATTERN_FILTER_CMD_INVERSE_MODE 0x02 +#define DC_PATTERN_FILTER_CMD_ADD_PREV 0x04 +#define DC_PATTERN_FILTER_CMD_MULTICAST 0x08 + + UCHAR Offset[DC_PATTERN_FILTERS]; + USHORT Crc[DC_PATTERN_FILTERS]; + }; + ULONG AsULONG[8]; +} DC_PATTERN_FILTER_BLOCK, *PDC_PATTERN_FILTER_BLOCK; + +C_ASSERT(sizeof(DC_PATTERN_FILTER_BLOCK) == 32); + +#include + +/* + * NIC Control and Status Registers + */ +typedef enum _DC_CSR +{ + DcCsr0_BusMode = 0x00, + DcCsr1_TxPoll = 0x08, + DcCsr1_WakeUpFilter = 0x08, + DcCsr2_RxPoll = 0x10, + DcCsr2_WakeUpControl = 0x10, + DcCsr3_RxRingAddress = 0x18, + DcCsr4_TxRingAddress = 0x20, + DcCsr5_Status = 0x28, + DcCsr6_OpMode = 0x30, + DcCsr7_IrqMask = 0x38, + DcCsr8_RxCounters = 0x40, + DcCsr9_SerialInterface = 0x48, + DcCsr10_BootRom = 0x50, + DcCsr11_FullDuplex = 0x58, + DcCsr11_Timer = 0x58, + DcCsr12_Gpio = 0x60, + DcCsr12_SiaStatus = 0x60, + DcCsr13_SiaConnectivity = 0x68, + DcCsr14_SiaTxRx = 0x70, + DcCsr15_SiaGeneral = 0x78, +} DC_CSR; + +/* + * CSR0 Bus Mode + */ +#define DC_BUS_MODE_SOFT_RESET 0x00000001 +#define DC_BUS_MODE_BUS_ARB 0x00000002 +#define DC_BUS_MODE_DESC_SKIP_LENGTH_MASK 0x0000007C +#define DC_BUS_MODE_BUFFERS_BIG_ENDIAN 0x00000080 +#define DC_BUS_MODE_BURST_LENGTH_MASK 0x00003F00 +#define DC_BUS_MODE_CACHE_ALIGNMENT_MASK 0x0000C000 +#define DC_BUS_MODE_DIAGNOSTIC_ADDRESS_SPACE 0x00010000 +#define DC_BUS_MODE_TX_POLL_MASK 0x000E0000 +#define DC_BUS_MODE_DESC_BIG_ENDIAN 0x00100000 +#define DC_BUS_MODE_READ_MULTIPLE 0x00200000 +#define DC_BUS_MODE_READ_LINE 0x00800000 +#define DC_BUS_MODE_WRITE_INVALIDATE 0x01000000 +#define DC_BUS_MODE_ON_NOW_UNLOCK 0x04000000 + +#define DC_BUS_MODE_BURST_LENGTH_NO_LIMIT 0x00000000 +#define DC_BUS_MODE_BURST_LENGTH_1 0x00000100 +#define DC_BUS_MODE_BURST_LENGTH_2 0x00000200 +#define DC_BUS_MODE_BURST_LENGTH_4 0x00000400 +#define DC_BUS_MODE_BURST_LENGTH_8 0x00000800 +#define DC_BUS_MODE_BURST_LENGTH_16 0x00001000 +#define DC_BUS_MODE_BURST_LENGTH_32 0x00002000 + +#define DC_BUS_MODE_CACHE_ALIGNMENT_NONE 0x00000000 +#define DC_BUS_MODE_CACHE_ALIGNMENT_8 0x00004000 +#define DC_BUS_MODE_CACHE_ALIGNMENT_16 0x00008000 +#define DC_BUS_MODE_CACHE_ALIGNMENT_32 0x0000C000 + +#define DC_BUS_MODE_TX_POLL_DISABLED 0x00000000 +#define DC_BUS_MODE_TX_POLL_1 0x00020000 +#define DC_BUS_MODE_TX_POLL_2 0x00040000 +#define DC_BUS_MODE_TX_POLL_3 0x00060000 +#define DC_BUS_MODE_TX_POLL_4 0x00080000 +#define DC_BUS_MODE_TX_POLL_5 0x000A0000 +#define DC_BUS_MODE_TX_POLL_6 0x000C0000 +#define DC_BUS_MODE_TX_POLL_7 0x000E0000 + +#define DC_BUS_MODE_DESC_SKIP_LENGTH_0 0x00000000 +#define DC_BUS_MODE_DESC_SKIP_LENGTH_1 0x00000004 +#define DC_BUS_MODE_DESC_SKIP_LENGTH_2 0x00000008 +#define DC_BUS_MODE_DESC_SKIP_LENGTH_4 0x00000010 +#define DC_BUS_MODE_DESC_SKIP_LENGTH_8 0x00000020 +#define DC_BUS_MODE_DESC_SKIP_LENGTH_16 0x00000040 +#define DC_BUS_MODE_DESC_SKIP_LENGTH_32 0x00000080 + +/* + * CSR1 Transmit Poll Demand + */ +#define DC_TX_POLL_DOORBELL 0x00000001 + +/* + * CSR2 Receive Poll Demand + */ +#define DC_RX_POLL_DOORBELL 0x00000001 + +/* + * CSR2 Wake Up Control + */ +#define DC_WAKE_UP_CONTROL_LINK_CHANGE 0x00000001 +#define DC_WAKE_UP_CONTROL_MAGIC_PACKET 0x00000002 +#define DC_WAKE_UP_CONTROL_PATTERN_MATCH 0x00000004 +#define DC_WAKE_UP_STATUS_LINK_CHANGE 0x00000010 +#define DC_WAKE_UP_STATUS_MAGIC_PACKET 0x00000020 +#define DC_WAKE_UP_STATUS_PATTERN_MATCH 0x00000040 +#define DC_WAKE_UP_CONTROL_GLOBAL_UNICAST 0x00000200 +#define DC_WAKE_UP_CONTROL_VLAN_ENABLE 0x00000800 +#define DC_WAKE_UP_CONTROL_VLAN_TYPE_MASK 0xFFFF0000 + +/* + * CSR5 Status, CSR7 Irq Mask + */ +#define DC_IRQ_TX_OK 0x00000001 +#define DC_IRQ_TX_STOPPED 0x00000002 +#define DC_IRQ_TX_NO_BUFFER 0x00000004 +#define DC_IRQ_TX_JABBER_TIMEOUT 0x00000008 +#define DC_IRQ_LINK_PASS 0x00000010 +#define DC_IRQ_TX_UNDERFLOW 0x00000020 +#define DC_IRQ_RX_OK 0x00000040 +#define DC_IRQ_RX_NO_BUFFER 0x00000080 +#define DC_IRQ_RX_STOPPED 0x00000100 +#define DC_IRQ_RX_WDT_TIMEOUT 0x00000200 +#define DC_IRQ_AUI 0x00000400 +#define DC_IRQ_TX_EARLY 0x00000400 +#define DC_IRQ_FD_FRAME_RECEIVED 0x00000800 +#define DC_IRQ_TIMER_TIMEOUT 0x00000800 +#define DC_IRQ_LINK_FAIL 0x00001000 +#define DC_IRQ_SYSTEM_ERROR 0x00002000 +#define DC_IRQ_RX_EARLY 0x00004000 +#define DC_IRQ_ABNORMAL_SUMMARY 0x00008000 +#define DC_IRQ_NORMAL_SUMMARY 0x00010000 +#define DC_STATUS_RX_STATE_MASK 0x000E0000 +#define DC_STATUS_TX_STATE_MASK 0x00700000 +#define DC_STATUS_SYSTEM_ERROR_MASK 0x03800000 +#define DC_IRQ_GPIO_PORT 0x04000000 +#define DC_IRQ_LINK_CHANGED 0x08000000 +#define DC_IRQ_HPNA_PHY 0x10000000 + +#define DC_STATUS_TX_STATE_STOPPED 0x00000000 +#define DC_STATUS_TX_STATE_FETCH 0x00100000 +#define DC_STATUS_TX_STATE_WAIT_FOR_END 0x00200000 +#define DC_STATUS_TX_STATE_READ 0x00300000 +#define DC_STATUS_TX_STATE_RESERVED 0x00400000 +#define DC_STATUS_TX_STATE_SETUP_PACKET 0x00500000 +#define DC_STATUS_TX_STATE_SUSPENDED 0x00600000 +#define DC_STATUS_TX_STATE_CLOSE 0x00700000 + +#define DC_STATUS_RX_STATE_STOPPED 0x00000000 +#define DC_STATUS_RX_STATE_FETCH 0x00020000 +#define DC_STATUS_RX_STATE_CHECK_END 0x00040000 +#define DC_STATUS_RX_STATE_WAIT_FOR_RCV 0x00060000 +#define DC_STATUS_RX_STATE_SUSPENDED 0x00080000 +#define DC_STATUS_RX_STATE_CLOSE_DESC 0x000A0000 +#define DC_STATUS_RX_STATE_FLUSH 0x000C0000 +#define DC_STATUS_RX_STATE_DEQUEUE 0x000E0000 + +#define DC_STATUS_SYSTEM_ERROR_PARITY 0x00000000 +#define DC_STATUS_SYSTEM_ERROR_MASTER_ABORT 0x00800000 +#define DC_STATUS_SYSTEM_ERROR_TARGET_ABORT 0x01000000 + +/* + * CSR6 Operation Mode + */ +#define DC_OPMODE_RX_HASH_PERFECT_FILT 0x00000001 +#define DC_OPMODE_RX_ENABLE 0x00000002 +#define DC_OPMODE_RX_HASH_ONLY_FILT 0x00000004 +#define DC_OPMODE_RX_RUNTS 0x00000008 +#define DC_OPMODE_RX_INVERSE_FILT 0x00000010 +#define DC_OPMODE_BACKOFF_COUNTER 0x00000020 +#define DC_OPMODE_RX_PROMISCUOUS 0x00000040 +#define DC_OPMODE_RX_ALL_MULTICAST 0x00000080 +#define DC_OPMODE_FKD 0x00000100 +#define DC_OPMODE_FULL_DUPLEX 0x00000200 +#define DC_OPMODE_LOOPBACK_MASK 0x00000C00 +#define DC_OPMODE_FORCE_COLLISIONS 0x00001000 +#define DC_OPMODE_TX_ENABLE 0x00002000 +#define DC_OPMODE_TX_THRESHOLD_CTRL_MASK 0x0000C000 +#define DC_OPMODE_TX_BACK_PRESSURE 0x00010000 +#define DC_OPMODE_TX_CAPTURE_EFFECT 0x00020000 +#define DC_OPMODE_PORT_SELECT 0x00040000 +#define DC_OPMODE_PORT_HEARTBEAT_DISABLE 0x00080000 +#define DC_OPMODE_STORE_AND_FORWARD 0x00200000 +#define DC_OPMODE_PORT_XMIT_10 0x00400000 +#define DC_OPMODE_PORT_PCS 0x00800000 +#define DC_OPMODE_PORT_SCRAMBLER 0x01000000 +#define DC_OPMODE_PORT_ALWAYS 0x02000000 +#define DC_OPMODE_ADDR_LSB_IGNORE 0x04000000 +#define DC_OPMODE_RX_RECEIVE_ALL 0x40000000 +#define DC_OPMODE_TX_SPECIAL_CAPTURE_EFFECT 0x80000000 + +#define DC_OPMODE_LOOPBACK_NORMAL 0x00000000 +#define DC_OPMODE_LOOPBACK_INTERNAL 0x00000400 +#define DC_OPMODE_LOOPBACK_EXTERNAL 0x00000800 + +#define DC_OPMODE_TX_THRESHOLD_LEVEL 0x00004000 +#define DC_OPMODE_TX_THRESHOLD_MAX 0x0000C000 + +#define DC_OPMODE_MEDIA_MASK ( \ + DC_OPMODE_TX_THRESHOLD_CTRL_MASK | \ + DC_OPMODE_LOOPBACK_MASK | \ + DC_OPMODE_FULL_DUPLEX | \ + DC_OPMODE_PORT_SELECT | \ + DC_OPMODE_PORT_HEARTBEAT_DISABLE | \ + DC_OPMODE_PORT_XMIT_10 | \ + DC_OPMODE_PORT_PCS | \ + DC_OPMODE_PORT_SCRAMBLER) + +/* + * CSR8 Receive Counters + */ +#define DC_COUNTER_RX_NO_BUFFER_MASK 0x0001FFFF +#define DC_COUNTER_RX_OVERFLOW_MASK 0x1FFE0000 + +#define DC_COUNTER_RX_OVERFLOW_SHIFT 17 + +/* + * CSR9 Serial Interface + */ +#define DC_SERIAL_EE_CS 0x00000001 +#define DC_SERIAL_EE_SK 0x00000002 +#define DC_SERIAL_EE_DI 0x00000004 +#define DC_SERIAL_EE_DO 0x00000008 +#define DC_SERIAL_EE_REG 0x00000400 +#define DC_SERIAL_EE_SR 0x00000800 +#define DC_SERIAL_EE_WR 0x00002000 +#define DC_SERIAL_EE_RD 0x00004000 +#define DC_SERIAL_EE_MOD 0x00008000 +#define DC_SERIAL_MII_MDC 0x00010000 +#define DC_SERIAL_MII_MDO 0x00020000 +#define DC_SERIAL_MII_MII 0x00040000 +#define DC_SERIAL_MII_MDI 0x00080000 +#define DC_SERIAL_EAR_DN 0x80000000 +#define DC_SERIAL_EAR_DT 0x000000FF +#define DC_SERIAL_SPI_CS 0x00100000 +#define DC_SERIAL_SPI_SK 0x00200000 +#define DC_SERIAL_SPI_DI 0x00400000 +#define DC_SERIAL_SPI_DO 0x00800000 + +#define DC_SERIAL_EE_DI_SHIFT 2 +#define DC_SERIAL_EE_DO_SHIFT 3 +#define DC_SERIAL_MII_MDO_SHIFT 17 +#define DC_SERIAL_MII_MDI_SHIFT 19 +#define DC_SERIAL_SPI_DI_SHIFT 22 +#define DC_SERIAL_SPI_DO_SHIFT 23 + +/* + * CSR11 Timer + */ +#define DC_TIMER_VALUE_MASK 0x0000FFFF +#define DC_TIMER_CONTINUOUS 0x00010000 +#define DC_TIMER_RX_NUMBER_MASK 0x000E0000 +#define DC_TIMER_RX_TIMER_MASK 0x00F00000 +#define DC_TIMER_TX_NUMBER_MASK 0x07000000 +#define DC_TIMER_TX_TIMER_MASK 0x78000000 +#define DC_TIMER_CYCLE_SIZE 0x80000000 + +#define DC_TIMER_RX_NUMBER_SHIFT 17 +#define DC_TIMER_RX_TIMER_SHIFT 20 +#define DC_TIMER_TX_NUMBER_SHIFT 24 +#define DC_TIMER_TX_TIMER_SHIFT 27 + +/* + * CSR12 SIA Status + */ +#define DC_SIA_STATUS_MII_RECEIVE_ACTIVITY 0x00000001 +#define DC_SIA_STATUS_NETWORK_CONNECTION_ERROR 0x00000002 +#define DC_SIA_STATUS_100T_LINK_FAIL 0x00000002 +#define DC_SIA_STATUS_10T_LINK_FAIL 0x00000004 +#define DC_SIA_STATUS_SELECTED_PORT_ACTIVITY 0x00000100 +#define DC_SIA_STATUS_AUI_ACTIVITY 0x00000100 +#define DC_SIA_STATUS_HPNA_ACTIVITY 0x00000100 +#define DC_SIA_STATUS_NONSEL_PORT_ACTIVITY 0x00000200 +#define DC_SIA_STATUS_10T_ACTIVITY 0x00000200 +#define DC_SIA_STATUS_NSN 0x00000400 +#define DC_SIA_STATUS_TX_REMOTE_FAULT 0x00000800 +#define DC_SIA_STATUS_ANS_MASK 0x00007000 +#define DC_SIA_STATUS_LP_AUTONED_SUPPORTED 0x00008000 +#define DC_SIA_STATUS_LP_CODE_WORD_MASK 0xFFFF0000 + +#define DC_SIA_STATUS_ANS_AUTONEG_DISABLED 0x00000000 +#define DC_SIA_STATUS_ANS_TX_DISABLE 0x00001000 +#define DC_SIA_STATUS_ANS_ABILITY_DETECT 0x00002000 +#define DC_SIA_STATUS_ANS_ACK_DETECT 0x00003000 +#define DC_SIA_STATUS_ANS_ACK_COMPLETE 0x00004000 +#define DC_SIA_STATUS_ANS_AUTONEG_COMPLETE 0x00005000 +#define DC_SIA_STATUS_ANS_LINK_CHECK 0x00006000 + +#define DC_SIA_STATUS_LP_CODE_WORD_SHIFT 16 + +#define DC_GPIO_CONTROL 0x100 + +/* + * CSR13 SIA Connectivity + */ +#define DC_SIA_CONN_RESET 0x00000000 +#define DC_SIA_CONN_HPNA 0x00000008 + +/* + * CSR14 SIA Transmit and Receive + */ +#define DC_SIA_TXRX_ENCODER 0x00000001 +#define DC_SIA_TXRX_LOOPBACK 0x00000002 +#define DC_SIA_TXRX_DRIVER 0x00000004 +#define DC_SIA_TXRX_LINK_PULSE 0x00000008 +#define DC_SIA_TXRX_COMPENSATION 0x00000030 +#define DC_SIA_TXRX_ADV_10T_HD 0x00000040 +#define DC_SIA_TXRX_AUTONEG 0x00000080 +#define DC_SIA_TXRX_RX_SQUELCH 0x00000100 +#define DC_SIA_TXRX_COLLISION_SQUELCH 0x00000200 +#define DC_SIA_TXRX_COLLISION_DETECT 0x00000400 +#define DC_SIA_TXRX_HEARTBEAT 0x00000800 +#define DC_SIA_TXRX_LINK_TEST 0x00001000 +#define DC_SIA_TXRX_AUTOPOLARITY 0x00002000 +#define DC_SIA_TXRX_SET_POLARITY_PLUS 0x00004000 +#define DC_SIA_TXRX_10T_AUTOSENSE 0x00008000 +#define DC_SIA_TXRX_ADV_100TX_HD 0x00010000 +#define DC_SIA_TXRX_ADV_100TX_FD 0x00020000 +#define DC_SIA_TXRX_ADV_100T4 0x00040000 + +/* + * CSR15 SIA and GPIO + */ +#define DC_SIA_GENERAL_JABBER_DISABLE 0x00000001 +#define DC_SIA_GENERAL_HOST_UNJAB 0x00000002 +#define DC_SIA_GENERAL_JABBER_CLOCK 0x00000004 +#define DC_SIA_GENERAL_AUI_BNC_MODE 0x00000008 +#define DC_SIA_GENERAL_RX_WDT_DISABLE 0x00000010 +#define DC_SIA_GENERAL_RX_WDT_RELEASE 0x00000020 +#define DC_SIA_GENERAL_LINK_EXTEND 0x00000800 +#define DC_SIA_GENERAL_RX_MAGIC_PACKET 0x00004000 +#define DC_SIA_GENERAL_HCKR 0x00008000 +#define DC_SIA_GENERAL_GPIO_MASK 0x000F0000 +#define DC_SIA_GENERAL_LGS3 0x00100000 +#define DC_SIA_GENERAL_LGS2 0x00200000 +#define DC_SIA_GENERAL_LGS1 0x00400000 +#define DC_SIA_GENERAL_LGS0 0x00800000 +#define DC_SIA_GENERAL_GEI0 0x01000000 +#define DC_SIA_GENERAL_GEI1 0x02000000 +#define DC_SIA_GENERAL_RECEIVE_MATCH 0x04000000 +#define DC_SIA_GENERAL_CONTROL_WRITE 0x08000000 +#define DC_SIA_GENERAL_GI0 0x10000000 +#define DC_SIA_GENERAL_GI1 0x20000000 +#define DC_SIA_GENERAL_IRQ_RX_MATCH 0x40000000 + +#define DC_RBD_STATUS_INVALID \ + (DC_RBD_STATUS_OVERRUN | \ + DC_RBD_STATUS_CRC_ERROR | \ + DC_RBD_STATUS_WDT_EXPIRED | \ + DC_RBD_STATUS_COLLISION_SEEN | \ + DC_RBD_STATUS_TOO_LONG | \ + DC_RBD_STATUS_RUNT | \ + DC_RBD_STATUS_LENGTH_ERROR) + +#define DC_GENERIC_IRQ_MASK \ + (DC_IRQ_TX_OK | DC_IRQ_TX_STOPPED | DC_IRQ_TX_JABBER_TIMEOUT | \ + DC_IRQ_RX_OK | DC_IRQ_TX_UNDERFLOW | \ + DC_IRQ_RX_STOPPED | \ + DC_IRQ_SYSTEM_ERROR | DC_IRQ_ABNORMAL_SUMMARY | DC_IRQ_NORMAL_SUMMARY) + +/* Errata: The programming guide incorrectly stated that CSR13 must be set to 0x30480009 */ +#define DC_HPNA_ANALOG_CTRL 0x708A0000 + +/* + * PCI Configuration Registers + */ +#define DC_PCI_DEVICE_CONFIG 0x40 +#define DC_PCI_DEVICE_CONFIG_SNOOZE 0x40000000 +#define DC_PCI_DEVICE_CONFIG_SLEEP 0x80000000 + +/* + * SPI Interface + */ +#define DC_SPI_BYTE_WRITE_OPERATION 2 +#define DC_SPI_BYTE_READ_OPERATION 3 +#define DC_SPI_CLEAR_WRITE_ENABLE 4 +#define DC_SPI_SET_WRITE_ENABLE 6 + +/* + * HomePNA PHY Registers + */ +#define HPNA_CONTROL_LOW 0x00 +#define HPNA_CONTROL_HIGH 0x01 +#define HPNA_NOISE 0x10 +#define HPNA_NOISE_FLOOR 0x12 +#define HPNA_NOISE_CEILING 0x13 +#define HPNA_NOISE_ATTACK 0x14 + +/* + * MDIO Protocol (IEEE 802.3) + */ +#define MDIO_START 0x01 +#define MDIO_WRITE 0x01 +#define MDIO_READ 0x02 +#define MDIO_TA 0x02 +#define MDIO_PREAMBLE 0xFFFFFFFF + +#define MII_MAX_PHY_ADDRESSES 32 + +/* + * PHY register definitions (IEEE 802.3) + */ +#define MII_CONTROL 0x00 +#define MII_CR_COLLISION_TEST 0x0080 +#define MII_CR_FULL_DUPLEX 0x0100 +#define MII_CR_AUTONEG_RESTART 0x0200 +#define MII_CR_ISOLATE 0x0400 +#define MII_CR_POWER_DOWN 0x0800 +#define MII_CR_AUTONEG 0x1000 +#define MII_CR_SPEED_SELECTION 0x2000 +#define MII_CR_LOOPBACK 0x4000 +#define MII_CR_RESET 0x8000 +#define MII_STATUS 0x01 +#define MII_SR_LINK_STATUS 0x0004 +#define MII_SR_AUTONEG_COMPLETE 0x0020 +#define MII_PHY_ID1 0x02 +#define MII_PHY_ID2 0x03 +#define MII_AUTONEG_ADVERTISE 0x04 +#define MII_ADV_CSMA 0x0001 +#define MII_ADV_10T_HD 0x0020 +#define MII_ADV_10T_FD 0x0040 +#define MII_ADV_100T_HD 0x0080 +#define MII_ADV_100T_FD 0x0100 +#define MII_ADV_100T4 0x0200 +#define MII_ADV_PAUSE_SYM 0x0400 +#define MII_ADV_PAUSE_ASYM 0x0800 +#define MII_AUTONEG_LINK_PARTNER 0x05 +#define MII_LP_10T_HD 0x0020 +#define MII_LP_10T_FD 0x0040 +#define MII_LP_100T_HD 0x0080 +#define MII_LP_100T_FD 0x0100 +#define MII_LP_100T4 0x0200 +#define MII_LP_PAUSE_SYM 0x0400 +#define MII_LP_PAUSE_ASYM 0x0800 +#define MII_AUTONEG_EXPANSION 0x06 +#define MII_EXP_LP_AUTONEG 0x0001 +#define MII_MASTER_SLAVE_CONTROL 0x09 +#define MII_MS_CR_1000T_HD 0x0100 +#define MII_MS_CR_1000T_FD 0x0200 +#define MII_MASTER_SLAVE_STATUS 0x0A +#define MII_MS_SR_1000T_FD 0x0800 + +#define MII_ADV_100 \ + (MII_ADV_100T_HD | MII_ADV_100T_FD | MII_ADV_100T4) diff --git a/drivers/network/dd/dc21x4/debug.c b/drivers/network/dd/dc21x4/debug.c new file mode 100644 index 00000000000..3dc9e5f073f --- /dev/null +++ b/drivers/network/dd/dc21x4/debug.c @@ -0,0 +1,70 @@ +/* + * PROJECT: ReactOS DC21x4 Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Debug routines + * COPYRIGHT: Copyright 2023 Dmitry Borisov + */ + +/* INCLUDES *******************************************************************/ + +#include "dc21x4.h" + +/* GLOBALS ********************************************************************/ + +static PCSTR MediaName[MEDIA_MAX] = +{ + "10Base-T", + "10Base-2 (BNC)", + "10Base-5 (AUI)", + "100Base-TX HD", + "10Base-T FD", + "100Base-TX FD", + "100Base-T4", + "100Base-FX HD", + "100Base-FX FD", + "HomePNA", + "MII", +}; + +/* FUNCTIONS ******************************************************************/ + +PCSTR +MediaNumber2Str( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG MediaNumber) +{ + switch (MediaNumber) + { + case MEDIA_100TX_HD: + { + if (Adapter->ChipType == DC21041) + return "10Base-T HD"; + break; + } + + default: + break; + } + + ASSERT(MediaNumber < MEDIA_MAX); + + return MediaName[MediaNumber]; +} + +PCSTR +DcDbgBusError( + _In_ ULONG InterruptStatus) +{ + switch (InterruptStatus & DC_STATUS_SYSTEM_ERROR_MASK) + { + case DC_STATUS_SYSTEM_ERROR_PARITY: + return "Parity Error"; + case DC_STATUS_SYSTEM_ERROR_MASTER_ABORT: + return "Master Abort"; + case DC_STATUS_SYSTEM_ERROR_TARGET_ABORT: + return "Target Abort"; + + default: + return ""; + } +} diff --git a/drivers/network/dd/dc21x4/debug.h b/drivers/network/dd/dc21x4/debug.h new file mode 100644 index 00000000000..f1a4e178de3 --- /dev/null +++ b/drivers/network/dd/dc21x4/debug.h @@ -0,0 +1,96 @@ +/* + * PROJECT: ReactOS DC21x4 Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Debug support header file + * COPYRIGHT: Copyright 2023 Dmitry Borisov + */ + +#pragma once + +#ifndef __RELFILE__ +#define __RELFILE__ __FILE__ +#endif + +#if DBG + +// #define DEBUG_TRACE +// #define DEBUG_INFO +#define DEBUG_INFO_VERB +#define DEBUG_WARN +#define DEBUG_ERR + +#ifdef DEBUG_TRACE +#define TRACE(fmt, ...) \ + do { \ + if (DbgPrint("(%s:%d) %s " fmt, __RELFILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)) \ + DbgPrint("(%s:%d) DbgPrint() failed!\n", __RELFILE__, __LINE__); \ + } while (0) + +#else +#define TRACE +#endif + +#ifdef DEBUG_INFO +#define INFO(fmt, ...) \ + do { \ + if (DbgPrint("(%s:%d) %s " fmt, __RELFILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)) \ + DbgPrint("(%s:%d) DbgPrint() failed!\n", __RELFILE__, __LINE__); \ + } while (0) + +#else +#define INFO +#endif + +#ifdef DEBUG_INFO_VERB +#define INFO_VERB(fmt, ...) \ + do { \ + if (DbgPrint("(%s:%d) %s " fmt, __RELFILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)) \ + DbgPrint("(%s:%d) DbgPrint() failed!\n", __RELFILE__, __LINE__); \ + } while (0) + +#else +#define INFO_VERB +#endif + +#ifdef DEBUG_WARN +#define WARN(fmt, ...) \ + do { \ + if (DbgPrint("(%s:%d) %s " fmt, __RELFILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)) \ + DbgPrint("(%s:%d) DbgPrint() failed!\n", __RELFILE__, __LINE__); \ + } while (0) + +#else +#define WARN +#endif + +#ifdef DEBUG_ERR +#define ERR(fmt, ...) \ + do { \ + if (DbgPrint("(%s:%d) %s " fmt, __RELFILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)) \ + DbgPrint("(%s:%d) DbgPrint() failed!\n", __RELFILE__, __LINE__); \ + } while (0) + +#else +#define ERR +#endif + +PCSTR +MediaNumber2Str( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG MediaNumber); + +PCSTR +DcDbgBusError( + _In_ ULONG InterruptStatus); + +#else + +#define TRACE +#define INFO +#define INFO_VERB +#define WARN +#define ERR +#define MediaNumber2Str +#define DcDbgBusError + +#endif diff --git a/drivers/network/dd/dc21x4/eeprom.c b/drivers/network/dd/dc21x4/eeprom.c new file mode 100644 index 00000000000..6937a5723df --- /dev/null +++ b/drivers/network/dd/dc21x4/eeprom.c @@ -0,0 +1,1525 @@ +/* + * PROJECT: ReactOS DC21x4 Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: EEPROM manipulation and parsing + * COPYRIGHT: Copyright 2023 Dmitry Borisov + */ + +/* INCLUDES *******************************************************************/ + +#include "dc21x4.h" + +#include + +/* GLOBALS ********************************************************************/ + +#define SROM_READ(Adapter, Data) \ + do { \ + *Data = DC_READ((Adapter), DcCsr9_SerialInterface); \ + NdisStallExecution(10); \ + } while (0) + +#define SROM_WRITE(Adapter, Value) \ + do { \ + DC_WRITE((Adapter), DcCsr9_SerialInterface, Value); \ + NdisStallExecution(10); \ + } while (0) + +extern DC_PG_DATA DC_SROM_REPAIR_ENTRY SRompRepairData[]; + +LIST_ENTRY SRompAdapterList; + +_Interlocked_ +static volatile LONG SRompAdapterLock = 0; + +/* PRIVATE FUNCTIONS **********************************************************/ + +static +CODE_SEG("PAGE") +VOID +SRomAcquireListMutex(VOID) +{ + PAGED_CODE(); + + while (_InterlockedCompareExchange(&SRompAdapterLock, 1, 0)) + { + NdisMSleep(10); + } +} + +static inline +CODE_SEG("PAGE") +VOID +SRomReleaseListMutex(VOID) +{ + PAGED_CODE(); + + _InterlockedDecrement(&SRompAdapterLock); +} + +static +CODE_SEG("PAGE") +BOOLEAN +SRomIsAdapterInList( + _In_ PDC21X4_ADAPTER Adapter, + _In_ BOOLEAN SearchForMaster, + _Out_opt_ PDC_SROM_ENTRY* FoundEntry) +{ + PLIST_ENTRY PrevEntry; + PDC_SROM_ENTRY SRomEntry; + + PAGED_CODE(); + + /* Loop the adapter list backwards */ + for (PrevEntry = (&SRompAdapterList)->Blink; + PrevEntry != &SRompAdapterList; + PrevEntry = PrevEntry->Blink) + { + SRomEntry = CONTAINING_RECORD(PrevEntry, DC_SROM_ENTRY, ListEntry); + + if ((SRomEntry->ChipType == Adapter->ChipType) && + (SRomEntry->BusNumber == Adapter->BusNumber) && + (!SearchForMaster || (SRomEntry->DeviceNumber == Adapter->DeviceNumber))) + { + if (FoundEntry) + *FoundEntry = SRomEntry; + + return TRUE; + } + } + + return FALSE; +} + +static +CODE_SEG("PAGE") +BOOLEAN +SRomRegisterMasterAdapter( + _In_ PDC21X4_ADAPTER Adapter, + _In_ PDC_SROM_ENTRY SRomEntry) +{ + BOOLEAN Success; + + PAGED_CODE(); + + SRomAcquireListMutex(); + + /* Check if board is already registered */ + if (SRomIsAdapterInList(Adapter, TRUE, NULL)) + { + Success = FALSE; + goto Exit; + } + + Adapter->SRomEntry = SRomEntry; + + SRomEntry->ChipType = Adapter->ChipType; + SRomEntry->BusNumber = Adapter->BusNumber; + SRomEntry->DeviceNumber = Adapter->DeviceNumber; + SRomEntry->InterruptLevel = Adapter->InterruptLevel; + SRomEntry->InterruptVector = Adapter->InterruptVector; + + /* Register the port */ + SRomEntry->DeviceBitmap |= 1 << Adapter->DeviceNumber; + + /* + * On some multiport boards only the first port contains an EEPROM. + * We put their references to the global adapter list. + */ + InsertTailList(&SRompAdapterList, &SRomEntry->ListEntry); + Success = TRUE; + +Exit: + SRomReleaseListMutex(); + + return Success; +} + +static +CODE_SEG("PAGE") +BOOLEAN +SRomFindMasterAdapter( + _In_ PDC21X4_ADAPTER Adapter, + _Out_ PDC_SROM_ENTRY* FoundEntry) +{ + PDC_SROM_ENTRY SRomEntry; + ULONG i; + BOOLEAN Found; + + PAGED_CODE(); + + SRomAcquireListMutex(); + + if (!SRomIsAdapterInList(Adapter, FALSE, &SRomEntry)) + { + Found = FALSE; + goto Exit; + } + + Adapter->SRomEntry = SRomEntry; + + /* Register the port */ + SRomEntry->DeviceBitmap |= 1 << Adapter->DeviceNumber; + + /* + * Determine the port index that should be used in order to + * (possibly) update the base MAC address. + */ + for (i = 0; i < PCI_MAX_DEVICES; ++i) + { + if (i == Adapter->DeviceNumber) + break; + + if (SRomEntry->DeviceBitmap & (1 << i)) + ++Adapter->ControllerIndex; + } + + /* + * On a multiport board there can be up to 4 ports + * connected through a 21050 or 21152 PCI-to-PCI Bridge. + * These boards share a single IRQ line between all of the chips. + * Some BIOSes incorrectly assign different IRQs to the different ports. + */ + Adapter->InterruptLevel = SRomEntry->InterruptLevel; + Adapter->InterruptVector = SRomEntry->InterruptVector; + + WARN("EEPROM is missing on controller %u, using image from the master at %u:%u\n", + Adapter->DeviceNumber, + SRomEntry->BusNumber, + SRomEntry->DeviceNumber); + + *FoundEntry = SRomEntry; + Found = TRUE; + +Exit: + SRomReleaseListMutex(); + + return Found; +} + +static +CODE_SEG("PAGE") +BOOLEAN +SRomIsEmpty( + _In_reads_bytes_(Length) const VOID* Buffer, + _In_ ULONG Length) +{ + const UCHAR* Data = Buffer; + const UCHAR FirstByte = Data[0]; + ULONG i; + + PAGED_CODE(); + + for (i = 1; i < Length; ++i) + { + if (FirstByte != Data[i]) + return FALSE; + } + + return TRUE; +} + +static +CODE_SEG("PAGE") +VOID +SRomNWayAdvertise( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG MediaCode) +{ + PAGED_CODE(); + + switch (MediaCode) + { + case MEDIA_10T: + Adapter->SymAdvertising |= MII_ADV_10T_HD; + break; + case MEDIA_10T_FD: + Adapter->SymAdvertising |= MII_ADV_10T_FD; + break; + case MEDIA_100TX_HD: + Adapter->SymAdvertising |= MII_ADV_100T_HD; + break; + case MEDIA_100TX_FD: + Adapter->SymAdvertising |= MII_ADV_100T_FD; + break; + case MEDIA_100T4: + Adapter->SymAdvertising |= MII_ADV_100T4; + break; + + default: + break; + } +} + +static +CODE_SEG("PAGE") +NDIS_STATUS +SRomDecodeBlockGpr( + _In_ PDC21X4_ADAPTER Adapter, + _In_ PUCHAR SRomEnd, + _In_ PUCHAR BlockData) +{ + PDC_MEDIA Media; + ULONG MediaCode, OpMode; + USHORT Command; + + PAGED_CODE(); + + if (BlockData > (SRomEnd - 4)) + return NDIS_STATUS_BUFFER_OVERFLOW; + + MediaCode = SRomGetMediaCode(*BlockData++); + if (MediaCode > SROM_MEDIA_MAX) + { + WARN("Unknown media code %u\n", MediaCode); + return NDIS_STATUS_SUCCESS; + } + Adapter->MediaBitmap |= 1 << MediaCode; + + Media = &Adapter->Media[MediaCode]; + + Media->GpioData = *BlockData++; + + Command = DcRetrieveWord(BlockData); + + OpMode = Media->OpMode; + OpMode &= ~SROM_OPMODE_MASK; + OpMode |= SRomCommandToOpMode(Command); + Media->OpMode = OpMode; + + if (SRomMediaHasActivityIndicator(Command)) + { + Media->LinkMask = SRomMediaGetSenseMask(Command); + } + if (SRomMediaActivityIsActiveLow(Command)) + { + Media->Polarity = 0xFFFFFFFF; + } + + INFO("GPR #%u %s, Command %04lx, Data %02lx\n", + MediaCode, + MediaNumber2Str(Adapter, MediaCode), + Command, + Media->GpioData); + + return NDIS_STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +NDIS_STATUS +SRomDecodeBlockMii( + _In_ PDC21X4_ADAPTER Adapter, + _In_ PUCHAR SRomEnd, + _In_ PUCHAR BlockData, + _In_ BOOLEAN IsOldVersion) +{ + PDC_MII_MEDIA Media; + ULONG i, Bytes, Offset; + UCHAR PhyNumber, StreamLength, InterruptInfo; + USHORT Capabilities, Fdx, Ttm; + + PAGED_CODE(); + + if (BlockData > (SRomEnd - 2)) + return NDIS_STATUS_BUFFER_OVERFLOW; + + PhyNumber = *BlockData++; + + /* + * Even though the SROM specification allows several + * PHY devices to be connected to the same chip on a board, + * most if not all boards never use more than 1 MII PHY device. + */ + if (Adapter->Features & DC_HAS_MII) + { + WARN("Unsupported PHY %u\n", PhyNumber); + return NDIS_STATUS_SUCCESS; + } + + Media = &Adapter->MiiMedia; + + /* + * PHY selection sequence + */ + + StreamLength = *BlockData++; + if (StreamLength > SROM_MAX_STREAM_REGS) + { + WARN("Too much registers %u\n", StreamLength); + return NDIS_STATUS_SUCCESS; + } + + Bytes = StreamLength; + if (!IsOldVersion) + { + /* In words */ + Bytes *= 2; + } + if ((BlockData + Bytes) > (SRomEnd - 1)) + { + return NDIS_STATUS_BUFFER_OVERFLOW; + } + + Media->SetupStreamLength = StreamLength; + + /* Check if we already have the GPIO direction data */ + if (Media->SetupStream[0] != 0) + { + Offset = 1; + ++Media->SetupStreamLength; + } + else + { + Offset = 0; + } + + for (i = 0; i < StreamLength; ++i) + { + if (IsOldVersion) + { + Media->SetupStream[i + Offset] = *BlockData++; + } + else + { + Media->SetupStream[i + Offset] = DcRetrieveWord(BlockData); + BlockData += sizeof(USHORT); + } + } + + /* + * PHY reset sequence + */ + + if (BlockData > (SRomEnd - 1)) + { + return NDIS_STATUS_BUFFER_OVERFLOW; + } + + StreamLength = *BlockData++; + if (StreamLength > SROM_MAX_STREAM_REGS) + { + WARN("Too much registers %u\n", StreamLength); + return NDIS_STATUS_SUCCESS; + } + + Bytes = StreamLength; + if (!IsOldVersion) + { + /* In words */ + Bytes *= 2; + } + if ((BlockData + Bytes) > (SRomEnd - 1)) + { + return NDIS_STATUS_BUFFER_OVERFLOW; + } + + Media->ResetStreamLength = StreamLength; + + for (i = 0; i < StreamLength; ++i) + { + if (IsOldVersion) + { + Media->ResetStream[i] = *BlockData++; + } + else + { + Media->ResetStream[i] = DcRetrieveWord(BlockData); + BlockData += sizeof(USHORT); + } + } + + /* + * MII data + */ + + Bytes = 4 * sizeof(USHORT); + if (!IsOldVersion) + { + Bytes += 1; + } + if (BlockData > (SRomEnd - Bytes)) + { + return NDIS_STATUS_BUFFER_OVERFLOW; + } + + Capabilities = DcRetrieveWord(BlockData); + BlockData += sizeof(USHORT); + + Media->Advertising = DcRetrieveWord(BlockData); + BlockData += sizeof(USHORT); + + Fdx = DcRetrieveWord(BlockData); + BlockData += sizeof(USHORT); + + Ttm = DcRetrieveWord(BlockData); + BlockData += sizeof(USHORT); + + InterruptInfo = IsOldVersion ? 0 : *BlockData; + + Adapter->Features |= DC_HAS_MII; + + INFO("MII #%u, Caps %04lx, Adv %04lx, Fdx %04lx, Ttm %04lx, Int %02x\n", + PhyNumber, + Capabilities, + Media->Advertising, + Fdx, + Ttm, + InterruptInfo); + + return NDIS_STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +NDIS_STATUS +SRomDecodeBlockSia( + _In_ PDC21X4_ADAPTER Adapter, + _In_ PUCHAR SRomEnd, + _In_ PUCHAR BlockData) +{ + PDC_MEDIA Media; + UCHAR BlockStart; + ULONG MediaCode; + BOOLEAN HasExtendedData; + + PAGED_CODE(); + + if (BlockData > (SRomEnd - 1)) + return NDIS_STATUS_BUFFER_OVERFLOW; + + BlockStart = *BlockData++; + + HasExtendedData = SRomBlockHasExtendedData(BlockStart); + if (BlockData > (SRomEnd - (HasExtendedData ? 10 : 4))) + return NDIS_STATUS_BUFFER_OVERFLOW; + + MediaCode = SRomGetMediaCode(BlockStart); + if (MediaCode > SROM_MEDIA_MAX && MediaCode != SROM_MEDIA_HMR) + { + WARN("Unknown media code %u\n", MediaCode); + return NDIS_STATUS_SUCCESS; + } + + /* TODO: There were a few 21143-based boards with HMR media */ + if ((MediaCode == SROM_MEDIA_HMR) && (Adapter->ChipType != DC21145)) + { + ERR("FIXME: 21143 HMR is not supported yet\n"); + return NDIS_STATUS_SUCCESS; + } + + /* Map the code to our internal value */ + if (MediaCode == SROM_MEDIA_HMR) + { + MediaCode = MEDIA_HMR; + } + + Adapter->MediaBitmap |= 1 << MediaCode; + + Media = &Adapter->Media[MediaCode]; + + if (HasExtendedData) + { + Media->Csr13 = DcRetrieveWord(BlockData); + BlockData += sizeof(USHORT); + + Media->Csr14 = DcRetrieveWord(BlockData); + BlockData += sizeof(USHORT); + + Media->Csr15 = DcRetrieveWord(BlockData); + BlockData += sizeof(USHORT); + } + + Media->GpioCtrl = DcRetrieveWord(BlockData); + BlockData += sizeof(USHORT); + + Media->GpioData = DcRetrieveWord(BlockData); + BlockData += sizeof(USHORT); + + SRomNWayAdvertise(Adapter, MediaCode); + + INFO("SIA #%u %s, %sCSR13 %04lx CSR14 %04lx CSR15 %04lx, " + "Ctrl %04lx, Data %04lx\n", + MediaCode, + MediaNumber2Str(Adapter, MediaCode), + HasExtendedData ? "EXT " : "", + Media->Csr13, + Media->Csr14, + Media->Csr15, + Media->GpioCtrl, + Media->GpioData); + + return NDIS_STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +NDIS_STATUS +SRomDecodeBlockSym( + _In_ PDC21X4_ADAPTER Adapter, + _In_ PUCHAR SRomEnd, + _In_ PUCHAR BlockData) +{ + PDC_MEDIA Media; + ULONG MediaCode, OpMode; + USHORT Command; + + PAGED_CODE(); + + if (BlockData > (SRomEnd - 7)) + return NDIS_STATUS_BUFFER_OVERFLOW; + + MediaCode = SRomGetMediaCode(*BlockData++); + if (MediaCode > SROM_MEDIA_MAX) + { + WARN("Unknown media code %u\n", MediaCode); + return NDIS_STATUS_SUCCESS; + } + Adapter->MediaBitmap |= 1 << MediaCode; + + Media = &Adapter->Media[MediaCode]; + + Media->GpioCtrl = DcRetrieveWord(BlockData); + BlockData += sizeof(USHORT); + + Media->GpioData = DcRetrieveWord(BlockData); + BlockData += sizeof(USHORT); + + Command = DcRetrieveWord(BlockData); + BlockData += sizeof(USHORT); + + OpMode = Media->OpMode; + OpMode &= ~SROM_OPMODE_MASK; + OpMode |= SRomCommandToOpMode(Command); + Media->OpMode = OpMode; + + SRomNWayAdvertise(Adapter, MediaCode); + + INFO("SYM #%u %s, Command %04lx, Ctrl %04lx, Data %04lx\n", + MediaCode, + MediaNumber2Str(Adapter, MediaCode), + Command, + Media->GpioCtrl, + Media->GpioData); + + return NDIS_STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +NDIS_STATUS +SRomDecodeBlockReset( + _In_ PDC21X4_ADAPTER Adapter, + _In_ PUCHAR SRomEnd, + _In_ PUCHAR BlockData) +{ + UCHAR i, StreamLength; + + PAGED_CODE(); + + if (BlockData > (SRomEnd - 1)) + return NDIS_STATUS_BUFFER_OVERFLOW; + + StreamLength = *BlockData++; + if (StreamLength > SROM_MAX_STREAM_REGS) + { + WARN("Too much registers %u\n", StreamLength); + return NDIS_STATUS_SUCCESS; + } + + if ((BlockData + StreamLength * 2) > (SRomEnd - 1)) + return NDIS_STATUS_BUFFER_OVERFLOW; + + Adapter->ResetStreamLength = StreamLength; + + for (i = 0; i < StreamLength; ++i) + { + Adapter->ResetStream[i] = DcRetrieveWord(BlockData); + BlockData += sizeof(USHORT); + } + + INFO("RESET, length %u\n", StreamLength); + + return NDIS_STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +NDIS_STATUS +SRomDecodeBlockHmr( + _In_ PDC21X4_ADAPTER Adapter, + _In_ PUCHAR SRomEnd, + _In_ PUCHAR BlockData, + _In_ UCHAR BlockLength) +{ + ULONG Offset, ExtraData, i; + + PAGED_CODE(); + + if (BlockData > (SRomEnd - (2 + 6))) + return NDIS_STATUS_BUFFER_OVERFLOW; + + Adapter->AnalogControl = DcRetrieveWord(BlockData) << 16; + BlockData += sizeof(USHORT); + + Adapter->HpnaRegister[HPNA_CONTROL_LOW] = *BlockData++; + Adapter->HpnaRegister[HPNA_CONTROL_HIGH] = *BlockData++; + Adapter->HpnaRegister[HPNA_NOISE] = *BlockData++; + Adapter->HpnaRegister[HPNA_NOISE_FLOOR] = *BlockData++; + Adapter->HpnaRegister[HPNA_NOISE_CEILING] = *BlockData++; + Adapter->HpnaRegister[HPNA_NOISE_ATTACK] = *BlockData++; + Adapter->HpnaInitBitmap |= ((1 << HPNA_CONTROL_LOW) | + (1 << HPNA_CONTROL_HIGH) | + (1 << HPNA_NOISE) | + (1 << HPNA_NOISE_FLOOR) | + (1 << HPNA_NOISE_CEILING) | + (1 << HPNA_NOISE_ATTACK)); + + Offset = 2 /* Length and type fields */ + 2 /* Analog ctrl */ + 6; /* Regs */ + ExtraData = (BlockLength - Offset); + + if ((BlockData + ExtraData) > (SRomEnd - 1)) + return NDIS_STATUS_BUFFER_OVERFLOW; + + for (i = 0; i < ExtraData / sizeof(USHORT); ++i) + { + UCHAR RegAddress = SRomHmrRegAddress(*BlockData++); + UCHAR RegValue = *BlockData++; + + Adapter->HpnaRegister[RegAddress] = RegValue; + Adapter->HpnaInitBitmap |= 1 << RegAddress; + } + +#if DBG + INFO_VERB("Analog Ctrl %04lx\n", Adapter->AnalogControl); + + for (i = 0; i < RTL_NUMBER_OF(Adapter->HpnaRegister); ++i) + { + if (Adapter->HpnaInitBitmap & (1 << i)) + { + INFO_VERB("HR Reg %02x = %02x\n", i, Adapter->HpnaRegister[i]); + } + } + + if (ExtraData % sizeof(USHORT)) + { + INFO_VERB("HR Data = %02x\n", *BlockData); + } +#endif + + return NDIS_STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +NDIS_STATUS +SRomParseExtendedBlock( + _In_ PDC21X4_ADAPTER Adapter, + _In_ PUCHAR SRomEnd, + _In_ PUCHAR BlockData, + _Out_ PULONG BlockSize) +{ + NDIS_STATUS Status; + ULONG Length, Type; + + PAGED_CODE(); + + if (BlockData > (SRomEnd - 2)) + return NDIS_STATUS_BUFFER_OVERFLOW; + + Length = SRomGetExtendedBlockLength(*BlockData++); + Type = *BlockData++; + + *BlockSize = Length; + + switch (Type) + { + case SROM_BLOCK_TYPE_GPR: + Status = SRomDecodeBlockGpr(Adapter, SRomEnd, BlockData); + break; + case SROM_BLOCK_TYPE_MII_1: + case SROM_BLOCK_TYPE_MII_2: + Status = SRomDecodeBlockMii(Adapter, + SRomEnd, + BlockData, + (Type == SROM_BLOCK_TYPE_MII_1)); + break; + case SROM_BLOCK_TYPE_SIA: + Status = SRomDecodeBlockSia(Adapter, SRomEnd, BlockData); + break; + case SROM_BLOCK_TYPE_SYM: + Status = SRomDecodeBlockSym(Adapter, SRomEnd, BlockData); + break; + case SROM_BLOCK_TYPE_RESET: + Status = SRomDecodeBlockReset(Adapter, SRomEnd, BlockData); + break; + case SROM_BLOCK_TYPE_HOMERUN: + Status = SRomDecodeBlockHmr(Adapter, SRomEnd, BlockData, Length); + break; + + /* Skip over the unused or unknown blocks */ + default: + WARN("Unknown block type %u, length %u\n", Type, Length); + case SROM_BLOCK_TYPE_PHY_SHUTDOWN: + Status = NDIS_STATUS_SUCCESS; + break; + } + + return Status; +} + +static +CODE_SEG("PAGE") +NDIS_STATUS +SRomParse21041Block( + _In_ PDC21X4_ADAPTER Adapter, + _In_ PUCHAR SRomEnd, + _In_ PUCHAR BlockData, + _Out_ PULONG BlockSize) +{ + PDC_MEDIA Media; + UCHAR BlockStart; + ULONG MediaCode; + BOOLEAN HasExtendedData; + + PAGED_CODE(); + + if (BlockData > (SRomEnd - 1)) + return NDIS_STATUS_BUFFER_OVERFLOW; + + BlockStart = *BlockData++; + + HasExtendedData = SRomBlockHasExtendedData(BlockStart); + if (BlockData > (SRomEnd - (HasExtendedData ? 7 : 1))) + return NDIS_STATUS_BUFFER_OVERFLOW; + + *BlockSize = HasExtendedData ? 7 : 1; + + MediaCode = SRomGetMediaCode(BlockStart); + if (MediaCode > SROM_MEDIA_MAX) + { + WARN("Unknown media code %u\n", MediaCode); + return NDIS_STATUS_SUCCESS; + } + Adapter->MediaBitmap |= 1 << MediaCode; + + Media = &Adapter->Media[MediaCode]; + + if (HasExtendedData) + { + Media->Csr13 = DcRetrieveWord(BlockData); + BlockData += sizeof(USHORT); + + Media->Csr14 = DcRetrieveWord(BlockData); + BlockData += sizeof(USHORT); + + Media->Csr15 = DcRetrieveWord(BlockData); + BlockData += sizeof(USHORT); + } + + INFO("SIA #%u %s, %sCSR13 %04lx CSR14 %04lx CSR15 %04lx\n", + MediaCode, + MediaNumber2Str(Adapter, MediaCode), + HasExtendedData ? "EXT " : "", + Media->Csr13, + Media->Csr14, + Media->Csr15); + + return NDIS_STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +BOOLEAN +SRomChecksumValid( + _In_ PUCHAR SRom) +{ + USHORT Checksum; + + PAGED_CODE(); + + Checksum = ~DcEthernetCrc(SRom, SROM_CHECKSUM_V1); + if (Checksum == DcRetrieveWord(&SRom[SROM_CHECKSUM_V1])) + return TRUE; + + Checksum = ~DcEthernetCrc(SRom, SROM_CHECKSUM_V2); + if (Checksum == DcRetrieveWord(&SRom[SROM_CHECKSUM_V2])) + return TRUE; + + return FALSE; +} + +static +CODE_SEG("PAGE") +BOOLEAN +AddressRomChecksumValid( + _In_reads_bytes_(EAR_SIZE) PVOID AddressRom) +{ + const UCHAR* Octet = AddressRom; + ULONG64 TestPatterm; + ULONG Checksum, i; + + NdisMoveMemory(&TestPatterm, &Octet[24], 8); + if (TestPatterm != EAR_TEST_PATTERN) + return FALSE; + + for (i = 0; i < 8; ++i) + { + if (Octet[i] != Octet[15 - i]) + return FALSE; + } + + Checksum = (Octet[0] << 10) + (Octet[2] << 9) + (Octet[4] << 8) + + (Octet[1] << 2) + (Octet[3] << 1) + Octet[5]; + Checksum %= 0xFFFF; + + return ((USHORT)Checksum == ((Octet[6] << 8) | Octet[7])); +} + +static +CODE_SEG("PAGE") +BOOLEAN +SRomReadMacAddress( + _In_ PDC21X4_ADAPTER Adapter, + _In_ PUCHAR SRom, + _Out_opt_ PULONG AddressOffset) +{ + ULONG MacOffset; + + /* Check if we have a board with an old EAR format */ + if (NdisEqualMemory(SRom, &SRom[16], 8)) + { + /* Validate the EAR checksum */ + if (!AddressRomChecksumValid(SRom)) + { + ERR("EAR has an invalid checksum\n"); + return FALSE; + } + + MacOffset = 0; + goto ReadMac; + } + + /* Check for a new SROM format */ + if (Adapter->ChipType != DC21040) + { + /* Validate the SROM checksum */ + if (SRomChecksumValid(SRom)) + { + MacOffset = SROM_MAC_ADDRESS; + goto ReadMac; + } + } + + /* Sanity check */ + if (*(PULONG)SRom == 0xFFFFFFF || *(PULONG)SRom == 0) + return FALSE; + + WARN("Legacy/unknown board found\n"); + MacOffset = 0; + +ReadMac: + if (AddressOffset) + *AddressOffset = MacOffset; + + NdisMoveMemory(Adapter->PermanentMacAddress, + &SRom[MacOffset], + ETH_LENGTH_OF_ADDRESS); + + return TRUE; +} + +static +CODE_SEG("PAGE") +NDIS_STATUS +SRomParseHeader( + _In_ PDC21X4_ADAPTER Adapter, + _In_ PUCHAR SRom, + _Out_ PUCHAR* InfoLeaf, + _Out_ PUCHAR* SRomEnd) +{ + ULONG i, MacOffset, LeafOffset; + + PAGED_CODE(); + + if (!SRomReadMacAddress(Adapter, SRom, &MacOffset)) + { + ERR("Unable to read the MAC address\n"); + return NDIS_STATUS_FAILURE; + } + + /* Assign our own fake info leaf */ + if (MacOffset != SROM_MAC_ADDRESS) + { + for (i = 0; SRompRepairData[i].InfoLeaf; ++i) + { + /* Check for a MAC match */ + if (NdisEqualMemory(SRompRepairData[i].InfoLeaf, &Adapter->PermanentMacAddress, 3)) + { + /* This check is used to distinguish Accton EN1207 from Maxtech */ + if ((Adapter->PermanentMacAddress[0] == 0xE8) && (SRom[0x1A] == 0x55)) + ++i; + + break; + } + } + if (!SRompRepairData[i].InfoLeaf) + { + ERR("Non-standard SROM format, OUI %02x:%02x:%02x\n", + Adapter->PermanentMacAddress[0], + Adapter->PermanentMacAddress[1], + Adapter->PermanentMacAddress[2]); + + return NDIS_STATUS_ADAPTER_NOT_FOUND; + } + + *InfoLeaf = &SRompRepairData[i].InfoLeaf[3]; + *SRomEnd = *InfoLeaf + SRompRepairData[i].Length; + + /* Update the base address on multiport boards */ + Adapter->PermanentMacAddress[5] += Adapter->ControllerIndex; + +#if DBG + WARN("Non-standard SROM format, using '%s' info leaf\n", SRompRepairData[i].Name); +#endif + return STATUS_SUCCESS; + } + + /* Check if the SROM chip is shared between multiple controllers on a multiport board */ + if (SRom[SROM_CONTROLLER_COUNT] > 1) + { + INFO("Multiport board, controller number %u (%u/%u)\n", + Adapter->DeviceNumber, + Adapter->ControllerIndex + 1, + SRom[SROM_CONTROLLER_COUNT]); + + for (i = 0; i < SRom[SROM_CONTROLLER_COUNT]; ++i) + { + if (SROM_DEVICE_NUMBER(i) >= EE_SIZE) + return NDIS_STATUS_BUFFER_OVERFLOW; + + if (SRom[SROM_DEVICE_NUMBER(i)] == Adapter->DeviceNumber) + break; + } + if (i == SRom[SROM_CONTROLLER_COUNT]) + { + ERR("Controller %u was not found in the SROM\n", Adapter->DeviceNumber); + return NDIS_STATUS_ADAPTER_NOT_FOUND; + } + + if (SROM_LEAF_OFFSET(i) >= EE_SIZE) + return NDIS_STATUS_BUFFER_OVERFLOW; + + /* Update the base address */ + Adapter->PermanentMacAddress[5] += i; + } + else + { + i = 0; + } + + /* Controller info block offset */ + LeafOffset = DcRetrieveWord(SRom + SROM_LEAF_OFFSET(i)); + if (LeafOffset > (EE_SIZE - sizeof(DC_SROM_COMPACT_BLOCK))) + return NDIS_STATUS_BUFFER_OVERFLOW; + + /* Controller info leaf */ + *InfoLeaf = &SRom[LeafOffset]; + + *SRomEnd = SRom + EE_SIZE; + + return STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +NDIS_STATUS +SRomParse( + _In_ PDC21X4_ADAPTER Adapter, + _In_ PUCHAR SRom) +{ + ULONG Index, BlockCount, BlockSize, DefaultMedia; + NDIS_STATUS Status; + USHORT GpioCtrl; + PUCHAR Data, SRomEnd; + + PAGED_CODE(); + + INFO("SROM Version %u, Controller count %u\n", + SRom[SROM_VERSION], + SRom[SROM_CONTROLLER_COUNT]); + + Status = SRomParseHeader(Adapter, SRom, &Data, &SRomEnd); + if (Status != NDIS_STATUS_SUCCESS) + return Status; + + DefaultMedia = DcRetrieveWord(Data); + Data += sizeof(USHORT); + + INFO("Default Media: %04lx\n", DefaultMedia); + + /* Direction of the GPIO pins */ + if (Adapter->ChipType == DC21140) + { + GpioCtrl = *Data++; + + INFO("GPIO Direction: %04lx\n", GpioCtrl); + + GpioCtrl |= DC_GPIO_CONTROL; + + for (Index = 0; Index < MEDIA_LIST_MAX; ++Index) + { + Adapter->Media[Index].GpioCtrl = GpioCtrl; + } + + /* Control word for block type 1 */ + Adapter->MiiMedia.SetupStream[0] = GpioCtrl; + } + + BlockCount = *Data++; + + INFO("Block Count: %u\n", BlockCount); + + if (BlockCount == 0 || BlockCount == 0xFF) + { + WARN("No media information found\n"); + return NDIS_STATUS_SUCCESS; + } + + /* Analyze and decode blocks */ + for (Index = 0; Index < BlockCount; ++Index) + { + if (Adapter->ChipType == DC21041) + { + Status = SRomParse21041Block(Adapter, SRomEnd, Data, &BlockSize); + } + else + { + if (Data > (SRomEnd - 1)) + return NDIS_STATUS_BUFFER_OVERFLOW; + + if (SRomIsBlockExtended(*Data)) + { + Status = SRomParseExtendedBlock(Adapter, SRomEnd, Data, &BlockSize); + } + else + { + Status = SRomDecodeBlockGpr(Adapter, SRomEnd, Data); + BlockSize = 4; + } + } + if (Status != NDIS_STATUS_SUCCESS) + return Status; + + Data += BlockSize; + } + + if ((Adapter->MediaBitmap == 0) && !(Adapter->Features & DC_HAS_MII)) + { + WARN("No media information found\n"); + } + + return NDIS_STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +VOID +SRomShiftOut( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG Sequence, + _In_ ULONG BitCount) +{ + LONG i; + + PAGED_CODE(); + + for (i = BitCount - 1; i >= 0; --i) + { + ULONG DataIn = ((Sequence >> i) & 1) << DC_SERIAL_EE_DI_SHIFT; + + SROM_WRITE(Adapter, DataIn | DC_SERIAL_EE_RD | DC_SERIAL_EE_SR | DC_SERIAL_EE_CS); + SROM_WRITE(Adapter, DataIn | DC_SERIAL_EE_RD | DC_SERIAL_EE_SR | DC_SERIAL_EE_CS | + DC_SERIAL_EE_SK); + SROM_WRITE(Adapter, DataIn | DC_SERIAL_EE_RD | DC_SERIAL_EE_SR | DC_SERIAL_EE_CS); + } +} + +static +CODE_SEG("PAGE") +USHORT +SRomShiftIn( + _In_ PDC21X4_ADAPTER Adapter) +{ + ULONG i, Csr; + USHORT SerialData; + + PAGED_CODE(); + + /* Shift the data out of the EEPROM */ + SerialData = 0; + for (i = 0; i < RTL_BITS_OF(USHORT); ++i) + { + SROM_WRITE(Adapter, DC_SERIAL_EE_RD | DC_SERIAL_EE_SR | DC_SERIAL_EE_CS | DC_SERIAL_EE_SK); + + SROM_READ(Adapter, &Csr); + SerialData = (SerialData << 1) | ((Csr >> DC_SERIAL_EE_DO_SHIFT) & 1); + + SROM_WRITE(Adapter, DC_SERIAL_EE_RD | DC_SERIAL_EE_SR | DC_SERIAL_EE_CS); + } + + /* End the read cycle */ + SROM_WRITE(Adapter, DC_SERIAL_EE_RD | DC_SERIAL_EE_SR); + + return SerialData; +} + +static +CODE_SEG("PAGE") +ULONG +SRomDetectAddressBusWidth( + _In_ PDC21X4_ADAPTER Adapter) +{ + ULONG Csr, BusWidth; + + PAGED_CODE(); + + /* Assume the SROM is a 1kB ROM, send the read command and zero address (6 bits) */ + SRomShiftOut(Adapter, EEPROM_CMD_READ << 6, EEPROM_CMD_LENGTH + 6); + + /* Check the preceding dummy zero bit */ + Csr = DC_READ(Adapter, DcCsr9_SerialInterface); + if (Csr & DC_SERIAL_EE_DO) + { + /* 4kB EEPROM */ + BusWidth = 8; + + /* Send the remaining part of the address */ + SRomShiftOut(Adapter, 0, 8 - 6); + + /* The preceding dummy bit must be zero */ + Csr = DC_READ(Adapter, DcCsr9_SerialInterface); + if (Csr & DC_SERIAL_EE_DO) + return 0; + } + else + { + /* 1kB EEPROM */ + BusWidth = 6; + } + + /* Complete the read cycle */ + (VOID)SRomShiftIn(Adapter); + + return BusWidth; +} + +static +CODE_SEG("PAGE") +BOOLEAN +SRomReadSRom( + _In_ PDC21X4_ADAPTER Adapter, + _Out_writes_all_(EE_SIZE) PVOID SRom) +{ + PUSHORT SRomWord = SRom; + BOOLEAN Success = TRUE; + ULONG BusWidth, Address; + + PAGED_CODE(); + + /* Select the device */ + SROM_WRITE(Adapter, DC_SERIAL_EE_RD | DC_SERIAL_EE_SR); + SROM_WRITE(Adapter, DC_SERIAL_EE_RD | DC_SERIAL_EE_SR | DC_SERIAL_EE_CS); + + BusWidth = SRomDetectAddressBusWidth(Adapter); + if (BusWidth == 0) + { + Success = FALSE; + goto Done; + } + INFO("SROM Bus width: %u\n", BusWidth); + + /* Read the SROM contents once */ + for (Address = 0; Address < (EE_SIZE / sizeof(USHORT)); ++Address) + { + /* Send the command and address */ + SRomShiftOut(Adapter, + (EEPROM_CMD_READ << BusWidth) | Address, + EEPROM_CMD_LENGTH + BusWidth); + + /* Read the data */ + SRomWord[Address] = SRomShiftIn(Adapter); + } + +Done: + /* End chip select */ + DC_WRITE(Adapter, DcCsr9_SerialInterface, 0); + + return Success; +} + +#if DBG +static +CODE_SEG("PAGE") +VOID +SRomDumpContents( + _In_reads_bytes_(Length) const VOID* Buffer, + _In_ ULONG Length) +{ + ULONG Offset, Count, i; + const UCHAR* Data = Buffer; + + PAGED_CODE(); + + DbgPrint("SROM data:\n"); + + Offset = 0; + while (Offset < Length) + { + DbgPrint("%04x:\t", Offset); + + Count = min(Length - Offset, 16); + for (i = 0; i < Count; ++i, ++Offset) + { + DbgPrint("0x%02x, ", Data[Offset], (i == 7) ? '-' : ' '); + } + + DbgPrint("\n"); + } +} +#endif // DBG + +static +CODE_SEG("PAGE") +NDIS_STATUS +SRomRead( + _In_ PDC21X4_ADAPTER Adapter) +{ + PDC_SROM_ENTRY SRomEntry; + NDIS_STATUS Status; + BOOLEAN ReleaseImage; + + PAGED_CODE(); + + Status = NdisAllocateMemoryWithTag((PVOID*)&SRomEntry, + FIELD_OFFSET(DC_SROM_ENTRY, SRomImage[EE_SIZE]), + DC21X4_TAG); + if (Status != NDIS_STATUS_SUCCESS) + return NDIS_STATUS_RESOURCES; + NdisZeroMemory(SRomEntry, FIELD_OFFSET(DC_SROM_ENTRY, SRomImage)); + + ReleaseImage = FALSE; + + if (SRomReadSRom(Adapter, SRomEntry->SRomImage)) + { + if (!SRomRegisterMasterAdapter(Adapter, SRomEntry)) + ReleaseImage = TRUE; + } + else + { + NdisFreeMemory(SRomEntry, 0, 0); + + if (!SRomFindMasterAdapter(Adapter, &SRomEntry)) + { + ERR("Failed to retrieve the SROM contents\n"); + return NDIS_STATUS_FAILURE; + } + } + + Status = SRomParse(Adapter, SRomEntry->SRomImage); + if (Status != NDIS_STATUS_SUCCESS) + { + ERR("Failed to parse SROM\n"); + } + +#if DBG + if (Status != NDIS_STATUS_SUCCESS) + SRomDumpContents(SRomEntry->SRomImage, EE_SIZE); +#endif + + if (ReleaseImage) + NdisFreeMemory(SRomEntry, 0, 0); + + return Status; +} + +static +CODE_SEG("PAGE") +BOOLEAN +AddressRomReadData( + _In_ PDC21X4_ADAPTER Adapter, + _Out_writes_all_(EAR_SIZE) PUCHAR AddressRom) +{ + ULONG Data, i, j; + + PAGED_CODE(); + + /* Reset the ROM pointer */ + DC_WRITE(Adapter, DcCsr9_SerialInterface, 0); + + for (i = 0; i < EAR_SIZE; ++i) + { + for (j = 10000; j > 0; --j) + { + NdisStallExecution(1); + Data = DC_READ(Adapter, DcCsr9_SerialInterface); + + if (!(Data & DC_SERIAL_EAR_DN)) + break; + } + AddressRom[i] = Data & DC_SERIAL_EAR_DT; + } + + if (SRomIsEmpty(AddressRom, EAR_SIZE)) + return FALSE; + + return TRUE; +} + +static +CODE_SEG("PAGE") +NDIS_STATUS +AddressRomRead( + _In_ PDC21X4_ADAPTER Adapter) +{ + PDC_SROM_ENTRY SRomEntry; + NDIS_STATUS Status; + BOOLEAN ReleaseImage; + + PAGED_CODE(); + + Status = NdisAllocateMemoryWithTag((PVOID*)&SRomEntry, + FIELD_OFFSET(DC_SROM_ENTRY, SRomImage[EAR_SIZE]), + DC21X4_TAG); + if (Status != NDIS_STATUS_SUCCESS) + return NDIS_STATUS_RESOURCES; + NdisZeroMemory(SRomEntry, FIELD_OFFSET(DC_SROM_ENTRY, SRomImage)); + + ReleaseImage = FALSE; + + if (AddressRomReadData(Adapter, SRomEntry->SRomImage)) + { + if (!SRomRegisterMasterAdapter(Adapter, SRomEntry)) + ReleaseImage = TRUE; + } + else + { + NdisFreeMemory(SRomEntry, 0, 0); + + if (!SRomFindMasterAdapter(Adapter, &SRomEntry)) + { + ERR("Failed to retrieve the EAR contents\n"); + return NDIS_STATUS_FAILURE; + } + } + + if (!SRomReadMacAddress(Adapter, SRomEntry->SRomImage, NULL)) + { + ERR("Unable to read the MAC address\n"); + Status = NDIS_STATUS_FAILURE; + } + + /* Update the base address on multiport boards */ + Adapter->PermanentMacAddress[5] += Adapter->ControllerIndex; + +#if DBG + if (Status != NDIS_STATUS_SUCCESS) + SRomDumpContents(SRomEntry->SRomImage, EAR_SIZE); +#endif + + if (ReleaseImage) + NdisFreeMemory(SRomEntry, 0, 0); + + return Status; +} + +/* PUBLIC FUNCTIONS ***********************************************************/ + +CODE_SEG("PAGE") +VOID +DcFreeEeprom( + _In_ PDC21X4_ADAPTER Adapter) +{ + PDC_SROM_ENTRY SRomEntry; + + PAGED_CODE(); + + SRomEntry = Adapter->SRomEntry; + if (!SRomEntry) + return; + + SRomAcquireListMutex(); + + /* Unregister the port */ + SRomEntry->DeviceBitmap &= ~(1 << Adapter->DeviceNumber); + + /* + * Free the SROM as soon as the last registered port has removed. + * We can't free it in an unload handler + * as the bus numbers can be changed by a resource rebalance. + */ + if (SRomEntry->DeviceBitmap == 0) + { + INFO("Freeing SROM %p at %u:%u\n", + SRomEntry, + SRomEntry->BusNumber, + SRomEntry->DeviceNumber); + + RemoveEntryList(&SRomEntry->ListEntry); + + NdisFreeMemory(SRomEntry, 0, 0); + } + + SRomReleaseListMutex(); +} + +CODE_SEG("PAGE") +NDIS_STATUS +DcReadEeprom( + _In_ PDC21X4_ADAPTER Adapter) +{ + NDIS_STATUS Status; + + PAGED_CODE(); + + if (Adapter->ChipType == DC21040) + { + /* Ethernet Address ROM */ + Status = AddressRomRead(Adapter); + } + else + { + /* MicroWire Compatible Serial EEPROM */ + Status = SRomRead(Adapter); + } + + if (Status != NDIS_STATUS_SUCCESS) + return Status; + + INFO("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", + Adapter->PermanentMacAddress[0], + Adapter->PermanentMacAddress[1], + Adapter->PermanentMacAddress[2], + Adapter->PermanentMacAddress[3], + Adapter->PermanentMacAddress[4], + Adapter->PermanentMacAddress[5]); + + if (ETH_IS_BROADCAST(Adapter->PermanentMacAddress) || + ETH_IS_EMPTY(Adapter->PermanentMacAddress) || + ETH_IS_MULTICAST(Adapter->PermanentMacAddress)) + { + ERR("Invalid permanent MAC address %02x:%02x:%02x:%02x:%02x:%02x\n", + Adapter->PermanentMacAddress[0], + Adapter->PermanentMacAddress[1], + Adapter->PermanentMacAddress[2], + Adapter->PermanentMacAddress[3], + Adapter->PermanentMacAddress[4], + Adapter->PermanentMacAddress[5]); + + NdisWriteErrorLogEntry(Adapter->AdapterHandle, NDIS_ERROR_CODE_NETWORK_ADDRESS, 0); + + return NDIS_STATUS_INVALID_ADDRESS; + } + + return NDIS_STATUS_SUCCESS; +} diff --git a/drivers/network/dd/dc21x4/eeprom.h b/drivers/network/dd/dc21x4/eeprom.h new file mode 100644 index 00000000000..58d44202def --- /dev/null +++ b/drivers/network/dd/dc21x4/eeprom.h @@ -0,0 +1,108 @@ +/* + * PROJECT: ReactOS DC21x4 Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: EEPROM specific definitions + * COPYRIGHT: Copyright 2023 Dmitry Borisov + */ + +#pragma once + +#include +typedef struct _DC_SROM_COMPACT_BLOCK +{ + USHORT SelectedConnectionType; + UCHAR GpioDirection; /* 21140 only */ + UCHAR BlockCount; +} DC_SROM_COMPACT_BLOCK, *PDC_SROM_COMPACT_BLOCK; +#include + +typedef struct _DC_SROM_REPAIR_ENTRY +{ +#if DBG + PCSTR Name; +#endif + PUCHAR InfoLeaf; + ULONG Length; +} DC_SROM_REPAIR_ENTRY, *PDC_SROM_REPAIR_ENTRY; + +typedef struct _DC_SROM_ENTRY +{ + LIST_ENTRY ListEntry; + ULONG BusNumber; + DC_CHIP_TYPE ChipType; + UCHAR DeviceNumber; + ULONG InterruptLevel; + ULONG InterruptVector; + ULONG DeviceBitmap; + UCHAR SRomImage[ANYSIZE_ARRAY]; +} DC_SROM_ENTRY, *PDC_SROM_ENTRY; + +#define SRomIsBlockExtended(Byte) ((Byte) & 0x80) +#define SRomGetExtendedBlockLength(Byte) (((Byte) & 0x7F) + 1) +#define SRomGetMediaCode(Byte) ((Byte) & 0x3F) +#define SRomBlockHasExtendedData(Byte) ((Byte) & 0x40) +#define SRomIsDefaultMedia(Word) ((Word) & 0x4000) +#define SRomMediaHasActivityIndicator(Word) (((Word) & 0x8000) == 0) +#define SRomMediaActivityIsActiveLow(Word) ((Word) & 0x80) +#define SRomMediaGetSenseMask(Word) (1 << (((Word) & 0x0E) >> 1)) +#define SRomCommandToOpMode(Word) (((Word) & 0x71) << 18) +#define SRomMediaAutoSense(Media) ((Media) & 0x800) +#define SRomMediaToMediaNumber(Word) ((Word) & 0x1F) +#define SRomHmrRegAddress(Byte) ((Byte) & 0x1F) + +#define SROM_OPMODE_MASK \ + (DC_OPMODE_PORT_SELECT | \ + DC_OPMODE_PORT_XMIT_10 | \ + DC_OPMODE_PORT_PCS | \ + DC_OPMODE_PORT_SCRAMBLER) + +#define EE_SIZE 128 +#define EAR_SIZE 32 + +#define EAR_TEST_PATTERN 0xAA5500FFAA5500FFULL + +#define EEPROM_CMD_WRITE 5 +#define EEPROM_CMD_READ 6 +#define EEPROM_CMD_ERASE 7 + +#define EEPROM_CMD_LENGTH 3 + +/* + * Various offsets in the SROM + */ +#define SROM_VERSION 18 +#define SROM_CONTROLLER_COUNT 19 +#define SROM_MAC_ADDRESS 20 +#define SROM_DEVICE_NUMBER(n) (26 + ((n) * 3)) +#define SROM_LEAF_OFFSET(n) (27 + ((n) * 3)) +#define SROM_CHECKSUM_V1 126 +#define SROM_CHECKSUM_V2 94 + +/* + * SROM compact and extended format types + */ +#define SROM_BLOCK_TYPE_GPR 0 +#define SROM_BLOCK_TYPE_MII_1 1 +#define SROM_BLOCK_TYPE_SIA 2 +#define SROM_BLOCK_TYPE_MII_2 3 +#define SROM_BLOCK_TYPE_SYM 4 +#define SROM_BLOCK_TYPE_RESET 5 +#define SROM_BLOCK_TYPE_PHY_SHUTDOWN 6 +#define SROM_BLOCK_TYPE_HOMERUN 7 + +#define SROM_MAX_STREAM_REGS 6 + +/* + * SROM media codes + */ +#define SROM_MEDIA_10T 0 +#define SROM_MEDIA_BNC 1 +#define SROM_MEDIA_AUI 2 +#define SROM_MEDIA_100T_HD 3 +#define SROM_MEDIA_10T_FD 4 +#define SROM_MEDIA_100TX_FD 5 +#define SROM_MEDIA_100T4 6 +#define SROM_MEDIA_100FX_HD 7 +#define SROM_MEDIA_100FX_FD 8 +#define SROM_MEDIA_MAX 8 +#define SROM_MEDIA_HMR 18 diff --git a/drivers/network/dd/dc21x4/eeprom_data.c b/drivers/network/dd/dc21x4/eeprom_data.c new file mode 100644 index 00000000000..f00f80b293d --- /dev/null +++ b/drivers/network/dd/dc21x4/eeprom_data.c @@ -0,0 +1,219 @@ +/* + * PROJECT: ReactOS DC21x4 Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: EEPROM data for boards without the standard SROM Format + * COPYRIGHT: Copyright 2023 Dmitry Borisov + */ + +/* Adapted from the Linux tulip driver written by Donald Becker */ + +/* INCLUDES *******************************************************************/ + +#include "dc21x4.h" + +/* GLOBALS ********************************************************************/ + +/* Asante */ +static DC_PG_DATA UCHAR SRompLeafAsante[] = +{ + 0x00, 0x00, 0x94, + + 0x00, 0x08, // Default Autoselect + 0x00, // GPIO direction + 0x01, // 1 block + + 0x80 + 12, // Extended block, 12 bytes + 0x01, // MII + 0x00, // PHY #0 + 0x00, // GPIO stream length + 0x00, // Reset stream length + 0x00, 0x78, // Capabilities + 0xE0, 0x01, // Advertisement + 0x00, 0x50, // FDX + 0x00, 0x18, // TTM +}; + +/* SMC 9332DST */ +static DC_PG_DATA UCHAR SRompLeaf9332[] = +{ + 0x00, 0x00, 0xC0, + + 0x00, 0x08, // Default Autoselect + 0x1F, // GPIO direction + 0x04, // 4 blocks + + 0x00, // GPR 0 + 0x00, // GPIO data + 0x9E, 0x00, // Command 0x009E + + 0x04, // GPR 4 + 0x00, // GPIO data + 0x9E, 0x00, // Command 0x009E + 0x03, // GPR 3 + 0x09, // GPIO data + 0x6D, 0x00, // Command 0x006D + + 0x05, // GPR 5 + 0x09, // GPIO data + 0x6D, 0x00, // Command 0x006D +}; + +/* Cogent EM100 */ +static DC_PG_DATA UCHAR SRompLeafEm100[] = +{ + 0x00, 0x00, 0x92, + + 0x00, 0x08, // Default Autoselect + 0x3F, // GPIO direction + 0x06, // 6 blocks + + 0x07, // GPR 7 + 0x01, // GPIO data + 0x21, 0x80, // Command 0x8021 + + 0x08, // GPR 8 + 0x01, // GPIO data + 0x21, 0x80, // Command 0x8021 + + 0x00, // GPR 0 + 0x01, // GPIO data + 0x9E, 0x00, // Command 0x009E + + 0x04, // GPR 4 + 0x01, // GPIO data + 0x9E, 0x00, // Command 0x009E + + 0x03, // GPR 3 + 0x01, // GPIO data + 0x6D, 0x00, // Command 0x006D + + 0x05, // GPR 5 + 0x01, // GPIO data + 0x6D, 0x00, // Command 0x006D +}; + +/* Maxtech NX-110 */ +static DC_PG_DATA UCHAR SRompLeafNx110[] = +{ + 0x00, 0x00, 0xE8, + + 0x00, 0x08, // Default Autoselect + 0x13, // GPIO direction + 0x05, // 5 blocks + + 0x01, // GPR 1 + 0x10, // GPIO data + 0x9E, 0x00, // Command 0x009E + + 0x00, // GPR 0 + 0x00, // GPIO data + 0x9E, 0x00, // Command 0x009E + + 0x04, // GPR 4 + 0x00, // GPIO data + 0x9E, 0x00, // Command 0x009E + + 0x03, // GPR 3 + 0x03, // GPIO data + 0x6D, 0x00, // Command 0x006D + + 0x05, // GPR 5 + 0x03, // GPIO data + 0x6D, 0x00, // Command 0x006D +}; + +/* Accton EN1207 */ +static DC_PG_DATA UCHAR SRompLeafEn1207[] = +{ + 0x00, 0x00, 0xE8, + + 0x00, 0x08, // Default Autoselect + 0x1F, // GPIO direction + 0x05, // 5 blocks + + 0x01, // GPR 1 + 0x1B, // GPIO data + 0x00, 0x00, // Command 0x0000 + + 0x00, // GPR 0 + 0x0B, // GPIO data + 0x9E, 0x00, // Command 0x009E + + 0x04, // GPR 4 + 0x0B, // GPIO data + 0x9E, 0x00, // Command 0x009E + + 0x03, // GPR 3 + 0x1B, // GPIO data + 0x6D, 0x00, // Command 0x006D + + 0x05, // GPR 5 + 0x1B, // GPIO data + 0x6D, 0x00, // Command 0x006D +}; + +/* NetWinder */ +static DC_PG_DATA UCHAR SRompLeafNetWinder[] = +{ + 0x00, 0x10, 0x57, + + 0x00, 0x08, // Default Autoselect + 0x01, // 1 block + + 0x80 + 21, // Extended block, 21 bytes + 0x03, // MII + 0x01, // PHY #1 + 0x00, // GPIO stream length + 0x03, // Reset stream length + 0x21, 0x08, + 0x00, 0x00, + 0x01, 0x00, + 0x00, 0x00, // Capabilities + 0xE1, 0x01, // Advertisement + 0x00, 0x00, // FDX + 0x00, 0x00, // TTM + 0x00, // PHY cannot be unplugged +}; + +/* Cobalt Microserver */ +static DC_PG_DATA UCHAR SRompLeafCobaltMicroserver[] = +{ + 0x00, 0x10, 0xE0, + + 0x00, 0x08, // Default Autoselect + 0x01, // 1 block + + 0x80 + 21, // Extended block, 21 bytes + 0x03, // MII + 0x00, // PHY #0 + 0x00, // GPIO stream length + 0x04, // Reset stream length + 0x01, 0x08, // Set control mode, GP0 output + 0x00, 0x00, // Drive GP0 Low (RST is active low) + 0x00, 0x08, // Control mode, GP0 input (undriven) + 0x00, 0x00, // Clear control mode + 0x00, 0x78, // Capabilities: 100TX FDX + HDX, 10bT FDX + HDX + 0xE0, 0x01, // Advertise all above + 0x00, 0x50, // FDX all above + 0x00, 0x18, // Set fast TTM in 100bt modes + 0x00, // PHY cannot be unplugged +}; + +#if DBG +#define DEFINE_BOARD(Leaf, Name) { Name, Leaf, sizeof(Leaf) - 3 /* OUI (3 bytes) */} +#else +#define DEFINE_BOARD(Leaf, Name) { Leaf, sizeof(Leaf) - 3 } +#endif + +DC_PG_DATA +DC_SROM_REPAIR_ENTRY SRompRepairData[] = +{ + DEFINE_BOARD(SRompLeafAsante, "Asante"), + DEFINE_BOARD(SRompLeaf9332, "SMC 9332DST"), + DEFINE_BOARD(SRompLeafEm100, "Cogent EM100"), + DEFINE_BOARD(SRompLeafNx110, "Maxtech NX-110"), + DEFINE_BOARD(SRompLeafEn1207, "Accton EN1207"), // Must be defined after the NX-110 + DEFINE_BOARD(SRompLeafNetWinder, "NetWinder"), + DEFINE_BOARD(SRompLeafCobaltMicroserver, "Cobalt Microserver"), + DEFINE_BOARD(NULL, NULL), +}; diff --git a/drivers/network/dd/dc21x4/hardware.c b/drivers/network/dd/dc21x4/hardware.c new file mode 100644 index 00000000000..971c10d6d1a --- /dev/null +++ b/drivers/network/dd/dc21x4/hardware.c @@ -0,0 +1,585 @@ +/* + * PROJECT: ReactOS DC21x4 Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Hardware specific functions + * COPYRIGHT: Copyright 2023 Dmitry Borisov + */ + +/* INCLUDES *******************************************************************/ + +#include "dc21x4.h" + +#include + +/* FUNCTIONS ******************************************************************/ + +VOID +DcDisableHw( + _In_ PDC21X4_ADAPTER Adapter) +{ + ULONG OpMode; + + /* Disable interrupts */ + DC_WRITE(Adapter, DcCsr7_IrqMask, 0); + + /* Stop DMA */ + OpMode = Adapter->OpMode; + OpMode &= ~(DC_OPMODE_RX_ENABLE | DC_OPMODE_TX_ENABLE); + DC_WRITE(Adapter, DcCsr6_OpMode, OpMode); + + /* Put the adapter to snooze mode */ + DcPowerSave(Adapter, TRUE); + + /* Perform a software reset */ + DC_WRITE(Adapter, DcCsr0_BusMode, DC_BUS_MODE_SOFT_RESET); +} + +VOID +DcStopTxRxProcess( + _In_ PDC21X4_ADAPTER Adapter) +{ + ULONG i, OpMode, Status; + + OpMode = Adapter->OpMode; + OpMode &= ~(DC_OPMODE_RX_ENABLE | DC_OPMODE_TX_ENABLE); + DC_WRITE(Adapter, DcCsr6_OpMode, OpMode); + + for (i = 0; i < 5000; ++i) + { + Status = DC_READ(Adapter, DcCsr5_Status); + + if (((Status & DC_STATUS_TX_STATE_MASK) == DC_STATUS_TX_STATE_STOPPED) && + ((Status & DC_STATUS_RX_STATE_MASK) == DC_STATUS_RX_STATE_STOPPED)) + { + return; + } + + NdisStallExecution(10); + } + + WARN("Failed to stop the TX/RX process 0x%08lx\n", Status); +} + +VOID +DcWriteGpio( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG Value) +{ + ULONG Data, Register; + + /* Some chips don't have a separate GPIO register */ + if (Adapter->Features & DC_SIA_GPIO) + { + Data = Adapter->SiaSetting; + Data &= 0x0000FFFF; /* SIA */ + Data |= Value << 16; /* GPIO */ + Adapter->SiaSetting = Data; + + Register = DcCsr15_SiaGeneral; + } + else + { + Data = Value; + Register = DcCsr12_Gpio; + } + DC_WRITE(Adapter, Register, Data); +} + +VOID +DcWriteSia( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG Csr13, + _In_ ULONG Csr14, + _In_ ULONG Csr15) +{ + ULONG SiaConn, SiaGen; + + TRACE("CSR13 %08lx, CSR14 %08lx, CSR15 %08lx\n", Csr13, Csr14, Csr15); + + SiaConn = 0; + + /* The 21145 comes with 16 new bits in CSR13 */ + if (Adapter->Features & DC_SIA_ANALOG_CONTROL) + { + SiaConn = Adapter->AnalogControl; + } + + /* Reset the transceiver */ + DC_WRITE(Adapter, DcCsr13_SiaConnectivity, SiaConn | DC_SIA_CONN_RESET); + NdisStallExecution(20); + + /* Some chips don't have a separate GPIO register */ + if (Adapter->Features & DC_SIA_GPIO) + { + SiaGen = Adapter->SiaSetting; + SiaGen &= 0xFFFF0000; /* GPIO */ + SiaGen |= Csr15; /* SIA */ + Adapter->SiaSetting = SiaGen; + } + else + { + SiaGen = Csr15; + } + + DC_WRITE(Adapter, DcCsr14_SiaTxRx, Csr14); + DC_WRITE(Adapter, DcCsr15_SiaGeneral, SiaGen); + + /* Don't reset the transceiver twice */ + if (Csr13 == DC_SIA_CONN_RESET) + return; + + DC_WRITE(Adapter, DcCsr13_SiaConnectivity, SiaConn | Csr13); +} + +VOID +DcTestPacket( + _In_ PDC21X4_ADAPTER Adapter) +{ + PDC_TCB Tcb; + PDC_TBD Tbd; + ULONG FrameNumber; + + Adapter->MediaTestStatus = FALSE; + Adapter->ModeFlags |= DC_MODE_TEST_PACKET; + + if (!Adapter->LoopbackFrameSlots) + { + ERR("Failed to complete test packets, CSR12 %08lx, CSR5 %08lx\n", + DC_READ(Adapter, DcCsr12_SiaStatus), + DC_READ(Adapter, DcCsr5_Status)); + + /* Try to recover the lost TX buffers */ + NdisScheduleWorkItem(&Adapter->TxRecoveryWorkItem); + return; + } + + --Adapter->LoopbackFrameSlots; + + FrameNumber = (Adapter->LoopbackFrameNumber++) % DC_LOOPBACK_FRAMES; + + Tbd = Adapter->CurrentTbd; + Adapter->CurrentTbd = DC_NEXT_TBD(Adapter, Tbd); + + Tcb = Adapter->CurrentTcb; + Adapter->CurrentTcb = DC_NEXT_TCB(Adapter, Tcb); + + Tcb->Tbd = Tbd; + Tcb->Packet = NULL; + + ASSERT(!(Tbd->Status & DC_TBD_STATUS_OWNED)); + + /* Put the loopback frame on the transmit ring */ + Tbd->Address1 = Adapter->LoopbackFramePhys[FrameNumber]; + Tbd->Address2 = 0; + Tbd->Control &= DC_TBD_CONTROL_END_OF_RING; + Tbd->Control |= DC_LOOPBACK_FRAME_SIZE | + DC_TBD_CONTROL_FIRST_FRAGMENT | + DC_TBD_CONTROL_LAST_FRAGMENT | + DC_TBD_CONTROL_REQUEST_INTERRUPT; + DC_WRITE_BARRIER(); + Tbd->Status = DC_TBD_STATUS_OWNED; + + /* Send the loopback packet to verify connectivity of a media */ + DC_WRITE(Adapter, DcCsr1_TxPoll, DC_TX_POLL_DOORBELL); +} + +BOOLEAN +DcSetupFrameDownload( + _In_ PDC21X4_ADAPTER Adapter, + _In_ BOOLEAN WaitForCompletion) +{ + PDC_TCB Tcb; + PDC_TBD Tbd; + ULONG i, Control; + + Tbd = Adapter->CurrentTbd; + + /* Ensure correct setup frame processing */ + if (Tbd != Adapter->HeadTbd) + { + ASSERT(!(Tbd->Status & DC_TBD_STATUS_OWNED)); + + /* Put the null frame on the transmit ring */ + Tbd->Control &= DC_TBD_CONTROL_END_OF_RING; + Tbd->Address1 = 0; + Tbd->Address2 = 0; + DC_WRITE_BARRIER(); + Tbd->Status = DC_TBD_STATUS_OWNED; + + Tbd = DC_NEXT_TBD(Adapter, Tbd); + } + + Adapter->CurrentTbd = DC_NEXT_TBD(Adapter, Tbd); + + Tcb = Adapter->CurrentTcb; + Adapter->CurrentTcb = DC_NEXT_TCB(Adapter, Tcb); + + Tcb->Tbd = Tbd; + Tcb->Packet = NULL; + + ASSERT(!(Tbd->Status & DC_TBD_STATUS_OWNED)); + + /* Prepare the setup frame */ + Tbd->Address1 = Adapter->SetupFramePhys; + Tbd->Address2 = 0; + Tbd->Control &= DC_TBD_CONTROL_END_OF_RING; + Control = DC_SETUP_FRAME_SIZE | DC_TBD_CONTROL_SETUP_FRAME; + if (!WaitForCompletion) + Control |= DC_TBD_CONTROL_REQUEST_INTERRUPT; + if (Adapter->ProgramHashPerfectFilter) + Control |= DC_TBD_CONTROL_HASH_PERFECT_FILTER; + Tbd->Control |= Control; + DC_WRITE_BARRIER(); + Tbd->Status = DC_TBD_STATUS_OWNED; + + DC_WRITE(Adapter, DcCsr1_TxPoll, DC_TX_POLL_DOORBELL); + + if (!WaitForCompletion) + return TRUE; + + /* Wait up to 500 ms for the chip to process the setup frame */ + for (i = 50000; i > 0; --i) + { + NdisStallExecution(10); + + KeMemoryBarrierWithoutFence(); + if (!(Tbd->Status & DC_TBD_STATUS_OWNED)) + break; + } + if (i == 0) + { + ERR("Failed to complete setup frame %08lx\n", Tbd->Status); + return FALSE; + } + + return TRUE; +} + +CODE_SEG("PAGE") +VOID +DcSetupFrameInitialize( + _In_ PDC21X4_ADAPTER Adapter) +{ + PULONG SetupFrame, SetupFrameStart; + PUSHORT MacAddress; + ULONG i; + + PAGED_CODE(); + + SetupFrame = Adapter->SetupFrame; + + /* Add the physical address entry */ + MacAddress = (PUSHORT)Adapter->CurrentMacAddress; + *SetupFrame++ = DC_SETUP_FRAME_ENTRY(MacAddress[0]); + *SetupFrame++ = DC_SETUP_FRAME_ENTRY(MacAddress[1]); + *SetupFrame++ = DC_SETUP_FRAME_ENTRY(MacAddress[2]); + + /* Pad to 16 addresses */ + SetupFrameStart = Adapter->SetupFrame; + for (i = 1; i < DC_SETUP_FRAME_PERFECT_FILTER_ADDRESSES; ++i) + { + *SetupFrame++ = SetupFrameStart[0]; + *SetupFrame++ = SetupFrameStart[1]; + *SetupFrame++ = SetupFrameStart[2]; + } +} + +static +VOID +DcSetupFramePerfectFiltering( + _In_ PDC21X4_ADAPTER Adapter) +{ + PULONG SetupFrame, SetupFrameStart; + PUSHORT MacAddress; + ULONG i; + + SetupFrame = Adapter->SetupFrame; + + /* Add the physical address entry */ + MacAddress = (PUSHORT)Adapter->CurrentMacAddress; + *SetupFrame++ = DC_SETUP_FRAME_ENTRY(MacAddress[0]); + *SetupFrame++ = DC_SETUP_FRAME_ENTRY(MacAddress[1]); + *SetupFrame++ = DC_SETUP_FRAME_ENTRY(MacAddress[2]); + + /* Store multicast addresses */ + for (i = 0; i < Adapter->MulticastCount; ++i) + { + MacAddress = (PUSHORT)Adapter->MulticastList[i].MacAddress; + + *SetupFrame++ = DC_SETUP_FRAME_ENTRY(MacAddress[0]); + *SetupFrame++ = DC_SETUP_FRAME_ENTRY(MacAddress[1]); + *SetupFrame++ = DC_SETUP_FRAME_ENTRY(MacAddress[2]); + } + + ++i; + + /* Add the broadcast address entry */ + if (Adapter->PacketFilter & NDIS_PACKET_TYPE_BROADCAST) + { + *SetupFrame++ = DC_SETUP_FRAME_ENTRY(0x0000FFFF); + *SetupFrame++ = DC_SETUP_FRAME_ENTRY(0x0000FFFF); + *SetupFrame++ = DC_SETUP_FRAME_ENTRY(0x0000FFFF); + + ++i; + } + + /* Pad to 16 addresses */ + SetupFrameStart = Adapter->SetupFrame; + while (i < DC_SETUP_FRAME_PERFECT_FILTER_ADDRESSES) + { + *SetupFrame++ = SetupFrameStart[0]; + *SetupFrame++ = SetupFrameStart[1]; + *SetupFrame++ = SetupFrameStart[2]; + + ++i; + } +} + +static +VOID +DcSetupFrameImperfectFiltering( + _In_ PDC21X4_ADAPTER Adapter) +{ + PULONG SetupFrame = Adapter->SetupFrame; + PUSHORT MacAddress; + ULONG Hash, i; + + RtlZeroMemory(SetupFrame, DC_SETUP_FRAME_SIZE); + + /* Fill up the 512-bit multicast hash table */ + for (i = 0; i < Adapter->MulticastCount; ++i) + { + MacAddress = (PUSHORT)Adapter->MulticastList[i].MacAddress; + + /* Only need lower 9 bits of the hash */ + Hash = DcEthernetCrc(MacAddress, ETH_LENGTH_OF_ADDRESS); + Hash &= 512 - 1; + SetupFrame[Hash / 16] |= 1 << (Hash % 16); + } + + /* Insert the broadcast address hash to the bin */ + if (Adapter->PacketFilter & NDIS_PACKET_TYPE_BROADCAST) + { + Hash = DC_SETUP_FRAME_BROADCAST_HASH; + SetupFrame[Hash / 16] |= 1 << (Hash % 16); + } + + /* Add the physical address entry */ + MacAddress = (PUSHORT)Adapter->CurrentMacAddress; + SetupFrame[39] = DC_SETUP_FRAME_ENTRY(MacAddress[0]); + SetupFrame[40] = DC_SETUP_FRAME_ENTRY(MacAddress[1]); + SetupFrame[41] = DC_SETUP_FRAME_ENTRY(MacAddress[2]); +} + +NDIS_STATUS +DcUpdateMulticastList( + _In_ PDC21X4_ADAPTER Adapter) +{ + BOOLEAN UsePerfectFiltering; + + /* If more than 14 addresses are requested, switch to hash filtering mode */ + UsePerfectFiltering = (Adapter->MulticastCount <= DC_SETUP_FRAME_ADDRESSES); + + Adapter->ProgramHashPerfectFilter = UsePerfectFiltering; + Adapter->OidPending = TRUE; + + if (UsePerfectFiltering) + DcSetupFramePerfectFiltering(Adapter); + else + DcSetupFrameImperfectFiltering(Adapter); + + NdisAcquireSpinLock(&Adapter->SendLock); + + DcSetupFrameDownload(Adapter, FALSE); + + NdisReleaseSpinLock(&Adapter->SendLock); + + return NDIS_STATUS_PENDING; +} + +NDIS_STATUS +DcApplyPacketFilter( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG PacketFilter) +{ + ULONG OpMode, OldPacketFilter; + + INFO("Packet filter value 0x%lx\n", PacketFilter); + + NdisAcquireSpinLock(&Adapter->ModeLock); + + /* Update the filtering mode */ + OpMode = Adapter->OpMode; + OpMode &= ~(DC_OPMODE_RX_PROMISCUOUS | DC_OPMODE_RX_ALL_MULTICAST); + if (PacketFilter & NDIS_PACKET_TYPE_PROMISCUOUS) + { + OpMode |= DC_OPMODE_RX_PROMISCUOUS; + } + else if (PacketFilter & NDIS_PACKET_TYPE_ALL_MULTICAST) + { + OpMode |= DC_OPMODE_RX_ALL_MULTICAST; + } + Adapter->OpMode = OpMode; + DC_WRITE(Adapter, DcCsr6_OpMode, OpMode); + + NdisReleaseSpinLock(&Adapter->ModeLock); + + OldPacketFilter = Adapter->PacketFilter; + Adapter->PacketFilter = PacketFilter; + + /* Program the NIC to receive or reject broadcast frames */ + if ((OldPacketFilter ^ PacketFilter) & NDIS_PACKET_TYPE_BROADCAST) + { + return DcUpdateMulticastList(Adapter); + } + + return NDIS_STATUS_SUCCESS; +} + +static +CODE_SEG("PAGE") +VOID +DcSoftReset( + _In_ PDC21X4_ADAPTER Adapter) +{ + PAGED_CODE(); + + /* Linux driver does this */ + if (Adapter->Features & DC_HAS_MII) + { + /* Select the MII/SYM port */ + DC_WRITE(Adapter, DcCsr6_OpMode, DC_OPMODE_PORT_SELECT); + } + + /* Perform a software reset */ + DC_WRITE(Adapter, DcCsr0_BusMode, DC_BUS_MODE_SOFT_RESET); + NdisMSleep(100); + DC_WRITE(Adapter, DcCsr0_BusMode, Adapter->BusMode); +} + +CODE_SEG("PAGE") +NDIS_STATUS +DcSetupAdapter( + _In_ PDC21X4_ADAPTER Adapter) +{ + PAGED_CODE(); + + DcInitTxRing(Adapter); + DcInitRxRing(Adapter); + + /* Initial values */ + if (!MEDIA_IS_FIXED(Adapter)) + { + Adapter->LinkSpeedMbps = 10; + } + Adapter->MediaNumber = Adapter->DefaultMedia; + Adapter->ModeFlags &= ~(DC_MODE_PORT_AUTOSENSE | DC_MODE_AUI_FAILED | DC_MODE_BNC_FAILED | + DC_MODE_TEST_PACKET | DC_MODE_AUTONEG_MASK); + + DcSoftReset(Adapter); + + /* Receive descriptor ring buffer */ + DC_WRITE(Adapter, DcCsr3_RxRingAddress, Adapter->RbdPhys); + + /* Transmit descriptor ring buffer */ + DC_WRITE(Adapter, DcCsr4_TxRingAddress, Adapter->TbdPhys); + + switch (Adapter->ChipType) + { + case DC21040: + { + DcWriteSia(Adapter, + Adapter->Media[Adapter->MediaNumber].Csr13, + Adapter->Media[Adapter->MediaNumber].Csr14, + Adapter->Media[Adapter->MediaNumber].Csr15); + + /* Explicitly specifed by user */ + if (Adapter->MediaNumber == MEDIA_10T_FD) + { + Adapter->OpMode |= DC_OPMODE_FULL_DUPLEX; + } + break; + } + + case DC21041: + { + MediaSiaSelect(Adapter); + break; + } + + case DC21140: + { + if (Adapter->MediaNumber == MEDIA_MII) + { + MediaSelectMiiPort(Adapter, !(Adapter->Flags & DC_FIRST_SETUP)); + MediaMiiSelect(Adapter); + } + else + { + /* All media use the same GPIO directon */ + DC_WRITE(Adapter, DcCsr12_Gpio, Adapter->Media[Adapter->MediaNumber].GpioCtrl); + NdisStallExecution(10); + + MediaGprSelect(Adapter); + } + break; + } + + case DC21143: + case DC21145: + { + /* Init the HPNA PHY */ + if ((Adapter->MediaBitmap & (1 << MEDIA_HMR)) && Adapter->HpnaInitBitmap) + { + HpnaPhyInit(Adapter); + } + + if (Adapter->MediaNumber == MEDIA_MII) + { + MediaSelectMiiPort(Adapter, !(Adapter->Flags & DC_FIRST_SETUP)); + MediaMiiSelect(Adapter); + break; + } + + /* If the current media is FX, assume we have a link */ + if (MEDIA_IS_FX(Adapter->MediaNumber)) + { + Adapter->LinkUp = TRUE; + + NdisMIndicateStatus(Adapter->AdapterHandle, + NDIS_STATUS_MEDIA_CONNECT, + NULL, + 0); + NdisMIndicateStatusComplete(Adapter->AdapterHandle); + } + + MediaSiaSelect(Adapter); + break; + } + + default: + ASSERT(FALSE); + UNREACHABLE; + break; + } + + /* Start the TX process */ + Adapter->OpMode |= DC_OPMODE_TX_ENABLE; + DC_WRITE(Adapter, DcCsr6_OpMode, Adapter->OpMode); + + /* Load the address recognition RAM */ + if (!DcSetupFrameDownload(Adapter, TRUE)) + { + /* This normally should not happen */ + ASSERT(FALSE); + + NdisWriteErrorLogEntry(Adapter->AdapterHandle, + NDIS_ERROR_CODE_HARDWARE_FAILURE, + 1, + __LINE__); + + return NDIS_STATUS_HARD_ERRORS; + } + + return NDIS_STATUS_SUCCESS; +} diff --git a/drivers/network/dd/dc21x4/init.c b/drivers/network/dd/dc21x4/init.c new file mode 100644 index 00000000000..1bcec4d9a65 --- /dev/null +++ b/drivers/network/dd/dc21x4/init.c @@ -0,0 +1,1321 @@ +/* + * 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 + */ + +/* INCLUDES *******************************************************************/ + +#include "dc21x4.h" + +#include + +/* 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; +} diff --git a/drivers/network/dd/dc21x4/interrupt.c b/drivers/network/dd/dc21x4/interrupt.c new file mode 100644 index 00000000000..2a0557f1589 --- /dev/null +++ b/drivers/network/dd/dc21x4/interrupt.c @@ -0,0 +1,625 @@ +/* + * PROJECT: ReactOS DC21x4 Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Interrupt handling + * COPYRIGHT: Copyright 2023 Dmitry Borisov + */ + +/* INCLUDES *******************************************************************/ + +#include "dc21x4.h" + +#include + +/* FUNCTIONS ******************************************************************/ + +static +VOID +DcAdjustTxFifoThreshold( + _In_ PDC21X4_ADAPTER Adapter) +{ + ULONG OpMode; + + TRACE("TX underrun\n"); + + /* Maximum threshold reached */ + if ((Adapter->OpMode & DC_OPMODE_STORE_AND_FORWARD) || + (++Adapter->TransmitUnderruns < DC_TX_UNDERRUN_LIMIT)) + { + NdisDprAcquireSpinLock(&Adapter->SendLock); + + /* Start the transmit process if it was suspended */ + DC_WRITE(Adapter, DcCsr1_TxPoll, DC_TX_POLL_DOORBELL); + + NdisDprReleaseSpinLock(&Adapter->SendLock); + return; + } + Adapter->TransmitUnderruns = 0; + + NdisDprAcquireSpinLock(&Adapter->ModeLock); + + OpMode = Adapter->OpMode; + + /* Update the FIFO threshold level to minimize Tx FIFO underrun */ + if ((OpMode & DC_OPMODE_TX_THRESHOLD_CTRL_MASK) != DC_OPMODE_TX_THRESHOLD_MAX) + { + OpMode += DC_OPMODE_TX_THRESHOLD_LEVEL; + + INFO("New OP Mode %08lx\n", OpMode); + } + else + { + OpMode |= DC_OPMODE_STORE_AND_FORWARD; + + INFO("Store & Forward\n"); + } + + DcStopTxRxProcess(Adapter); + + Adapter->OpMode = OpMode; + + /* Restart the transmit process */ + DC_WRITE(Adapter, DcCsr6_OpMode, OpMode); + + NdisDprReleaseSpinLock(&Adapter->ModeLock); +} + +static +VOID +DcHandleTxJabberTimeout( + _In_ PDC21X4_ADAPTER Adapter) +{ + WARN("Transmit jabber timer timed out\n"); + + NdisWriteErrorLogEntry(Adapter->AdapterHandle, NDIS_ERROR_CODE_HARDWARE_FAILURE, 1, __LINE__); + + NdisDprAcquireSpinLock(&Adapter->ModeLock); + + /* Start the transmit process if it was stopped */ + DC_WRITE(Adapter, DcCsr6_OpMode, Adapter->OpMode); + + NdisDprReleaseSpinLock(&Adapter->ModeLock); +} + +static +VOID +DcHandleTxCompletedFrames( + _In_ PDC21X4_ADAPTER Adapter, + _Inout_ PLIST_ENTRY SendReadyList, + _Out_ PULONG DpcEvents) +{ + PDC_TCB Tcb; + PDC_TBD Tbd; + ULONG TbdStatus, Collisions; + + for (Tcb = Adapter->LastTcb; + Tcb != Adapter->CurrentTcb; + Tcb = DC_NEXT_TCB(Adapter, Tcb)) + { + Tbd = Tcb->Tbd; + TbdStatus = Tbd->Status; + + if (TbdStatus & DC_TBD_STATUS_OWNED) + break; + + ++Adapter->TcbCompleted; + + /* Complete the packet filter change request asynchronously */ + if (Tbd->Control & DC_TBD_CONTROL_SETUP_FRAME) + { + Tbd->Control &= ~DC_TBD_CONTROL_SETUP_FRAME; + + if (Tbd->Control & DC_TBD_CONTROL_REQUEST_INTERRUPT) + { + *DpcEvents |= DC_EVENT_SETUP_FRAME_COMPLETED; + } + + continue; + } + + /* This is our media test packet, so no need to update the TX statistics */ + if (!Tcb->Packet) + { + _InterlockedExchange(&Adapter->MediaTestStatus, + !(TbdStatus & DC_TBD_STATUS_ERROR_SUMMARY)); + + ASSERT(Adapter->LoopbackFrameSlots < DC_LOOPBACK_FRAMES); + + ++Adapter->LoopbackFrameSlots; + + continue; + } + + if (TbdStatus & DC_TBD_STATUS_ERROR_SUMMARY) + { + ++Adapter->Statistics.TransmitErrors; + + if (TbdStatus & DC_TBD_STATUS_UNDERFLOW) + ++Adapter->Statistics.TransmitUnderrunErrors; + else if (TbdStatus & DC_TBD_STATUS_LATE_COLLISION) + ++Adapter->Statistics.TransmitLateCollisions; + + if (TbdStatus & DC_TBD_STATUS_RETRY_ERROR) + ++Adapter->Statistics.TransmitExcessiveCollisions; + if (TbdStatus & DC_TBD_STATUS_CARRIER_LOST) + ++Adapter->Statistics.TransmitLostCarrierSense; + } + else + { + ++Adapter->Statistics.TransmitOk; + + if (TbdStatus & DC_TBD_STATUS_DEFFERED) + ++Adapter->Statistics.TransmitDeferred; + if (TbdStatus & DC_TBD_STATUS_HEARTBEAT_FAIL) + ++Adapter->Statistics.TransmitHeartbeatErrors; + + Collisions = (TbdStatus & DC_TBD_STATUS_COLLISIONS_MASK) >> + DC_TBD_STATUS_COLLISIONS_SHIFT; + if (Collisions == 1) + ++Adapter->Statistics.TransmitOneRetry; + else if (Collisions > 1) + ++Adapter->Statistics.TransmitMoreCollisions; + } + + InsertTailList(SendReadyList, DC_LIST_ENTRY_FROM_PACKET(Tcb->Packet)); + + DC_RELEASE_TCB(Adapter, Tcb); + } + + Adapter->LastTcb = Tcb; +} + +static +VOID +DcHandleTx( + _In_ PDC21X4_ADAPTER Adapter) +{ + LIST_ENTRY SendReadyList; + ULONG DpcEvents; + + TRACE("Handle TX\n"); + + InitializeListHead(&SendReadyList); + DpcEvents = 0; + + NdisDprAcquireSpinLock(&Adapter->SendLock); + + DcHandleTxCompletedFrames(Adapter, &SendReadyList, &DpcEvents); + + if (!IsListEmpty(&Adapter->SendQueueList)) + { + DcProcessPendingPackets(Adapter); + } + + NdisDprReleaseSpinLock(&Adapter->SendLock); + + while (!IsListEmpty(&SendReadyList)) + { + PLIST_ENTRY Entry = RemoveHeadList(&SendReadyList); + + TRACE("Complete TX packet %p\n", DC_PACKET_FROM_LIST_ENTRY(Entry)); + + NdisMSendComplete(Adapter->AdapterHandle, + DC_PACKET_FROM_LIST_ENTRY(Entry), + NDIS_STATUS_SUCCESS); + } + + /* We have to complete the OID request outside of the spinlock */ + if (DpcEvents & DC_EVENT_SETUP_FRAME_COMPLETED) + { + TRACE("SP completed\n"); + + Adapter->OidPending = FALSE; + + NdisMSetInformationComplete(Adapter->AdapterHandle, NDIS_STATUS_SUCCESS); + } +} + +static +VOID +DcStopRxProcess( + _In_ PDC21X4_ADAPTER Adapter) +{ + ULONG i, OpMode, Status; + + OpMode = Adapter->OpMode; + OpMode &= ~DC_OPMODE_RX_ENABLE; + DC_WRITE(Adapter, DcCsr6_OpMode, OpMode); + + for (i = 0; i < 5000; ++i) + { + Status = DC_READ(Adapter, DcCsr5_Status); + + if ((Status & DC_STATUS_RX_STATE_MASK) == DC_STATUS_RX_STATE_STOPPED) + return; + + NdisStallExecution(10); + } + + WARN("Failed to stop the RX process 0x%08lx\n", Status); +} + +VOID +NTAPI +DcReturnPacket( + _In_ NDIS_HANDLE MiniportAdapterContext, + _In_ PNDIS_PACKET Packet) +{ + PDC21X4_ADAPTER Adapter = (PDC21X4_ADAPTER)MiniportAdapterContext; + PDC_RCB Rcb; + + Rcb = *DC_RCB_FROM_PACKET(Packet); + + NdisAcquireSpinLock(&Adapter->ReceiveLock); + + PushEntryList(&Adapter->FreeRcbList, &Rcb->ListEntry); + + ++Adapter->RcbFree; + + NdisReleaseSpinLock(&Adapter->ReceiveLock); +} + +static +VOID +DcIndicateReceivePackets( + _In_ PDC21X4_ADAPTER Adapter, + _In_ PNDIS_PACKET* ReceiveArray, + _In_ ULONG PacketsToIndicate) +{ + PNDIS_PACKET Packet; + PDC_RBD Rbd; + PDC_RCB Rcb; + ULONG i; + + NdisDprReleaseSpinLock(&Adapter->ReceiveLock); + + NdisMIndicateReceivePacket(Adapter->AdapterHandle, + ReceiveArray, + PacketsToIndicate); + + NdisDprAcquireSpinLock(&Adapter->ReceiveLock); + + for (i = 0; i < PacketsToIndicate; ++i) + { + Packet = ReceiveArray[i]; + Rcb = *DC_RCB_FROM_PACKET(Packet); + + /* Reuse the RCB immediately */ + if (Rcb->Flags & DC_RCB_FLAG_RECLAIM) + { + Rbd = *DC_RBD_FROM_PACKET(Packet); + + Rbd->Status = DC_RBD_STATUS_OWNED; + } + } +} + +static +VOID +DcHandleRxReceivedFrames( + _In_ PDC21X4_ADAPTER Adapter) +{ + PDC_RBD Rbd, StartRbd, LastRbd; + ULONG PacketsToIndicate; + PNDIS_PACKET ReceiveArray[DC_RECEIVE_ARRAY_SIZE]; + + Rbd = StartRbd = LastRbd = Adapter->CurrentRbd; + PacketsToIndicate = 0; + + while (PacketsToIndicate < RTL_NUMBER_OF(ReceiveArray)) + { + PDC_RCB Rcb; + PDC_RCB* RcbSlot; + PNDIS_PACKET Packet; + ULONG RbdStatus, PacketLength, RxCounters; + + if (Rbd->Status & DC_RBD_STATUS_OWNED) + break; + + /* Work around the RX overflow bug */ + if ((Adapter->Features & DC_NEED_RX_OVERFLOW_WORKAROUND) && (Rbd == LastRbd)) + { + /* Find the last received packet, to correctly catch invalid packets */ + do + { + LastRbd = DC_NEXT_RBD(Adapter, LastRbd); + + if (LastRbd->Status & DC_RBD_STATUS_OWNED) + break; + } + while (LastRbd != Rbd); + + RxCounters = DC_READ(Adapter, DcCsr8_RxCounters); + + Adapter->Statistics.ReceiveNoBuffers += RxCounters & DC_COUNTER_RX_NO_BUFFER_MASK; + + /* A receive overflow might indicate a data corruption */ + if (RxCounters & DC_COUNTER_RX_OVERFLOW_MASK) + { + ERR("RX overflow, dropping the packets\n"); + + Adapter->Statistics.ReceiveOverrunErrors += + (RxCounters & DC_COUNTER_RX_OVERFLOW_MASK) >> DC_COUNTER_RX_OVERFLOW_SHIFT; + + NdisDprAcquireSpinLock(&Adapter->ModeLock); + + /* Stop the receive process */ + DcStopRxProcess(Adapter); + + /* Drop all received packets regardless of what the status indicates */ + while (TRUE) + { + if (Rbd->Status & DC_RBD_STATUS_OWNED) + break; + + ++Adapter->Statistics.ReceiveOverrunErrors; + + Rbd->Status = DC_RBD_STATUS_OWNED; + + Rbd = DC_NEXT_RBD(Adapter, Rbd); + } + LastRbd = Rbd; + + /* Restart the receive process */ + DC_WRITE(Adapter, DcCsr6_OpMode, Adapter->OpMode); + + NdisDprReleaseSpinLock(&Adapter->ModeLock); + + continue; + } + } + + RbdStatus = Rbd->Status; + + /* Ignore oversized packets */ + if (!(RbdStatus & DC_RBD_STATUS_LAST_DESCRIPTOR)) + { + Rbd->Status = DC_RBD_STATUS_OWNED; + goto NextRbd; + } + + /* Check for an invalid packet */ + if (RbdStatus & DC_RBD_STATUS_INVALID) + { + ++Adapter->Statistics.ReceiveErrors; + + if (RbdStatus & DC_RBD_STATUS_OVERRUN) + ++Adapter->Statistics.ReceiveOverrunErrors; + + if (RbdStatus & DC_RBD_STATUS_CRC_ERROR) + { + if (RbdStatus & DC_RBD_STATUS_DRIBBLE) + ++Adapter->Statistics.ReceiveAlignmentErrors; + else + ++Adapter->Statistics.ReceiveCrcErrors; + } + + Rbd->Status = DC_RBD_STATUS_OWNED; + goto NextRbd; + } + + ++Adapter->Statistics.ReceiveOk; + + PacketLength = (RbdStatus & DC_RBD_STATUS_FRAME_LENGTH_MASK) >> + DC_RBD_STATUS_FRAME_LENGTH_SHIFT; + + /* Omit the CRC */ + PacketLength -= 4; + + RcbSlot = DC_GET_RCB_SLOT(Adapter, Rbd); + Rcb = *RcbSlot; + + TRACE("RX packet (len %u), RCB %p\n", PacketLength, Rcb); + + NdisAdjustBufferLength(Rcb->NdisBuffer, PacketLength); + + /* Receive buffers are in cached memory */ + NdisFlushBuffer(Rcb->NdisBuffer, FALSE); + + if (RbdStatus & DC_RBD_STATUS_MULTICAST) + { + if (ETH_IS_BROADCAST(Rcb->VirtualAddress)) + ++Adapter->Statistics.ReceiveBroadcast; + else + ++Adapter->Statistics.ReceiveMulticast; + } + else + { + ++Adapter->Statistics.ReceiveUnicast; + } + + Packet = Rcb->Packet; + + ReceiveArray[PacketsToIndicate++] = Packet; + + if (Adapter->FreeRcbList.Next) + { + Rcb->Flags = 0; + NDIS_SET_PACKET_STATUS(Packet, NDIS_STATUS_SUCCESS); + + Rcb = (PDC_RCB)DcPopEntryList(&Adapter->FreeRcbList); + *RcbSlot = Rcb; + + ASSERT(Adapter->RcbFree > 0); + --Adapter->RcbFree; + + Rbd->Address1 = Rcb->PhysicalAddress; + DC_WRITE_BARRIER(); + Rbd->Status = DC_RBD_STATUS_OWNED; + } + else + { + Rcb->Flags = DC_RCB_FLAG_RECLAIM; + NDIS_SET_PACKET_STATUS(Packet, NDIS_STATUS_RESOURCES); + + *DC_RBD_FROM_PACKET(Packet) = Rbd; + } + +NextRbd: + Rbd = DC_NEXT_RBD(Adapter, Rbd); + + /* + * Check the next descriptor to prevent wrap-around. + * Since we don't use a fixed-sized ring, + * the receive ring may be smaller in length than the ReceiveArray[]. + */ + if (Rbd == StartRbd) + break; + } + + Adapter->CurrentRbd = Rbd; + + /* Pass the packets up */ + if (PacketsToIndicate) + { + DcIndicateReceivePackets(Adapter, ReceiveArray, PacketsToIndicate); + } +} + +static +VOID +DcHandleRx( + _In_ PDC21X4_ADAPTER Adapter) +{ + NdisDprAcquireSpinLock(&Adapter->ReceiveLock); + + do + { + DcHandleRxReceivedFrames(Adapter); + } + while (!(Adapter->CurrentRbd->Status & DC_RBD_STATUS_OWNED)); + + NdisDprReleaseSpinLock(&Adapter->ReceiveLock); +} + +static +VOID +DcHandleSystemError( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG InterruptStatus) +{ + ERR("%s error occured, CSR5 %08lx\n", DcDbgBusError(InterruptStatus), InterruptStatus); + + NdisWriteErrorLogEntry(Adapter->AdapterHandle, NDIS_ERROR_CODE_HARDWARE_FAILURE, 1, __LINE__); + + /* Issue a software reset, which also enables the interrupts */ + if (_InterlockedCompareExchange(&Adapter->ResetLock, 2, 0) == 0) + { + NdisScheduleWorkItem(&Adapter->ResetWorkItem); + } +} + +VOID +NTAPI +DcHandleInterrupt( + _In_ NDIS_HANDLE MiniportAdapterContext) +{ + ULONG InterruptStatus, IoLimit; + PDC21X4_ADAPTER Adapter = (PDC21X4_ADAPTER)MiniportAdapterContext; + + TRACE("Events %08lx\n", Adapter->InterruptStatus); + + if (!(Adapter->Flags & DC_ACTIVE)) + return; + + IoLimit = DC_INTERRUPT_PROCESSING_LIMIT; + InterruptStatus = Adapter->InterruptStatus; + + /* Loop until the condition to stop is encountered */ + while (TRUE) + { + /* Uncommon interrupts */ + if (InterruptStatus & DC_IRQ_ABNORMAL_SUMMARY) + { + /* PCI bus error detected */ + if (InterruptStatus & DC_IRQ_SYSTEM_ERROR) + { + DcHandleSystemError(Adapter, InterruptStatus); + return; + } + + /* Transmit jabber timeout */ + if (InterruptStatus & DC_IRQ_TX_JABBER_TIMEOUT) + { + DcHandleTxJabberTimeout(Adapter); + } + + /* Link state changed */ + if (InterruptStatus & Adapter->LinkStateChangeMask) + { + Adapter->HandleLinkStateChange(Adapter, InterruptStatus); + } + } + + /* Handling receive interrupts */ + if (InterruptStatus & (DC_IRQ_RX_OK | DC_IRQ_RX_STOPPED)) + { + DcHandleRx(Adapter); + } + + /* Handling transmit interrupts */ + if (InterruptStatus & (DC_IRQ_TX_OK | DC_IRQ_TX_STOPPED)) + { + DcHandleTx(Adapter); + } + + /* Transmit underflow error detected */ + if (InterruptStatus & DC_IRQ_TX_UNDERFLOW) + { + DcAdjustTxFifoThreshold(Adapter); + } + + /* Limit in order to avoid doing too much work at DPC level */ + if (!--IoLimit) + break; + + /* Check if new events have occurred */ + InterruptStatus = DC_READ(Adapter, DcCsr5_Status); + if (InterruptStatus == 0xFFFFFFFF || !(InterruptStatus & Adapter->InterruptMask)) + break; + + /* Acknowledge the events */ + DC_WRITE(Adapter, DcCsr5_Status, InterruptStatus); + } + + /* TODO: Add interrupt mitigation (CSR11) */ + + /* Reenable interrupts */ + _InterlockedExchange((PLONG)&Adapter->CurrentInterruptMask, Adapter->InterruptMask); + DC_WRITE(Adapter, DcCsr7_IrqMask, Adapter->InterruptMask); +} + +VOID +NTAPI +DcIsr( + _Out_ PBOOLEAN InterruptRecognized, + _Out_ PBOOLEAN QueueMiniportHandleInterrupt, + _In_ NDIS_HANDLE MiniportAdapterContext) +{ + PDC21X4_ADAPTER Adapter = (PDC21X4_ADAPTER)MiniportAdapterContext; + ULONG InterruptStatus; + + if (Adapter->CurrentInterruptMask == 0) + goto NotOurs; + + InterruptStatus = DC_READ(Adapter, DcCsr5_Status); + if (InterruptStatus == 0xFFFFFFFF || !(InterruptStatus & Adapter->CurrentInterruptMask)) + goto NotOurs; + + /* Disable further interrupts */ + DC_WRITE(Adapter, DcCsr7_IrqMask, 0); + + /* Clear all pending events */ + DC_WRITE(Adapter, DcCsr5_Status, InterruptStatus); + + Adapter->InterruptStatus = InterruptStatus; + Adapter->CurrentInterruptMask = 0; + + *InterruptRecognized = TRUE; + *QueueMiniportHandleInterrupt = TRUE; + return; + +NotOurs: + *InterruptRecognized = FALSE; + *QueueMiniportHandleInterrupt = FALSE; +} diff --git a/drivers/network/dd/dc21x4/media.c b/drivers/network/dd/dc21x4/media.c new file mode 100644 index 00000000000..f5ae38661d6 --- /dev/null +++ b/drivers/network/dd/dc21x4/media.c @@ -0,0 +1,634 @@ +/* + * PROJECT: ReactOS DC21x4 Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Media common code + * COPYRIGHT: Copyright 2023 Dmitry Borisov + */ + +/* INCLUDES *******************************************************************/ + +#include "dc21x4.h" + +#include + +/* FUNCTIONS ******************************************************************/ + +VOID +MediaIndicateConnect( + _In_ PDC21X4_ADAPTER Adapter, + _In_ BOOLEAN LinkUp) +{ + /* Nothing to do */ + if (Adapter->LinkUp == LinkUp) + return; + + Adapter->LinkUp = LinkUp; + + INFO_VERB("Link %sconnected, media is %s\n", + LinkUp ? "" : "dis", + MediaNumber2Str(Adapter, Adapter->MediaNumber)); + + NdisDprReleaseSpinLock(&Adapter->ModeLock); + + NdisMIndicateStatus(Adapter->AdapterHandle, + LinkUp ? NDIS_STATUS_MEDIA_CONNECT : NDIS_STATUS_MEDIA_DISCONNECT, + NULL, + 0); + NdisMIndicateStatusComplete(Adapter->AdapterHandle); + + NdisDprAcquireSpinLock(&Adapter->ModeLock); +} + +static +ULONG +MediaMiiNextMedia( + _In_ PDC21X4_ADAPTER Adapter) +{ + Adapter->ModeFlags &= ~(DC_MODE_TEST_PACKET | DC_MODE_AUI_FAILED | DC_MODE_BNC_FAILED); + Adapter->LastReceiveActivity = (ULONG)Adapter->Statistics.ReceiveOk; + + /* + * In MII mode, we don't know exactly which port is active. + * Switch to the media with a higher priority. + */ + if (Adapter->MediaBitmap & (1 << MEDIA_HMR)) + return MEDIA_HMR; + else if (Adapter->MediaBitmap & (1 << MEDIA_AUI)) + return MEDIA_AUI; + else + return MEDIA_BNC; +} + +static +VOID +MediaMiiSetSpeedAndDuplex( + _In_ PDC21X4_ADAPTER Adapter, + _In_ BOOLEAN FullDuplex, + _In_ BOOLEAN Speed100) +{ + ULONG OpMode = Adapter->OpMode; + + if (FullDuplex) + OpMode |= DC_OPMODE_FULL_DUPLEX; + else + OpMode &= ~DC_OPMODE_FULL_DUPLEX; + + if (Speed100) + OpMode &= ~DC_OPMODE_PORT_XMIT_10; + else + OpMode |= DC_OPMODE_PORT_XMIT_10; + + /* Nothing to do */ + if (OpMode == Adapter->OpMode) + return; + + INFO_VERB("Configuring MAC from %u %s-duplex to %u %s-duplex\n", + Adapter->LinkSpeedMbps, + (Adapter->OpMode & DC_OPMODE_FULL_DUPLEX) ? "full" : "half", + Speed100 ? 100 : 10, + FullDuplex ? "full" : "half"); + + Adapter->LinkSpeedMbps = Speed100 ? 100 : 10; + + DcStopTxRxProcess(Adapter); + + Adapter->OpMode = OpMode; + DC_WRITE(Adapter, DcCsr6_OpMode, Adapter->OpMode); +} + +static +VOID +MediaMiiGetSpeedAndDuplex( + _In_ PDC21X4_ADAPTER Adapter, + _Out_ PBOOLEAN FullDuplex, + _Out_ PBOOLEAN Speed100) +{ + ULONG MiiLinkPartnerAbility, AdvLpa; + + MiiRead(Adapter, Adapter->PhyAddress, MII_AUTONEG_LINK_PARTNER, &MiiLinkPartnerAbility); + + TRACE("MII LPA %04lx\n", MiiLinkPartnerAbility); + + AdvLpa = Adapter->MiiMedia.Advertising & MiiLinkPartnerAbility; + if (AdvLpa & MII_LP_100T_FD) + { + *FullDuplex = TRUE; + *Speed100 = TRUE; + } + else if (AdvLpa & MII_LP_100T4) + { + *FullDuplex = FALSE; + *Speed100 = TRUE; + } + else if (AdvLpa & MII_LP_100T_HD) + { + *FullDuplex = FALSE; + *Speed100 = TRUE; + } + else if (AdvLpa & MII_LP_10T_FD) + { + *FullDuplex = TRUE; + *Speed100 = FALSE; + } + else + { + *FullDuplex = FALSE; + *Speed100 = FALSE; + } +} + +BOOLEAN +MediaMiiCheckLink( + _In_ PDC21X4_ADAPTER Adapter) +{ + ULONG MiiStatus; + BOOLEAN FullDuplex, Speed100; + + /* The link status is a latched-low bit, read it twice */ + if (!MiiRead(Adapter, Adapter->PhyAddress, MII_STATUS, &MiiStatus)) + { + goto NoLink; + } + if (!(MiiStatus & MII_SR_LINK_STATUS)) + { + MiiRead(Adapter, Adapter->PhyAddress, MII_STATUS, &MiiStatus); + } + TRACE("MII Status %04lx\n", MiiStatus); + + /* Check the link status */ + if (!(MiiStatus & MII_SR_LINK_STATUS)) + { +NoLink: + /* No link detected, check the other port */ + if (Adapter->MediaBitmap & ((1 << MEDIA_HMR) | (1 << MEDIA_AUI) | (1 << MEDIA_BNC))) + { + if ((Adapter->Features & DC_MII_AUTOSENSE) && !MEDIA_IS_FIXED(Adapter)) + { + Adapter->MediaNumber = MediaMiiNextMedia(Adapter); + MediaSiaSelect(Adapter); + } + } + + return FALSE; + } + + /* If we are forcing speed and duplex */ + if (MEDIA_IS_FIXED(Adapter)) + { + FullDuplex = !!(Adapter->MiiControl & MII_CR_FULL_DUPLEX); + Speed100 = !!(Adapter->MiiControl & MII_CR_SPEED_SELECTION); + } + else + { + /* Check auto-negotiation is complete */ + if (!(MiiStatus & MII_SR_AUTONEG_COMPLETE)) + return FALSE; + + MediaMiiGetSpeedAndDuplex(Adapter, &FullDuplex, &Speed100); + } + + /* Set the link speed and duplex */ + MediaMiiSetSpeedAndDuplex(Adapter, FullDuplex, Speed100); + + return TRUE; +} + +VOID +MediaMiiSelect( + _In_ PDC21X4_ADAPTER Adapter) +{ + ULONG MiiControl, MiiAdvertise; + + MiiRead(Adapter, Adapter->PhyAddress, MII_CONTROL, &MiiControl); + MiiControl &= ~(MII_CR_POWER_DOWN | MII_CR_ISOLATE | MII_CR_RESET | MII_CR_SPEED_SELECTION | + MII_CR_FULL_DUPLEX | MII_CR_AUTONEG | MII_CR_AUTONEG_RESTART); + MiiWrite(Adapter, Adapter->PhyAddress, MII_CONTROL, MiiControl); + + MiiControl |= Adapter->MiiControl; + MiiAdvertise = Adapter->MiiAdvertising; + + MiiWrite(Adapter, Adapter->PhyAddress, MII_AUTONEG_ADVERTISE, MiiAdvertise | MII_ADV_CSMA); + MiiWrite(Adapter, Adapter->PhyAddress, MII_CONTROL, MiiControl); +} + +VOID +MediaSelectMiiPort( + _In_ PDC21X4_ADAPTER Adapter, + _In_ BOOLEAN ResetPhy) +{ + ULONG OpMode, i; + + if (Adapter->ChipType != DC21140) + { + DcWriteSia(Adapter, 0, 0, 0); + } + + OpMode = Adapter->OpMode; + OpMode &= ~DC_OPMODE_MEDIA_MASK; + OpMode |= DC_OPMODE_PORT_SELECT | DC_OPMODE_PORT_HEARTBEAT_DISABLE; + Adapter->OpMode = OpMode; + + DC_WRITE(Adapter, DcCsr6_OpMode, OpMode); + + NdisStallExecution(10); + + if (ResetPhy) + { + ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); + + /* Execute the GPIO reset sequence */ + if (Adapter->MiiMedia.ResetStreamLength) + { + /* Set the GPIO direction */ + DcWriteGpio(Adapter, Adapter->MiiMedia.SetupStream[0]); + + for (i = 0; i < Adapter->MiiMedia.ResetStreamLength; ++i) + { + NdisMSleep(100); + DcWriteGpio(Adapter, Adapter->MiiMedia.ResetStream[i]); + } + + /* Give the PHY some time to reset */ + NdisMSleep(5000); + } + } + + /* Set the GPIO direction */ + DcWriteGpio(Adapter, Adapter->MiiMedia.SetupStream[0]); + + /* Execute the GPIO setup sequence */ + for (i = 1; i < Adapter->MiiMedia.SetupStreamLength; ++i) + { + NdisStallExecution(10); + DcWriteGpio(Adapter, Adapter->MiiMedia.SetupStream[i]); + } +} + +VOID +MediaSiaSelect( + _In_ PDC21X4_ADAPTER Adapter) +{ + ULONG OpMode; + PDC_MEDIA Media; + + INFO_VERB("Selected media %s\n", + MediaNumber2Str(Adapter, Adapter->MediaNumber)); + + Media = &Adapter->Media[Adapter->MediaNumber]; + + DcStopTxRxProcess(Adapter); + + if (Adapter->ChipType != DC21041) + { + /* Make sure the reset pulse is wide enough */ + NdisStallExecution(100); + DcWriteGpio(Adapter, Media->GpioCtrl); + NdisStallExecution(100); + DcWriteGpio(Adapter, Media->GpioData); + } + + DcWriteSia(Adapter, Media->Csr13, Media->Csr14, Media->Csr15); + + NdisStallExecution(10); + + OpMode = Adapter->OpMode; + OpMode &= ~DC_OPMODE_MEDIA_MASK; + OpMode |= Media->OpMode; + Adapter->OpMode = OpMode; + + DC_WRITE(Adapter, DcCsr6_OpMode, OpMode); +} + +VOID +MediaGprSelect( + _In_ PDC21X4_ADAPTER Adapter) +{ + ULONG OpMode; + PDC_MEDIA Media; + + INFO("Selected media %s\n", MediaNumber2Str(Adapter, Adapter->MediaNumber)); + + Media = &Adapter->Media[Adapter->MediaNumber]; + + DC_WRITE(Adapter, DcCsr12_Gpio, Media->GpioData); + + OpMode = Adapter->OpMode; + OpMode &= ~DC_OPMODE_MEDIA_MASK; + OpMode |= Media->OpMode; + Adapter->OpMode = OpMode; + + DC_WRITE(Adapter, DcCsr6_OpMode, OpMode); +} + +CODE_SEG("PAGE") +VOID +MediaInitDefaultMedia( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG MediaNumber) +{ + ULONG Csr14, MiiAdvertising, MiiControl, i; + BOOLEAN UseMii; + + PAGED_CODE(); + + /* Media auto-detection */ + if (MediaNumber == MEDIA_AUTO) + { + Adapter->Flags |= DC_AUTOSENSE; + + /* Initial value for all boards */ + Adapter->DefaultMedia = MEDIA_10T; + + Adapter->MiiAdvertising = Adapter->MiiMedia.Advertising; + Adapter->MiiControl = MII_CR_AUTONEG | MII_CR_AUTONEG_RESTART; + + switch (Adapter->ChipType) + { + case DC21041: + { + /* Errata: don't enable auto-negotiation */ + if (Adapter->RevisionId < 0x20) + break; + + /* Advertise 10T HD and 10T FD. The chip chooses the 10T FD mode automatically */ + Adapter->Media[MEDIA_10T].Csr14 |= DC_SIA_TXRX_AUTONEG | DC_SIA_TXRX_ADV_10T_HD; + Adapter->Media[MEDIA_10T].OpMode |= DC_OPMODE_FULL_DUPLEX; + break; + } + + case DC21140: + { + /* Pick the default media */ + if (Adapter->Features & DC_HAS_MII) + { + Adapter->DefaultMedia = MEDIA_MII; + break; + } + + /* The final entry in the media list should be checked first */ + _BitScanReverse(&Adapter->DefaultMedia, Adapter->MediaBitmap); + + /* + * Select the first half-duplex media. + * If you want to be able to use 21140 boards without MII in full-duplex mode, + * you have to manually select the media. + */ + for (i = Adapter->DefaultMedia; i > 0; --i) + { + if ((Adapter->MediaBitmap & (1 << i)) && !MEDIA_IS_FD(i)) + break; + } + Adapter->DefaultMedia = i; + break; + } + + case DC21143: + case DC21145: + { + /* Pick the default media */ + if (Adapter->Features & DC_HAS_MII) + { + Adapter->DefaultMedia = MEDIA_MII; + } + else if (Adapter->MediaBitmap & (1 << MEDIA_10T)) + { + /* Start at 10mbps to do internal auto-negotiation */ + Adapter->DefaultMedia = MEDIA_10T; + } + else + { + /* The final entry in the media list should be checked first */ + _BitScanReverse(&Adapter->DefaultMedia, Adapter->MediaBitmap); + } + + /* Enable the PCS function to do 100mbps parallel detection */ + if (Adapter->SymAdvertising & MII_ADV_100) + { + Adapter->Media[MEDIA_10T].OpMode |= DC_OPMODE_PORT_PCS; + Adapter->Media[MEDIA_10T_FD].OpMode |= DC_OPMODE_PORT_PCS; + Adapter->Media[MEDIA_AUI].OpMode |= DC_OPMODE_PORT_PCS; + Adapter->Media[MEDIA_BNC].OpMode |= DC_OPMODE_PORT_PCS; + Adapter->Media[MEDIA_HMR].OpMode |= DC_OPMODE_PORT_PCS; + } + + Csr14 = DC_SIA_TXRX_AUTONEG; + + if (Adapter->SymAdvertising & MII_ADV_10T_HD) + Csr14 |= DC_SIA_TXRX_ADV_10T_HD; + + /* When NWay is turned on, the FDX bit advertises 10T FD */ + if (Adapter->SymAdvertising & MII_ADV_10T_FD) + Adapter->Media[MEDIA_10T].OpMode |= DC_OPMODE_FULL_DUPLEX; + + if (Adapter->SymAdvertising & MII_ADV_100T_HD) + Csr14 |= DC_SIA_TXRX_ADV_100TX_HD; + + if (Adapter->SymAdvertising & MII_ADV_100T_FD) + Csr14 |= DC_SIA_TXRX_ADV_100TX_FD; + + if (Adapter->SymAdvertising & MII_ADV_100T4) + Csr14 |= DC_SIA_TXRX_ADV_100T4; + + /* Advertise the PHY capability */ + Adapter->Media[MEDIA_10T].Csr14 |= Csr14; + + /* This media may use GPIO data different from the 10T HD */ + Adapter->Media[MEDIA_10T_FD].Csr14 |= Csr14; + break; + } + + default: + break; + } + } + else /* Forced speed and duplex */ + { + UseMii = FALSE; + + if (Adapter->Features & DC_HAS_MII) + { + if (!MEDIA_MII_OVERRIDE(MediaNumber)) + { + UseMii = TRUE; + } + } + + if (!UseMii) + { + Adapter->DefaultMedia = MediaNumber; + + if (MEDIA_IS_10T(MediaNumber)) + { + Adapter->InterruptMask &= ~DC_IRQ_LINK_CHANGED; + Adapter->LinkStateChangeMask &= ~DC_IRQ_LINK_CHANGED; + } + + if (MEDIA_IS_100(MediaNumber)) + { + Adapter->InterruptMask &= ~(DC_IRQ_LINK_FAIL | DC_IRQ_LINK_PASS); + Adapter->LinkStateChangeMask &= ~(DC_IRQ_LINK_FAIL | DC_IRQ_LINK_PASS); + } + } + else + { + Adapter->DefaultMedia = MEDIA_MII; + + switch (MediaNumber) + { + case MEDIA_10T: + MiiAdvertising = MII_ADV_10T_HD; + break; + case MEDIA_10T_FD: + MiiAdvertising = MII_ADV_10T_FD; + MiiControl = MII_CR_FULL_DUPLEX; + break; + case MEDIA_100TX_HD: + MiiAdvertising = MII_ADV_100T_HD; + MiiControl = MII_CR_SPEED_SELECTION; + break; + case MEDIA_100TX_FD: + MiiAdvertising = MII_ADV_100T_FD; + MiiControl = MII_CR_FULL_DUPLEX | MII_CR_SPEED_SELECTION; + break; + case MEDIA_100T4: + MiiAdvertising = MII_ADV_100T4 | MII_CR_SPEED_SELECTION; + break; + + default: + MiiAdvertising = 0; + MiiControl = 0; + break; + } + + if (MiiControl & MII_CR_SPEED_SELECTION) + Adapter->LinkSpeedMbps = 100; + else + Adapter->LinkSpeedMbps = 10; + + Adapter->MiiAdvertising = MiiAdvertising; + Adapter->MiiControl = MiiControl; + } + } + + INFO("Default media is %s\n", MediaNumber2Str(Adapter, Adapter->DefaultMedia)); +} + +static +CODE_SEG("PAGE") +VOID +MediaInitOpMode2114x( + _In_ PDC21X4_ADAPTER Adapter) +{ + PAGED_CODE(); + + Adapter->Media[MEDIA_10T ].OpMode = 0; + Adapter->Media[MEDIA_BNC ].OpMode = 0; + Adapter->Media[MEDIA_AUI ].OpMode = 0; + Adapter->Media[MEDIA_100TX_HD].OpMode = DC_OPMODE_PORT_SELECT | + DC_OPMODE_PORT_HEARTBEAT_DISABLE; + Adapter->Media[MEDIA_10T_FD ].OpMode = DC_OPMODE_FULL_DUPLEX; + Adapter->Media[MEDIA_100TX_FD].OpMode = DC_OPMODE_PORT_SELECT | DC_OPMODE_FULL_DUPLEX | + DC_OPMODE_PORT_HEARTBEAT_DISABLE; + Adapter->Media[MEDIA_100T4 ].OpMode = DC_OPMODE_PORT_SELECT | + DC_OPMODE_PORT_HEARTBEAT_DISABLE; + Adapter->Media[MEDIA_100FX_HD].OpMode = DC_OPMODE_PORT_SELECT | + DC_OPMODE_PORT_HEARTBEAT_DISABLE | + DC_OPMODE_PORT_PCS; + Adapter->Media[MEDIA_100FX_FD].OpMode = DC_OPMODE_PORT_SELECT | DC_OPMODE_FULL_DUPLEX | + DC_OPMODE_PORT_HEARTBEAT_DISABLE | + DC_OPMODE_PORT_PCS; + Adapter->Media[MEDIA_HMR ].OpMode = DC_OPMODE_PORT_HEARTBEAT_DISABLE; +} + +CODE_SEG("PAGE") +VOID +MediaInitMediaList( + _In_ PDC21X4_ADAPTER Adapter) +{ + PAGED_CODE(); + + /* + * Set the default internal values for the SIA/SYM operating modes. + * The SROM parsing code may later overwrite them. + */ + switch (Adapter->ChipType) + { + case DC21040: + { + Adapter->Media[MEDIA_10T].Csr13 = 0x8F01; + Adapter->Media[MEDIA_10T].Csr14 = 0xFFFF; + Adapter->Media[MEDIA_10T].Csr15 = 0x0000; + + Adapter->Media[MEDIA_BNC].Csr13 = 0x8F09; + Adapter->Media[MEDIA_BNC].Csr14 = 0x0705; + Adapter->Media[MEDIA_BNC].Csr15 = 0x0006; + + Adapter->Media[MEDIA_10T_FD].Csr13 = 0x8F01; + Adapter->Media[MEDIA_10T_FD].Csr14 = 0xFFFD; + Adapter->Media[MEDIA_10T_FD].Csr15 = 0x0000; + Adapter->Media[MEDIA_10T_FD].OpMode = DC_OPMODE_FULL_DUPLEX; + break; + } + + case DC21041: + { + Adapter->Media[MEDIA_10T].Csr13 = 0xEF01; + Adapter->Media[MEDIA_10T].Csr14 = 0xFF3F; + Adapter->Media[MEDIA_10T].Csr15 = 0x0008; + + Adapter->Media[MEDIA_BNC].Csr13 = 0xEF09; + Adapter->Media[MEDIA_BNC].Csr14 = 0xF7FD; + Adapter->Media[MEDIA_BNC].Csr15 = 0x0006; + + Adapter->Media[MEDIA_AUI].Csr13 = 0xEF09; + Adapter->Media[MEDIA_AUI].Csr14 = 0xF7FD; + Adapter->Media[MEDIA_AUI].Csr15 = 0x000E; + + Adapter->Media[MEDIA_10T_HD].Csr13 = 0xEF01; + Adapter->Media[MEDIA_10T_HD].Csr14 = 0x7F3F; + Adapter->Media[MEDIA_10T_HD].Csr15 = 0x0008; + + Adapter->Media[MEDIA_10T_FD].Csr13 = 0xEF01; + Adapter->Media[MEDIA_10T_FD].Csr14 = 0x7F3D; + Adapter->Media[MEDIA_10T_FD].Csr15 = 0x0008; + Adapter->Media[MEDIA_10T_FD].OpMode = DC_OPMODE_FULL_DUPLEX; + break; + } + + case DC21140: + { + MediaInitOpMode2114x(Adapter); + break; + } + + case DC21143: + case DC21145: + { + Adapter->Media[MEDIA_10T].Csr13 = 0x0001; + Adapter->Media[MEDIA_10T].Csr14 = 0x7F3F; + Adapter->Media[MEDIA_10T].Csr15 = 0x0008; + + Adapter->Media[MEDIA_BNC].Csr13 = 0x0009; + Adapter->Media[MEDIA_BNC].Csr14 = 0x0705; + Adapter->Media[MEDIA_BNC].Csr15 = 0x0006; + + Adapter->Media[MEDIA_AUI].Csr13 = 0x0009; + Adapter->Media[MEDIA_AUI].Csr14 = 0x0705; + Adapter->Media[MEDIA_AUI].Csr15 = 0x000E; + + Adapter->Media[MEDIA_10T_FD].Csr13 = 0x0001; + Adapter->Media[MEDIA_10T_FD].Csr14 = 0x7F3D; + Adapter->Media[MEDIA_10T_FD].Csr15 = 0x0008; + + Adapter->Media[MEDIA_HMR].Csr13 = 0x0009; + Adapter->Media[MEDIA_HMR].Csr14 = 0x0505; + Adapter->Media[MEDIA_HMR].Csr15 = 0x0010; + + MediaInitOpMode2114x(Adapter); + break; + } + + default: + break; + } +} diff --git a/drivers/network/dd/dc21x4/media.h b/drivers/network/dd/dc21x4/media.h new file mode 100644 index 00000000000..68fa627435a --- /dev/null +++ b/drivers/network/dd/dc21x4/media.h @@ -0,0 +1,127 @@ +/* + * PROJECT: ReactOS DC21x4 Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Media support header file + * COPYRIGHT: Copyright 2023 Dmitry Borisov + */ + +#pragma once + +typedef struct _DC_MEDIA +{ + ULONG OpMode; + + USHORT GpioCtrl; + USHORT GpioData; + + union + { + struct _DC_MEDIA_SIA_DATA + { + ULONG Csr13; + ULONG Csr14; + ULONG Csr15; + }; + + struct _DC_MEDIA_GPR_DATA + { + ULONG LinkMask; + ULONG Polarity; + }; + }; +} DC_MEDIA, *PDC_MEDIA; + +typedef struct _DC_MII_MEDIA +{ + UCHAR SetupStreamLength; + UCHAR ResetStreamLength; + USHORT Advertising; + USHORT SetupStream[SROM_MAX_STREAM_REGS + 1]; // +1 for GPIO direction (21140) + USHORT ResetStream[SROM_MAX_STREAM_REGS]; +} DC_MII_MEDIA, *PDC_MII_MEDIA; + +/* + * SROM-defined media values + */ +#define MEDIA_10T 0 +#define MEDIA_BNC 1 +#define MEDIA_AUI 2 +#define MEDIA_100TX_HD 3 +#define MEDIA_10T_HD MEDIA_100TX_HD /* 21041 only */ +#define MEDIA_10T_FD 4 +#define MEDIA_100TX_FD 5 +#define MEDIA_100T4 6 +#define MEDIA_100FX_HD 7 +#define MEDIA_100FX_FD 8 +#define MEDIA_HMR 9 +#define MEDIA_LIST_MAX 10 + +/* + * Registry configuration + */ +#define MEDIA_AUTO MEDIA_LIST_MAX + +/* + * Extra codes + */ +#define MEDIA_MII MEDIA_LIST_MAX +#define MEDIA_MAX (MEDIA_LIST_MAX + 1) + +#define MEDIA_MII_OVERRIDE_MASK \ + ((1 << MEDIA_AUI) | \ + (1 << MEDIA_BNC) | \ + (1 << MEDIA_100FX_HD) | \ + (1 << MEDIA_100FX_FD) | \ + (1 << MEDIA_HMR)) + +#define MEDIA_FD_MASK \ + ((1 << MEDIA_10T_FD) | \ + (1 << MEDIA_100TX_FD) | \ + (1 << MEDIA_100FX_FD)) + +#define MEDIA_AUI_BNC_MASK \ + ((1 << MEDIA_AUI) | \ + (1 << MEDIA_BNC)) + +#define MEDIA_10T_MASK \ + ((1 << MEDIA_10T) | \ + (1 << MEDIA_10T_FD)) + +#define MEDIA_100_MASK \ + ((1 << MEDIA_100TX_HD) | \ + (1 << MEDIA_100TX_FD) | \ + (1 << MEDIA_100T4) | \ + (1 << MEDIA_100FX_HD) | \ + (1 << MEDIA_100FX_FD)) + +#define MEDIA_FX_MASK \ + ((1 << MEDIA_100FX_HD) | \ + (1 << MEDIA_100FX_FD)) + +/* Specifying this media code override the default MII selection */ +#define MEDIA_MII_OVERRIDE(MediaNumber) \ + (((1 << (MediaNumber)) & MEDIA_MII_OVERRIDE_MASK) != 0) + +/* Full-duplex media */ +#define MEDIA_IS_FD(MediaNumber) \ + (((1 << (MediaNumber)) & MEDIA_FD_MASK) != 0) + +/* AUI or BNC media */ +#define MEDIA_IS_AUI_BNC(MediaNumber) \ + (((1 << (MediaNumber)) & MEDIA_AUI_BNC_MASK) != 0) + +/* 10Base-T media */ +#define MEDIA_IS_10T(MediaNumber) \ + (((1 << (MediaNumber)) & MEDIA_10T_MASK) != 0) + +/* 100mbps media */ +#define MEDIA_IS_100(MediaNumber) \ + (((1 << (MediaNumber)) & MEDIA_100_MASK) != 0) + +/* 100Base-FX media */ +#define MEDIA_IS_FX(MediaNumber) \ + (((1 << (MediaNumber)) & MEDIA_FX_MASK) != 0) + +/* Forced speed and duplex */ +#define MEDIA_IS_FIXED(Adapter) \ + (((Adapter)->Flags & DC_AUTOSENSE) == 0) diff --git a/drivers/network/dd/dc21x4/media040.c b/drivers/network/dd/dc21x4/media040.c new file mode 100644 index 00000000000..2802631ffe0 --- /dev/null +++ b/drivers/network/dd/dc21x4/media040.c @@ -0,0 +1,143 @@ +/* + * PROJECT: ReactOS DC21x4 Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: 21040 media support code + * COPYRIGHT: Copyright 2023 Dmitry Borisov + */ + +/* INCLUDES *******************************************************************/ + +#include "dc21x4.h" + +#include + +/* FUNCTIONS ******************************************************************/ + +VOID +MediaLinkStateChange21040( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG InterruptStatus) +{ + UNREFERENCED_PARAMETER(InterruptStatus); + + INFO_VERB("Link failed, CSR12 %08lx\n", DC_READ(Adapter, DcCsr12_SiaStatus)); + + NdisDprAcquireSpinLock(&Adapter->ModeLock); + + /* Start the media timer when the 10Base-T link has changed state */ + if (Adapter->MediaNumber != MEDIA_BNC) + { + MediaIndicateConnect(Adapter, FALSE); + + NdisMSetTimer(&Adapter->MediaMonitorTimer, 3000); + } + + NdisDprReleaseSpinLock(&Adapter->ModeLock); +} + +VOID +NTAPI +MediaMonitor21040Dpc( + _In_ PVOID SystemSpecific1, + _In_ PVOID FunctionContext, + _In_ PVOID SystemSpecific2, + _In_ PVOID SystemSpecific3) +{ + PDC21X4_ADAPTER Adapter = FunctionContext; + BOOLEAN LinkUp, Report, RunAgain; + + UNREFERENCED_PARAMETER(SystemSpecific1); + UNREFERENCED_PARAMETER(SystemSpecific2); + UNREFERENCED_PARAMETER(SystemSpecific3); + + if (!(Adapter->Flags & DC_ACTIVE)) + return; + + LinkUp = FALSE; + Report = FALSE; + RunAgain = TRUE; + + NdisDprAcquireSpinLock(&Adapter->ModeLock); + + if (Adapter->MediaNumber != MEDIA_BNC) + { + ULONG SiaStatus = DC_READ(Adapter, DcCsr12_SiaStatus); + + Report = TRUE; + LinkUp = !(SiaStatus & (DC_SIA_STATUS_NETWORK_CONNECTION_ERROR | + DC_SIA_STATUS_10T_LINK_FAIL)); + + if (!LinkUp) + { + /* Select the other port */ + if (!MEDIA_IS_FIXED(Adapter)) + { + Adapter->MediaNumber = MEDIA_BNC; + + DcWriteSia(Adapter, + Adapter->Media[MEDIA_BNC].Csr13, + Adapter->Media[MEDIA_BNC].Csr14, + Adapter->Media[MEDIA_BNC].Csr15); + + Adapter->LastReceiveActivity = (ULONG)Adapter->Statistics.ReceiveOk; + } + } + else + { + /* Wait until the next link change event */ + RunAgain = FALSE; + } + } + else + { + ULONG ReceiveActivity = (ULONG)Adapter->Statistics.ReceiveOk; + + if (!(Adapter->ModeFlags & DC_MODE_TEST_PACKET)) + { + /* Check for any received packets */ + if (ReceiveActivity != Adapter->LastReceiveActivity) + { + LinkUp = TRUE; + Report = TRUE; + } + else + { + /* Send a loopback packet */ + NdisDprAcquireSpinLock(&Adapter->SendLock); + DcTestPacket(Adapter); + NdisDprReleaseSpinLock(&Adapter->SendLock); + } + } + else + { + Adapter->ModeFlags &= ~DC_MODE_TEST_PACKET; + + LinkUp = !!Adapter->MediaTestStatus; + Report = TRUE; + + /* Select the other port */ + if (!LinkUp && !MEDIA_IS_FIXED(Adapter)) + { + Adapter->MediaNumber = MEDIA_10T; + + DcWriteSia(Adapter, + Adapter->Media[MEDIA_10T].Csr13, + Adapter->Media[MEDIA_10T].Csr14, + Adapter->Media[MEDIA_10T].Csr15); + } + } + Adapter->LastReceiveActivity = ReceiveActivity; + } + + if (Report) + { + MediaIndicateConnect(Adapter, LinkUp); + } + + NdisDprReleaseSpinLock(&Adapter->ModeLock); + + if (RunAgain) + { + NdisMSetTimer(&Adapter->MediaMonitorTimer, LinkUp ? 6000 : 3000); + } +} diff --git a/drivers/network/dd/dc21x4/media041.c b/drivers/network/dd/dc21x4/media041.c new file mode 100644 index 00000000000..e1eab6b3d3f --- /dev/null +++ b/drivers/network/dd/dc21x4/media041.c @@ -0,0 +1,222 @@ +/* + * PROJECT: ReactOS DC21x4 Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: 21041 media support code + * COPYRIGHT: Copyright 2023 Dmitry Borisov + */ + +/* INCLUDES *******************************************************************/ + +#include "dc21x4.h" + +#include + +/* FUNCTIONS ******************************************************************/ + +VOID +MediaLinkStateChange21041( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG InterruptStatus) +{ + NdisDprAcquireSpinLock(&Adapter->ModeLock); + + if (InterruptStatus & DC_IRQ_LINK_PASS) + { + INFO_VERB("Link passed, CSR12 %08lx\n", DC_READ(Adapter, DcCsr12_SiaStatus)); + + /* 10Base-T is the active port now */ + if (MEDIA_IS_AUI_BNC(Adapter->MediaNumber)) + { + /* Switch to TP medium */ + if (!MEDIA_IS_FIXED(Adapter)) + { + Adapter->MediaNumber = MEDIA_10T; + MediaSiaSelect(Adapter); + + NdisMSetTimer(&Adapter->MediaMonitorTimer, 3000); + } + } + + /* 10Base-T link is up */ + if (!MEDIA_IS_AUI_BNC(Adapter->MediaNumber)) + { + MediaIndicateConnect(Adapter, TRUE); + } + } + else // DC_IRQ_LINK_FAIL + { + INFO_VERB("Link failed, CSR12 %08lx\n", DC_READ(Adapter, DcCsr12_SiaStatus)); + + /* 10Base-T link is down */ + if (!MEDIA_IS_AUI_BNC(Adapter->MediaNumber)) + { + MediaIndicateConnect(Adapter, FALSE); + } + } + + NdisDprReleaseSpinLock(&Adapter->ModeLock); +} + +static +VOID +Media041SelectNextMedia( + _In_ PDC21X4_ADAPTER Adapter) +{ + if (Adapter->MediaNumber == MEDIA_AUI) + Adapter->ModeFlags |= DC_MODE_AUI_FAILED; + else if (Adapter->MediaNumber == MEDIA_BNC) + Adapter->ModeFlags |= DC_MODE_BNC_FAILED; + + if ((Adapter->ModeFlags & (DC_MODE_AUI_FAILED | DC_MODE_BNC_FAILED)) == + (DC_MODE_AUI_FAILED | DC_MODE_BNC_FAILED)) + { + Adapter->MediaNumber = MEDIA_10T; + } + else + { + Adapter->MediaNumber = (MEDIA_BNC + MEDIA_AUI) - Adapter->MediaNumber; + } + + MediaSiaSelect(Adapter); +} + +VOID +NTAPI +MediaMonitor21041Dpc( + _In_ PVOID SystemSpecific1, + _In_ PVOID FunctionContext, + _In_ PVOID SystemSpecific2, + _In_ PVOID SystemSpecific3) +{ + PDC21X4_ADAPTER Adapter = FunctionContext; + BOOLEAN LinkUp, Report; + ULONG DelayMs, SiaStatus; + + UNREFERENCED_PARAMETER(SystemSpecific1); + UNREFERENCED_PARAMETER(SystemSpecific2); + UNREFERENCED_PARAMETER(SystemSpecific3); + + if (!(Adapter->Flags & DC_ACTIVE)) + return; + + Report = FALSE; + DelayMs = 5000; + + NdisDprAcquireSpinLock(&Adapter->ModeLock); + + SiaStatus = DC_READ(Adapter, DcCsr12_SiaStatus); + + if (MEDIA_IS_AUI_BNC(Adapter->MediaNumber)) + { + if ((Adapter->ModeFlags & DC_MODE_PORT_AUTOSENSE)) + { + Adapter->ModeFlags &= ~DC_MODE_PORT_AUTOSENSE; + + /* Select the other port */ + if (!(SiaStatus & DC_SIA_STATUS_SELECTED_PORT_ACTIVITY)) + { + Adapter->MediaNumber = MEDIA_BNC; + MediaSiaSelect(Adapter); + } + + DelayMs = 1000; + } + else + { + ULONG ReceiveActivity = (ULONG)Adapter->Statistics.ReceiveOk; + + if (!(Adapter->ModeFlags & DC_MODE_TEST_PACKET)) + { + if ((Adapter->MediaNumber == MEDIA_AUI) && + (SiaStatus & DC_SIA_STATUS_SELECTED_PORT_ACTIVITY)) + { + /* Clear the selected port activity bit */ + DC_WRITE(Adapter, DcCsr12_SiaStatus, DC_SIA_STATUS_SELECTED_PORT_ACTIVITY); + + LinkUp = TRUE; + Report = TRUE; + } + /* Check for any received packets */ + else if (ReceiveActivity != Adapter->LastReceiveActivity) + { + LinkUp = TRUE; + Report = TRUE; + + DelayMs = 3000; + } + else + { + /* Send a loopback packet */ + NdisDprAcquireSpinLock(&Adapter->SendLock); + DcTestPacket(Adapter); + NdisDprReleaseSpinLock(&Adapter->SendLock); + + DelayMs = 3000; + } + } + else + { + Adapter->ModeFlags &= ~DC_MODE_TEST_PACKET; + + LinkUp = !!Adapter->MediaTestStatus; + Report = TRUE; + + /* Select the other port */ + if (!LinkUp && !MEDIA_IS_FIXED(Adapter)) + { + Media041SelectNextMedia(Adapter); + + DelayMs = 3000; + } + } + Adapter->LastReceiveActivity = ReceiveActivity; + } + } + else // 10Base-T + { + Report = TRUE; + LinkUp = !(SiaStatus & (DC_SIA_STATUS_NETWORK_CONNECTION_ERROR | + DC_SIA_STATUS_10T_LINK_FAIL)); + + if (!LinkUp) + { + DelayMs = 3000; + + /* Select the AUI or BNC port */ + if (!MEDIA_IS_FIXED(Adapter) && (Adapter->MediaBitmap & MEDIA_AUI_BNC_MASK)) + { + Adapter->ModeFlags &= ~(DC_MODE_AUI_FAILED | + DC_MODE_BNC_FAILED | + DC_MODE_TEST_PACKET); + + if (SiaStatus & DC_SIA_STATUS_NONSEL_PORT_ACTIVITY) + { + Adapter->MediaNumber = MEDIA_AUI; + Adapter->ModeFlags |= DC_MODE_PORT_AUTOSENSE; + } + else + { + Adapter->MediaNumber = MEDIA_BNC; + } + MediaSiaSelect(Adapter); + + Adapter->LastReceiveActivity = (ULONG)Adapter->Statistics.ReceiveOk; + + /* Clear the port activity bits */ + DC_WRITE(Adapter, DcCsr12_SiaStatus, DC_SIA_STATUS_SELECTED_PORT_ACTIVITY | + DC_SIA_STATUS_NONSEL_PORT_ACTIVITY); + + DelayMs = 1000; + } + } + } + + if (Report) + { + MediaIndicateConnect(Adapter, LinkUp); + } + + NdisDprReleaseSpinLock(&Adapter->ModeLock); + + NdisMSetTimer(&Adapter->MediaMonitorTimer, DelayMs); +} diff --git a/drivers/network/dd/dc21x4/media140.c b/drivers/network/dd/dc21x4/media140.c new file mode 100644 index 00000000000..916b14475da --- /dev/null +++ b/drivers/network/dd/dc21x4/media140.c @@ -0,0 +1,136 @@ +/* + * PROJECT: ReactOS DC21x4 Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: 21140 media support code + * COPYRIGHT: Copyright 2023 Dmitry Borisov + */ + +/* INCLUDES *******************************************************************/ + +#include "dc21x4.h" + +#include + +/* FUNCTIONS ******************************************************************/ + +static +BOOLEAN +MediaGprCheckLink( + _In_ PDC21X4_ADAPTER Adapter) +{ + ULONG GpioData; + PDC_MEDIA Media; + + Media = &Adapter->Media[Adapter->MediaNumber]; + + /* No media activity indicator */ + if (Media->LinkMask == 0) + { + TRACE("No activity indicator\n"); + + /* Assume we have a link */ + if (MEDIA_IS_FIXED(Adapter)) + return TRUE; + + return FALSE; + } + + GpioData = DC_READ(Adapter, DcCsr12_Gpio); + + TRACE("CSR12 %08lx\n", GpioData); + + /* This media supports link indication via GPIO pins */ + return !!((GpioData & Media->LinkMask) ^ Media->Polarity); +} + +static +ULONG +MediaGprNextMedia( + _In_ PDC21X4_ADAPTER Adapter) +{ + ULONG i; + + /* No half-duplex media in the media list */ + if (!(Adapter->MediaBitmap & ~MEDIA_FD_MASK)) + { + return Adapter->MediaNumber; + } + + /* Find the next half-duplex media */ + i = Adapter->MediaNumber; + while (TRUE) + { + /* We have reached the end of the media list, try the first media */ + if (i == 0) + { + _BitScanReverse(&i, Adapter->MediaBitmap); + } + else + { + --i; + } + + if ((Adapter->MediaBitmap & (1 << i)) && !MEDIA_IS_FD(i)) + { + return i; + } + } +} + +VOID +NTAPI +MediaMonitor21140Dpc( + _In_ PVOID SystemSpecific1, + _In_ PVOID FunctionContext, + _In_ PVOID SystemSpecific2, + _In_ PVOID SystemSpecific3) +{ + PDC21X4_ADAPTER Adapter = FunctionContext; + ULONG DelayMs, MediaNumber; + BOOLEAN LinkUp; + + UNREFERENCED_PARAMETER(SystemSpecific1); + UNREFERENCED_PARAMETER(SystemSpecific2); + UNREFERENCED_PARAMETER(SystemSpecific3); + + if (!(Adapter->Flags & DC_ACTIVE)) + return; + + NdisDprAcquireSpinLock(&Adapter->ModeLock); + + if (Adapter->MediaNumber == MEDIA_MII) + { + LinkUp = MediaMiiCheckLink(Adapter); + + DelayMs = 5000; + } + else + { + LinkUp = MediaGprCheckLink(Adapter); + + /* This media is unconnected, try the next media */ + if (!LinkUp && !MEDIA_IS_FIXED(Adapter)) + { + MediaNumber = MediaGprNextMedia(Adapter); + + if (Adapter->MediaNumber != MediaNumber) + { + Adapter->MediaNumber = MediaNumber; + MediaGprSelect(Adapter); + } + + DelayMs = 3000; + } + else + { + /* If we are forcing media, then we need to poll the media less frequently */ + DelayMs = 5000; + } + } + + MediaIndicateConnect(Adapter, LinkUp); + + NdisDprReleaseSpinLock(&Adapter->ModeLock); + + NdisMSetTimer(&Adapter->MediaMonitorTimer, DelayMs); +} diff --git a/drivers/network/dd/dc21x4/media143.c b/drivers/network/dd/dc21x4/media143.c new file mode 100644 index 00000000000..2ae4eb7467e --- /dev/null +++ b/drivers/network/dd/dc21x4/media143.c @@ -0,0 +1,510 @@ +/* + * PROJECT: ReactOS DC21x4 Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: 21142/21143/21145 media support code + * COPYRIGHT: Copyright 2023 Dmitry Borisov + */ + +/* INCLUDES *******************************************************************/ + +#include "dc21x4.h" + +#include + +/* FUNCTIONS ******************************************************************/ + +static +VOID +Media143SelectNextSerialMedia( + _In_ PDC21X4_ADAPTER Adapter) +{ + ULONG MediaNumber; + + MediaNumber = Adapter->MediaNumber; + + /* The HMR media isn't checked as HMR boards use it instead of AUI and BNC */ + if (MediaNumber == MEDIA_AUI || MediaNumber == MEDIA_BNC) + { + if (MediaNumber == MEDIA_AUI) + Adapter->ModeFlags |= DC_MODE_AUI_FAILED; + else if (MediaNumber == MEDIA_BNC) + Adapter->ModeFlags |= DC_MODE_BNC_FAILED; + + if ((Adapter->ModeFlags & (DC_MODE_AUI_FAILED | DC_MODE_BNC_FAILED)) != + (DC_MODE_AUI_FAILED | DC_MODE_BNC_FAILED)) + { + MediaNumber = (MEDIA_BNC + MEDIA_AUI) - MediaNumber; + + if (Adapter->MediaBitmap & (1 << MediaNumber)) + { + Adapter->MediaNumber = MediaNumber; + MediaSiaSelect(Adapter); + return; + } + } + } + + if (Adapter->Features & DC_HAS_MII) + { + Adapter->MediaNumber = MEDIA_MII; + + DcStopTxRxProcess(Adapter); + MediaSelectMiiPort(Adapter, FALSE); + MediaMiiSelect(Adapter); + } + else + { + Adapter->MediaNumber = MEDIA_10T; + MediaSiaSelect(Adapter); + } +} + +static +VOID +Media143SelectNextMedia( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG SiaStatus) +{ + ULONG MediaBitmap, MediaNumber; + + MediaIndicateConnect(Adapter, FALSE); + + MediaBitmap = Adapter->MediaBitmap; + + if (MediaBitmap & (1 << MEDIA_HMR)) + { + MediaNumber = MEDIA_HMR; + } + else if ((MediaBitmap & MEDIA_AUI_BNC_MASK) == MEDIA_AUI_BNC_MASK) + { + if (SiaStatus & DC_SIA_STATUS_AUI_ACTIVITY) + { + MediaNumber = MEDIA_AUI; + } + else + { + MediaNumber = MEDIA_BNC; + } + } + else if (MediaBitmap & (1 << MEDIA_AUI)) + { + MediaNumber = MEDIA_AUI; + } + else if (MediaBitmap & (1 << MEDIA_BNC)) + { + MediaNumber = MEDIA_BNC; + } + else + { + MediaNumber = MEDIA_10T; + } + + Adapter->ModeFlags &= ~DC_MODE_AUTONEG_MASK; + Adapter->ModeFlags |= DC_MODE_AUTONEG_NONE; + NdisMSetTimer(&Adapter->MediaMonitorTimer, 3000); + + if (Adapter->MediaNumber != MediaNumber) + { + Adapter->MediaNumber = MediaNumber; + MediaSiaSelect(Adapter); + } + + Adapter->ModeFlags &= ~(DC_MODE_TEST_PACKET | + DC_MODE_AUI_FAILED | + DC_MODE_BNC_FAILED); + Adapter->LastReceiveActivity = (ULONG)Adapter->Statistics.ReceiveOk; +} + +static +VOID +Media143Handle10LinkFail( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG SiaStatus) +{ + INFO_VERB("Link failed, CSR12 %08lx\n", SiaStatus); + + /* 10Base-T link is down */ + MediaIndicateConnect(Adapter, FALSE); + + /* Select the other port */ + if (!MEDIA_IS_FIXED(Adapter)) + { + Media143SelectNextMedia(Adapter, SiaStatus); + } +} + +static +VOID +Media143Handle10LinkPass( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG SiaStatus) +{ + INFO_VERB("Link passed, CSR12 %08lx\n", SiaStatus); + + /* 10Base-T is the active port now */ + if (!MEDIA_IS_10T(Adapter->MediaNumber)) + { + /* Switch to TP medium */ + if (!MEDIA_IS_FIXED(Adapter)) + { + Adapter->MediaNumber = MEDIA_10T; + MediaSiaSelect(Adapter); + + /* Wait for a link pass interrupt to signal the link test completed */ + Adapter->ModeFlags &= ~DC_MODE_AUTONEG_MASK; + Adapter->ModeFlags |= DC_MODE_AUTONEG_WAIT_INTERRUPT; + NdisMSetTimer(&Adapter->MediaMonitorTimer, 3000); + } + } + else + { + /* 10Base-T link is up */ + MediaIndicateConnect(Adapter, TRUE); + } +} + +static +VOID +Media143Handle100LinkChange( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG SiaStatus) +{ + BOOLEAN LinkUp; + + INFO_VERB("Link changed, CSR12 %08lx\n", SiaStatus); + + LinkUp = !(SiaStatus & DC_SIA_STATUS_100T_LINK_FAIL); + + if (MEDIA_IS_FIXED(Adapter)) + { + MediaIndicateConnect(Adapter, LinkUp); + } + else + { + /* Select the other port */ + if (!LinkUp) + { + Media143SelectNextMedia(Adapter, SiaStatus); + } + else + { + /* Ignore this hint */ + } + } +} + +static +VOID +Media143HandleNWayComplete( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG SiaStatus) +{ + ULONG MediaNumber, AdvLpa; + + /* Select media according to auto-negotiation result */ + if (SiaStatus & DC_SIA_STATUS_LP_AUTONED_SUPPORTED) + { + INFO_VERB("Auto-negotiation has completed, LPA %08lx ADV %08lx\n", + SiaStatus, Adapter->SymAdvertising); + + AdvLpa = (SiaStatus >> DC_SIA_STATUS_LP_CODE_WORD_SHIFT) & Adapter->SymAdvertising; + if (AdvLpa & MII_ADV_100T_FD) + { + MediaNumber = MEDIA_100TX_FD; + } + else if (AdvLpa & MII_ADV_100T4) + { + MediaNumber = MEDIA_100T4; + } + else if (AdvLpa & MII_ADV_100T_HD) + { + MediaNumber = MEDIA_100TX_HD; + } + else if (AdvLpa & MII_ADV_10T_FD) + { + MediaNumber = MEDIA_10T_FD; + } + else if (AdvLpa & MII_ADV_10T_HD) + { + MediaNumber = MEDIA_10T; + } + else + { + INFO_VERB("No common mode\n"); + + /* No common mode, select the other port */ + Media143SelectNextMedia(Adapter, SiaStatus); + return; + } + } + else + { + INFO_VERB("Link partner does not support auto-negotiation, CSR12 %08lx\n", SiaStatus); + + /* Check the results of parallel detection */ + if (!(SiaStatus & DC_SIA_STATUS_100T_LINK_FAIL)) + { + MediaNumber = MEDIA_100TX_HD; + } + else if (!(SiaStatus & DC_SIA_STATUS_10T_LINK_FAIL)) + { + MediaNumber = MEDIA_10T; + } + else + { + /* No link detected, select the other port */ + Media143SelectNextMedia(Adapter, SiaStatus); + return; + } + } + + if (MEDIA_IS_10T(MediaNumber) && (MediaNumber != Adapter->MediaNumber)) + { + /* Set the time limit for auto-negotiation */ + Adapter->ModeFlags &= ~DC_MODE_AUTONEG_MASK; + Adapter->ModeFlags |= DC_MODE_AUTONEG_WAIT_INTERRUPT; + NdisMSetTimer(&Adapter->MediaMonitorTimer, 5000); + } + else + { + /* Wait for the link integrity test to complete before we can read the link status */ + Adapter->ModeFlags &= ~DC_MODE_AUTONEG_MASK; + Adapter->ModeFlags |= DC_MODE_AUTONEG_LINK_STATUS_CHECK; + NdisMSetTimer(&Adapter->MediaMonitorTimer, 1000); + } + + if (Adapter->MediaNumber != MediaNumber) + { + Adapter->MediaNumber = MediaNumber; + MediaSiaSelect(Adapter); + } +} + +VOID +MediaLinkStateChange21143( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG InterruptStatus) +{ + ULONG SiaStatus; + + INFO_VERB("Link interrupt, CSR5 %08lx\n", InterruptStatus); + + NdisDprAcquireSpinLock(&Adapter->ModeLock); + + /* Ignore link changes caused by media being estabilished */ + if ((Adapter->ModeFlags & DC_MODE_AUTONEG_MASK) == DC_MODE_AUTONEG_LINK_STATUS_CHECK) + { + NdisDprReleaseSpinLock(&Adapter->ModeLock); + return; + } + + SiaStatus = DC_READ(Adapter, DcCsr12_SiaStatus); + + if ((InterruptStatus & DC_IRQ_LINK_FAIL) && MEDIA_IS_10T(Adapter->MediaNumber)) + { + /* Link has failed */ + Media143Handle10LinkFail(Adapter, SiaStatus); + } + else if (InterruptStatus & DC_IRQ_LINK_PASS) + { + if (DC_READ(Adapter, DcCsr14_SiaTxRx) & DC_SIA_TXRX_AUTONEG) + { + /* Auto-negotiation has completed */ + Media143HandleNWayComplete(Adapter, SiaStatus); + } + else + { + /* Link has passed */ + Media143Handle10LinkPass(Adapter, SiaStatus); + } + } + else + { + /* NOTE: The Link Changed bit is reserved on the 21142 and always reads as 1 */ + if (InterruptStatus & Adapter->LinkStateChangeMask & DC_IRQ_LINK_CHANGED) + { + /* Link has changed */ + Media143Handle100LinkChange(Adapter, SiaStatus); + } + } + + NdisDprReleaseSpinLock(&Adapter->ModeLock); +} + +static +BOOLEAN +Media143CheckLink( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG SiaStatus) +{ + if (MEDIA_IS_100(Adapter->MediaNumber)) + { + if (SiaStatus & DC_SIA_STATUS_100T_LINK_FAIL) + return FALSE; + } + else + { + /* The auto-negotiation process can be restarted upon link failure in 10Base-T mode */ + if ((SiaStatus & DC_SIA_STATUS_ANS_MASK) != DC_SIA_STATUS_ANS_AUTONEG_COMPLETE) + return FALSE; + + if (SiaStatus & DC_SIA_STATUS_10T_LINK_FAIL) + return FALSE; + } + + return TRUE; +} + +static +VOID +MediaMonitor143( + _In_ PDC21X4_ADAPTER Adapter) +{ + ULONG SiaStatus; + BOOLEAN LinkUp; + + SiaStatus = DC_READ(Adapter, DcCsr12_SiaStatus); + + switch (Adapter->ModeFlags & DC_MODE_AUTONEG_MASK) + { + case DC_MODE_AUTONEG_WAIT_INTERRUPT: + { + /* Timeout, select the other port */ + Media143SelectNextMedia(Adapter, SiaStatus); + break; + } + + case DC_MODE_AUTONEG_LINK_STATUS_CHECK: + { + /* Check the link status */ + LinkUp = Media143CheckLink(Adapter, SiaStatus); + if (LinkUp) + { + Adapter->ModeFlags &= ~DC_MODE_AUTONEG_MASK; + Adapter->ModeFlags |= DC_MODE_AUTONEG_NONE; + + MediaIndicateConnect(Adapter, TRUE); + } + else + { + /* No link detected, select the other port */ + Media143SelectNextMedia(Adapter, SiaStatus); + } + break; + } + + case DC_MODE_AUTONEG_NONE: + { + break; + } + + default: + ASSERT(FALSE); + UNREACHABLE; + break; + } +} + +VOID +NTAPI +MediaMonitor21143Dpc( + _In_ PVOID SystemSpecific1, + _In_ PVOID FunctionContext, + _In_ PVOID SystemSpecific2, + _In_ PVOID SystemSpecific3) +{ + PDC21X4_ADAPTER Adapter = FunctionContext; + ULONG DelayMs; + BOOLEAN LinkUp; + + UNREFERENCED_PARAMETER(SystemSpecific1); + UNREFERENCED_PARAMETER(SystemSpecific2); + UNREFERENCED_PARAMETER(SystemSpecific3); + + if (!(Adapter->Flags & DC_ACTIVE)) + return; + + NdisDprAcquireSpinLock(&Adapter->ModeLock); + + switch (Adapter->MediaNumber) + { + case MEDIA_MII: + { + LinkUp = MediaMiiCheckLink(Adapter); + + MediaIndicateConnect(Adapter, LinkUp); + + NdisMSetTimer(&Adapter->MediaMonitorTimer, 5000); + break; + } + + case MEDIA_AUI: + case MEDIA_BNC: + case MEDIA_HMR: + { + ULONG ReceiveActivity = (ULONG)Adapter->Statistics.ReceiveOk; + + if (!(Adapter->ModeFlags & DC_MODE_TEST_PACKET)) + { + if ((Adapter->MediaNumber == MEDIA_AUI || Adapter->MediaNumber == MEDIA_HMR) && + (DC_READ(Adapter, DcCsr12_SiaStatus) & DC_SIA_STATUS_AUI_ACTIVITY)) + { + /* Clear the AUI/HMR port activity bit */ + DC_WRITE(Adapter, DcCsr12_SiaStatus, DC_SIA_STATUS_AUI_ACTIVITY); + + MediaIndicateConnect(Adapter, TRUE); + + DelayMs = 5000; + } + /* Check for any received packets */ + else if (ReceiveActivity != Adapter->LastReceiveActivity) + { + MediaIndicateConnect(Adapter, TRUE); + + DelayMs = 3000; + } + else + { + /* Send a loopback packet */ + NdisDprAcquireSpinLock(&Adapter->SendLock); + DcTestPacket(Adapter); + NdisDprReleaseSpinLock(&Adapter->SendLock); + + DelayMs = 3000; + } + } + else + { + Adapter->ModeFlags &= ~DC_MODE_TEST_PACKET; + + LinkUp = !!Adapter->MediaTestStatus; + + MediaIndicateConnect(Adapter, LinkUp); + + /* Select the other port */ + if (!LinkUp && !MEDIA_IS_FIXED(Adapter)) + { + Media143SelectNextSerialMedia(Adapter); + + DelayMs = 3000; + } + else + { + DelayMs = 5000; + } + } + Adapter->LastReceiveActivity = ReceiveActivity; + + NdisMSetTimer(&Adapter->MediaMonitorTimer, DelayMs); + break; + } + + default: + { + MediaMonitor143(Adapter); + break; + } + } + + NdisDprReleaseSpinLock(&Adapter->ModeLock); +} diff --git a/drivers/network/dd/dc21x4/net21x4.inf b/drivers/network/dd/dc21x4/net21x4.inf new file mode 100644 index 00000000000..fc30ddc3142 --- /dev/null +++ b/drivers/network/dd/dc21x4/net21x4.inf @@ -0,0 +1,158 @@ +; NET21X4.INF + +; Installation file for DC21x4-based NICs + +[Version] +Signature = "$Windows NT$" +;Signature = "$ReactOS$" +LayoutFile = layout.inf +Class = Net +ClassGUID = {4D36E972-E325-11CE-BFC1-08002BE10318} +Provider = %ReactOS% +DriverVer = 08/24/2023,1.00 + +[DestinationDirs] +DefaultDestDir = 12 + +[Manufacturer] +%IntelMfg% = IntelMfg + +[ControlFlags] +ExcludeFromSelect = * + +[IntelMfg] +%DC21040.DeviceDesc% = DC21040.ndi,PCI\VEN_1011&DEV_0002 +%DC21041.DeviceDesc% = DC21041.ndi,PCI\VEN_1011&DEV_0014 +%DC21140.DeviceDesc% = DC21140.ndi,PCI\VEN_1011&DEV_0009 +%DC21143.DeviceDesc% = DC21143.ndi,PCI\VEN_1011&DEV_0019 +%DC21145.DeviceDesc% = DC21145.ndi,PCI\VEN_8086&DEV_0039 +%DC21140_VPC.DeviceDesc% = DC21140.ndi,PCI\VEN_1011&DEV_0009&SUBSYS_21140A00&REV_20 + +[DC21040.ndi.NT] +Characteristics = 0x84 ; NCF_PHYSICAL | NCF_HAS_UI +BusType = 5 ; PCIBus +CopyFiles = DC_CopyFiles.NT +AddReg = Common, DC_SD, DC_AUIBNC, DC_10 + +[DC21041.ndi.NT] +Characteristics = 0x84 +BusType = 5 +CopyFiles = DC_CopyFiles.NT +AddReg = Common, DC_SD, DC_AUI_BNC, DC_10 + +[DC21140.ndi.NT] +Characteristics = 0x84 +BusType = 5 +CopyFiles = DC_CopyFiles.NT +AddReg = Common, DC_SD, DC_10, DC_100 + +[DC21143.ndi.NT] +Characteristics = 0x84 +BusType = 5 +CopyFiles = DC_CopyFiles.NT +AddReg = Common, DC_SD, DC_AUI_BNC, DC_10, DC_100 + +[DC21145.ndi.NT] +Characteristics = 0x84 +BusType = 5 +CopyFiles = DC_CopyFiles.NT +AddReg = Common, DC_SD, DC_10, DC_HMR + +[DC_CopyFiles.NT] +dc21x4.sys + +[DC21040.ndi.NT.Services] +AddService = dc21x4, 2, DC_Service_Inst, DC_EventLog + +[DC21041.ndi.NT.Services] +AddService = dc21x4, 2, DC_Service_Inst, DC_EventLog + +[DC21140.ndi.NT.Services] +AddService = dc21x4, 2, DC_Service_Inst, DC_EventLog + +[DC21143.ndi.NT.Services] +AddService = dc21x4, 2, DC_Service_Inst, DC_EventLog + +[DC21145.ndi.NT.Services] +AddService = dc21x4, 2, DC_Service_Inst, DC_EventLog + +[Common] +HKR, Ndi, Service, 0, "dc21x4" +HKR, Ndi\Interfaces, UpperRange, 0, "ndis5" +HKR, Ndi\Interfaces, LowerRange, 0, "ethernet" + +HKR, Ndi\params\NetworkAddress, ParamDesc, 0, %NA% +HKR, Ndi\params\NetworkAddress, type, 0, "edit" +HKR, Ndi\params\NetworkAddress, LimitText, 0, "12" +HKR, Ndi\params\NetworkAddress, UpperCase, 0, "1" +HKR, Ndi\params\NetworkAddress, default, 0, " " +HKR, Ndi\params\NetworkAddress, optional, 0, "1" + +[DC_SD] +HKR, Ndi\params\SpeedDuplex, ParamDesc, 0, %SD% +HKR, Ndi\params\SpeedDuplex, type, 0, "enum" +HKR, Ndi\params\SpeedDuplex, default, 0, "0" +HKR, Ndi\params\SpeedDuplex\enum, "10", 0, "%Auto%" + +[DC_AUIBNC] +HKR, Ndi\params\SpeedDuplex\enum, "1", 0, "AUI/BNC" + +[DC_AUI_BNC] +HKR, Ndi\params\SpeedDuplex\enum, "1", 0, "BNC" +HKR, Ndi\params\SpeedDuplex\enum, "2", 0, "AUI" + +[DC_10] +HKR, Ndi\params\SpeedDuplex\enum, "0", 0, "10T HD" +HKR, Ndi\params\SpeedDuplex\enum, "4", 0, "10T FD" + +[DC_100] +HKR, Ndi\params\SpeedDuplex\enum, "3", 0, "100TX HD" +HKR, Ndi\params\SpeedDuplex\enum, "5", 0, "100TX FD" +HKR, Ndi\params\SpeedDuplex\enum, "6", 0, "100T4" +HKR, Ndi\params\SpeedDuplex\enum, "7", 0, "100FX HD" +HKR, Ndi\params\SpeedDuplex\enum, "8", 0, "100FX FD" + +[DC_HMR] +HKR, Ndi\params\SpeedDuplex\enum, "9", 0, "HomePNA" + +[DC_Service_Inst] +ServiceType = 1 +StartType = 3 +ErrorControl = 1 +ServiceBinary = %12%\dc21x4.sys +LoadOrderGroup = NDIS + +[DC_EventLog] +AddReg = DC_EventLog_AddReg + +[DC_EventLog_AddReg] +HKR, , EventMessageFile, 0x00020000, "%%SystemRoot%%\System32\netevent.dll" +HKR, , TypesSupported, 0x00010001, 7 + +[Strings] +; Non-localizable +ReactOS="ReactOS Team" +IntelMfg="Intel" + +NA="Network Address" +SD="Speed & Duplex" +Auto="Auto" + +DC21040.DeviceDesc="Intel 21040-based PCI Ethernet Adapter" +DC21041.DeviceDesc="Intel 21041-based PCI Ethernet Adapter" +DC21140.DeviceDesc="Intel 21140-based PCI Ethernet Adapter" +DC21143.DeviceDesc="Intel 21143-based PCI Ethernet Adapter" +DC21145.DeviceDesc="Intel 21145-based PCI Ethernet Adapter" +DC21140_VPC.DeviceDesc="Intel 21140-based PCI Ethernet Adapter (emulated)" + +[Strings.0419] +NA="Сетевой адрес" +SD="Скорость и дуплекс" +Auto="Авто" + +DC21040.DeviceDesc="Intel 21040-based PCI сетевой адаптер" +DC21041.DeviceDesc="Intel 21041-based PCI сетевой адаптер" +DC21140.DeviceDesc="Intel 21140-based PCI сетевой адаптер" +DC21143.DeviceDesc="Intel 21143-based PCI сетевой адаптер" +DC21145.DeviceDesc="Intel 21145-based PCI сетевой адаптер" +DC21140_VPC.DeviceDesc="Intel 21140-based PCI сетевой адаптер (эмуляция)" diff --git a/drivers/network/dd/dc21x4/phy.c b/drivers/network/dd/dc21x4/phy.c new file mode 100644 index 00000000000..af325f67a97 --- /dev/null +++ b/drivers/network/dd/dc21x4/phy.c @@ -0,0 +1,263 @@ +/* + * PROJECT: ReactOS DC21x4 Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: PHY layer setup and management + * COPYRIGHT: Copyright 2023 Dmitry Borisov + */ + +/* INCLUDES *******************************************************************/ + +#include "dc21x4.h" + +#include + +/* GLOBALS ********************************************************************/ + +#define MII_READ(Adapter, Data) \ + do { \ + *Data = DC_READ((Adapter), DcCsr9_SerialInterface); \ + NdisStallExecution(2); \ + } while (0) + +#define MII_WRITE(Adapter, Value) \ + do { \ + DC_WRITE((Adapter), DcCsr9_SerialInterface, Value); \ + NdisStallExecution(2); \ + } while (0) + +/* FUNCTIONS ******************************************************************/ + +static +VOID +MiiMdioPacket( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG Sequence, + _In_ ULONG BitCount) +{ + LONG i; + + for (i = BitCount - 1; i >= 0; --i) + { + ULONG Mdo = ((Sequence >> i) & 1) << DC_SERIAL_MII_MDO_SHIFT; + + MII_WRITE(Adapter, Mdo); + MII_WRITE(Adapter, Mdo | DC_SERIAL_MII_MDC); + } +} + +static +ULONG +MiiMdioShiftIn( + _In_ PDC21X4_ADAPTER Adapter) +{ + ULONG i, Csr; + ULONG Result = 0; + + for (i = 0; i < RTL_BITS_OF(USHORT); ++i) + { + MII_WRITE(Adapter, DC_SERIAL_MII_MII); + MII_WRITE(Adapter, DC_SERIAL_MII_MII | DC_SERIAL_MII_MDC); + + MII_READ(Adapter, &Csr); + Result = (Result << 1) | ((Csr >> DC_SERIAL_MII_MDI_SHIFT) & 1); + } + + return Result; +} + +static +VOID +MiiMdioClearExtraBits( + _In_ PDC21X4_ADAPTER Adapter) +{ + MII_WRITE(Adapter, DC_SERIAL_MII_MII); + MII_WRITE(Adapter, DC_SERIAL_MII_MII | DC_SERIAL_MII_MDC); +} + +BOOLEAN +MiiWrite( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG PhyAddress, + _In_ ULONG RegAddress, + _In_ ULONG Data) +{ + MiiMdioPacket(Adapter, MDIO_PREAMBLE, 32); + MiiMdioPacket(Adapter, + (MDIO_START << 30) | + (MDIO_WRITE << 28) | + (PhyAddress << 23) | + (RegAddress << 18) | + (MDIO_TA << 16) | + Data, + 32); + + /* Idle state */ + MiiMdioClearExtraBits(Adapter); + + return TRUE; +} + +BOOLEAN +MiiRead( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG PhyAddress, + _In_ ULONG RegAddress, + _Out_ PULONG Data) +{ + ULONG Csr; + BOOLEAN Success; + + MiiMdioPacket(Adapter, MDIO_PREAMBLE, 32); + MiiMdioPacket(Adapter, + (MDIO_START << 12) | + (MDIO_READ << 10) | + (PhyAddress << 5) | + RegAddress, + 14); + + /* Turnaround */ + MiiMdioClearExtraBits(Adapter); + + Csr = DC_READ(Adapter, DcCsr9_SerialInterface); + Success = !(Csr & DC_SERIAL_MII_MDI); + + *Data = MiiMdioShiftIn(Adapter); + + /* Idle state */ + MiiMdioClearExtraBits(Adapter); + + return Success; +} + +static +CODE_SEG("PAGE") +VOID +HpnaSpiClose( + _In_ PDC21X4_ADAPTER Adapter) +{ + PAGED_CODE(); + + DC_WRITE(Adapter, DcCsr9_SerialInterface, 0); + DC_WRITE(Adapter, DcCsr9_SerialInterface, DC_SERIAL_SPI_SK); +} + +static +CODE_SEG("PAGE") +VOID +HpnaSpiShiftOut( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG Sequence, + _In_ ULONG BitCount) +{ + LONG i; + + PAGED_CODE(); + + DC_WRITE(Adapter, DcCsr9_SerialInterface, 0); + DC_WRITE(Adapter, DcCsr9_SerialInterface, DC_SERIAL_SPI_CS); + + for (i = BitCount - 1; i >= 0; --i) + { + ULONG DataIn = ((Sequence >> i) & 1) << DC_SERIAL_SPI_DI_SHIFT; + + DC_WRITE(Adapter, DcCsr9_SerialInterface, DataIn | DC_SERIAL_SPI_CS); + DC_WRITE(Adapter, DcCsr9_SerialInterface, DataIn | DC_SERIAL_SPI_CS | DC_SERIAL_SPI_SK); + } + + DC_WRITE(Adapter, DcCsr9_SerialInterface, 0); + DC_WRITE(Adapter, DcCsr9_SerialInterface, DC_SERIAL_SPI_SK); +} + +static +CODE_SEG("PAGE") +VOID +HpnaWrite( + _In_ PDC21X4_ADAPTER Adapter, + _In_ ULONG RegAddress, + _In_ ULONG Data) +{ + PAGED_CODE(); + + HpnaSpiShiftOut(Adapter, DC_SPI_SET_WRITE_ENABLE, 8); + HpnaSpiClose(Adapter); + + HpnaSpiShiftOut(Adapter, + (Data << 16) | + (RegAddress << 8) | + DC_SPI_BYTE_WRITE_OPERATION, + RTL_BITS_OF(UCHAR) * 3); + HpnaSpiClose(Adapter); +} + +CODE_SEG("PAGE") +VOID +HpnaPhyInit( + _In_ PDC21X4_ADAPTER Adapter) +{ + ULONG SiaConn, i; + + PAGED_CODE(); + + /* Select the HPNA interface */ + SiaConn = DC_READ(Adapter, DcCsr13_SiaConnectivity); + SiaConn |= DC_SIA_CONN_HPNA; + DC_WRITE(Adapter, DcCsr13_SiaConnectivity, SiaConn); + + for (i = 0; i < RTL_NUMBER_OF(Adapter->HpnaRegister); ++i) + { + if (Adapter->HpnaInitBitmap & (1 << i)) + { + HpnaWrite(Adapter, i, Adapter->HpnaRegister[i]); + } + } +} + +CODE_SEG("PAGE") +BOOLEAN +DcFindMiiPhy( + _In_ PDC21X4_ADAPTER Adapter) +{ + ULONG Phy; + + PAGED_CODE(); + + /* Look for the first connected PHY */ + for (Phy = 1; Phy <= MII_MAX_PHY_ADDRESSES; ++Phy) + { + ULONG PhyAddress = Phy % MII_MAX_PHY_ADDRESSES; /* Check the PHY 0 last */ + ULONG MiiStatus; +#if DBG + ULONG PhyIdLow, PhyIdHigh, MiiControl, MiiAdvertise; +#endif + + /* + * Read the status register. Some PHYs, such as the ML6692, + * don't implement the IEEE ID registers. + */ + if (!MiiRead(Adapter, PhyAddress, MII_STATUS, &MiiStatus)) + continue; + if (MiiStatus == 0xFFFF || MiiStatus == 0) + continue; + +#if DBG + MiiRead(Adapter, PhyAddress, MII_PHY_ID1, &PhyIdLow); + MiiRead(Adapter, PhyAddress, MII_PHY_ID2, &PhyIdHigh); + MiiRead(Adapter, PhyAddress, MII_CONTROL, &MiiControl); + MiiRead(Adapter, PhyAddress, MII_AUTONEG_ADVERTISE, &MiiAdvertise); + + INFO_VERB("Found PHY at address %u: ID %04lx:%04lx, Ctrl %04lx, Status %04lx, Adv %04lx\n", + PhyAddress, + PhyIdLow, + PhyIdHigh, + MiiControl, + MiiStatus, + MiiAdvertise); +#endif + + Adapter->PhyAddress = PhyAddress; + + return TRUE; + } + + return FALSE; +} diff --git a/drivers/network/dd/dc21x4/power.c b/drivers/network/dd/dc21x4/power.c new file mode 100644 index 00000000000..38ea470724a --- /dev/null +++ b/drivers/network/dd/dc21x4/power.c @@ -0,0 +1,252 @@ +/* + * PROJECT: ReactOS DC21x4 Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Power management + * COPYRIGHT: Copyright 2023 Dmitry Borisov + */ + +/* INCLUDES *******************************************************************/ + +#include "dc21x4.h" + +#include + +/* FUNCTIONS ******************************************************************/ + +static +CODE_SEG("PAGE") +VOID +DcDownloadPatternFilter( + _In_ PDC21X4_ADAPTER Adapter, + _In_ PDC_PATTERN_FILTER_BLOCK FilterBlock) +{ + ULONG i; + + PAGED_CODE(); + + for (i = 0; i < sizeof(*FilterBlock) / sizeof(ULONG); ++i) + { + DC_WRITE(Adapter, DcCsr1_WakeUpFilter, FilterBlock->AsULONG[i]); + } +} + +static +CODE_SEG("PAGE") +VOID +DcSetupWakeUpFilter( + _In_ PDC21X4_ADAPTER Adapter) +{ + DC_PATTERN_FILTER_BLOCK FilterBlock; + + PAGED_CODE(); + + /* Save the address filtering */ + NdisMoveMemory(Adapter->SetupFrameSaved, Adapter->SetupFrame, DC_SETUP_FRAME_SIZE); + + NdisZeroMemory(&FilterBlock, sizeof(FilterBlock)); + + // TODO: Convert NDIS patterns to HW filter and prepare a setup frame + + DcDownloadPatternFilter(Adapter, &FilterBlock); +} + +static +CODE_SEG("PAGE") +VOID +DcProgramWakeUpEvents( + _In_ PDC21X4_ADAPTER Adapter) +{ + ULONG WakeUpControl; + + PAGED_CODE(); + + /* Clear the wake-up events */ + WakeUpControl = (DC_WAKE_UP_STATUS_LINK_CHANGE | + DC_WAKE_UP_STATUS_MAGIC_PACKET | + DC_WAKE_UP_CONTROL_PATTERN_MATCH); + + /* Convert NDIS flags to hardware-specific values */ + if (Adapter->WakeUpFlags & NDIS_PNP_WAKE_UP_LINK_CHANGE) + WakeUpControl |= DC_WAKE_UP_CONTROL_LINK_CHANGE; + if (Adapter->WakeUpFlags & NDIS_PNP_WAKE_UP_MAGIC_PACKET) + WakeUpControl |= DC_WAKE_UP_CONTROL_MAGIC_PACKET; +#if 0 // TODO: Pattern matching is not yet supported + if (Adapter->WakeUpFlags & NDIS_PNP_WAKE_UP_PATTERN_MATCH) + WakeUpControl |= DC_WAKE_UP_CONTROL_PATTERN_MATCH; +#endif + + DC_WRITE(Adapter, DcCsr2_WakeUpControl, WakeUpControl); +} + +static +CODE_SEG("PAGE") +VOID +DcPowerDown( + _In_ PDC21X4_ADAPTER Adapter) +{ + ULONG SiaState, SerialInterface; + + PAGED_CODE(); + + /* Stop the receive and transmit processes */ + DcStopAdapter(Adapter, FALSE); + + Adapter->CurrentInterruptMask = 0; + + /* Enable the link integrity test bit */ + switch (Adapter->MediaNumber) + { + case MEDIA_AUI: + case MEDIA_BNC: + case MEDIA_HMR: + { + SiaState = DC_READ(Adapter, DcCsr14_SiaTxRx); + if (!(SiaState & DC_SIA_TXRX_LINK_TEST)) + { + SiaState |= DC_SIA_TXRX_LINK_TEST; + DC_WRITE(Adapter, DcCsr14_SiaTxRx, SiaState); + } + break; + } + + default: + break; + } + + /* Clear the MDC bit */ + SerialInterface = DC_READ(Adapter, DcCsr9_SerialInterface); + if (SerialInterface & DC_SERIAL_MII_MDC) + { + SerialInterface &= ~DC_SERIAL_MII_MDC; + DC_WRITE(Adapter, DcCsr9_SerialInterface, SerialInterface); + } + + /* Unprotect PM access */ + DC_WRITE(Adapter, DcCsr0_BusMode, Adapter->BusMode | DC_BUS_MODE_ON_NOW_UNLOCK); + + /* Program the requested WOL events */ + DcSetupWakeUpFilter(Adapter); + DcProgramWakeUpEvents(Adapter); + + /* Protect PM access */ + DC_WRITE(Adapter, DcCsr0_BusMode, Adapter->BusMode); +} + +static +CODE_SEG("PAGE") +VOID +DcPowerUp( + _In_ PDC21X4_ADAPTER Adapter) +{ + PAGED_CODE(); + + /* Restore the address filtering */ + NdisMoveMemory(Adapter->SetupFrame, Adapter->SetupFrameSaved, DC_SETUP_FRAME_SIZE); + + /* Re-initialize the chip to leave D3 state */ + if (Adapter->PrevPowerState == NdisDeviceStateD3) + { + NT_VERIFY(DcSetupAdapter(Adapter) == TRUE); + } + else + { + /* Start the transmit process */ + Adapter->OpMode |= DC_OPMODE_TX_ENABLE; + DC_WRITE(Adapter, DcCsr6_OpMode, Adapter->OpMode); + + /* Load the address recognition RAM */ + NT_VERIFY(DcSetupFrameDownload(Adapter, TRUE) == TRUE); + } + + DcStartAdapter(Adapter); +} + +CODE_SEG("PAGE") +VOID +NTAPI +DcPowerWorker( + _In_ PNDIS_WORK_ITEM WorkItem, + _In_opt_ PVOID Context) +{ + PDC21X4_ADAPTER Adapter = Context; + + UNREFERENCED_PARAMETER(WorkItem); + + PAGED_CODE(); + + if (Adapter->PowerState == NdisDeviceStateD0) + { + DcPowerUp(Adapter); + } + else + { + DcPowerDown(Adapter); + } + Adapter->PrevPowerState = Adapter->PowerState; + + NdisMSetInformationComplete(Adapter->AdapterHandle, NDIS_STATUS_SUCCESS); +} + +NDIS_STATUS +DcSetPower( + _In_ PDC21X4_ADAPTER Adapter, + _In_ NDIS_DEVICE_POWER_STATE PowerState) +{ + INFO("Power state %u\n", PowerState); + + Adapter->PowerState = PowerState; + + NdisScheduleWorkItem(&Adapter->PowerWorkItem); + + return NDIS_STATUS_PENDING; +} + +NDIS_STATUS +DcRemoveWakeUpPattern( + _In_ PDC21X4_ADAPTER Adapter, + _In_ PNDIS_PM_PACKET_PATTERN PmPattern) +{ + // TODO: Not implemented + ERR("FIXME: Not implemented\n"); + return NDIS_STATUS_NOT_SUPPORTED; +} + +NDIS_STATUS +DcAddWakeUpPattern( + _In_ PDC21X4_ADAPTER Adapter, + _In_ PNDIS_PM_PACKET_PATTERN PmPattern) +{ + // TODO: Not implemented + ERR("FIXME: Not implemented\n"); + return NDIS_STATUS_NOT_SUPPORTED; +} + +VOID +DcPowerSave( + _In_ PDC21X4_ADAPTER Adapter, + _In_ BOOLEAN Enable) +{ + ULONG ConfigValue; + + if (!(Adapter->Features & DC_HAS_POWER_SAVING)) + return; + + NdisReadPciSlotInformation(Adapter->AdapterHandle, + 0, + DC_PCI_DEVICE_CONFIG, + &ConfigValue, + sizeof(ConfigValue)); + + ConfigValue &= ~DC_PCI_DEVICE_CONFIG_SLEEP; + + if (Enable) + ConfigValue |= DC_PCI_DEVICE_CONFIG_SNOOZE; + else + ConfigValue &= ~DC_PCI_DEVICE_CONFIG_SNOOZE; + + NdisWritePciSlotInformation(Adapter->AdapterHandle, + 0, + DC_PCI_DEVICE_CONFIG, + &ConfigValue, + sizeof(ConfigValue)); +} diff --git a/drivers/network/dd/dc21x4/requests.c b/drivers/network/dd/dc21x4/requests.c new file mode 100644 index 00000000000..c23cf1cef38 --- /dev/null +++ b/drivers/network/dd/dc21x4/requests.c @@ -0,0 +1,660 @@ +/* + * PROJECT: ReactOS DC21x4 Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Miniport information callbacks + * COPYRIGHT: Copyright 2023 Dmitry Borisov + */ + +/* INCLUDES *******************************************************************/ + +#include "dc21x4.h" + +#include + +/* GLOBALS ********************************************************************/ + +static const NDIS_OID DcpSupportedOidList[] = +{ + OID_GEN_SUPPORTED_LIST, + OID_GEN_CURRENT_PACKET_FILTER, + OID_GEN_HARDWARE_STATUS, + OID_GEN_MEDIA_SUPPORTED, + OID_GEN_MEDIA_IN_USE, + OID_GEN_MAXIMUM_LOOKAHEAD, + OID_GEN_MAXIMUM_FRAME_SIZE, + OID_GEN_MAXIMUM_SEND_PACKETS, + OID_GEN_LINK_SPEED, + OID_GEN_TRANSMIT_BUFFER_SPACE, + OID_GEN_RECEIVE_BUFFER_SPACE, + OID_GEN_RECEIVE_BLOCK_SIZE, + OID_GEN_TRANSMIT_BLOCK_SIZE, + OID_GEN_VENDOR_ID, + OID_GEN_VENDOR_DESCRIPTION, + OID_GEN_VENDOR_DRIVER_VERSION, + OID_GEN_CURRENT_LOOKAHEAD, + OID_GEN_DRIVER_VERSION, + OID_GEN_MAXIMUM_TOTAL_SIZE, + OID_GEN_MAC_OPTIONS, + OID_GEN_MEDIA_CONNECT_STATUS, + OID_802_3_PERMANENT_ADDRESS, + OID_802_3_CURRENT_ADDRESS, + OID_802_3_MULTICAST_LIST, + OID_802_3_MAXIMUM_LIST_SIZE, + + /* Statistics */ + OID_GEN_XMIT_OK, + OID_GEN_RCV_OK, + OID_GEN_XMIT_ERROR, + OID_GEN_RCV_ERROR, + OID_GEN_RCV_NO_BUFFER, + OID_GEN_DIRECTED_FRAMES_RCV, + OID_GEN_MULTICAST_FRAMES_RCV, + OID_GEN_BROADCAST_FRAMES_RCV, + OID_GEN_RCV_CRC_ERROR, + OID_GEN_TRANSMIT_QUEUE_LENGTH, + OID_802_3_RCV_ERROR_ALIGNMENT, + OID_802_3_XMIT_ONE_COLLISION, + OID_802_3_XMIT_MORE_COLLISIONS, + OID_802_3_XMIT_DEFERRED, + OID_802_3_XMIT_MAX_COLLISIONS, + OID_802_3_RCV_OVERRUN, + OID_802_3_XMIT_UNDERRUN, + OID_802_3_XMIT_HEARTBEAT_FAILURE, + OID_802_3_XMIT_TIMES_CRS_LOST, + OID_802_3_XMIT_LATE_COLLISIONS, + + /* Power management */ + OID_PNP_CAPABILITIES, + OID_PNP_SET_POWER, + OID_PNP_QUERY_POWER, + OID_PNP_ADD_WAKE_UP_PATTERN, + OID_PNP_REMOVE_WAKE_UP_PATTERN, + OID_PNP_ENABLE_WAKE_UP +}; + +/* FUNCTIONS ******************************************************************/ + +static +VOID +DcQueryStatisticCounter( + _In_ PDC21X4_ADAPTER Adapter, + _In_ NDIS_OID Oid, + _Out_ PULONG64 Counter) +{ + /* When there is no workaround, this function is used to read the hardware RX counters */ + if (!(Adapter->Features & DC_NEED_RX_OVERFLOW_WORKAROUND)) + { + ULONG RxCounters; + + /* + * Read the RX missed frame counter. Note that the RX overflow counter is not supported + * on older chips without the workaround enabled and reads will return 0xFFFE****. + */ + RxCounters = DC_READ(Adapter, DcCsr8_RxCounters); + + Adapter->Statistics.ReceiveNoBuffers += RxCounters & DC_COUNTER_RX_NO_BUFFER_MASK; + } + + switch (Oid) + { + case OID_GEN_XMIT_OK: + *Counter = Adapter->Statistics.TransmitOk; + break; + case OID_GEN_RCV_OK: + *Counter = Adapter->Statistics.ReceiveOk; + break; + case OID_GEN_XMIT_ERROR: + *Counter = Adapter->Statistics.TransmitErrors; + break; + case OID_GEN_RCV_ERROR: + *Counter = Adapter->Statistics.ReceiveErrors; + break; + case OID_GEN_RCV_NO_BUFFER: + *Counter = Adapter->Statistics.ReceiveNoBuffers; + break; + case OID_GEN_DIRECTED_FRAMES_RCV: + *Counter = Adapter->Statistics.ReceiveUnicast; + break; + case OID_GEN_MULTICAST_FRAMES_RCV: + *Counter = Adapter->Statistics.ReceiveMulticast; + break; + case OID_GEN_BROADCAST_FRAMES_RCV: + *Counter = Adapter->Statistics.ReceiveBroadcast; + break; + case OID_GEN_RCV_CRC_ERROR: + *Counter = Adapter->Statistics.ReceiveCrcErrors; + break; + case OID_802_3_RCV_ERROR_ALIGNMENT: + *Counter = Adapter->Statistics.ReceiveAlignmentErrors; + break; + case OID_802_3_XMIT_ONE_COLLISION: + *Counter = Adapter->Statistics.TransmitOneRetry; + break; + case OID_802_3_XMIT_MORE_COLLISIONS: + *Counter = Adapter->Statistics.TransmitMoreCollisions; + break; + case OID_802_3_XMIT_DEFERRED: + *Counter = Adapter->Statistics.TransmitDeferred; + break; + case OID_802_3_XMIT_MAX_COLLISIONS: + *Counter = Adapter->Statistics.TransmitExcessiveCollisions; + break; + case OID_802_3_RCV_OVERRUN: + *Counter = Adapter->Statistics.ReceiveOverrunErrors; + break; + case OID_802_3_XMIT_UNDERRUN: + *Counter = Adapter->Statistics.TransmitUnderrunErrors; + break; + case OID_802_3_XMIT_HEARTBEAT_FAILURE: + *Counter = Adapter->Statistics.TransmitHeartbeatErrors; + break; + case OID_802_3_XMIT_TIMES_CRS_LOST: + *Counter = Adapter->Statistics.TransmitLostCarrierSense; + break; + case OID_802_3_XMIT_LATE_COLLISIONS: + *Counter = Adapter->Statistics.TransmitLateCollisions; + break; + + default: + ASSERT(FALSE); + UNREACHABLE; + break; + } +} + +static +ULONG +DcGetLinkSpeed( + _In_ PDC21X4_ADAPTER Adapter) +{ + ULONG LinkSpeedMbps; + + switch (Adapter->MediaNumber) + { + case MEDIA_HMR: + LinkSpeedMbps = 1; + break; + + case MEDIA_10T: + case MEDIA_BNC: + case MEDIA_AUI: + case MEDIA_10T_FD: + LinkSpeedMbps = 10; + break; + + case MEDIA_100TX_HD: + case MEDIA_100TX_FD: + case MEDIA_100T4: + case MEDIA_100FX_HD: + case MEDIA_100FX_FD: + LinkSpeedMbps = 100; + break; + + case MEDIA_MII: + LinkSpeedMbps = Adapter->LinkSpeedMbps; + break; + + default: + ASSERT(FALSE); + UNREACHABLE; + break; + } + + return LinkSpeedMbps; +} + +NDIS_STATUS +NTAPI +DcQueryInformation( + _In_ NDIS_HANDLE MiniportAdapterContext, + _In_ NDIS_OID Oid, + _In_ PVOID InformationBuffer, + _In_ ULONG InformationBufferLength, + _Out_ PULONG BytesWritten, + _Out_ PULONG BytesNeeded) +{ + PDC21X4_ADAPTER Adapter = (PDC21X4_ADAPTER)MiniportAdapterContext; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + ULONG InfoLength; + PVOID InfoPtr; + union _GENERIC_INFORMATION + { + USHORT Ushort; + ULONG Ulong; + ULONG64 Ulong64; + NDIS_MEDIUM Medium; + NDIS_HARDWARE_STATUS Status; + NDIS_DEVICE_POWER_STATE PowerState; + } GenericInfo; + + InfoLength = sizeof(ULONG); + InfoPtr = &GenericInfo; + + switch (Oid) + { + case OID_GEN_SUPPORTED_LIST: + InfoPtr = (PVOID)&DcpSupportedOidList; + InfoLength = sizeof(DcpSupportedOidList); + break; + + case OID_GEN_CURRENT_PACKET_FILTER: + GenericInfo.Ulong = Adapter->PacketFilter; + break; + + case OID_802_3_MULTICAST_LIST: + InfoPtr = Adapter->MulticastList; + InfoLength = Adapter->MulticastCount * ETH_LENGTH_OF_ADDRESS; + break; + + case OID_802_3_MAXIMUM_LIST_SIZE: + GenericInfo.Ulong = Adapter->MulticastMaxEntries; + break; + + case OID_GEN_MEDIA_SUPPORTED: + case OID_GEN_MEDIA_IN_USE: + { + GenericInfo.Medium = NdisMedium802_3; + InfoLength = sizeof(NDIS_MEDIUM); + break; + } + + case OID_GEN_HARDWARE_STATUS: + { + ULONG InterruptStatus; + + /* NOTE: Reading the status register has no effect on the events */ + InterruptStatus = DC_READ(Adapter, DcCsr5_Status); + + /* Inserted into the motherboard */ + if (InterruptStatus != 0xFFFFFFFF) + GenericInfo.Status = NdisHardwareStatusReady; + else + GenericInfo.Status = NdisHardwareStatusNotReady; + + InfoLength = sizeof(NDIS_HARDWARE_STATUS); + break; + } + + case OID_GEN_MAXIMUM_FRAME_SIZE: + case OID_GEN_MAXIMUM_LOOKAHEAD: + case OID_GEN_CURRENT_LOOKAHEAD: + GenericInfo.Ulong = DC_MAXIMUM_FRAME_SIZE - DC_ETHERNET_HEADER_SIZE; + break; + + case OID_GEN_MAXIMUM_TOTAL_SIZE: + GenericInfo.Ulong = DC_MAXIMUM_FRAME_SIZE; + break; + + case OID_GEN_TRANSMIT_BLOCK_SIZE: + GenericInfo.Ulong = DC_TRANSMIT_BLOCK_SIZE; + break; + + case OID_GEN_TRANSMIT_BUFFER_SPACE: + GenericInfo.Ulong = DC_TRANSMIT_BLOCK_SIZE * DC_TRANSMIT_BLOCKS; + break; + + case OID_GEN_RECEIVE_BLOCK_SIZE: + GenericInfo.Ulong = DC_RECEIVE_BLOCK_SIZE; + break; + + case OID_GEN_RECEIVE_BUFFER_SPACE: + GenericInfo.Ulong = DC_RECEIVE_BLOCK_SIZE * Adapter->RcbCount; + break; + + case OID_GEN_LINK_SPEED: + GenericInfo.Ulong = DcGetLinkSpeed(Adapter) * 10000; + break; + + case OID_GEN_VENDOR_ID: + GenericInfo.Ulong = 0; + GenericInfo.Ulong |= (Adapter->PermanentMacAddress[0] << 16); + GenericInfo.Ulong |= (Adapter->PermanentMacAddress[1] << 8); + GenericInfo.Ulong |= (Adapter->PermanentMacAddress[2] & 0xFF); + break; + + case OID_GEN_VENDOR_DESCRIPTION: + { + static const CHAR VendorDesc[] = "DC21x4 compatible Ethernet Adapter"; + InfoPtr = (PVOID)&VendorDesc; + InfoLength = sizeof(VendorDesc); + break; + } + + case OID_GEN_VENDOR_DRIVER_VERSION: + /* 1.0.0 */ + GenericInfo.Ulong = 0x100; + break; + + case OID_GEN_DRIVER_VERSION: + { + InfoLength = sizeof(USHORT); + GenericInfo.Ushort = (NDIS_MINIPORT_MAJOR_VERSION << 8) | NDIS_MINIPORT_MINOR_VERSION; + break; + } + + case OID_GEN_MAXIMUM_SEND_PACKETS: + GenericInfo.Ulong = DC_TRANSMIT_BLOCKS - DC_TCB_RESERVE; + break; + + case OID_GEN_MAC_OPTIONS: + GenericInfo.Ulong = NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA | + NDIS_MAC_OPTION_TRANSFERS_NOT_PEND | + NDIS_MAC_OPTION_NO_LOOPBACK; + break; + + case OID_GEN_MEDIA_CONNECT_STATUS: + GenericInfo.Ulong = Adapter->LinkUp ? NdisMediaStateConnected + : NdisMediaStateDisconnected; + break; + + case OID_802_3_PERMANENT_ADDRESS: + InfoPtr = Adapter->PermanentMacAddress; + InfoLength = ETH_LENGTH_OF_ADDRESS; + break; + + case OID_802_3_CURRENT_ADDRESS: + InfoPtr = Adapter->CurrentMacAddress; + InfoLength = ETH_LENGTH_OF_ADDRESS; + break; + + case OID_GEN_XMIT_OK: + case OID_GEN_RCV_OK: + case OID_GEN_XMIT_ERROR: + case OID_GEN_RCV_ERROR: + case OID_GEN_RCV_NO_BUFFER: + case OID_GEN_DIRECTED_FRAMES_RCV: + case OID_GEN_MULTICAST_FRAMES_RCV: + case OID_GEN_BROADCAST_FRAMES_RCV: + case OID_GEN_RCV_CRC_ERROR: + case OID_802_3_RCV_ERROR_ALIGNMENT: + case OID_802_3_XMIT_ONE_COLLISION: + case OID_802_3_XMIT_MORE_COLLISIONS: + case OID_802_3_XMIT_DEFERRED: + case OID_802_3_XMIT_MAX_COLLISIONS: + case OID_802_3_RCV_OVERRUN: + case OID_802_3_XMIT_UNDERRUN: + case OID_802_3_XMIT_HEARTBEAT_FAILURE: + case OID_802_3_XMIT_TIMES_CRS_LOST: + case OID_802_3_XMIT_LATE_COLLISIONS: + { + DcQueryStatisticCounter(Adapter, Oid, &GenericInfo.Ulong64); + + *BytesNeeded = sizeof(ULONG64); + if (InformationBufferLength < sizeof(ULONG)) + { + *BytesWritten = 0; + return NDIS_STATUS_BUFFER_TOO_SHORT; + } + if (InformationBufferLength >= sizeof(ULONG64)) + { + *BytesWritten = sizeof(ULONG64); + NdisMoveMemory(InformationBuffer, InfoPtr, sizeof(ULONG64)); + } + else + { + *BytesWritten = sizeof(ULONG); + NdisMoveMemory(InformationBuffer, InfoPtr, sizeof(ULONG)); + } + + return NDIS_STATUS_SUCCESS; + } + + case OID_GEN_TRANSMIT_QUEUE_LENGTH: + GenericInfo.Ulong = (DC_TRANSMIT_BLOCKS - DC_TCB_RESERVE) - Adapter->TcbSlots; + break; + + case OID_PNP_CAPABILITIES: + { + PNDIS_PNP_CAPABILITIES Capabilities; + + InfoLength = sizeof(NDIS_PNP_CAPABILITIES); + + if (InformationBufferLength < InfoLength) + { + *BytesWritten = 0; + *BytesNeeded = InfoLength; + return NDIS_STATUS_BUFFER_TOO_SHORT; + } + + if (!(Adapter->Features & DC_HAS_POWER_MANAGEMENT)) + return NDIS_STATUS_NOT_SUPPORTED; + + *BytesWritten = InfoLength; + *BytesNeeded = 0; + + Capabilities = InformationBuffer; + Capabilities->WakeUpCapabilities.MinMagicPacketWakeUp = NdisDeviceStateD3; + Capabilities->WakeUpCapabilities.MinPatternWakeUp = NdisDeviceStateD3; + Capabilities->WakeUpCapabilities.MinLinkChangeWakeUp = NdisDeviceStateD3; + + return NDIS_STATUS_SUCCESS; + } + + case OID_PNP_QUERY_POWER: + { + if (!(Adapter->Features & DC_HAS_POWER_MANAGEMENT)) + return NDIS_STATUS_NOT_SUPPORTED; + + return NDIS_STATUS_SUCCESS; + } + + case OID_PNP_ENABLE_WAKE_UP: + { + if (!(Adapter->Features & DC_HAS_POWER_MANAGEMENT)) + return NDIS_STATUS_NOT_SUPPORTED; + + GenericInfo.Ulong = Adapter->WakeUpFlags & (NDIS_PNP_WAKE_UP_MAGIC_PACKET | + NDIS_PNP_WAKE_UP_PATTERN_MATCH | + NDIS_PNP_WAKE_UP_LINK_CHANGE); + break; + } + + default: + Status = NDIS_STATUS_INVALID_OID; + break; + } + + if (Status == NDIS_STATUS_SUCCESS) + { + if (InfoLength > InformationBufferLength) + { + *BytesWritten = 0; + *BytesNeeded = InfoLength; + Status = NDIS_STATUS_BUFFER_TOO_SHORT; + } + else + { + NdisMoveMemory(InformationBuffer, InfoPtr, InfoLength); + *BytesWritten = InfoLength; + *BytesNeeded = 0; + } + } + else + { + *BytesWritten = 0; + *BytesNeeded = 0; + } + + return Status; +} + +NDIS_STATUS +NTAPI +DcSetInformation( + _In_ NDIS_HANDLE MiniportAdapterContext, + _In_ NDIS_OID Oid, + _In_ PVOID InformationBuffer, + _In_ ULONG InformationBufferLength, + _Out_ PULONG BytesRead, + _Out_ PULONG BytesNeeded) +{ + PDC21X4_ADAPTER Adapter = (PDC21X4_ADAPTER)MiniportAdapterContext; + NDIS_STATUS Status = NDIS_STATUS_SUCCESS; + ULONG GenericUlong; + + *BytesRead = 0; + *BytesNeeded = 0; + + switch (Oid) + { + case OID_GEN_CURRENT_PACKET_FILTER: + { + if (InformationBufferLength < sizeof(ULONG)) + { + *BytesNeeded = sizeof(ULONG); + Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + + *BytesRead = sizeof(ULONG); + NdisMoveMemory(&GenericUlong, InformationBuffer, sizeof(ULONG)); + + if (GenericUlong & ~DC_PACKET_FILTERS) + { + Status = NDIS_STATUS_NOT_SUPPORTED; + break; + } + + Status = DcApplyPacketFilter(Adapter, GenericUlong); + break; + } + + case OID_802_3_MULTICAST_LIST: + { + ULONG Size; + + if (InformationBufferLength % ETH_LENGTH_OF_ADDRESS) + { + *BytesNeeded = (InformationBufferLength / ETH_LENGTH_OF_ADDRESS) * + ETH_LENGTH_OF_ADDRESS; + Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + + Size = Adapter->MulticastMaxEntries * ETH_LENGTH_OF_ADDRESS; + if (InformationBufferLength > Size) + { + *BytesNeeded = Size; + Status = NDIS_STATUS_MULTICAST_FULL; + break; + } + + *BytesRead = InformationBufferLength; + NdisMoveMemory(Adapter->MulticastList, InformationBuffer, InformationBufferLength); + + Adapter->MulticastCount = InformationBufferLength / ETH_LENGTH_OF_ADDRESS; + + Status = DcUpdateMulticastList(Adapter); + break; + } + + case OID_GEN_CURRENT_LOOKAHEAD: + { + if (InformationBufferLength < sizeof(ULONG)) + { + *BytesNeeded = sizeof(ULONG); + Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + + /* Nothing to do */ + *BytesRead = sizeof(ULONG); + break; + } + + case OID_PNP_ENABLE_WAKE_UP: + { + if (InformationBufferLength < sizeof(ULONG)) + { + *BytesNeeded = sizeof(ULONG); + Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + + if (!(Adapter->Features & DC_HAS_POWER_MANAGEMENT)) + { + return NDIS_STATUS_NOT_SUPPORTED; + } + + *BytesRead = sizeof(ULONG); + NdisMoveMemory(&GenericUlong, InformationBuffer, sizeof(ULONG)); + + Adapter->WakeUpFlags = GenericUlong; + break; + } + + case OID_PNP_ADD_WAKE_UP_PATTERN: + { + if (InformationBufferLength < sizeof(NDIS_PM_PACKET_PATTERN)) + { + *BytesNeeded = sizeof(NDIS_PM_PACKET_PATTERN); + Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + + if (!(Adapter->Features & DC_HAS_POWER_MANAGEMENT)) + { + return NDIS_STATUS_NOT_SUPPORTED; + } + + *BytesRead = sizeof(NDIS_PM_PACKET_PATTERN); + + Status = DcAddWakeUpPattern(Adapter, InformationBuffer); + break; + } + + case OID_PNP_REMOVE_WAKE_UP_PATTERN: + { + if (InformationBufferLength < sizeof(NDIS_PM_PACKET_PATTERN)) + { + *BytesNeeded = sizeof(NDIS_PM_PACKET_PATTERN); + Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + + if (!(Adapter->Features & DC_HAS_POWER_MANAGEMENT)) + { + return NDIS_STATUS_NOT_SUPPORTED; + } + + *BytesRead = sizeof(NDIS_PM_PACKET_PATTERN); + + Status = DcRemoveWakeUpPattern(Adapter, InformationBuffer); + break; + } + + case OID_PNP_SET_POWER: + { + if (InformationBufferLength < sizeof(NDIS_DEVICE_POWER_STATE)) + { + *BytesNeeded = sizeof(NDIS_DEVICE_POWER_STATE); + Status = NDIS_STATUS_INVALID_LENGTH; + break; + } + + if (!(Adapter->Features & DC_HAS_POWER_MANAGEMENT)) + { + return NDIS_STATUS_NOT_SUPPORTED; + } + + *BytesRead = sizeof(ULONG); + NdisMoveMemory(&GenericUlong, InformationBuffer, sizeof(ULONG)); + + if (GenericUlong < NdisDeviceStateD0 || GenericUlong > NdisDeviceStateD3) + { + ASSERT(FALSE); + Status = NDIS_STATUS_INVALID_DATA; + break; + } + + DcSetPower(Adapter, GenericUlong); + break; + } + + default: + Status = NDIS_STATUS_NOT_SUPPORTED; + break; + } + + return Status; +} diff --git a/drivers/network/dd/dc21x4/send.c b/drivers/network/dd/dc21x4/send.c new file mode 100644 index 00000000000..acfca6abf2f --- /dev/null +++ b/drivers/network/dd/dc21x4/send.c @@ -0,0 +1,313 @@ +/* + * PROJECT: ReactOS DC21x4 Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Packet sending + * COPYRIGHT: Copyright 2023 Dmitry Borisov + */ + +/* INCLUDES *******************************************************************/ + +#include "dc21x4.h" + +#include + +/* FUNCTIONS ******************************************************************/ + +static +VOID +DcTransmitPacket( + _In_ PDC21X4_ADAPTER Adapter, + _In_ PDC_TCB Tcb, + _In_ PSCATTER_GATHER_LIST SgList) +{ + PDC_TBD Tbd, NextTbd, FirstTbd, LastTbd; + ULONG i, TbdStatus; + + TbdStatus = 0; + Tbd = Adapter->CurrentTbd; + + for (i = 0; i < SgList->NumberOfElements; ++i) + { + ULONG Address, Length; + + LastTbd = Tbd; + + /* Not owned by NIC */ + ASSERT(!(((i % 2) == 0) && (Tbd->Status & DC_TBD_STATUS_OWNED))); + + Tbd->Status = TbdStatus; + + /* 32-bit DMA */ + ASSERT(SgList->Elements[i].Address.HighPart == 0); + + Address = SgList->Elements[i].Address.LowPart; + Length = SgList->Elements[i].Length; + + /* Two data buffers per descriptor */ + if ((i % 2) == 0) + { + Tbd->Control &= DC_TBD_CONTROL_END_OF_RING; + + Tbd->Control |= Length; + Tbd->Address1 = Address; + + NextTbd = DC_NEXT_TBD(Adapter, Tbd); + } + else + { + Tbd->Control |= Length << DC_TBD_CONTROL_LENGTH_2_SHIFT; + Tbd->Address2 = Address; + + Tbd = NextTbd; + TbdStatus = DC_TBD_STATUS_OWNED; + } + } + + /* Enable IRQ on last element */ + LastTbd->Control |= DC_TBD_CONTROL_LAST_FRAGMENT | DC_TBD_CONTROL_REQUEST_INTERRUPT; + + Tcb->Tbd = LastTbd; + + FirstTbd = Adapter->CurrentTbd; + Adapter->CurrentTbd = NextTbd; + + /* Not owned by NIC */ + ASSERT(!(FirstTbd->Status & DC_TBD_STATUS_OWNED)); + + FirstTbd->Control |= DC_TBD_CONTROL_FIRST_FRAGMENT; + DC_WRITE_BARRIER(); + FirstTbd->Status = DC_TBD_STATUS_OWNED; +} + +static +BOOLEAN +DcCopyPacket( + _In_ PDC21X4_ADAPTER Adapter, + _In_ PNDIS_PACKET Packet, + _In_ PDC_COALESCE_BUFFER Buffer) +{ + PNDIS_BUFFER CurrentBuffer; + PVOID Address; + UINT CurrentLength, PacketLength; + PUCHAR Destination; + + NdisGetFirstBufferFromPacketSafe(Packet, + &CurrentBuffer, + &Address, + &CurrentLength, + &PacketLength, + HighPagePriority); + if (!Address) + return FALSE; + + Destination = Buffer->VirtualAddress; + + while (TRUE) + { + NdisMoveMemory(Destination, Address, CurrentLength); + Destination += CurrentLength; + + NdisGetNextBuffer(CurrentBuffer, &CurrentBuffer); + if (!CurrentBuffer) + break; + + NdisQueryBufferSafe(CurrentBuffer, + &Address, + &CurrentLength, + HighPagePriority); + if (!Address) + return FALSE; + } + + return TRUE; +} + +static +NDIS_STATUS +DcSendPacket( + _In_ NDIS_HANDLE MiniportAdapterContext, + _In_ PNDIS_PACKET Packet) +{ + PDC21X4_ADAPTER Adapter = (PDC21X4_ADAPTER)MiniportAdapterContext; + PSCATTER_GATHER_LIST SgList; + PDC_TCB Tcb; + ULONG SlotsUsed; + + if (!Adapter->TcbSlots) + return NDIS_STATUS_RESOURCES; + + SgList = NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, ScatterGatherListPacketInfo); + + if (SgList->NumberOfElements > DC_FRAGMENTATION_THRESHOLD) + { + PDC_COALESCE_BUFFER CoalesceBuffer; + UINT PacketLength; + + if (!Adapter->TbdSlots || !Adapter->SendBufferList.Next) + { + return NDIS_STATUS_RESOURCES; + } + + NdisQueryPacketLength(Packet, &PacketLength); + + CoalesceBuffer = (PDC_COALESCE_BUFFER)PopEntryList(&Adapter->SendBufferList); + + if (!DcCopyPacket(Adapter, Packet, CoalesceBuffer)) + { + PushEntryList(&Adapter->SendBufferList, &CoalesceBuffer->ListEntry); + return NDIS_STATUS_RESOURCES; + } + + SgList = &Adapter->LocalSgList; + SgList->Elements[0].Address.LowPart = CoalesceBuffer->PhysicalAddress; + SgList->Elements[0].Length = PacketLength; + SgList->NumberOfElements = 1; + SlotsUsed = 1; + + Tcb = Adapter->CurrentTcb; + Tcb->SlotsUsed = 1; + Tcb->Buffer = CoalesceBuffer; + } + else + { + /* We use two data buffers per descriptor */ + SlotsUsed = (SgList->NumberOfElements + 1) / 2; + + if (SlotsUsed > Adapter->TbdSlots) + return NDIS_STATUS_RESOURCES; + + Tcb = Adapter->CurrentTcb; + Tcb->SlotsUsed = SlotsUsed; + Tcb->Buffer = NULL; + } + + --Adapter->TcbSlots; + + Adapter->CurrentTcb = DC_NEXT_TCB(Adapter, Tcb); + + Tcb->Packet = Packet; + + ASSERT(Adapter->TbdSlots >= Tcb->SlotsUsed); + Adapter->TbdSlots -= SlotsUsed; + + DcTransmitPacket(Adapter, Tcb, SgList); + + DC_WRITE(Adapter, DcCsr1_TxPoll, DC_TX_POLL_DOORBELL); + + return NDIS_STATUS_PENDING; +} + +VOID +DcProcessPendingPackets( + _In_ PDC21X4_ADAPTER Adapter) +{ + PLIST_ENTRY Entry; + NDIS_STATUS Status; + PNDIS_PACKET Packet; + + ASSERT(!IsListEmpty(&Adapter->SendQueueList)); + + do + { + Entry = RemoveHeadList(&Adapter->SendQueueList); + + Packet = DC_PACKET_FROM_LIST_ENTRY(Entry); + + Status = DcSendPacket(Adapter, Packet); + if (Status == NDIS_STATUS_RESOURCES) + { + InsertHeadList(&Adapter->SendQueueList, DC_LIST_ENTRY_FROM_PACKET(Packet)); + break; + } + } + while (!IsListEmpty(&Adapter->SendQueueList)); +} + +VOID +NTAPI +DcSendPackets( + _In_ NDIS_HANDLE MiniportAdapterContext, + _In_ PPNDIS_PACKET PacketArray, + _In_ UINT NumberOfPackets) +{ + PDC21X4_ADAPTER Adapter = (PDC21X4_ADAPTER)MiniportAdapterContext; + NDIS_STATUS Status; + ULONG i; + + NdisAcquireSpinLock(&Adapter->SendLock); + + if (!(Adapter->Flags & DC_ACTIVE)) + { + NdisReleaseSpinLock(&Adapter->SendLock); + + for (i = 0; i < NumberOfPackets; ++i) + { + NdisMSendComplete(Adapter->AdapterHandle, + PacketArray[i], + NDIS_STATUS_NOT_ACCEPTED); + } + + return; + } + + TRACE("Send packets %u\n", NumberOfPackets); + + for (i = 0; i < NumberOfPackets; ++i) + { + PNDIS_PACKET Packet = PacketArray[i]; + + Status = DcSendPacket(Adapter, Packet); + if (Status == NDIS_STATUS_RESOURCES) + { + InsertTailList(&Adapter->SendQueueList, DC_LIST_ENTRY_FROM_PACKET(Packet)); + } + } + + NdisReleaseSpinLock(&Adapter->SendLock); +} + +VOID +NTAPI +DcCancelSendPackets( + _In_ NDIS_HANDLE MiniportAdapterContext, + _In_ PVOID CancelId) +{ + PDC21X4_ADAPTER Adapter = (PDC21X4_ADAPTER)MiniportAdapterContext; + LIST_ENTRY DoneList; + PLIST_ENTRY Entry, NextEntry; + + TRACE("Called\n"); + + InitializeListHead(&DoneList); + + NdisAcquireSpinLock(&Adapter->SendLock); + + NextEntry = Adapter->SendQueueList.Flink; + while (NextEntry != &Adapter->SendQueueList) + { + PNDIS_PACKET Packet; + + Entry = NextEntry; + NextEntry = NextEntry->Flink; + + Packet = DC_PACKET_FROM_LIST_ENTRY(Entry); + + if (NDIS_GET_PACKET_CANCEL_ID(Packet) == CancelId) + { + RemoveEntryList(DC_LIST_ENTRY_FROM_PACKET(Packet)); + + InsertTailList(&DoneList, DC_LIST_ENTRY_FROM_PACKET(Packet)); + } + } + + NdisReleaseSpinLock(&Adapter->SendLock); + + while (!IsListEmpty(&DoneList)) + { + Entry = RemoveHeadList(&DoneList); + + NdisMSendComplete(Adapter->AdapterHandle, + DC_PACKET_FROM_LIST_ENTRY(Entry), + NDIS_STATUS_REQUEST_ABORTED); + } +} diff --git a/drivers/network/dd/dc21x4/sendrcv.h b/drivers/network/dd/dc21x4/sendrcv.h new file mode 100644 index 00000000000..78135165873 --- /dev/null +++ b/drivers/network/dd/dc21x4/sendrcv.h @@ -0,0 +1,115 @@ +/* + * PROJECT: ReactOS DC21x4 Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Send and receive definitions + * COPYRIGHT: Copyright 2023 Dmitry Borisov + */ + +#pragma once + +#define DC_LIST_ENTRY_FROM_PACKET(Packet) \ + ((PLIST_ENTRY)(&(Packet)->MiniportReservedEx[0])) + +#define DC_PACKET_FROM_LIST_ENTRY(ListEntry) \ + (CONTAINING_RECORD(ListEntry, NDIS_PACKET, MiniportReservedEx)) + +#define DC_RCB_FROM_PACKET(Packet) \ + ((PDC_RCB*)&(Packet)->MiniportReservedEx[0]) + +#define DC_RBD_FROM_PACKET(Packet) \ + ((PDC_RBD*)&(Packet)->MiniportReservedEx[sizeof(PVOID)]) + +typedef struct _DC_COALESCE_BUFFER +{ + /* Must be the first entry */ + SINGLE_LIST_ENTRY ListEntry; + + PVOID VirtualAddress; + ULONG PhysicalAddress; +} DC_COALESCE_BUFFER, *PDC_COALESCE_BUFFER; + +typedef struct _DC_TCB +{ + PDC_TBD Tbd; + PNDIS_PACKET Packet; + PDC_COALESCE_BUFFER Buffer; + ULONG SlotsUsed; +} DC_TCB, *PDC_TCB; + +typedef struct _DC_RCB +{ + /* Must be the first entry */ + SINGLE_LIST_ENTRY ListEntry; + + ULONG PhysicalAddress; + ULONG Flags; +#define DC_RCB_FLAG_RECLAIM 0x80000000 + + PNDIS_PACKET Packet; + PNDIS_BUFFER NdisBuffer; + PVOID VirtualAddress; + PVOID VirtualAddressOriginal; + NDIS_PHYSICAL_ADDRESS PhysicalAddressOriginal; + SINGLE_LIST_ENTRY AllocListEntry; +} DC_RCB, *PDC_RCB; + +FORCEINLINE +VOID +DC_RELEASE_TCB( + _In_ PDC21X4_ADAPTER Adapter, + _In_ PDC_TCB Tcb) +{ + if (Tcb->Buffer) + { + PushEntryList(&Adapter->SendBufferList, &Tcb->Buffer->ListEntry); + } + + ++Adapter->TcbSlots; + + Adapter->TbdSlots += Tcb->SlotsUsed; +} + +FORCEINLINE +PDC_TCB +DC_NEXT_TCB( + _In_ PDC21X4_ADAPTER Adapter, + _In_ PDC_TCB Tcb) +{ + if (Tcb++ == Adapter->TailTcb) + return Adapter->HeadTcb; + else + return Tcb; +} + +FORCEINLINE +PDC_TBD +DC_NEXT_TBD( + _In_ PDC21X4_ADAPTER Adapter, + _In_ PDC_TBD Tbd) +{ + if (Tbd++ == Adapter->TailTbd) + return Adapter->HeadTbd; + else + return Tbd; +} + +FORCEINLINE +PDC_RBD +DC_NEXT_RBD( + _In_ PDC21X4_ADAPTER Adapter, + _In_ PDC_RBD Rbd) +{ + if (Rbd++ == Adapter->TailRbd) + return Adapter->HeadRbd; + else + return Rbd; +} + +FORCEINLINE +PDC_RCB* +DC_GET_RCB_SLOT( + _In_ PDC21X4_ADAPTER Adapter, + _In_ PDC_RBD Rbd) +{ + return Adapter->RcbArray + (((ULONG_PTR)(Rbd - Adapter->HeadRbd))); +} diff --git a/drivers/network/dd/dc21x4/util.h b/drivers/network/dd/dc21x4/util.h new file mode 100644 index 00000000000..b7f947e14c7 --- /dev/null +++ b/drivers/network/dd/dc21x4/util.h @@ -0,0 +1,84 @@ +/* + * PROJECT: ReactOS DC21x4 Driver + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Utility header file + * COPYRIGHT: Copyright 2023 Dmitry Borisov + */ + +#pragma once + +#include +typedef struct _ETH_HEADER +{ + UCHAR Destination[ETH_LENGTH_OF_ADDRESS]; + UCHAR Source[ETH_LENGTH_OF_ADDRESS]; + USHORT PayloadType; +} ETH_HEADER, *PETH_HEADER; +#include + +#define ETH_IS_LOCALLY_ADMINISTERED(Address) \ + ((BOOLEAN)(((PUCHAR)(Address))[0] & ((UCHAR)0x02))) + +#define ETH_IS_EMPTY(Address) \ + ((BOOLEAN)((((PUCHAR)(Address))[0] | ((PUCHAR)(Address))[1] | ((PUCHAR)(Address))[2] | \ + ((PUCHAR)(Address))[3] | ((PUCHAR)(Address))[4] | ((PUCHAR)(Address))[5]) == 0)) + +#if defined(_M_IX86) || defined(_M_AMD64) +/* Strict memory model, does not reorder Write-Write operations */ +#define DC_WRITE_BARRIER() KeMemoryBarrierWithoutFence() +#else +#define DC_WRITE_BARRIER() KeMemoryBarrier() +#endif + +#if defined(_MSC_VER) +/* + * Merge with PAGE, we don't need a new pageable section. For a small amount of data, + * there is additional size overhead if the actual data size is smaller than section alignment. + * GCC doesn't seem to appreciate this idea. + */ +#define DC_PG_DATA DATA_SEG("PAGE") +#else +#define DC_PG_DATA +#endif + +/* Access to unaligned memory */ +FORCEINLINE +USHORT +DcRetrieveWord( + _In_ const VOID* Data) +{ +#if defined(_M_IX86) || defined(_M_AMD64) + /* Supported by ISA */ + return *(const UNALIGNED USHORT*)Data; +#else + USHORT Result; + + NdisMoveMemory(&Result, Data, sizeof(Result)); + return Result; +#endif +} + +#if DBG +#define DcPopEntryList PopEntryList +#else +/* + * This is an optimized version of the PopEntryList() function. + * We assume that the next entry has already been checked for nullability + * so we don't need to. + */ +FORCEINLINE +PSINGLE_LIST_ENTRY +DcPopEntryList( + _Inout_ PSINGLE_LIST_ENTRY ListHead) +{ + PSINGLE_LIST_ENTRY FirstEntry; + + FirstEntry = ListHead->Next; + + ASSERT(FirstEntry); + + ListHead->Next = FirstEntry->Next; + + return FirstEntry; +} +#endif