# mdio-phy-tool **Repository Path**: simamunan/mdio-phy-tool ## Basic Information - **Project Name**: mdio-phy-tool - **Description**: 通用 MDIO/PHY 寄存器访问与解析工具 (内核模块 CLI, 标准MII 厂商profile) - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-21 - **Last Updated**: 2026-09-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # mdio-phy-tool — 通用 MDIO/PHY 寄存器访问与解析工具 在 Linux 系统下对**任意网卡 MDIO(Management Data Input/Output)总线上任意 PHY** 做 **读 / 写 / dump / 寄存器解析**的通用工具。 - **内核模块** `phy_mdio_ctl`:走内核标准 MDIO API(`mdiobus_read/write`), 不依赖任何 SoC 私有寄存器,跨平台通用; - **用户态 CLI** `mdio_phy`:寄存器 读 / 写 / dump / **decode(解析)**; - **解析策略(标准优先 + 厂商 profile)**: - **标准 MII(IEEE 802.3 Clause 22/45)寄存器解析对所有 PHY 通用**, 兼容主流千兆/百兆芯片(Realtek RTL8211、IP101、YT8512、澜博 LY1211 等); - **厂商 profile**:由 phy_id 的 OUI 自动识别厂商,对私有寄存器(0x0B~0x3F) 按该厂商数据手册解析,协商结果汇总与其官方驱动 / ethtool 对齐。 当前内置一个 profile:澜博 Linkyum LY12xx(OUI 0xADB4),**可扩展**; - 未识别的 PHY 只做标准 MII 解析,私有区间显示为"厂商私有"; - `MDIO_STD=1` 可强制纯标准解析(与厂商解析交叉对照)。 > **实测平台**:飞腾 D3000M(aarch64,Linux 6.6)+ 飞腾内置网卡 + 澜博 LY1211S 千兆 PHY, > 默认(厂商 profile)/ `MDIO_STD=1`(标准)/ `ethtool` 三路结果一致(1000M/Full/链路 up)。 > 工具不绑定该平台:适用于任何 MDIO 总线挂有 PHY 的 Linux 系统(x86/ARM)。 > 内核版本要求 **6.2+**(模块依赖 6.2 引入的 `phy_device.mdio` 结构,见 FAQ)。 --- ## 1. 背景:什么是 MDIO 与 PHY - **PHY**:以太网的物理层芯片,负责电信号收发、自协商、链路检测等,挂在网口 MAC 后面。 - **MDIO**:一种简单的两线管理总线(CLK + DATA),MAC 通过它读写 PHY 的寄存器。 地址 5 位(PHY 地址 0~31),寄存器 16 位(Clause 22)。 - 内核的 **phylib + mdio core** 在启动时会探测 MDIO 总线上的 PHY 并绑定驱动; 网卡的链路状态最终由 PHY 上报。 **为什么需要"通用 MDIO 工具":** 成熟工具(ethtool / mii-tool)只能看**链路/速率/双工/自协商**等由驱动汇总的状态, **无法读写任意 PHY 寄存器**。做底层调试(看 PHY ID、读写厂商寄存器、复位、强制速率、 读错误计数等)必须能直接访问 MDIO 寄存器——这正是本工具要补的能力。 --- ## 2. 工具链分层(成熟工具优先) | 层级 | 工具 | 来源 | 能力 | 适用 | |------|------|------|------|------| | 一线诊断 | **ethtool** | 系统自带(成熟) | 链路/速率/双工/自协商/PHYAD/驱动信息 | 日常链路检查(首选) | | 一线诊断 | **mii-tool** | 系统自带(成熟) | 协商结果、链路状态一句话 | 快速看链路 | | 通用寄存器 | **mdio_phy**(本工具) | 源码编译(内核模块+CLI) | **任意** PHY 寄存器 读/写/dump/解析 | 底层调试(缺口能力) | | 可选补充 | mii-diag | mii-tools 源码(成熟) | 标准寄存器/自协商诊断(只读) | 可选,见 §9 | **原则:先看 ethtool(成熟、快、安全),不够再用 mdio_phy 深入寄存器。** --- ## 3. 安装 ### 3.1 前置条件 - root 权限 - 与运行内核匹配的**内核头文件**:`/lib/modules/$(uname -r)/build` 必须存在 (通常安装发行版对应的 `linux-headers-$(uname -r)` 包即可) - 编译工具链:`gcc`、`make` ### 3.2 一键安装 ```bash cd mdio-phy-tool ./install.sh # 编译 CLI + 内核模块,insmod 加载,装 /usr/local/bin/mdio_phy ``` 产物: - `/usr/local/bin/mdio_phy`(用户态 CLI) - `phy_mdio_ctl.ko`(内核模块,加载后生成 `/dev/phy_mdio_ctl`) ### 3.3 卸载 / 状态 ```bash ./install.sh uninstall # rmmod + 删 /usr/local/bin/mdio_phy ./install.sh status # 看模块/CLI/网卡状态 ``` ### 3.4 开机自启(可选) 模块默认不随系统自启。若要开机自动加载: ```bash # 复制到内核模块目录并登记 cp phy_mdio_ctl.ko /lib/modules/$(uname -r)/updates/ # Debian/Ubuntu: update-modules # 其它发行版 (openEuler/CentOS 等): # depmod -a $(uname -r) echo phy_mdio_ctl > /etc/modules-load.d/mdio-phy-tool.conf # systemd 通用 ``` --- ## 4. 用法(mdio_phy) ``` mdio_phy bus 列出所有带 PHY 的网卡 (if bus phyaddr) mdio_phy read 读寄存器 mdio_phy write 写寄存器 (val 支持 0x) mdio_phy dump [start [count]] 连续读寄存器区间 (默认 0~31) mdio_phy decode [start [count]] 读并解析寄存器 (标准MII+厂商profile, 推荐) ``` - `` 为网卡名;用 `-` 表示"自动选第一个带 PHY 的网卡"(`mdio_phy bus` 可查)。 - `` 为 PHY 地址(0~31)。寄存器为 16 位,值以 `0xNNNN` 显示/接受。 - 命令支持短别名:`bus`=list、`read`=r、`write`=w、`dump`=d、`decode`=dec。 ### 4.1 decode 的解析策略 1. **先读 Reg0x02/0x03**(PHY ID,不受 dump 区间限制)→ 得到 phy_id 与厂商 profile; 2. **逐寄存器解析**: - 标准寄存器(0x00~0x06、0x09、0x0A 等):**一律按 IEEE 802.3 标准位定义**解析; - 私有区间(0x0B~0x3F):命中厂商 profile 时按该厂商数据手册解析, 否则显示"厂商私有(见该芯片数据手册)"; 3. **协商结果汇总**(单独读寄存器,独立于 dump 区间): - 厂商 profile 命中 → 用其**私有状态寄存器**(与该厂商驱动/ethtool 对齐); - 未命中或 `MDIO_STD=1` → **标准 MII 解析**:1000T 看 STAT1000、 10/100 看 ANLPAR&ANAR 交集、链路看 BMSR bit2、强制模式看 BMCR 速率位 (逻辑与内核 `genphy_read_status` / U-Boot `mii` 工具一致)。 ### 4.2 输出示例 > 示例环境:飞腾 D3000M 板卡 + 澜博 LY1211S 千兆 PHY(PHY 地址 3,网卡 enaphyt56i0)。 > 其它 PHY/板卡格式相同;私有区间解析内容随 profile 不同。 ``` $ mdio_phy bus enaphyt56i0 phytmac_mii_bus-PHYT0056:00 3 $ mdio_phy decode - 3 寄存器解析: 标准 MII (IEEE 802.3) + 厂商 profile (phy_id=0xadb40412, 澜博 Linkyum) 协商汇总: 厂商 profile -> 其私有状态寄存器(对齐该厂商驱动/ethtool); 其余走标准 MII 解析 0x0000: 0x1140 BMCR: AN-enable,duplex | speed-select=1000M(仅强制模式有效) 0x0001: 0x796d BMSR: 100TX-FD,100TX-HD,10T-FD,10T-HD,ext-st,mf-preamble-suppr,AN-complete,AN-cap,LINK,ext-cap 0x0002: 0xadb4 PHY ID1 (OUI 高16位) 0x0003: 0x0412 PHY ID2 (OUI低2+型号+版本) => phy_id=0xadb40412 OUI=0x2b6d01 Model=0x001 Rev=0x2 [已知: 澜博 LY1211A/LY1211S 千兆 PHY] 0x0004: 0x09e1 ANAR(本端通告): [asym-pause,100TX-FD,100TX-HD,10T-FD,10T-HD] (1000T能力见Reg0x09) 0x0005: 0xc1e1 ANLPAR(对端能力): [100TX-FD,100TX-HD,10T-FD,10T-HD] (1000T见Reg0x0A) ... 0x0009: 0x0700 CTRL1000(1000T控制): 1000T-FD-cap=1, 1000T-HD-cap=1, test-mode=0 0x000a: 0x7800 STAT1000(1000T状态): MS-resolution,local-rx-ok,remote-rx-ok,1000T-FD 0x0011: 0x0457 LINK_ST: link=UP, speed=1000M, duplex=Full | MDIx,fifo-err,M/S(master) ... 0x0016: 0x0000 MDIO_CTL: page=0(电口/UTP), broadcast-en=0 ... ==== 协商结果汇总 (厂商 profile: 澜博 Linkyum, 数据源=Reg0x11 LINK_ST, 当前页=0) ==== Link : UP Speed : 1000M (ethtool: 1000baseT/Full) Duplex : Full MDI/X : MDIx(交叉) M/S: Master ethtool 等价: Speed: 1000baseT/Full, Link detected: yes ``` `MDIO_STD=1` 强制标准解析(同一块 PHY,私有区间显示为"厂商私有",汇总走标准 MII): ``` $ MDIO_STD=1 mdio_phy decode - 3 寄存器解析: 标准 MII (IEEE 802.3) + 厂商 profile (phy_id=0xadb40412, MDIO_STD=1 强制标准解析) ... 0x0011: 0x0457 厂商私有 (0x0B~0x3F, 见该芯片数据手册) ... ==== 协商结果汇总 (标准 MII 解析, 适用主流 10/100/1000T PHY) ==== Link : UP Speed : 1000M Duplex : Full 来源 : AN 协商 (STAT1000 + ANLPAR&ANAR) ethtool 等价: Speed: 1000Mb/s, Duplex: Full, Link detected: yes ``` > 两条路径结果一致(1000M/Full/UP),且与 `ethtool` 一致——既验证了该 PHY 的标准寄存器 > 实现符合 IEEE 规范,也验证了工具标准解析路径的正确性。 --- ## 5. 寄存器解析 ### 5.1 标准 MII 寄存器(IEEE 802.3 Clause 22/45,所有 PHY 通用) | 寄存器 | 名称 | 说明 | |--------|------|------| | **0x0000** | **BMCR** | 基础模式控制(复位/环回/自协商/速率/双工) | | **0x0001** | **BMSR** | 基础模式状态(**bit2=链路**、能力位、自协商完成) | | 0x0002 | PHY ID 1 | 厂商 OUI 高 16 位 | | 0x0003 | PHY ID 2 | OUI 低 2 位 + 型号 + 版本(与 0x02 合成 22 位 phy_id) | | 0x0004 | ANAR | 自协商通告(本端 10/100 能力;1000T 能力在 0x09) | | 0x0005 | ANLPAR | 自协商链路伙伴能力(对端) | | 0x0006 | ANER | 自协商扩展 | | 0x0007/0x0008 | 保留 | Clause 22 未定义,厂商可私有使用 | | 0x0009 | CTRL1000 | 1000BASE-T 控制(bit9=1000T-FD 能力,bit8=1000T-HD 能力) | | 0x000A | STAT1000 | 1000BASE-T 状态(bit11/10=1000T-FD/HD 协商结果,bit13/12=RX OK) | | 0x000B/0x000C | 保留 | Clause 22 未定义 | | 0x000D/0x000E | MMD 访问 | Clause 45:0x0D=设备地址/控制,0x0E=数据(部分厂商改作扩展寄存器接口,见 §5.3) | | 0x000F | 保留 | 部分厂商用作扩展状态寄存器(ESTATUS) | | 0x0010~0x3F | 厂商私有 | 无统一定义,由各厂商数据手册规定(工具按 profile 解析,见 §5.2) | **BMCR(0x0000) 关键位(IEEE 802.3 标准,各主流 PHY 通用):** - bit15 复位(写 1 触发,自清,寄存器恢复默认) - bit14 环回 - bit13 速率选择 bit0(低)(强制模式有效) - **bit12 自协商使能(1=AN 开)** - bit11 掉电 / bit10 隔离 / **bit9 重启自协商** - bit8 双工(1=全双工,强制模式有效)/ bit7 冲突测试 - bit6 速率选择 bit1(高)(强制模式有效) - 速率:`bit6:bit13` = 10→1000M, 01→100M, 00→10M(仅 bit12=0 强制时有效) > ✅ 速率位是 **IEEE 标准**(bit6=1000M、bit13=100M),与 Linux 内核 > `uapi/linux/mii.h`(`BMCR_SPEED1000=0x0040`/`BMCR_SPEED100=0x2000`)、 > U-Boot `mii` 工具一致。本工具按标准解析,**兼容市面上主流 PHY**。 **BMSR(0x0001) 关键位(IEEE 802.3 标准):** - bit15 100BASE-T4 / bit14 100TX-FD / bit13 100TX-HD(能力) - bit12 10T-FD / bit11 10T-HD / bit10 100T2-FD / bit9 100T2-HD(能力) - bit8 扩展状态 / bit6 MF 前导码抑制 / bit5 自协商完成 / bit4 远端故障 - bit3 自协商能力 / **bit2 链路状态(1=有链路)** / bit1 Jabber / bit0 扩展能力 ### 5.2 厂商寄存器 profile(扩展机制) 标准寄存器之外,各厂商可在 **0x0B~0x3F 私有区间**自定义寄存器;部分厂商的私有寄存器 还是官方驱动(及 ethtool)速率/双工/链路的数据源。本工具用 **profile** 机制处理: - **识别**:`phy_id = (Reg0x02 << 16) | Reg0x03`,取 OUI(Reg0x02)匹配 (`mdio_phy.c` 的 `phy_profile()`,当前:OUI `0xADB4` → 澜博 Linkyum); - **作用域**:只影响 ① 私有区间的逐寄存器解析 ② 协商结果汇总的数据源; **标准寄存器的定义不受影响**(永远是 IEEE 标准); - **未命中**:私有区间显示"厂商私有",汇总走标准 MII 解析——对未收录的 PHY 依然可用; - **交叉验证**:`MDIO_STD=1` 强制纯标准路径,可与厂商路径对照。 **扩展一个新厂商 profile(4 步):** 1. `mdio_phy.c` 的 `enum phy_profile` 加一个枚举值; 2. `phy_profile()` 加 OUI → 枚举 的匹配; 3. `reg_decode()` 加该厂商私有寄存器的 case(名称/位域,依据其数据手册); 4. `main()` 汇总处加分支:该厂商的"驱动数据源寄存器"解析(与其驱动保持一致)。 ### 5.3 Profile 示例:澜博 Linkyum LY12xx(OUI 0xADB4) 依据:LY1211S 数据手册 v1.4 + 官方 Linux 驱动(`linkyum.c`)。 | 寄存器 | 名称 | 说明 | |--------|------|------| | 0x000D | EMA_DAT | 扩展寄存器数据(配合 0x0E 地址指针读/写扩展寄存器;**地址/数据方向与标准 MMD 相反**) | | 0x000E | EMA_AD | 扩展寄存器地址指针 | | 0x000F | EX_ST | 扩展状态(1000X/1000T 能力) | | 0x0010 | SF_ST | 环回/EEE/ED 状态(EEE1000M=bit1,EEE100M=bit0) | | **0x0011** | **LINK_ST** | **bit4:3 速率(10=1000M, 01=100M, 00=10M)、bit2 链路、bit1 双工、bit0 M/S;bit10 MDIx;bit6 fifo-err(闩锁位);bit15:12 线对极性** ← 官方驱动数据源 | | 0x0012 | AUX_CTL | 自动 MDIx(bit15)、强制速率(bit11/10)、EEE 通告(bit1/0) | | 0x0013 | SF_CTL | 环回控制 | | 0x0014/0x0015/0x0017 | INT_ST / INT_CLR / INT_MSK | 中断状态 / 清除 / 屏蔽 | | 0x0018~0x001B | BIST_CON / BIST_CNT / BIST0_CFG / BIST1_CFG | 内建自测(自发帧) | | **0x0016** | **MDIO_CTL** | **页选择 bit8:0=电口/UTP,1=Serdes/光** | > 💡 **扩展寄存器访问方法(澜博)**:先写 0x0E(EMA_AD)为目标扩展寄存器地址, > 再对 0x0D(EMA_DAT)做读/写即可访问该扩展寄存器(两步操作,可用 > `mdio_phy write 0x0e ` + `mdio_phy read 0x0d`)。 > 例:官方驱动读端口类型寄存器(0x062B)判断电口/光口并据此选页。 > 完整扩展寄存器表见数据手册。 > ⚠️ **页机制**(combo PHY):LY1211 是电口+光口 combo PHY,Reg0x16 的 **bit8** 选页 > (写 0x0000=电口 page0 / 0x0100=Serdes page1)。**不同页的同一寄存器号指向不同子端口** > (如 0x01 BMSR、0x11 LINK_ST)。驱动按端口类型选页,铜口链路用 page0。 > 上表地址为 page0/电口布局;链路状态以 `ethtool`(驱动汇总)为准。 > 📌 **为什么 profile 汇总用 Reg0x11 而不是标准寄存器**:该厂商官方驱动 > `ly1211_read_status()` 从 **LINK_ST(Reg0x11)** 读速率/双工/链路,ethtool 展示的即此数据源; > profile 解析与其对齐。若只用标准寄存器(`MDIO_STD=1`),本例也能解出同样结果 > (见 §4.2),但对某些私有实现较强的 PHY,标准路径可能不准——两条路径对照最稳妥。 --- ## 6. 调试示例 > 以下示例以"某 D3000M 板卡 + 澜博 LY1211S(网卡 enaphyt56i0,PHY 地址 3)"实测值为参照; > 你的系统请替换 `mdio_phy bus` 给出的实际 `` / ``。 ### 6.1 识别 PHY 芯片(读 PHY ID) ```bash mdio_phy read enaphyt56i0 3 2 # -> 0xadb4 (OUI 高16位) mdio_phy read enaphyt56i0 3 3 # -> 0x0412 (OUI低2+型号+版本) # 组合 0xadb40412,与 sysfs 对照验证 MDIO 读取正确(设备名 = :): cat /sys/bus/mdio_bus/devices/phytmac_mii_bus-PHYT0056:00:03/phy_id # -> 0xadb40412(bus 名可用 mdio_phy bus 或 ls /sys/bus/mdio_bus/devices/ 查看) ``` ### 6.2 查看链路状态(首选成熟工具 ethtool) ```bash ethtool enaphyt56i0 | grep -E 'Speed|Duplex|Auto|Link|PHYAD' # Speed: 1000Mb/s / Duplex: Full / Auto-negotiation: on / Link detected: yes / PHYAD: 3 mii-tool enaphyt56i0 # enaphyt56i0: negotiated 1000baseT-FD, link ok ``` ### 6.3 读链路状态寄存器(BMSR bit2) ```bash mdio_phy read enaphyt56i0 3 1 # BMSR (Reg0x01);bit2=1 表示有链路 # 也可看 bit5(自协商完成) bit3(自协商能力) ``` ### 6.4 批量 dump 寄存器(看整体状态) ```bash mdio_phy dump enaphyt56i0 3 0 32 # 读 0~31 mdio_phy decode enaphyt56i0 3 # 读并解析(推荐) mdio_phy decode enaphyt56i0 3 16 16 # 只解析 0x10~0x1F 私有区间 ``` ### 6.5 写寄存器(PHY 软复位) ```bash # BMCR = Reg0x00;写 bit15=1 触发软复位(自清,寄存器恢复默认值) mdio_phy write enaphyt56i0 3 0 0x8000 # 触发软复位(bit15) sleep 3; ethtool enaphyt56i0 | grep -E 'Speed|Duplex|Link' # 重新协商约 1~3s ``` > ⚠️ 写寄存器会直接改 PHY 硬件状态,**务必确认知道该寄存器含义**。复位后寄存器恢复默认 > (通常自协商开启),链路会重新协商。 ### 6.6 强制速率/双工(慎用,会关自协商) ```bash # 强制 1000M 全双工:写 BMCR (Reg0x00)(标准位定义,各 PHY 通用) # 清 bit12(AN=0)、设 bit6(速率高=1)+bit13(速率低=0)→1000M、bit8(双工=1) # bit6 = 0x0040, bit8 = 0x0100 => 0x0140 v=$(mdio_phy read enaphyt56i0 3 0) # 先读原 BMCR 值,例如 0x1140 mdio_phy write enaphyt56i0 3 0 0x0140 # 1000M-FD, 关自协商 # (仅当对端也强制相同速率/双工时链路才 up) # 恢复自协商:写回原值(bit12=AN=1),需要时再置 bit9(0x0200) 重启自协商 mdio_phy write enaphyt56i0 3 0 $v # 写回原值(AN 恢复) mdio_phy write enaphyt56i0 3 0 0x1340 # 原值|bit9 触发自协商重启 (0x1140|0x0200) sleep 3; ethtool enaphyt56i0 | grep -E 'Speed|Duplex|Link' # 重新协商约 1~3s ``` > ⚠️ 仅在对端也强制相同速率/双工时链路才能 up,否则 link down。调试完务必恢复自协商。 > 强制值速查(标准 BMCR 位):10M-FD=0x0100, 100M-FD=0x2100, 1000M-FD=0x0140 > (均为 bit8=FD、bit12=AN=0)。 > > ⚠️ **通过 SSH 远程操作且 `` 是唯一管理口时**:进入强制模式链路即断, > 交互式会话里的"恢复"命令**不会执行**,会造成失联(本工具实测踩过的坑)。 > 务必把"强制 + 延时 + 恢复"整段丢到板端后台执行: > ```bash > nohup bash -c 'v=$(mdio_phy read 0); \ > mdio_phy write 0 0x0140; sleep 5; \ > mdio_phy write 0 $v; \ > mdio_phy write 0 0x1340' > /tmp/force_test.log 2>&1 & > ``` > (0x1340 = 0x1140|0x0200,按实际原值计算"原值|bit9") ### 6.7 交叉验证:厂商解析 vs 标准解析 ```bash mdio_phy decode enaphyt56i0 3 # 厂商 profile 路径(若已识别) MDIO_STD=1 mdio_phy decode enaphyt56i0 3 # 强制标准 MII 路径 ethtool enaphyt56i0 # 驱动视角 # 三者速率/双工/链路一致 => 读取与解析均正确 ``` --- ## 7. 常用方法(速查) | 目的 | 命令 | |------|------| | 看有哪些 PHY 网卡 | `mdio_phy bus` | | 识别 PHY 型号 | `mdio_phy read 2` + `... 3`(对照 phy_id) | | 看链路 | `ethtool `(首选)/ `mdio_phy read 1`(BMSR bit2) | | 看速率/双工 | `mdio_phy decode `(厂商 profile → 其私有状态寄存器;否则标准 MII 解析)/ `ethtool ` | | 厂商/标准交叉对照 | `MDIO_STD=1 mdio_phy decode ` | | dump 全部寄存器 | `mdio_phy dump 0 32` | | 软复位 PHY | 写 BMCR(0) bit15(0x8000) | | 强制速率/双工 | 写 BMCR(0):清 bit12(AN)、设 bit6/bit13(速率)+bit8(双工) | | 读厂商私有寄存器 | `mdio_phy read/dump `(按该芯片数据手册) | | 自动选网卡 | 命令里 `` 用 `-` | --- ## 8. 源码包结构与原理 ``` mdio-phy-tool/ ├── phy_mdio_ctl.c 内核模块:字符设备 /dev/phy_mdio_ctl,走内核标准 MDIO API ├── mdio_phy.c 用户态 CLI:文本协议 + 标准 MII 解析 + 厂商 profile ├── Makefile 编译 CLI + 内核模块(针对 $(uname -r)) ├── install.sh install / uninstall / status ├── README.md 本文档 ├── LICENSE MIT 许可证 └── .gitignore 构建产物忽略规则 ``` **内核模块(phy_mdio_ctl.c):** - 通过 `net_device -> phydev -> mdio.bus` 拿到 `struct mii_bus *`, 用内核标准 `mdiobus_read()/mdiobus_write()` 访问任意 PHY 寄存器。 **不依赖任何 SoC 私有寄存器**,跨平台通用。 - 用户态用简单文本协议:`write()` 发一行命令,`read()` 读到 EOF 取完整响应。 命令:`B` 列网卡 / `R` 读 / `W` 写 / `D` 区间 dump。 - 适配 **Linux 6.x MDIO 重构**:`phy_device` 内嵌 `struct mdio_device mdio`, 总线在 `mdio.bus`、地址在 `mdio.addr`;头文件为 `linux/mdio.h` (旧 `linux/mdiobus.h` 已拆分,见 FAQ)。 **CLI(mdio_phy.c)解析架构:** - `phy_profile()`:按 phy_id 的 OUI 识别厂商 profile(当前:0xADB4 → 澜博); - `reg_decode()`:标准寄存器按 IEEE 802.3 解析;私有区间按 profile 解析(未命中 → 通用标签); - `std_negotiation()`:标准 MII 协商解析(BMCR/BMSR/ANAR/ANLPAR/STAT1000), 与内核 `genphy_read_status` / U-Boot `mii` 工具逻辑一致; - 汇总:厂商 profile 命中 → 其私有状态寄存器;否则 → `std_negotiation()`; `MDIO_STD=1` 强制后者。 **为什么是内核模块而不是纯用户态:** Linux 没有成熟的"用户态任意 MDIO 寄存器"接口;MDIO 操作在内核 mdio core 里。 内核模块调用 `mdiobus_read/write` 是最标准、最通用的途径。 --- ## 9. 可选:mii-diag(成熟只读诊断工具) mii-diag 来自成熟的 **mii-tools** 项目(只读,读标准寄存器/自协商状态),可作为补充。 若系统包管理器没有 mii-tools,可从源码编译: ```bash curl -LO https://www.kernel.org/pub/software/network/mii-tools/mii-tools-1.11.tar.gz tar xzf mii-tools-1.11.tar.gz && cd mii-tools-1.11 make mii-diag # 只需 mii-diag sudo cp mii-diag /usr/local/bin/ mii-diag ``` > 说明:mii-diag 信息基本与 `ethtool` 重叠,且只读,**不是必需**。需要任意寄存器访问时 > 仍用本工具的 `mdio_phy`。 --- ## 10. FAQ / 排障 | 现象 | 原因 / 处理 | |------|-------------| | `mdio_phy: 打开 /dev/phy_mdio_ctl 失败` | 模块未加载。`insmod phy_mdio_ctl.ko`,或 `./install.sh` | | `no PHY bus on ` | 该网卡无 PHY(如 docker0/bridge)或 PHY 未探测。用 `mdio_phy bus` 看实际网卡 | | 编译报 `linux/mdiobus.h: No such file` | 内核 6.x 头文件拆分为 `linux/mdio.h`,本工具已适配。**本模块最低支持 Linux 6.2**(MDIO 重构:`phy_device` 内嵌 `struct mdio_device`)。5.x 内核需在 `resolve_bus()` 改用 `phydev->bus`/`phydev->addr` 并包含 `linux/mdiobus.h` | | 编译报 `dev_open 重名` | 内核已有 `dev_open`,本工具已改名为 `ctl_open` | | 读到 0xFFFF / 0x0000 | PHY 地址不对或 MDIO 总线异常。核对 `mdio_phy bus` 的 phyaddr | | 读 PHY ID 与 sysfs 不一致 | 检查 PHY 地址;或该 PHY 有页机制,ID 寄存器在不同页 | | 写寄存器后链路异常 | 写错了控制位。立即写回原值;不确定含义的寄存器不要写 | | 模块加载后重编译 insmod 失败 | 先 `rmmod phy_mdio_ctl` 再 `insmod` | | `sudo: unable to resolve host <主机名>` | 外观警告:主机名未写入 `/etc/hosts`,命令本身已成功。`echo "127.0.1.1 <主机名>" >> /etc/hosts` 可消除 | | 编译提示 `the compiler differs from the one used to build the kernel` | 当前 gcc 与构建内核的 gcc 小版本不同,模块可正常编译加载,忽略 | | decode 私有寄存器显示"厂商私有" | 该 PHY 未收录 profile(正常现象,标准寄存器解析不受影响);需要私有解析时按 §5.2 扩展 | | (澜博 profile 示例)LINK_ST 出现 `fifo-err` | Reg0x11 bit6 = FIFO 错误,**闩锁位**(link down 才清);链路健康时无需处理。看真实计数:`ethtool -S \| grep -iE 'err\|fifo'`;清位:`ip link set down && ip link set up` | | 标准解析 vs 厂商解析交叉对照 | `MDIO_STD=1 mdio_phy decode ` 强制走标准 MII 路径;示例板(LY1211)两路均给出 1000M/Full/UP,与 ethtool 一致 | --- ## 11. 安全与注意 1. **写 PHY 寄存器 = 直接改硬件**,可能断链路/复位/改变自协商。原则:先读原值、改完写回、 只写你确定含义的寄存器。 2. 内核模块需 root;`/dev/phy_mdio_ctl` 权限 0600。 3. 模块针对**当前内核**编译(`$(uname -r)`)。换内核后需重新 `make` + `insmod`。 4. 本工具只做 MDIO 寄存器访问,**不替代**驱动/ethtool 的正常链路管理。 --- ## 12. 实测平台与已知 PHY 库 **实测平台:** | 项目 | 内容 | |------|------| | 板卡 | 飞腾 D3000M(aarch64) | | OS | phytium-Ubuntu(Ubuntu 系) | | 内核 | 6.6.0-20251012-d3000m-v12 | | 网卡 | 飞腾内置千兆(enaphyt56i0,bus phytmac_mii_bus-PHYT0056:00) | | PHY | 澜博 LY1211S(phy_id 0xadb40412,PHY 地址 3) | | 验证 | ① 默认 profile / `MDIO_STD=1` / `ethtool` 三路一致:1000M/Full/链路 up;
② PHY ID 与 sysfs 一致;③ 软复位后链路自恢复;
④ 强制速率(nohup 后台自恢复)后链路/AN 全恢复;
⑤ 异常地址读回 0xffff、非法命令 usage 提示 | **已知 PHY 库**(`mdio_phy.c` 的 `known_phys[]`,decode 时用于提示型号,可扩展): | phy_id | 型号 | |--------|------| | 0xadb40412 | 澜博 LY1211A/LY1211S 千兆 PHY | | 0xadb40411 | 澜博 LY1241A/LY1241B 千兆 PHY | | 0xadb40400 | 澜博 LY1210A 百兆 PHY | | 0x001cc816 | Realtek RTL8211E | | 0x001cc916 | Realtek RTL8211F | | 0x001cc810 | Realtek RTL8201F | > 扩展识别:在 `known_phys[]` 加一条 `{ phy_id, "型号描述" }` 即可 > (phy_id 可在 sysfs 的 `phy_id` 或 `mdio_phy read 2/3` 查到)。 --- ## 13. License 本项目采用 **MIT 许可证**,完整文本见 [LICENSE](LICENSE) 文件。 (内核模块 `phy_mdio_ctl` 的 `MODULE_LICENSE` 按内核惯例声明为 `"GPL"`, 源码授权本身为 MIT,二者兼容。)