背景
上一篇介绍了PHY设备的识别,构建phy_device结构体的同时,将PHY状态机放在延时队列里,实时更新PHY的状态。介绍文件系统调用命令来对网口进行操作涉及驱动流程。
解析状态机
// 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 the link is down, give up on negotiation for now */
if (!phydev->link) {
phydev->state = PHY_NOLINK;
netif_carrier_off(phydev->attached_dev);
phydev->adjust_link(phydev->attached_dev);
break;
}
/* Check if negotiation is done. Break if there's an error */
err = phy_aneg_done(phydev);
if (err < 0)
break;
/* If AN is done, we're running */
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: // 运行
/* Only register a CHANGE if we are
* polling or ignoring interrupts
*/
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;
/* err > 0 if AN is done.
* Otherwise, it's 0, and we're still waiting for AN
*/
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);
}
网络操作命令解析
(1)运行ifconfig eth0 up命令
// 文件系统输入ifconfig eth0 up
// 状态机切换phy_Up并启动自动配置
case PHY_UP: // 开启(ifconfig eth0 up)
needs_aneg = true;
phydev->link_timeout = PHY_AN_TIMEOUT;
break;
if (needs_aneg) // 需要自动配置(例如ifconfig eth0 up就会调用)
err = phy_start_aneg(phydev); // 开始自动配置
// 1、调用驱动的配置函数(没有特殊的phy芯片配置,就调用通用的配置)
// 2、调整phy当前的状态
int phy_start_aneg(struct phy_device *phydev)
{
int err;
mutex_lock(&phydev->lock);
if (AUTONEG_DISABLE == phydev->autoneg)
phy_sanitize_settings(phydev);
/* Invalidate LP advertising flags */
phydev->lp_advertising = 0;
err = phydev->drv->config_aneg(phydev); // 调用驱动的config_aneg方法,默认是genphy_config_aneg
if (err < 0)
goto out_unlock;
if (phydev->state != PHY_HALTED) { // 调整修改PHY设备状态
if (AUTONEG_ENABLE == phydev->autoneg) {
phydev->state = PHY_AN;
phydev->link_timeout = PHY_AN_TIMEOUT;
} else {
phydev->state = PHY_FORCING;
phydev->link_timeout = PHY_FORCE_TIMEOUT;
}
}
out_unlock:
mutex_unlock(&phydev->lock);
return err;
}
EXPORT_SYMBOL(phy_start_aneg);
// drivers/net/phy/phy_device.c
int genphy_config_aneg(struct phy_device *phydev)
{
int result;
if (AUTONEG_ENABLE != phydev->autoneg)
return genphy_setup_forced(phydev);
result = genphy_config_advert(phydev);
if (result < 0) /* error */
return result;
if (result == 0) {
/* Advertisement hasn't changed, but maybe aneg was never on to
* begin with? Or maybe phy was isolated?
*/
int ctl = phy_read(phydev, MII_BMCR); // 获取状态
if (ctl < 0)
return ctl;
if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE))
result = 1; /* do restart aneg */
}
/* Only restart aneg if we are advertising something different
* than we were before.
*/
if (result > 0)
result = genphy_restart_aneg(phydev); // 重新开启自动协商机制
return result;
}
EXPORT_SYMBOL(genphy_config_aneg);
// drivers/net/phy/phy_device.c
int genphy_restart_aneg(struct phy_device *phydev)
{
int ctl = phy_read(phydev, MII_BMCR); // 获取基本状态
if (ctl < 0)
return ctl;
ctl |= BMCR_ANENABLE | BMCR_ANRESTART; // 使能自动协商机制及支援重启
/* Don't isolate the PHY if we're negotiating */
ctl &= ~BMCR_ISOLATE;
return phy_write(phydev, MII_BMCR, ctl); // 写命令
}
EXPORT_SYMBOL(genphy_restart_aneg);