4. 100BASE-T1 PHY主从模式配置与切换

任务说明

100BASE-T1 链路要求两端 PHY 一主一从:主端发送时钟,从端恢复时钟。本案例通过寄存器配置主从模式,读取 BMSR 链路状态位,验证切换对链路建立的影响。

寄存器与流程

使用标准寄存器:BMCR(0x00) 的 bit12 选择主从(1=主,0=从),BMSR(0x01) 的 bit2 为 Link Status。流程:软复位 → 写主从位 → 轮询 BMSR 直到链路建立或超时。

软复位 BMCR 写主从位 BMCR bit12 轮询 BMSR bit2 Link 判定结果

完整代码

#include <stdio.h>
#include <stdint.h>
#include <unistd.h>

/* MDIO 读写接口:由具体平台实现,此处声明 */
extern int mdio_read(uint8_t phy, uint8_t reg, uint16_t *val);
extern int mdio_write(uint8_t phy, uint8_t reg, uint16_t val);

#define BMCR 0x00
#define BMSR 0x01
#define BMCR_RESET   (1u << 15)
#define BMCR_MASTER  (1u << 12)
#define BMSR_LINK    (1u << 2)

/* 配置主从模式:master=1 主,0 从 */
int phy_set_master_slave(uint8_t phy, int master)
{
    uint16_t v;
    if (mdio_read(phy, BMCR, &v)) return -1;
    v |= BMCR_RESET;
    if (mdio_write(phy, BMCR, v)) return -1;
    usleep(2000);                       /* 等待复位完成 */

    if (mdio_read(phy, BMCR, &v)) return -1;
    if (master) v |= BMCR_MASTER;
    else        v &= ~BMCR_MASTER;
    return mdio_write(phy, BMCR, v);
}

/* 轮询链路状态,返回 1 建立 / 0 超时 / -1 错误 */
int phy_wait_link(uint8_t phy, int timeout_ms)
{
    uint16_t v;
    for (int i = 0; i < timeout_ms; i++) {
        if (mdio_read(phy, BMSR, &v)) return -1;
        if (v & BMSR_LINK) return 1;
        usleep(1000);
    }
    return 0;
}

int main(void)
{
    uint8_t phy = 0;
    int modes[2] = {1, 0};              /* 先主后从 */
    const char *name[2] = {"MASTER", "SLAVE"};

    for (int i = 0; i < 2; i++) {
        if (phy_set_master_slave(phy, modes[i])) {
            printf("%s: config error\n", name[i]);
            continue;
        }
        int r = phy_wait_link(phy, 3000);
        printf("%s: link %s\n", name[i],
               r == 1 ? "UP" : r == 0 ? "TIMEOUT" : "ERROR");
    }
    return 0;
}

运行与验证

将 mdio_read/mdio_write 对接实际 MAC 的 MDIO 驱动后编译运行:gcc phy_ms.c mdio_impl.c -o phy_ms && ./phy_ms。

判定标准:对端配置为相反模式时,对应模式应打印 link UP;若两端同为 MASTER 或同为 SLAVE,则打印 link TIMEOUT,即验证主从切换对链路建立的影响。