Linux内核(八) PHY状态机以及网络相关操作命令解析

背景

上一篇介绍了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);
  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bazinga bingo

您的鼓励就是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值