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 | 194 | !phydev->drv->cable_test_get_status) |
| 195 | 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 | 198 | if (ret) |
| 199 | 199 | return ret; |
| 200 | 200 | |
| @@ -519,7 +519,7 @@ int phy_start_cable_test(struct phy_device *phydev, · 518 unmodified lines | ||
| 519 | 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 | 523 | if (err) |
| 524 | 524 | goto out; |
| 525 | 525 | |
| @@ -552,6 +552,69 @@ out: · 26 unmodified lines | ||
| 552 | 552 | } |
| 553 | 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 | 618 | static int phy_config_aneg(struct phy_device *phydev) |
| 556 | 619 | { |
| 557 | 620 | if (phydev->drv->config_aneg) |
| @@ -17,13 +17,13 @@ enum ethtool_multicast_groups { · 16 unmodified lines | ||
| 17 | 17 | struct phy_device; |
| 18 | 18 | |
| 19 | 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 | 21 | void ethnl_cable_test_free(struct phy_device *phydev); |
| 22 | 22 | void ethnl_cable_test_finished(struct phy_device *phydev); |
| 23 | 23 | int ethnl_cable_test_result(struct phy_device *phydev, u8 pair, u8 result); |
| 24 | 24 | int ethnl_cable_test_fault_length(struct phy_device *phydev, u8 pair, u32 cm); |
| 25 | 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 | 28 | return -EOPNOTSUPP; |
| 29 | 29 | } |
| @@ -699,6 +699,10 @@ struct phy_driver { · 698 unmodified lines | ||
| 699 | 699 | |
| 700 | 700 | /* Start a cable test */ |
| 701 | 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 | 706 | /* Once per second, or on interrupt, request the status of the |
| 703 | 707 | * test. |
| 704 | 708 | */ |
| @@ -1251,6 +1255,8 @@ int phy_reset_after_clk_enable(struct phy_device *phydev); · 546 unmodified lines | ||
| 1251 | 1255 | #if IS_ENABLED(CONFIG_PHYLIB) |
| 1252 | 1256 | int phy_start_cable_test(struct phy_device *phydev, |
| 1253 | 1257 | struct netlink_ext_ack *extack); |
| 1258 | +int phy_start_cable_test_tdr(struct phy_device *phydev, | |
| 1259 | + struct netlink_ext_ack *extack); | |
| 1254 | 1260 | #else |
| 1255 | 1261 | static inline |
| 1256 | 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 | 1265 | NL_SET_ERR_MSG(extack, "Kernel not compiled with PHYLIB support"); |
| 1260 | 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 | 1275 | #endif |
| 1263 | 1276 | |
| 1264 | 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 | 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 | 18 | struct sk_buff *skb; |
| 19 | 19 | int err = -ENOMEM; |
| @@ -23,7 +23,7 @@ static int ethnl_cable_test_started(struct phy_device *phydev) · 3 unmodified lines | ||
| 23 | 23 | if (!skb) |
| 24 | 24 | goto out; |
| 25 | 25 | |
| 26 | − ehdr = ethnl_bcastmsg_put(skb, ETHTOOL_MSG_CABLE_TEST_NTF); | |
| 26 | + ehdr = ethnl_bcastmsg_put(skb, cmd); | |
| 27 | 27 | if (!ehdr) { |
| 28 | 28 | err = -EMSGSIZE; |
| 29 | 29 | goto out; |
| @@ -86,7 +86,8 @@ int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info) · 56 unmodified lines | ||
| 86 | 86 | ethnl_ops_complete(dev); |
| 87 | 87 | |
| 88 | 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 | 92 | out_rtnl: |
| 92 | 93 | rtnl_unlock(); |
| @@ -95,7 +96,7 @@ out_dev_put: · 2 unmodified lines | ||
| 95 | 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 | 101 | int err = -ENOMEM; |
| 101 | 102 | |
| @@ -103,8 +104,7 @@ int ethnl_cable_test_alloc(struct phy_device *phydev) · 1 unmodified line | ||
| 103 | 104 | if (!phydev->skb) |
| 104 | 105 | goto out; |
| 105 | 106 | |
| 106 | − phydev->ehdr = ethnl_bcastmsg_put(phydev->skb, | |
| 107 | − ETHTOOL_MSG_CABLE_TEST_NTF); | |
| 107 | + phydev->ehdr = ethnl_bcastmsg_put(phydev->skb, cmd); | |
| 108 | 108 | if (!phydev->ehdr) { |
| 109 | 109 | err = -EMSGSIZE; |
| 110 | 110 | goto out; |
| @@ -199,3 +199,55 @@ err: · 88 unmodified lines | ||
| 199 | 199 | return ret; |
| 200 | 200 | } |
| 201 | 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 | 844 | .flags = GENL_UNS_ADMIN_PERM, |
| 845 | 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 | 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 | 360 | int ethnl_set_pause(struct sk_buff *skb, struct genl_info *info); |
| 361 | 361 | int ethnl_set_eee(struct sk_buff *skb, struct genl_info *info); |
| 362 | 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 | 365 | #endif /* _NET_ETHTOOL_NETLINK_H */ |