- Linux中,phy状态的定义如下:
- 源码路径: includelinuxphy.h
enum phy_state {
PHY_DOWN = 0, //0 down 如关闭网卡,ifconfig eth0 down
PHY_STARTING, //1 PHY芯片OK了,但驱动还没有准备好
PHY_READY, //2 phy设备注册成功
PHY_PENDING, //3 phy芯片挂起
PHY_UP, //4 开启网卡,ifconfig eth0 up
PHY_AN, //5 网卡自动协商
PHY_RUNNING, //6 网卡上已插入网线、并建立物理连接
PHY_NOLINK, //7 断网,如拔掉网线
PHY_FORCING, //8 自动协商标识未被使能,就强制执行自动协商(读取phy寄存器、并设置通讯速率、半双工或全双工模式、等)
PHY_CHANGELINK, //9 当连接时,会换到PHY_RUNNING,当断网时,会切到PHY_NOLINK
PHY_HALTED, //10 即phy挂起
PHY_RESUMING //11 即phy恢复
};
2. phy状态检测流程
如图,Linux内部有一个状态机机制,一直在循环检测phy的状态,大致可分为三种,phy停止工作,phy开始工作,和phy link-up 或者link-down,然后会根据不同的phy状态去执行相对应代码,即使phy已经正常工作了,状态机仍会去检测phy状态,具体如下图:
- 当网线插拔时,phy就会打印出link-down和link-up如下图,在link-up之后,就会进入phy模式设置,大多数时候,phy默认都是自协商,特别情况才会固定设置phy模式
- 当phy_device被注册进入mii_bus后,状态机就会自动检测phy_dev的状态,具体源码如下:
- 源码路径: driversnetphyphy.c
// drivers/net/phy/phy.c
// 注册phy_device后,状态机就开始启动,监测phy状态
void phy_state_machine(struct work_struct *work)
{
struct delayed_work *dwork = to_delayed_work(work);
struct phy_device *phydev =
container_of(dwork, struct phy_device, state_queue);
bool needs_aneg = false, do_suspend = false;
int err = 0;
mutex_lock(&phydev->lock);
if (phydev->drv->link_change_notify)
phydev->drv->link_change_notify(phydev);
switch (phydev->state) {
case PHY_DOWN: // 关闭((ifconfig eth0 down)
case PHY_STARTING: // 开始
case PHY_READY: // 准备好
case PHY_PENDING: // 挂起
break;
case PHY_UP: // 开启(ifconfig eth0 up)
needs_aneg = true;
phydev->link_timeout = PHY_AN_TIMEOUT;
break;
case PHY_AN: // 判断连接状态中 negotiating
err = phy_read_status(phydev);
if (err < 0)
break;
if (!phydev->link) {
phydev->state = PHY_NOLINK;
netif_carrier_off(phydev->attached_dev);
phydev->adjust_link(phydev->attached_dev);
break;
}
err = phy_aneg_done(phydev);
if (err < 0)
break;
if (err > 0) {
phydev->state = PHY_RUNNING;
netif_carrier_on(phydev->attached_dev);
phydev->adjust_link(phydev->attached_dev);
} else if (0 == phydev->link_timeout--)
needs_aneg = true;
break;
case PHY_NOLINK: // 开启 未连接
err = phy_read_status(phydev);
if (err)
break;
if (phydev->link) {
if (AUTONEG_ENABLE == phydev->autoneg) {
err = phy_aneg_done(phydev);
if (err < 0)
break;
if (!err) {
phydev->state = PHY_AN;
phydev->link_timeout = PHY_AN_TIMEOUT;
break;
}
}
phydev->state = PHY_RUNNING;
netif_carrier_on(phydev->attached_dev);
phydev->adjust_link(phydev->attached_dev);
}
break;
case PHY_FORCING: // 设置中
err = genphy_update_link(phydev);
if (err)
break;
if (phydev->link) {
phydev->state = PHY_RUNNING;
netif_carrier_on(phydev->attached_dev);
} else {
if (0 == phydev->link_timeout--)
needs_aneg = true;
}
phydev->adjust_link(phydev->attached_dev);
break;
case PHY_RUNNING: // 运行
if (!phy_interrupt_is_valid(phydev))
phydev->state = PHY_CHANGELINK;
break;
case PHY_CHANGELINK: // 连接状态改变
err = phy_read_status(phydev);
if (err)
break;
if (phydev->link) {
phydev->state = PHY_RUNNING;
netif_carrier_on(phydev->attached_dev);
} else {
phydev->state = PHY_NOLINK;
netif_carrier_off(phydev->attached_dev);
}
phydev->adjust_link(phydev->attached_dev);
if (phy_interrupt_is_valid(phydev))
err = phy_config_interrupt(phydev,
PHY_INTERRUPT_ENABLED);
break;
case PHY_HALTED: // 停止
if (phydev->link) {
phydev->link = 0;
netif_carrier_off(phydev->attached_dev);
phydev->adjust_link(phydev->attached_dev);
do_suspend = true;
}
break;
case PHY_RESUMING: // 唤醒
if (AUTONEG_ENABLE == phydev->autoneg) {
err = phy_aneg_done(phydev);
if (err < 0)
break;
if (err > 0) {
err = phy_read_status(phydev);
if (err)
break;
if (phydev->link) {
phydev->state = PHY_RUNNING;
netif_carrier_on(phydev->attached_dev);
} else {
phydev->state = PHY_NOLINK;
}
phydev->adjust_link(phydev->attached_dev);
} else {
phydev->state = PHY_AN;
phydev->link_timeout = PHY_AN_TIMEOUT;
}
} else {
err = phy_read_status(phydev);
if (err)
break;
if (phydev->link) {
phydev->state = PHY_RUNNING;
netif_carrier_on(phydev->attached_dev);
} else {
phydev->state = PHY_NOLINK;
}
phydev->adjust_link(phydev->attached_dev);
}
break;
}
mutex_unlock(&phydev->lock);
if (needs_aneg) // 需要自动配置(例如ifconfig eth0 up就会调用)
err = phy_start_aneg(phydev); // 开始自动配置
else if (do_suspend)
phy_suspend(phydev);
if (err < 0)
phy_error(phydev);
queue_delayed_work(system_power_efficient_wq, &phydev->state_queue,
PHY_STATE_TIME * HZ);
}
- 需要注意的是,当phy状态改变的时候,状态机会检测phy的link情况是RUNNING还是NOLINK,此时就可以判断phy是否还是连接状态。
返回总目录



