Add the generic parts of the code used to trigger a cable test and return raw TDR data. Any PHY driver which support this must implement the new driver op. Signed-off-by: Andrew Lunn <andrew@lunn.ch> v2 Update nxp-tja11xx for API change.
Unified · Split
| @@ -194,7 +194,7 @@ static int tja11xx_config_aneg_cable_test(struct phy_device *phydev) · 193 unmodified lines | |||
| 194 | !phydev->drv->cable_test_get_status) | 194 | !phydev->drv->cable_test_get_status) |
| 195 | return 0; | 195 | return 0; |
| 196 | 196 | ||
| 197 | ret = ethnl_cable_test_alloc(phydev); | 197 | ret = ethnl_cable_test_alloc(phydev, ETHTOOL_MSG_CABLE_TEST_NTF); |
| 198 | if (ret) | 198 | if (ret) |
| 199 | return ret; | 199 | return ret; |
| 200 | 200 | ||
| @@ -519,7 +519,7 @@ int phy_start_cable_test(struct phy_device *phydev, · 518 unmodified lines | |||
| 519 | goto out; | 519 | goto out; |
| 520 | } | 520 | } |
| 521 | 521 | ||
| 522 | err = ethnl_cable_test_alloc(phydev); | 522 | err = ethnl_cable_test_alloc(phydev, ETHTOOL_MSG_CABLE_TEST_NTF); |
| 523 | if (err) | 523 | if (err) |
| 524 | goto out; | 524 | goto out; |
| 525 | 525 | ||
| @@ -552,6 +552,69 @@ out: · 26 unmodified lines | |||
| 552 | } | 552 | } |
| 553 | EXPORT_SYMBOL(phy_start_cable_test); | 553 | EXPORT_SYMBOL(phy_start_cable_test); |
| 554 | 554 | ||
| 555 | int phy_start_cable_test_tdr(struct phy_device *phydev, | ||
| 556 | struct netlink_ext_ack *extack) | ||
| 557 | { | ||
| 558 | struct net_device *dev = phydev->attached_dev; | ||
| 559 | int err = -ENOMEM; | ||
| 560 | |||
| 561 | if (!(phydev->drv && | ||
| 562 | phydev->drv->cable_test_tdr_start && | ||
| 563 | phydev->drv->cable_test_get_status)) { | ||
| 564 | NL_SET_ERR_MSG(extack, | ||
| 565 | "PHY driver does not support cable test TDR"); | ||
| 566 | return -EOPNOTSUPP; | ||
| 567 | } | ||
| 568 | |||
| 569 | mutex_lock(&phydev->lock); | ||
| 570 | if (phydev->state == PHY_CABLETEST) { | ||
| 571 | NL_SET_ERR_MSG(extack, | ||
| 572 | "PHY already performing a test"); | ||
| 573 | err = -EBUSY; | ||
| 574 | goto out; | ||
| 575 | } | ||
| 576 | |||
| 577 | if (phydev->state < PHY_UP || | ||
| 578 | phydev->state > PHY_CABLETEST) { | ||
| 579 | NL_SET_ERR_MSG(extack, | ||
| 580 | "PHY not configured. Try setting interface up"); | ||
| 581 | err = -EBUSY; | ||
| 582 | goto out; | ||
| 583 | } | ||
| 584 | |||
| 585 | err = ethnl_cable_test_alloc(phydev, ETHTOOL_MSG_CABLE_TEST_TDR_NTF); | ||
| 586 | if (err) | ||
| 587 | goto out; | ||
| 588 | |||
| 589 | /* Mark the carrier down until the test is complete */ | ||
| 590 | phy_link_down(phydev); | ||
| 591 | |||
| 592 | netif_testing_on(dev); | ||
| 593 | err = phydev->drv->cable_test_tdr_start(phydev); | ||
| 594 | if (err) { | ||
| 595 | netif_testing_off(dev); | ||
| 596 | phy_link_up(phydev); | ||
| 597 | goto out_free; | ||
| 598 | } | ||
| 599 | |||
| 600 | phydev->state = PHY_CABLETEST; | ||
| 601 | |||
| 602 | if (phy_polling_mode(phydev)) | ||
| 603 | phy_trigger_machine(phydev); | ||
| 604 | |||
| 605 | mutex_unlock(&phydev->lock); | ||
| 606 | |||
| 607 | return 0; | ||
| 608 | |||
| 609 | out_free: | ||
| 610 | ethnl_cable_test_free(phydev); | ||
| 611 | out: | ||
| 612 | mutex_unlock(&phydev->lock); | ||
| 613 | |||
| 614 | return err; | ||
| 615 | } | ||
| 616 | EXPORT_SYMBOL(phy_start_cable_test_tdr); | ||
| 617 | |||
| 555 | static int phy_config_aneg(struct phy_device *phydev) | 618 | static int phy_config_aneg(struct phy_device *phydev) |
| 556 | { | 619 | { |
| 557 | if (phydev->drv->config_aneg) | 620 | if (phydev->drv->config_aneg) |
| @@ -17,13 +17,13 @@ enum ethtool_multicast_groups { · 16 unmodified lines | |||
| 17 | struct phy_device; | 17 | struct phy_device; |
| 18 | 18 | ||
| 19 | #if IS_ENABLED(CONFIG_ETHTOOL_NETLINK) | 19 | #if IS_ENABLED(CONFIG_ETHTOOL_NETLINK) |
| 20 | int ethnl_cable_test_alloc(struct phy_device *phydev); | 20 | int ethnl_cable_test_alloc(struct phy_device *phydev, u8 cmd); |
| 21 | void ethnl_cable_test_free(struct phy_device *phydev); | 21 | void ethnl_cable_test_free(struct phy_device *phydev); |
| 22 | void ethnl_cable_test_finished(struct phy_device *phydev); | 22 | void ethnl_cable_test_finished(struct phy_device *phydev); |
| 23 | int ethnl_cable_test_result(struct phy_device *phydev, u8 pair, u8 result); | 23 | int ethnl_cable_test_result(struct phy_device *phydev, u8 pair, u8 result); |
| 24 | int ethnl_cable_test_fault_length(struct phy_device *phydev, u8 pair, u32 cm); | 24 | int ethnl_cable_test_fault_length(struct phy_device *phydev, u8 pair, u32 cm); |
| 25 | #else | 25 | #else |
| 26 | static inline int ethnl_cable_test_alloc(struct phy_device *phydev) | 26 | static inline int ethnl_cable_test_alloc(struct phy_device *phydev, u8 cmd) |
| 27 | { | 27 | { |
| 28 | return -EOPNOTSUPP; | 28 | return -EOPNOTSUPP; |
| 29 | } | 29 | } |
| @@ -699,6 +699,10 @@ struct phy_driver { · 698 unmodified lines | |||
| 699 | 699 | ||
| 700 | /* Start a cable test */ | 700 | /* Start a cable test */ |
| 701 | int (*cable_test_start)(struct phy_device *dev); | 701 | int (*cable_test_start)(struct phy_device *dev); |
| 702 | |||
| 703 | /* Start a raw TDR cable test */ | ||
| 704 | int (*cable_test_tdr_start)(struct phy_device *dev); | ||
| 705 | |||
| 702 | /* Once per second, or on interrupt, request the status of the | 706 | /* Once per second, or on interrupt, request the status of the |
| 703 | * test. | 707 | * test. |
| 704 | */ | 708 | */ |
| @@ -1251,6 +1255,8 @@ int phy_reset_after_clk_enable(struct phy_device *phydev); · 546 unmodified lines | |||
| 1251 | #if IS_ENABLED(CONFIG_PHYLIB) | 1255 | #if IS_ENABLED(CONFIG_PHYLIB) |
| 1252 | int phy_start_cable_test(struct phy_device *phydev, | 1256 | int phy_start_cable_test(struct phy_device *phydev, |
| 1253 | struct netlink_ext_ack *extack); | 1257 | struct netlink_ext_ack *extack); |
| 1258 | int phy_start_cable_test_tdr(struct phy_device *phydev, | ||
| 1259 | struct netlink_ext_ack *extack); | ||
| 1254 | #else | 1260 | #else |
| 1255 | static inline | 1261 | static inline |
| 1256 | int phy_start_cable_test(struct phy_device *phydev, | 1262 | int phy_start_cable_test(struct phy_device *phydev, |
| @@ -1259,6 +1265,13 @@ int phy_start_cable_test(struct phy_device *phydev, · 2 unmodified lines | |||
| 1259 | NL_SET_ERR_MSG(extack, "Kernel not compiled with PHYLIB support"); | 1265 | NL_SET_ERR_MSG(extack, "Kernel not compiled with PHYLIB support"); |
| 1260 | return -EOPNOTSUPP; | 1266 | return -EOPNOTSUPP; |
| 1261 | } | 1267 | } |
| 1268 | static inline | ||
| 1269 | int phy_start_cable_test_tdr(struct phy_device *phydev, | ||
| 1270 | struct netlink_ext_ack *extack) | ||
| 1271 | { | ||
| 1272 | NL_SET_ERR_MSG(extack, "Kernel not compiled with PHYLIB support"); | ||
| 1273 | return -EOPNOTSUPP; | ||
| 1274 | } | ||
| 1262 | #endif | 1275 | #endif |
| 1263 | 1276 | ||
| 1264 | int phy_cable_test_result(struct phy_device *phydev, u8 pair, u16 result); | 1277 | int phy_cable_test_result(struct phy_device *phydev, u8 pair, u16 result); |
| @@ -13,7 +13,7 @@ cable_test_act_policy[ETHTOOL_A_CABLE_TEST_MAX + 1] = { · 12 unmodified lines | |||
| 13 | [ETHTOOL_A_CABLE_TEST_HEADER] = { .type = NLA_NESTED }, | 13 | [ETHTOOL_A_CABLE_TEST_HEADER] = { .type = NLA_NESTED }, |
| 14 | }; | 14 | }; |
| 15 | 15 | ||
| 16 | static int ethnl_cable_test_started(struct phy_device *phydev) | 16 | static int ethnl_cable_test_started(struct phy_device *phydev, u8 cmd) |
| 17 | { | 17 | { |
| 18 | struct sk_buff *skb; | 18 | struct sk_buff *skb; |
| 19 | int err = -ENOMEM; | 19 | int err = -ENOMEM; |
| @@ -23,7 +23,7 @@ static int ethnl_cable_test_started(struct phy_device *phydev) · 3 unmodified lines | |||
| 23 | if (!skb) | 23 | if (!skb) |
| 24 | goto out; | 24 | goto out; |
| 25 | 25 | ||
| 26 | ehdr = ethnl_bcastmsg_put(skb, ETHTOOL_MSG_CABLE_TEST_NTF); | 26 | ehdr = ethnl_bcastmsg_put(skb, cmd); |
| 27 | if (!ehdr) { | 27 | if (!ehdr) { |
| 28 | err = -EMSGSIZE; | 28 | err = -EMSGSIZE; |
| 29 | goto out; | 29 | goto out; |
| @@ -86,7 +86,8 @@ int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info) · 56 unmodified lines | |||
| 86 | ethnl_ops_complete(dev); | 86 | ethnl_ops_complete(dev); |
| 87 | 87 | ||
| 88 | if (!ret) | 88 | if (!ret) |
| 89 | ethnl_cable_test_started(dev->phydev); | 89 | ethnl_cable_test_started(dev->phydev, |
| 90 | ETHTOOL_MSG_CABLE_TEST_NTF); | ||
| 90 | 91 | ||
| 91 | out_rtnl: | 92 | out_rtnl: |
| 92 | rtnl_unlock(); | 93 | rtnl_unlock(); |
| @@ -95,7 +96,7 @@ out_dev_put: · 2 unmodified lines | |||
| 95 | return ret; | 96 | return ret; |
| 96 | } | 97 | } |
| 97 | 98 | ||
| 98 | int ethnl_cable_test_alloc(struct phy_device *phydev) | 99 | int ethnl_cable_test_alloc(struct phy_device *phydev, u8 cmd) |
| 99 | { | 100 | { |
| 100 | int err = -ENOMEM; | 101 | int err = -ENOMEM; |
| 101 | 102 | ||
| @@ -103,8 +104,7 @@ int ethnl_cable_test_alloc(struct phy_device *phydev) · 1 unmodified line | |||
| 103 | if (!phydev->skb) | 104 | if (!phydev->skb) |
| 104 | goto out; | 105 | goto out; |
| 105 | 106 | ||
| 106 | phydev->ehdr = ethnl_bcastmsg_put(phydev->skb, | 107 | phydev->ehdr = ethnl_bcastmsg_put(phydev->skb, cmd); |
| 107 | ETHTOOL_MSG_CABLE_TEST_NTF); | ||
| 108 | if (!phydev->ehdr) { | 108 | if (!phydev->ehdr) { |
| 109 | err = -EMSGSIZE; | 109 | err = -EMSGSIZE; |
| 110 | goto out; | 110 | goto out; |
| @@ -199,3 +199,55 @@ err: · 88 unmodified lines | |||
| 199 | return ret; | 199 | return ret; |
| 200 | } | 200 | } |
| 201 | EXPORT_SYMBOL_GPL(ethnl_cable_test_fault_length); | 201 | EXPORT_SYMBOL_GPL(ethnl_cable_test_fault_length); |
| 202 | |||
| 203 | static const struct nla_policy | ||
| 204 | cable_test_tdr_act_policy[ETHTOOL_A_CABLE_TEST_TDR_MAX + 1] = { | ||
| 205 | [ETHTOOL_A_CABLE_TEST_TDR_UNSPEC] = { .type = NLA_REJECT }, | ||
| 206 | [ETHTOOL_A_CABLE_TEST_TDR_HEADER] = { .type = NLA_NESTED }, | ||
| 207 | }; | ||
| 208 | |||
| 209 | int ethnl_act_cable_test_tdr(struct sk_buff *skb, struct genl_info *info) | ||
| 210 | { | ||
| 211 | struct nlattr *tb[ETHTOOL_A_CABLE_TEST_TDR_MAX + 1]; | ||
| 212 | struct ethnl_req_info req_info = {}; | ||
| 213 | struct net_device *dev; | ||
| 214 | int ret; | ||
| 215 | |||
| 216 | ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb, | ||
| 217 | ETHTOOL_A_CABLE_TEST_TDR_MAX, | ||
| 218 | cable_test_tdr_act_policy, info->extack); | ||
| 219 | if (ret < 0) | ||
| 220 | return ret; | ||
| 221 | |||
| 222 | ret = ethnl_parse_header_dev_get(&req_info, | ||
| 223 | tb[ETHTOOL_A_CABLE_TEST_TDR_HEADER], | ||
| 224 | genl_info_net(info), info->extack, | ||
| 225 | true); | ||
| 226 | if (ret < 0) | ||
| 227 | return ret; | ||
| 228 | |||
| 229 | dev = req_info.dev; | ||
| 230 | if (!dev->phydev) { | ||
| 231 | ret = -EOPNOTSUPP; | ||
| 232 | goto out_dev_put; | ||
| 233 | } | ||
| 234 | |||
| 235 | rtnl_lock(); | ||
| 236 | ret = ethnl_ops_begin(dev); | ||
| 237 | if (ret < 0) | ||
| 238 | goto out_rtnl; | ||
| 239 | |||
| 240 | ret = phy_start_cable_test_tdr(dev->phydev, info->extack); | ||
| 241 | |||
| 242 | ethnl_ops_complete(dev); | ||
| 243 | |||
| 244 | if (!ret) | ||
| 245 | ethnl_cable_test_started(dev->phydev, | ||
| 246 | ETHTOOL_MSG_CABLE_TEST_TDR_NTF); | ||
| 247 | |||
| 248 | out_rtnl: | ||
| 249 | rtnl_unlock(); | ||
| 250 | out_dev_put: | ||
| 251 | dev_put(dev); | ||
| 252 | return ret; | ||
| 253 | } | ||
| @@ -844,6 +844,11 @@ static const struct genl_ops ethtool_genl_ops[] = { · 843 unmodified lines | |||
| 844 | .flags = GENL_UNS_ADMIN_PERM, | 844 | .flags = GENL_UNS_ADMIN_PERM, |
| 845 | .doit = ethnl_act_cable_test, | 845 | .doit = ethnl_act_cable_test, |
| 846 | }, | 846 | }, |
| 847 | { | ||
| 848 | .cmd = ETHTOOL_MSG_CABLE_TEST_TDR_ACT, | ||
| 849 | .flags = GENL_UNS_ADMIN_PERM, | ||
| 850 | .doit = ethnl_act_cable_test_tdr, | ||
| 851 | }, | ||
| 847 | }; | 852 | }; |
| 848 | 853 | ||
| 849 | static const struct genl_multicast_group ethtool_nl_mcgrps[] = { | 854 | static const struct genl_multicast_group ethtool_nl_mcgrps[] = { |
| @@ -360,5 +360,6 @@ int ethnl_set_coalesce(struct sk_buff *skb, struct genl_info *info); · 359 unmodified lines | |||
| 360 | int ethnl_set_pause(struct sk_buff *skb, struct genl_info *info); | 360 | int ethnl_set_pause(struct sk_buff *skb, struct genl_info *info); |
| 361 | int ethnl_set_eee(struct sk_buff *skb, struct genl_info *info); | 361 | int ethnl_set_eee(struct sk_buff *skb, struct genl_info *info); |
| 362 | int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info); | 362 | int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info); |
| 363 | int ethnl_act_cable_test_tdr(struct sk_buff *skb, struct genl_info *info); | ||
| 363 | 364 | ||
| 364 | #endif /* _NET_ETHTOOL_NETLINK_H */ | 365 | #endif /* _NET_ETHTOOL_NETLINK_H */ |