]> git.baikalelectronics.ru Git - kernel.git/commitdiff
net: ethtool: Add generic parts of cable test TDR
authorAndrew Lunn <andrew@lunn.ch>
Tue, 26 May 2020 22:21:38 +0000 (00:21 +0200)
committerDavid S. Miller <davem@davemloft.net>
Wed, 27 May 2020 06:21:48 +0000 (23:21 -0700)
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.

Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/phy/nxp-tja11xx.c
drivers/net/phy/phy.c
include/linux/ethtool_netlink.h
include/linux/phy.h
net/ethtool/cabletest.c
net/ethtool/netlink.c
net/ethtool/netlink.h

index 1e79c30ca81a57a58ba24cea8dc8c3511b519766..a72fa0d2e7c79c3a5408f5ae7ce2a50f5ac9ef1a 100644 (file)
@@ -194,7 +194,7 @@ static int tja11xx_config_aneg_cable_test(struct phy_device *phydev)
            !phydev->drv->cable_test_get_status)
                return 0;
 
-       ret = ethnl_cable_test_alloc(phydev);
+       ret = ethnl_cable_test_alloc(phydev, ETHTOOL_MSG_CABLE_TEST_NTF);
        if (ret)
                return ret;
 
index 27da0c94818f34d640fa0564003c931b50ac9313..495d9ba3d5bf20a6d3d48214b779014d09a704ff 100644 (file)
@@ -519,7 +519,7 @@ int phy_start_cable_test(struct phy_device *phydev,
                goto out;
        }
 
-       err = ethnl_cable_test_alloc(phydev);
+       err = ethnl_cable_test_alloc(phydev, ETHTOOL_MSG_CABLE_TEST_NTF);
        if (err)
                goto out;
 
@@ -552,6 +552,69 @@ out:
 }
 EXPORT_SYMBOL(phy_start_cable_test);
 
+int phy_start_cable_test_tdr(struct phy_device *phydev,
+                            struct netlink_ext_ack *extack)
+{
+       struct net_device *dev = phydev->attached_dev;
+       int err = -ENOMEM;
+
+       if (!(phydev->drv &&
+             phydev->drv->cable_test_tdr_start &&
+             phydev->drv->cable_test_get_status)) {
+               NL_SET_ERR_MSG(extack,
+                              "PHY driver does not support cable test TDR");
+               return -EOPNOTSUPP;
+       }
+
+       mutex_lock(&phydev->lock);
+       if (phydev->state == PHY_CABLETEST) {
+               NL_SET_ERR_MSG(extack,
+                              "PHY already performing a test");
+               err = -EBUSY;
+               goto out;
+       }
+
+       if (phydev->state < PHY_UP ||
+           phydev->state > PHY_CABLETEST) {
+               NL_SET_ERR_MSG(extack,
+                              "PHY not configured. Try setting interface up");
+               err = -EBUSY;
+               goto out;
+       }
+
+       err = ethnl_cable_test_alloc(phydev, ETHTOOL_MSG_CABLE_TEST_TDR_NTF);
+       if (err)
+               goto out;
+
+       /* Mark the carrier down until the test is complete */
+       phy_link_down(phydev);
+
+       netif_testing_on(dev);
+       err = phydev->drv->cable_test_tdr_start(phydev);
+       if (err) {
+               netif_testing_off(dev);
+               phy_link_up(phydev);
+               goto out_free;
+       }
+
+       phydev->state = PHY_CABLETEST;
+
+       if (phy_polling_mode(phydev))
+               phy_trigger_machine(phydev);
+
+       mutex_unlock(&phydev->lock);
+
+       return 0;
+
+out_free:
+       ethnl_cable_test_free(phydev);
+out:
+       mutex_unlock(&phydev->lock);
+
+       return err;
+}
+EXPORT_SYMBOL(phy_start_cable_test_tdr);
+
 static int phy_config_aneg(struct phy_device *phydev)
 {
        if (phydev->drv->config_aneg)
index e317fc99565e45f091df80d1eda87e5f4d1943ef..24817ba252a0c6ee4260685749c9c9cd9437e986 100644 (file)
@@ -17,13 +17,13 @@ enum ethtool_multicast_groups {
 struct phy_device;
 
 #if IS_ENABLED(CONFIG_ETHTOOL_NETLINK)
-int ethnl_cable_test_alloc(struct phy_device *phydev);
+int ethnl_cable_test_alloc(struct phy_device *phydev, u8 cmd);
 void ethnl_cable_test_free(struct phy_device *phydev);
 void ethnl_cable_test_finished(struct phy_device *phydev);
 int ethnl_cable_test_result(struct phy_device *phydev, u8 pair, u8 result);
 int ethnl_cable_test_fault_length(struct phy_device *phydev, u8 pair, u32 cm);
 #else
-static inline int ethnl_cable_test_alloc(struct phy_device *phydev)
+static inline int ethnl_cable_test_alloc(struct phy_device *phydev, u8 cmd)
 {
        return -EOPNOTSUPP;
 }
index 6d256e720a660062af388212f0f86597be2a62ac..d3c384f353ca8ee084c4ae99078d15b8d8b4efc9 100644 (file)
@@ -699,6 +699,10 @@ struct phy_driver {
 
        /* Start a cable test */
        int (*cable_test_start)(struct phy_device *dev);
+
+       /* Start a raw TDR cable test */
+       int (*cable_test_tdr_start)(struct phy_device *dev);
+
        /* Once per second, or on interrupt, request the status of the
         * test.
         */
@@ -1251,6 +1255,8 @@ int phy_reset_after_clk_enable(struct phy_device *phydev);
 #if IS_ENABLED(CONFIG_PHYLIB)
 int phy_start_cable_test(struct phy_device *phydev,
                         struct netlink_ext_ack *extack);
+int phy_start_cable_test_tdr(struct phy_device *phydev,
+                            struct netlink_ext_ack *extack);
 #else
 static inline
 int phy_start_cable_test(struct phy_device *phydev,
@@ -1259,6 +1265,13 @@ int phy_start_cable_test(struct phy_device *phydev,
        NL_SET_ERR_MSG(extack, "Kernel not compiled with PHYLIB support");
        return -EOPNOTSUPP;
 }
+static inline
+int phy_start_cable_test_tdr(struct phy_device *phydev,
+                            struct netlink_ext_ack *extack)
+{
+       NL_SET_ERR_MSG(extack, "Kernel not compiled with PHYLIB support");
+       return -EOPNOTSUPP;
+}
 #endif
 
 int phy_cable_test_result(struct phy_device *phydev, u8 pair, u16 result);
index 5ba06eabe8c2838d5aa9c57934b06f992237bb2d..94e9d5f043535d759bc06f9a4053ab6f4010a346 100644 (file)
@@ -13,7 +13,7 @@ cable_test_act_policy[ETHTOOL_A_CABLE_TEST_MAX + 1] = {
        [ETHTOOL_A_CABLE_TEST_HEADER]           = { .type = NLA_NESTED },
 };
 
-static int ethnl_cable_test_started(struct phy_device *phydev)
+static int ethnl_cable_test_started(struct phy_device *phydev, u8 cmd)
 {
        struct sk_buff *skb;
        int err = -ENOMEM;
@@ -23,7 +23,7 @@ static int ethnl_cable_test_started(struct phy_device *phydev)
        if (!skb)
                goto out;
 
-       ehdr = ethnl_bcastmsg_put(skb, ETHTOOL_MSG_CABLE_TEST_NTF);
+       ehdr = ethnl_bcastmsg_put(skb, cmd);
        if (!ehdr) {
                err = -EMSGSIZE;
                goto out;
@@ -86,7 +86,8 @@ int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info)
        ethnl_ops_complete(dev);
 
        if (!ret)
-               ethnl_cable_test_started(dev->phydev);
+               ethnl_cable_test_started(dev->phydev,
+                                        ETHTOOL_MSG_CABLE_TEST_NTF);
 
 out_rtnl:
        rtnl_unlock();
@@ -95,7 +96,7 @@ out_dev_put:
        return ret;
 }
 
-int ethnl_cable_test_alloc(struct phy_device *phydev)
+int ethnl_cable_test_alloc(struct phy_device *phydev, u8 cmd)
 {
        int err = -ENOMEM;
 
@@ -103,8 +104,7 @@ int ethnl_cable_test_alloc(struct phy_device *phydev)
        if (!phydev->skb)
                goto out;
 
-       phydev->ehdr = ethnl_bcastmsg_put(phydev->skb,
-                                         ETHTOOL_MSG_CABLE_TEST_NTF);
+       phydev->ehdr = ethnl_bcastmsg_put(phydev->skb, cmd);
        if (!phydev->ehdr) {
                err = -EMSGSIZE;
                goto out;
@@ -199,3 +199,55 @@ err:
        return ret;
 }
 EXPORT_SYMBOL_GPL(ethnl_cable_test_fault_length);
+
+static const struct nla_policy
+cable_test_tdr_act_policy[ETHTOOL_A_CABLE_TEST_TDR_MAX + 1] = {
+       [ETHTOOL_A_CABLE_TEST_TDR_UNSPEC]       = { .type = NLA_REJECT },
+       [ETHTOOL_A_CABLE_TEST_TDR_HEADER]       = { .type = NLA_NESTED },
+};
+
+int ethnl_act_cable_test_tdr(struct sk_buff *skb, struct genl_info *info)
+{
+       struct nlattr *tb[ETHTOOL_A_CABLE_TEST_TDR_MAX + 1];
+       struct ethnl_req_info req_info = {};
+       struct net_device *dev;
+       int ret;
+
+       ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb,
+                         ETHTOOL_A_CABLE_TEST_TDR_MAX,
+                         cable_test_tdr_act_policy, info->extack);
+       if (ret < 0)
+               return ret;
+
+       ret = ethnl_parse_header_dev_get(&req_info,
+                                        tb[ETHTOOL_A_CABLE_TEST_TDR_HEADER],
+                                        genl_info_net(info), info->extack,
+                                        true);
+       if (ret < 0)
+               return ret;
+
+       dev = req_info.dev;
+       if (!dev->phydev) {
+               ret = -EOPNOTSUPP;
+               goto out_dev_put;
+       }
+
+       rtnl_lock();
+       ret = ethnl_ops_begin(dev);
+       if (ret < 0)
+               goto out_rtnl;
+
+       ret = phy_start_cable_test_tdr(dev->phydev, info->extack);
+
+       ethnl_ops_complete(dev);
+
+       if (!ret)
+               ethnl_cable_test_started(dev->phydev,
+                                        ETHTOOL_MSG_CABLE_TEST_TDR_NTF);
+
+out_rtnl:
+       rtnl_unlock();
+out_dev_put:
+       dev_put(dev);
+       return ret;
+}
index 0f2f4754dcf92d4c368c86353a130e42df77573c..88fd07f47040ca57ca4a215b0d30d830a70c9ecc 100644 (file)
@@ -844,6 +844,11 @@ static const struct genl_ops ethtool_genl_ops[] = {
                .flags  = GENL_UNS_ADMIN_PERM,
                .doit   = ethnl_act_cable_test,
        },
+       {
+               .cmd    = ETHTOOL_MSG_CABLE_TEST_TDR_ACT,
+               .flags  = GENL_UNS_ADMIN_PERM,
+               .doit   = ethnl_act_cable_test_tdr,
+       },
 };
 
 static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
index b0eb5d9200992986461e055a05cdfdfe7f1895a5..9a96b6e90dc2283b07e812e73393972220dca883 100644 (file)
@@ -360,5 +360,6 @@ int ethnl_set_coalesce(struct sk_buff *skb, struct genl_info *info);
 int ethnl_set_pause(struct sk_buff *skb, struct genl_info *info);
 int ethnl_set_eee(struct sk_buff *skb, struct genl_info *info);
 int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info);
+int ethnl_act_cable_test_tdr(struct sk_buff *skb, struct genl_info *info);
 
 #endif /* _NET_ETHTOOL_NETLINK_H */