分享

linux phy运行状态

 XeonGate 2020-01-07

https://blog.csdn.net/chenliang0224/article/details/81024026

一、phy执行的枚举状态定义

  1. enum phy_state {
  2. PHY_DOWN=0, //down 如关闭网卡,ifconfig eth0 down
  3. PHY_STARTING, //1
  4. PHY_READY, //2 phy设备注册成功
  5. PHY_PENDING, //3 phy芯片挂起
  6. PHY_UP, //4 开启网卡,ifconfig eth0 up
  7. PHY_AN, //5 网卡自动协商
  8. PHY_RUNNING, //6 网卡上已插入网线、并建立物理连接,同时会从这个状态切换到PHY_CHANGELINK
  9. PHY_NOLINK, //7 断网,如拔掉网线
  10. PHY_FORCING, //8 自动协商标识未被使能,就强制执行自动协商(读取phy寄存器、并设置通讯速率、半双工或全双工模式、等)
  11. PHY_CHANGELINK, //9 当连接时,会换到PHY_RUNNING,当断网时,会切到PHY_NOLINK
  12. PHY_HALTED, //10 在执行网卡关闭时(ifconfig eth0 down)会执行到这个状态,即phy挂起
  13. PHY_RESUMING //11 在执行网卡开启时(ifconfig eth0 up)会执行到这个状态,即phy恢复
  14. };

二、emac接口驱动下的phy设备注册

  1. nuc970_ether_probe //探测函数驱动
  2. nuc970_mii_setup
  3. mdiobus_register
  4. mdiobus_scan
  5. get_phy_device
  6. get_phy_id
  7. phy_device_create
  8. dev->autoneg = AUTONEG_ENABLE; //设置phy 自动协商使能
  9. dev->state = PHY_DOWN; //设置phy的状态 ----1-----
  10. INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine); //初始化一个中断顶版本的延时工作队列,用来处理phy状态机
  11. phy_device_register //phy设备注册
  12. device_add //这里是通过堆栈信息打印的,详见下面的 堆栈信息
  13. bus_probe_device
  14. device_attach
  15. bus_for_each_drv
  16. __device_attach
  17. driver_probe_device
  18. phy_probe //非常重要的一个探测接口
  19. phydev->supported = phydrv->features; //phydrv->features = PHY_BASIC_FEATURES | SUPPORTED_Pause | SUPPORTED_Asym_Pause
  20. phydev->advertising = phydrv->features;
  21. phydev->state = PHY_READY; //设置phy的状态 ----2-----
  22. phy_connect(dev, dev_name(&phydev->dev), &adjust_link, PHY_INTERFACE_MODE_RMII);
  23. phy_connect_direct
  24. phy_prepare_link
  25. phydev->adjust_link = handler; //handler = adjust_link
  26. phy_start_machine
  27. phydev->adjust_state = handler; //handler = NULL
  28. schedule_delayed_work(&phydev->state_queue, HZ); //启动上面注册的延时工作队列状态机 phy_state_machine

总结:上面已经将phy的状态设置成了 phydev->state = PHY_READY,同时完成了emac接口下的phy设备驱动注册,接下来将分析如何使用该phy设备驱动。

三、应用层执行ifconfig eth0 up时phy的设备状态

  1. ifconfig eth0 up----->
  2. nuc970_ether_open
  3. phy_start
  4. //......
  5. switch (phydev->state) { //上面已经 phydev->state = PHY_READY
  6. case PHY_STARTING:
  7. phydev->state = PHY_PENDING;
  8. break;
  9. case PHY_READY:
  10. phydev->state = PHY_UP; //所以到这里, 设置phy的状态为 PHY_UP ----3-----
  11. break;
  12. case PHY_HALTED:
  13. phydev->state = PHY_RESUMING;
  14. default:
  15. break;
  16. }
  17. //......

总结:上面在执行 ifconfig eth0 up 后,已将phydev->state = PHY_UP

四、phy状态机phy_state_machine执行状态分析

  1. phy_state_machine
  2. switch(phydev->state) { //关于该phydev->state的各执行状态详见本“第一”小节的介绍
  3. case PHY_DOWN:
  4. case PHY_STARTING:
  5. case PHY_READY:
  6. case PHY_PENDING:
  7. break;
  8. case PHY_UP:
  9. needs_aneg = 1;
  10. phydev->link_timeout = PHY_AN_TIMEOUT;
  11. break;
  12. .....
  13. phy_start_aneg //启动自动协商
  14. phydev->drv->config_aneg(phydev); //这里将调用 genphy_config_aneg
  15. genphy_config_aneg
  16. genphy_config_advert //自动协商声明,关于其源码详见如下
  17. phydev->state = PHY_AN; //设置为自动协商,设置phy的状态为 PHY_AN ----4-----
  18. //此时函数返回,又重新进入到上面的状态机 phy_state_machine,不过此时的状态已经变为 PHY_AN
  19. case PHY_AN: //自动协商
  20. phy_read_status //读取phy的状态
  21. phydev->drv->read_status(phydev); //.read_status = genphy_read_status
  22. genphy_read_status
  23. genphy_update_link
  24. phy_read(phydev, MII_BMSR); //读取phy的状态寄存器
  25. if ((status & BMSR_LSTATUS) == 0) //phy 的链路状态
  26. phydev->link = 0; //设置网卡的状态处于断开状态
  27. else
  28. phydev->link = 1; //设置网卡的状态处于连接状态
  29. if (!phydev->link) { //链路状态
  30. phydev->state = PHY_NOLINK; //协商失败,未建立连接,设置phy的状态为 PHY_NOLINK ----5-----
  31. netif_carrier_off(phydev->attached_dev);
  32. phydev->adjust_link(phydev->attached_dev);
  33. break;
  34. }
  35. err = phy_aneg_done(phydev); //判断自动协商是否完成
  36. /* If AN is done, we're running */
  37. if (err > 0) { //自动协商完成
  38. phydev->state = PHY_RUNNING; //协商成功,建立连接,设置phy的状态为 PHY_RUNNING ----6-----
  39. netif_carrier_on(phydev->attached_dev);
  40. phydev->adjust_link(phydev->attached_dev); //adjust_link = adjust_link 调用nuc970_ether0里面的接口函数
  41. } else if (0 == phydev->link_timeout--) { //自动协商未完成,将根据 link_timeout 次数继续尝试自动协商
  42. needs_aneg = 1;
  43. /* If we have the magic_aneg bit,
  44. * we try again */
  45. if (phydev->drv->flags & PHY_HAS_MAGICANEG)
  46. break;
  47. }
  48. case PHY_RUNNING:
  49. /* Only register a CHANGE if we are
  50. * polling */
  51. if (PHY_POLL == phydev->irq)
  52. phydev->state = PHY_CHANGELINK; //正常运行,设置phy的状态为 PHY_CHANGELINK ----7-----
  53. break;
  54. case PHY_CHANGELINK:
  55. err = phy_read_status(phydev); //再次获取 phy 链路状态
  56. if (err)
  57. break;
  58. if (phydev->link) {
  59. phydev->state = PHY_RUNNING; //协商成功,建立连接,设置phy的状态为 PHY_RUNNING ----8-----
  60. netif_carrier_on(phydev->attached_dev);
  61. } else {
  62. phydev->state = PHY_NOLINK; //协商失败,未建立连接,设置phy的状态为 PHY_NOLINK ----9-----
  63. netif_carrier_off(phydev->attached_dev);
  64. }
  65. phydev->adjust_link(phydev->attached_dev); //adjust_link = adjust_link 调用nuc970_ether0里面的接口函数
  66. if (PHY_POLL != phydev->irq)
  67. err = phy_config_interrupt(phydev,
  68. PHY_INTERRUPT_ENABLED);
  69. break;

emac接口驱动配置phy

  1. int genphy_config_aneg(struct phy_device *phydev)
  2. {
  3. int result;
  4. if (AUTONEG_ENABLE != phydev->autoneg) //在phy创建的时候,初始了ev->autoneg = AUTONEG_ENABLE; //设置phy 自动协商使能
  5. return genphy_setup_forced(phydev);
  6. result = genphy_config_advert(phydev);
  7. if (result < 0) /* error */
  8. return result;
  9. if (result == 0) {
  10. /* Advertisement hasn't changed, but maybe aneg was never on to
  11. * begin with? Or maybe phy was isolated? */
  12. int ctl = phy_read(phydev, MII_BMCR); //获取模式控制寄存器,详见该链接对该寄存器的定义:https://wenku.baidu.com/view/b8704335ee06eff9aef807bd.html
  13. if (ctl < 0)
  14. return ctl;
  15. //不能自动协商 || 自动协商复位
  16. if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE)) //BMCR_ANENABLE: 自动协商使能 ||
  17. result = 1; /* do restart aneg */
  18. }
  19. /* Only restart aneg if we are advertising something different
  20. * than we were before. */
  21. if (result > 0)
  22. result = genphy_restart_aneg(phydev); //重启自动协商
  23. return result;
  24. }

获取自动协商配置

  1. static int genphy_config_advert(struct phy_device *phydev)
  2. {
  3. u32 advertise;
  4. int oldadv, adv;
  5. int err, changed = 0;
  6. /* Only allow advertising what
  7. * this PHY supports */
  8. /*
  9. phy 设备在注册时初始化为如下:
  10. phydev->supported = phydrv->features; //phydrv->features = PHY_BASIC_FEATURES | SUPPORTED_Pause | SUPPORTED_Asym_Pause
  11. phydev->advertising = phydrv->features;
  12. */
  13. phydev->advertising &= phydev->supported;
  14. advertise = phydev->advertising;
  15. /* Setup standard advertisement */
  16. oldadv = adv = phy_read(phydev, MII_ADVERTISE); //自动协商声明
  17. if (adv < 0)
  18. return adv;
  19. adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP |
  20. ADVERTISE_PAUSE_ASYM);
  21. adv |= ethtool_adv_to_mii_adv_t(advertise);
  22. if (adv != oldadv) { //自动协商结果不一致就重新设置
  23. err = phy_write(phydev, MII_ADVERTISE, adv);
  24. if (err < 0)
  25. return err;
  26. changed = 1;
  27. }
  28. /* Configure gigabit if it's supported */
  29. if (phydev->supported & (SUPPORTED_1000baseT_Half |
  30. SUPPORTED_1000baseT_Full)) {
  31. oldadv = adv = phy_read(phydev, MII_CTRL1000);
  32. if (adv < 0)
  33. return adv;
  34. adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF);
  35. adv |= ethtool_adv_to_mii_ctrl1000_t(advertise);
  36. if (adv != oldadv) {
  37. err = phy_write(phydev, MII_CTRL1000, adv);
  38. if (err < 0)
  39. return err;
  40. changed = 1;
  41. }
  42. }
  43. return changed;
  44. }

重启自动协商

  1. int genphy_restart_aneg(struct phy_device *phydev)
  2. {
  3. int ctl;
  4. ctl = phy_read(phydev, MII_BMCR);
  5. if (ctl < 0)
  6. return ctl;
  7. ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
  8. /* Don't isolate the PHY if we're negotiating */
  9. ctl &= ~(BMCR_ISOLATE);
  10. ctl = phy_write(phydev, MII_BMCR, ctl); //设置自动协商
  11. return ctl;
  12. }

总结:上面源码只要是完成emac接口与phy之间的自动协商、链路状态检测等

五、拓展信息

1. phy_probe匹配时的探测信息

  1. ===========================堆栈信息===========================
  2. Backtrace:
  3. [<c0012df8>] (dump_backtrace+0x0/0x10c) from [<c0012f1c>] (show_stack+0x18/0x1c)
  4. r7:c39c8374 r6:c39c8208 r5:c0474914 r4:c39c8200
  5. [<c0012f04>] (show_stack+0x0/0x1c) from [<c0324ef4>] (dump_stack+0x20/0x2c)
  6. [<c0324ed4>] (dump_stack+0x0/0x2c) from [<c022ae08>] (phy_probe+0x58/0x88)
  7. [<c022adb0>] (phy_probe+0x0/0x88) from [<c01f69e4>] (driver_probe_device+0x9c/0x238)
  8. r7:c047496c r6:00000000 r5:c39c8208 r4:c047496c
  9. [<c01f6948>] (driver_probe_device+0x0/0x238) from [<c01f6c5c>] (__device_attach+0x44/0x48)
  10. [<c01f6c18>] (__device_attach+0x0/0x48) from [<c01f4ff8>] (bus_for_each_drv+0x68/0x94)
  11. r5:c3843cd8 r4:00000000
  12. [<c01f4f90>] (bus_for_each_drv+0x0/0x94) from [<c01f6d14>] (device_attach+0x88/0xa0)
  13. r7:00000001 r6:c39c823c r5:c0474820 r4:c39c8208
  14. [<c01f6c8c>] (device_attach+0x0/0xa0) from [<c01f5cc0>] (bus_probe_device+0x8c/0xb4)
  15. r7:00000001 r6:c39c8208 r5:c0474820 r4:c04b84f8
  16. [<c01f5c34>] (bus_probe_device+0x0/0xb4) from [<c01f45d0>] (device_add+0x310/0x648)
  17. r7:00000001 r6:00000000 r5:c39c8208 r4:c04b84f8
  18. [<c01f42c0>] (device_add+0x0/0x648) from [<c022bac8>] (phy_device_register+0x44/0x70)
  19. [<c022ba84>] (phy_device_register+0x0/0x70) from [<c022bdd0>] (mdiobus_scan+0x2c/0x4c)
  20. r7:00000001 r6:00000000 r5:00000001 r4:c39c8200
  21. [<c022bda4>] (mdiobus_scan+0x0/0x4c) from [<c022bf88>] (mdiobus_register+0xec/0x184)
  22. r5:00000001 r4:c39c8000
  23. [<c022be9c>] (mdiobus_register+0x0/0x184) from [<c022d874>] (nuc970_ether_probe+0x2d8/0x4b4)
  24. [<c022d59c>] (nuc970_ether_probe+0x0/0x4b4) from [<c01f7d08>] (platform_drv_probe+0x20/0x24)
  25. [<c01f7ce8>] (platform_drv_probe+0x0/0x24) from [<c01f69e4>] (driver_probe_device+0x9c/0x238)
  26. [<c01f6948>] (driver_probe_device+0x0/0x238) from [<c01f6c14>] (__driver_attach+0x94/0x98)
  27. [<c01f6b80>] (__driver_attach+0x0/0x98) from [<c01f50a0>] (bus_for_each_dev+0x7c/0xa0)
  28. r7:c01f6b80 r6:c0474a58 r5:c3843e78 r4:00000000
  29. [<c01f5024>] (bus_for_each_dev+0x0/0xa0) from [<c01f6828>] (driver_attach+0x20/0x28)
  30. r7:c046fc68 r6:c0474a58 r5:c0474a58 r4:c39a69c0
  31. [<c01f6808>] (driver_attach+0x0/0x28) from [<c01f5990>] (bus_add_driver+0xec/0x240)
  32. [<c01f58a4>] (bus_add_driver+0x0/0x240) from [<c01f6ffc>] (driver_register+0x60/0x150)
  33. r7:c3842000 r6:00000000 r5:c0474a58 r4:c044396c
  34. [<c01f6f9c>] (driver_register+0x0/0x150) from [<c01f7fe8>] (platform_driver_register+0x4c/0x60)
  35. r7:c3842000 r6:00000000 r5:c043f414 r4:c044396c
  36. [<c01f7f9c>] (platform_driver_register+0x0/0x60) from [<c04395b0>] (nuc970_ether_init+0x14/0x1c)
  37. [<c043959c>] (nuc970_ether_init+0x0/0x1c) from [<c00087c4>] (do_one_initcall+0x38/0x150)
  38. [<c000878c>] (do_one_initcall+0x0/0x150) from [<c04235d4>] (kernel_init_freeable+0x14c/0x224)
  39. [<c0423488>] (kernel_init_freeable+0x0/0x224) from [<c0324528>] (kernel_init+0x10/0x15c)
  40. [<c0324518>] (kernel_init+0x0/0x15c) from [<c000f850>] (ret_from_fork+0x14/0x24)
  41. r5:c0324518 r4:00000000
  42. libphy: nuc970_rmii0: probed

2. phy标准寄存器的定义

  1. /* Generic MII registers. */
  2. //控制寄存器
  3. #define MII_BMCR 0x00 /* Basic mode control register */
  4. //状态寄存器
  5. #define MII_BMSR 0x01 /* Basic mode status register */
  6. //PHY 标志
  7. #define MII_PHYSID1 0x02 /* PHYS ID 1 */
  8. #define MII_PHYSID2 0x03 /* PHYS ID 2 */
  9. //4~8寄存器是用于自动协商的
  10. #define MII_ADVERTISE 0x04 /* Advertisement control reg 自动协商声明*/
  11. #define MII_LPA 0x05 /* Link partner ability reg */
  12. #define MII_EXPANSION 0x06 /* Expansion register */
  13. //保留
  14. #define MII_CTRL1000 0x09 /* 1000BASE-T control */
  15. #define MII_STAT1000 0x0a /* 1000BASE-T status */
  16. #define MII_MMD_CTRL 0x0d /* MMD Access Control Register */
  17. #define MII_MMD_DATA 0x0e /* MMD Access Data Register */
  18. #define MII_ESTATUS 0x0f /* Extended Status */
  19. //制造商指定寄存器
  20. #define MII_DCOUNTER 0x12 /* Disconnect counter */
  21. #define MII_FCSCOUNTER 0x13 /* False carrier counter */
  22. #define MII_NWAYTEST 0x14 /* N-way auto-neg test reg */
  23. #define MII_RERRCOUNTER 0x15 /* Receive error counter */
  24. #define MII_SREVISION 0x16 /* Silicon revision */
  25. #define MII_RESV1 0x17 /* Reserved... */
  26. #define MII_LBRERROR 0x18 /* Lpback, rx, bypass error */
  27. #define MII_PHYADDR 0x19 /* PHY address */
  28. #define MII_RESV2 0x1a /* Reserved... */
  29. #define MII_TPISTATUS 0x1b /* TPI status for 10mbps */
  30. #define MII_NCONFIG 0x1c /* Network interface config */

关于phy的寄存器定义详见:https://wenku.baidu.com/view/b8704335ee06eff9aef807bd.html 第5页

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多