栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

Linux ARM平台开发系列讲解(网络篇)1.7 PHY状态机分析

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Linux ARM平台开发系列讲解(网络篇)1.7 PHY状态机分析

1. PHY状态的定义
  • 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模式

3. 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是否还是连接状态。

返回总目录

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/974635.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号