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

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

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

510 lines
13 KiB
C

/*
* 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 <di.sean@protonmail.com>
*/
/* INCLUDES *******************************************************************/
#include "dc21x4.h"
#include <debug.h>
/* 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);
}