陈施有 发表于 2023-2-8 12:43:09

PHY状态机分析

PHY的12种状态

enum phy_state {
        PHY_DOWN = 0, //关闭网卡
        PHY_STARTING, //PHY设备准备好了,PHY driver尚为准备好
        PHY_READY,       //PHY设备注册成功
        PHY_PENDING,//PHY芯片挂起
        PHY_UP,            //开启网卡
        PHY_AN,             //网卡自协商
        PHY_RUNNING, //网卡已经插入网线并建立物理连接,该状态可切换到PHY_CHANGELINK
        PHY_NOLINK,    //断网,拔掉网线
        PHY_FORCING,//自动协商失败,强制处理(读phy状态寄存器,设置速率,设置工作模式)
        PHY_CHANGELINK, //LINK检查,当物理连接存在时切换到PHY_RUNING,物理连接不存在时切换到PHY_NOLINK
        PHY_HALTED,   //网卡关闭时,PHY挂起
        PHY_RESUMING //网卡开启时,PHY恢复
};PHY状态机


PHY指PHY芯片,负责数据传送与接收所需要的电与光信号、线路状态、时钟基准、数据编码和电路等,并向数据链路层设备提供标准接口。
MAC指MAC芯片,属于数据链路层,提供寻址机构、数据帧的构建、数据差错检查、传送控制、向网络层提供标准的数据接口等功能。
PHY_DOWN: phy、phy driver、mac都没准备好

[*]如果phy driver被集成在内核中,PHY.probe后,phydev状态为PHY_READY。
[*]如果phy driver被未集成在内核中,PHY.probe后,phydev状态为PHY_STARTING。
PHY_READY:phy、phy driver已经就绪,mac未准备好
当MAC层加载时,在PHY.start后,phydev状态切换为PHY_UP。
PHY_STARTING:phy准备就绪,phy driver、mac未准备好

[*]当MAC加载时,PHY.start后,phydev状态为PHY_PENDING。
[*]当phy driver加载时,phydev状态为PHY_READY。
PHY_PENDING:phy、mac准备就绪,phy driver未准备好
当phy dirver加载后,phdev状态为PHY_UP
上图中0-->1-->2-->4、0-->2-->4代表phy、phy dirver、mac顺序加载。
0-->1-->3-->4代表phy、mac、phy driver顺序加载。
PHY_UP:phy、phy driver、mac准备就绪
当前状态将启动自动协商,若启动成功则进入PHY_AN,若启动失败则进入PHY_FORCING。
PHY_AN:网卡自协商模式,检测自协商是否完成。
先判断物理链路的状态,如果未LINK则进入PHY_NOLINK,如果LINK则判断自协商是否完成,
自协商完成进入PHY_RUNNING,若自协商超时则重新开启自协商。
PHY_FORCING:强制协商
读link和自协商状态寄存器,如果状态正常则进入PHY_RUNNING模式。
PHY_NOLINK:物理链路未连接
判断物理链路状态,如果LINK,再判断是否支持自协商,若支持待自协商完成后进入PHY_RUNNING模式,
若不支持,直接进入PHY_RUNNING模式。若自协商处于挂起状态,则进入PHY_AN模式。
PHY_RUNNING:正常运行中
获取当前link状态,当link状态发生改变时,进入PHY_CHANGELINK模式。
PHY_CHANGELINK:检查物理链路
物理链路link时,切换到PHY_RUNNING,非LINK时切换到PHY_NOLINK。
PHY_HALTED:网卡关闭phy_stop
挂起phy
PHY_RESUMING: 网卡启用phy_start
恢复phy
phy_state_machine是PHY的状态机函数
/**
* phy_state_machine - Handle the state machine
* @work: work_struct that describes the work to be done
*/
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;
        enum phy_state old_state;
        int err = 0;
        int old_link;

        mutex_lock(&phydev->lock);

        old_state = phydev->state;

        if (phydev->drv->link_change_notify)
                phydev->drv->link_change_notify(phydev);

        switch (phydev->state) {
        case PHY_DOWN:
        case PHY_STARTING:
        case PHY_READY:
        case PHY_PENDING:
                break;
        case PHY_UP:
                needs_aneg = true;

                phydev->link_timeout = PHY_AN_TIMEOUT;

                break;
        case PHY_AN:
                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:
                if (phy_interrupt_is_valid(phydev))
                        break;

                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 and link changed since latest checking.
               */
                if (!phy_interrupt_is_valid(phydev)) {
                        old_link = phydev->link;
                        err = phy_read_status(phydev);
                        if (err)
                                break;

                        if (old_link != phydev->link)
                                phydev->state = PHY_CHANGELINK;
                }
                /*
               * Failsafe: check that nobody set phydev->link=0 between two
               * poll cycles, otherwise we won't leave RUNNING state as long
               * as link remains down.
               */
                if (!phydev->link && phydev->state == PHY_RUNNING) {
                        phydev->state = PHY_CHANGELINK;
                        dev_err(&phydev->dev, "no link in PHY_RUNNING\n");
                }
                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'restill 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)
                err = phy_start_aneg(phydev);
        else if (do_suspend)
                phy_suspend(phydev);

        if (err < 0)
                phy_error(phydev);

        dev_dbg(&phydev->dev, "PHY state change %s -> %s\n",
                phy_state_to_str(old_state), phy_state_to_str(phydev->state));

        queue_delayed_work(system_power_efficient_wq, &phydev->state_queue,
                           PHY_STATE_TIME * HZ);
}
来源:https://www.cnblogs.com/forwards/p/17101019.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: PHY状态机分析