Files
Patricio Whittingslow 2e5cbd6f76 improvements to phy package
2026-01-25 22:30:26 -03:00

291 lines
8.7 KiB
Go

package phy
// See https://github.com/PieVo/mdio-tool/blob/master/mii.h
// Registers 0..15 as defined by 802.3.
const (
// First two registers are BMCR and BMSR. See below.
regPhyId1 = 0x02
regPhyId2 = 0x03
regAutoNegotiationAdvertisement = 0x04
regAutoNegotiationLinkPartnerAbility = 0x05
regAutoNegotiationExpansion = 0x06
regModeControlStatus = 0x11
regSpecialModes = 0x12
regSymbolErorCounter = 0x1a
regSpecialControlStatusIndications = 0x1b
regIRQSourceFlag = 0x1d
regIRQMask = 0x1e
regPhySpecialScontrolStatus = 0x1f
)
// BMCR represents the Basic Mode Control Register at address 0x00.
// Reference: IEEE 802.3 Clause 22.2.4.1
type BMCR uint16
const (
AddrBMCR = 0x00 // Address of Basic Mode Control Register.
BMCRSpeed1000 BMCR = 0x0040 // MSB of Speed (1000Mbps)
BMCRCollision BMCR = 0x0080 // Collision test
BMCRFullDuplex BMCR = 0x0100 // Full duplex mode
BMCRANRestart BMCR = 0x0200 // Restart auto-negotiation
BMCRIsolate BMCR = 0x0400 // Isolate PHY from MII
BMCRPowerDown BMCR = 0x0800 // Power down PHY
BMCRANEnable BMCR = 0x1000 // Enable auto-negotiation
BMCRSpeed100 BMCR = 0x2000 // Select 100Mbps
BMCRLoopback BMCR = 0x4000 // Enable TXD loopback
BMCRReset BMCR = 0x8000 // Software reset (self-clearing)
)
// BMSR represents the Basic Mode Status Register at address 0x01.
// Reference: IEEE 802.3 Clause 22.2.4.2
type BMSR uint16
const (
AddrBMSR = 0x01 // Address of Basic Mode Status Register.
BMSRExtCap BMSR = 0x0001 // Extended register capability
BMSRJabber BMSR = 0x0002 // Jabber detected
BMSRLinkStatus BMSR = 0x0004 // Link status (1=up)
BMSRANCap BMSR = 0x0008 // Auto-negotiation capable
BMSRRemoteFault BMSR = 0x0010 // Remote fault detected
BMSRANComplete BMSR = 0x0020 // Auto-negotiation complete
BMSRNoPreamble BMSR = 0x0040 // Preamble suppression capable
BMSRExtStatus BMSR = 0x0100 // Extended status in register 15
BMSR100Half2 BMSR = 0x0200 // 100BASE-T2 half-duplex capable
BMSR100Full2 BMSR = 0x0400 // 100BASE-T2 full-duplex capable
BMSR10Half BMSR = 0x0800 // 10Mbps half-duplex capable
BMSR10Full BMSR = 0x1000 // 10Mbps full-duplex capable
BMSR100Half BMSR = 0x2000 // 100Mbps half-duplex capable
BMSR100Full BMSR = 0x4000 // 100Mbps full-duplex capable
BMSR100Base4 BMSR = 0x8000 // 100BASE-T4 capable
)
// JabberDetected returns true if jabber condition was detected (bit 1).
// Jabber occurs when transmission exceeds the maximum allowed frame time.
// This bit is latched-high and cleared on read. Only meaningful for 10BASE-T.
func (b BMSR) JabberDetected() bool {
return b&BMSRJabber != 0
}
// RemoteFault returns true if the link partner has detected a fault (bit 4).
// This bit is latched-high and cleared on read.
func (b BMSR) RemoteFault() bool {
return b&BMSRRemoteFault != 0
}
// LinkUp returns true if link is established (bit 2).
func (b BMSR) LinkUp() bool {
return b&BMSRLinkStatus != 0
}
// AutoNegotiationComplete returns true if auto-negotiation has completed (bit 5).
func (b BMSR) AutoNegotiationComplete() bool {
return b&BMSRANComplete != 0
}
// ANAR represents the Auto-Negotiation Advertisement Register value at address 0x04.
// ANLPAR (Link Partner Ability Register at 0x05) shares the same bit layout.
// Reference: IEEE 802.3 Clause 28.2.4.1
type ANAR uint16
const (
AddrANAR = 0x04 // Address of Auto-Negotiation Advertisement Register.
AddrANLPAR = 0x05 // Address of Auto-Negotiation Link Partner Advertisement Register.
AddrANER = 0x06 // Address of Auto-Negotiation Error Register.
ANARSelector ANAR = 0x001f // Protocol selector mask
ANARSelector8023 ANAR = 0x0001 // IEEE 802.3 selector value (required)
ANAR10Half ANAR = 0x0020 // 10BASE-T half-duplex
ANAR10Full ANAR = 0x0040 // 10BASE-T full-duplex
ANAR100Half ANAR = 0x0080 // 100BASE-TX half-duplex
ANAR100Full ANAR = 0x0100 // 100BASE-TX full-duplex
ANAR100BaseT4 ANAR = 0x0200 // 100BASE-T4
ANARPause ANAR = 0x0400 // Pause capability
ANARPauseAsym ANAR = 0x0800 // Asymmetric pause
ANARRemoteFault ANAR = 0x2000 // Remote fault
ANARAck ANAR = 0x4000 // Acknowledge (ANLPAR only)
ANARNextPage ANAR = 0x8000 // Next page capable
// Convenience masks
ANARSpeedMask ANAR = ANAR10Half | ANAR10Full | ANAR100Half | ANAR100Full | ANAR100BaseT4
ANARPauseMask ANAR = ANARPause | ANARPauseAsym
)
// WithPause returns ANAR with pause bits set according to parameters.
//
// Flow control allows a receiver to signal the sender to pause transmission.
// Common combinations:
// - (true, false): Symmetric pause - both ends can pause each other
// - (true, true): Full flow control with asymmetric fallback
// - (false, true): Rx-only pause - we can be paused, won't pause partner
// - (false, false): No flow control
func (a ANAR) WithPause(symmetric, asymmetric bool) ANAR {
a &^= ANARPauseMask
if symmetric {
a |= ANARPause
}
if asymmetric {
a |= ANARPauseAsym
}
return a
}
// WithMaxSpeed returns ANAR with only speeds at or below maxMbps enabled.
// Preserves non-speed bits (pause, selector, etc).
func (a ANAR) WithMaxSpeed(maxMbps int) ANAR {
a &^= ANARSpeedMask
switch {
case maxMbps >= 100:
a |= ANAR100Half | ANAR100Full
fallthrough
case maxMbps >= 10:
a |= ANAR10Half | ANAR10Full
}
return a
}
// FullDuplexOnly returns ANAR with half-duplex modes cleared.
func (a ANAR) FullDuplexOnly() ANAR {
return a &^ (ANAR10Half | ANAR100Half)
}
// HalfDuplexOnly returns ANAR with full-duplex modes cleared.
func (a ANAR) HalfDuplexOnly() ANAR {
return a &^ (ANAR10Full | ANAR100Full)
}
// NewANAR returns an ANAR with the IEEE 802.3 selector set.
// Always start with this when building an advertisement value.
func NewANAR() ANAR {
return ANARSelector8023
}
// With10M returns ANAR with 10Mbps modes (half and full) enabled.
func (a ANAR) With10M() ANAR {
return a | ANAR10Half | ANAR10Full
}
// With100M returns ANAR with 100Mbps modes (half and full) enabled.
func (a ANAR) With100M() ANAR {
return a | ANAR100Half | ANAR100Full
}
// Without10M returns ANAR with 10Mbps modes cleared.
func (a ANAR) Without10M() ANAR {
return a &^ (ANAR10Half | ANAR10Full)
}
// Without100M returns ANAR with 100Mbps modes cleared.
func (a ANAR) Without100M() ANAR {
return a &^ (ANAR100Half | ANAR100Full | ANAR100BaseT4)
}
// LinkMode returns the highest priority LinkMode from the ANAR speed bits.
// Priority order per IEEE 802.3 Annex 28B.3.
// Returns LinkDown if no speed bits are set.
func (a ANAR) LinkMode() (l LinkMode) {
switch {
case a&ANAR100Full != 0:
l = Link100FDX
case a&ANAR100BaseT4 != 0:
l = Link100T4
case a&ANAR100Half != 0:
l = Link100HDX
case a&ANAR10Full != 0:
l = Link10FDX
case a&ANAR10Half != 0:
l = Link10HDX
default:
l = LinkDown
}
return l
}
// LinkMode represents the negotiated/force-set Ethernet link speed and duplex mode.
//
// Naming convention:
// - H/HDX: Half-duplex (one direction at a time)
// - F/FDX: Full-duplex (simultaneous bidirectional)
// - T4: 100BASE-T4 (100Mbps over 4 twisted pairs, legacy)
// - G: Gigabit, implies number is multiplied by 1000 (1G=1000M)
type LinkMode uint8
const (
LinkDown LinkMode = iota // down
Link10HDX // 10M-H
Link10FDX // 10M-F
Link100HDX // 100M-H
Link100FDX // 100M-F
Link100T4 // 100M-T4
Link1000HDX // 1000M-H
Link1000FDX // 1000M-F
// Clause 45 speeds (10Gbps+, full-duplex only):
Link2500FDX // 2.5G-F
Link5GFDX // 5G-F
Link10GFDX // 10G-F
Link25GFDX // 25G-F
Link40GFDX // 40G-F
Link100GFDX // 100G-F
)
func (l LinkMode) ANAR() (a ANAR) {
a = NewANAR()
switch l {
case Link10HDX:
a |= ANAR10Half
case Link10FDX:
a |= ANAR10Full
case Link100HDX:
a |= ANAR100Half
case Link100FDX:
a |= ANAR100Full
case Link100T4:
a |= ANAR100BaseT4
}
return a
}
// SpeedMbps returns the link speed in megabits per second.
func (lm LinkMode) SpeedMbps() (Mbps int) {
switch lm {
case Link10HDX, Link10FDX:
Mbps = 10
case Link100HDX, Link100FDX, Link100T4:
Mbps = 100
case Link1000HDX, Link1000FDX:
Mbps = 1000
case Link2500FDX:
Mbps = 2500
case Link5GFDX:
Mbps = 5000
case Link10GFDX:
Mbps = 10_000
case Link25GFDX:
Mbps = 25_000
case Link40GFDX:
Mbps = 40_000
case Link100GFDX:
Mbps = 100_000
default:
Mbps = 0
}
return Mbps
}
// IsFullDuplex returns true if the link mode is full duplex.
func (lm LinkMode) IsFullDuplex() bool {
switch lm {
case Link10FDX, Link100FDX, Link1000FDX,
Link2500FDX, Link5GFDX, Link10GFDX, Link25GFDX, Link40GFDX, Link100GFDX:
return true
default:
return false
}
}