Skip to main content

eth_mdio_phy/
types.rs

1// SPDX-License-Identifier: GPL-2.0-or-later OR Apache-2.0
2// Copyright (c) Viacheslav Bocharov <v@baodeep.com> and JetHome (r)
3
4//! Shared types for Ethernet PHY drivers.
5//!
6//! The names ([`Speed`], [`Duplex`], [`LinkState`]) and variant
7//! identifiers (`Speed::_10M`, `Speed::_100M`) intentionally mirror
8//! the upstream `esp_hal::ethernet::mac` shape so the bridge between
9//! a [`PhyDriver`](crate::PhyDriver) implementation and an
10//! `esp_hal::ethernet::phy::Phy` adapter is a near-identity
11//! transform — no field-by-field rebuild on the hot path.
12
13/// Ethernet link speed.
14///
15/// Marked `#[non_exhaustive]` so future variants (e.g. `_1000M` for
16/// gigabit-capable PHYs) can land as a non-breaking minor release —
17/// downstream `match` expressions are required to keep a wildcard arm.
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19#[cfg_attr(feature = "defmt", derive(defmt::Format))]
20#[non_exhaustive]
21pub enum Speed {
22    /// 10 Mbit/s
23    _10M,
24    /// 100 Mbit/s
25    _100M,
26}
27
28/// Ethernet duplex mode.
29///
30/// Marked `#[non_exhaustive]` for symmetry with [`Speed`] — downstream
31/// `match` arms must include a wildcard so a future variant can land
32/// in a minor release.
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34#[cfg_attr(feature = "defmt", derive(defmt::Format))]
35#[non_exhaustive]
36pub enum Duplex {
37    /// Half duplex
38    Half,
39    /// Full duplex
40    Full,
41}
42
43/// Negotiated link state reported by the PHY.
44///
45/// Shape matches `esp_hal::ethernet::mac::LinkState`; the `up` flag
46/// signals whether a carrier is present. When `up == false` the
47/// `speed` and `duplex` fields are unspecified — they may hold the
48/// last known good values or any default the driver chose. Callers
49/// must check `up` before acting on the negotiated parameters.
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51#[cfg_attr(feature = "defmt", derive(defmt::Format))]
52pub struct LinkState {
53    /// Whether the link is currently established (carrier present).
54    pub up: bool,
55    /// Negotiated link speed. Valid only when `up == true`.
56    pub speed: Speed,
57    /// Negotiated duplex mode. Valid only when `up == true`.
58    pub duplex: Duplex,
59}
60
61impl LinkState {
62    /// Construct a `LinkState` reporting the link as down. The
63    /// `speed` / `duplex` fields are set to a deterministic default
64    /// (`Speed::_100M`, `Duplex::Full`) so the value compares equal
65    /// across calls; callers must not act on those fields while
66    /// `up == false`.
67    pub const fn down() -> Self {
68        Self {
69            up: false,
70            speed: Speed::_100M,
71            duplex: Duplex::Full,
72        }
73    }
74
75    /// Construct an "up" `LinkState` with the given parameters.
76    pub const fn up(speed: Speed, duplex: Duplex) -> Self {
77        Self {
78            up: true,
79            speed,
80            duplex,
81        }
82    }
83}
84
85/// PHY hardware capabilities.
86#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
87#[cfg_attr(feature = "defmt", derive(defmt::Format))]
88pub struct PhyCapabilities {
89    /// 100BASE-TX full duplex
90    pub speed_100_fd: bool,
91    /// 100BASE-TX half duplex
92    pub speed_100_hd: bool,
93    /// 10BASE-T full duplex
94    pub speed_10_fd: bool,
95    /// 10BASE-T half duplex
96    pub speed_10_hd: bool,
97    /// Auto-negotiation supported
98    pub auto_negotiation: bool,
99    /// PAUSE flow control supported
100    pub pause: bool,
101}
102
103#[cfg(test)]
104mod tests {
105    extern crate alloc;
106
107    use super::*;
108    use alloc::format;
109
110    #[test]
111    fn link_state_up_constructor() {
112        let ls = LinkState::up(Speed::_100M, Duplex::Full);
113        assert!(ls.up);
114        assert_eq!(ls.speed, Speed::_100M);
115        assert_eq!(ls.duplex, Duplex::Full);
116    }
117
118    #[test]
119    fn link_state_down_constructor() {
120        let ls = LinkState::down();
121        assert!(!ls.up);
122    }
123
124    #[test]
125    fn link_state_equality() {
126        let a = LinkState::up(Speed::_10M, Duplex::Half);
127        let b = LinkState::up(Speed::_10M, Duplex::Half);
128        let c = LinkState::up(Speed::_100M, Duplex::Full);
129        assert_eq!(a, b);
130        assert_ne!(a, c);
131        // `down()` returns a deterministic value
132        assert_eq!(LinkState::down(), LinkState::down());
133    }
134
135    #[test]
136    fn link_state_all_up_combinations() {
137        let combos = [
138            LinkState::up(Speed::_10M, Duplex::Half),
139            LinkState::up(Speed::_10M, Duplex::Full),
140            LinkState::up(Speed::_100M, Duplex::Half),
141            LinkState::up(Speed::_100M, Duplex::Full),
142        ];
143        for i in 0..combos.len() {
144            for j in (i + 1)..combos.len() {
145                assert_ne!(combos[i], combos[j], "combo {i} == combo {j}");
146            }
147        }
148    }
149
150    #[test]
151    fn phy_capabilities_default_all_false() {
152        let caps = PhyCapabilities::default();
153        assert!(!caps.speed_100_fd);
154        assert!(!caps.speed_100_hd);
155        assert!(!caps.speed_10_fd);
156        assert!(!caps.speed_10_hd);
157        assert!(!caps.auto_negotiation);
158        assert!(!caps.pause);
159    }
160
161    #[test]
162    fn phy_capabilities_equality() {
163        let a = PhyCapabilities {
164            speed_100_fd: true,
165            auto_negotiation: true,
166            ..Default::default()
167        };
168        let b = PhyCapabilities {
169            speed_100_fd: true,
170            auto_negotiation: true,
171            ..Default::default()
172        };
173        let c = PhyCapabilities::default();
174        assert_eq!(a, b);
175        assert_ne!(a, c);
176    }
177
178    #[test]
179    fn speed_clone_copy() {
180        let s = Speed::_100M;
181        let s2 = s;
182        let s3 = Clone::clone(&s);
183        assert_eq!(s, s2);
184        assert_eq!(s, s3);
185    }
186
187    #[test]
188    fn duplex_clone_copy() {
189        let d = Duplex::Full;
190        let d2 = d;
191        let d3 = Clone::clone(&d);
192        assert_eq!(d, d2);
193        assert_eq!(d, d3);
194    }
195
196    #[test]
197    fn link_state_debug() {
198        let ls = LinkState::up(Speed::_100M, Duplex::Full);
199        let dbg = format!("{:?}", ls);
200        assert!(dbg.contains("_100M"), "debug missing Speed: {dbg}");
201        assert!(dbg.contains("Full"), "debug missing Duplex: {dbg}");
202        assert!(dbg.contains("up: true"), "debug missing up flag: {dbg}");
203    }
204}