aboutsummaryrefslogtreecommitdiffstats
path: root/net/ethtool
diff options
context:
space:
mode:
Diffstat (limited to 'net/ethtool')
-rw-r--r--net/ethtool/cabletest.c104
1 files changed, 102 insertions, 2 deletions
diff --git a/net/ethtool/cabletest.c b/net/ethtool/cabletest.c
index 390d0673ff01..9991688d7d1d 100644
--- a/net/ethtool/cabletest.c
+++ b/net/ethtool/cabletest.c
@@ -5,7 +5,11 @@
#include "netlink.h"
#include "common.h"
-/* CABLE_TEST_ACT */
+/* 802.3 standard allows 100 meters for BaseT cables. However longer
+ * cables might work, depending on the quality of the cables and the
+ * PHY. So allow testing for up to 150 meters.
+ */
+#define MAX_CABLE_LENGTH_CM (150 * 100)
static const struct nla_policy
cable_test_act_policy[ETHTOOL_A_CABLE_TEST_MAX + 1] = {
@@ -203,16 +207,107 @@ err:
}
EXPORT_SYMBOL_GPL(ethnl_cable_test_fault_length);
+struct cable_test_tdr_req_info {
+ struct ethnl_req_info base;
+};
+
+static const struct nla_policy
+cable_test_tdr_act_cfg_policy[ETHTOOL_A_CABLE_TEST_TDR_CFG_MAX + 1] = {
+ [ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST] = { .type = NLA_U32 },
+ [ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST] = { .type = NLA_U32 },
+ [ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP] = { .type = NLA_U32 },
+ [ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR] = { .type = NLA_U8 },
+};
+
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 },
+ [ETHTOOL_A_CABLE_TEST_TDR_CFG] = { .type = NLA_NESTED },
};
+/* CABLE_TEST_TDR_ACT */
+int ethnl_act_cable_test_tdr_cfg(const struct nlattr *nest,
+ struct genl_info *info,
+ struct phy_tdr_config *cfg)
+{
+ struct nlattr *tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_MAX + 1];
+ int ret;
+
+ ret = nla_parse_nested(tb, ETHTOOL_A_CABLE_TEST_TDR_CFG_MAX, nest,
+ cable_test_tdr_act_cfg_policy, info->extack);
+ if (ret < 0)
+ return ret;
+
+ if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST])
+ cfg->first = nla_get_u32(
+ tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST]);
+ else
+ cfg->first = 100;
+ if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST])
+ cfg->last = nla_get_u32(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST]);
+ else
+ cfg->last = MAX_CABLE_LENGTH_CM;
+
+ if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP])
+ cfg->step = nla_get_u32(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP]);
+ else
+ cfg->step = 100;
+
+ if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]) {
+ cfg->pair = nla_get_u8(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]);
+ if (cfg->pair > ETHTOOL_A_CABLE_PAIR_D) {
+ NL_SET_ERR_MSG_ATTR(
+ info->extack,
+ tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR],
+ "invalid pair parameter");
+ return -EINVAL;
+ }
+ } else {
+ cfg->pair = PHY_PAIR_ALL;
+ }
+
+ if (cfg->first > MAX_CABLE_LENGTH_CM) {
+ NL_SET_ERR_MSG_ATTR(info->extack,
+ tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST],
+ "invalid first parameter");
+ return -EINVAL;
+ }
+
+ if (cfg->last > MAX_CABLE_LENGTH_CM) {
+ NL_SET_ERR_MSG_ATTR(info->extack,
+ tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST],
+ "invalid last parameter");
+ return -EINVAL;
+ }
+
+ if (cfg->first > cfg->last) {
+ NL_SET_ERR_MSG(info->extack, "invalid first/last parameter");
+ return -EINVAL;
+ }
+
+ if (!cfg->step) {
+ NL_SET_ERR_MSG_ATTR(info->extack,
+ tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP],
+ "invalid step parameter");
+ return -EINVAL;
+ }
+
+ if (cfg->step > (cfg->last - cfg->first)) {
+ NL_SET_ERR_MSG_ATTR(info->extack,
+ tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP],
+ "step parameter too big");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
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 phy_tdr_config cfg;
struct net_device *dev;
int ret;
@@ -235,12 +330,17 @@ int ethnl_act_cable_test_tdr(struct sk_buff *skb, struct genl_info *info)
goto out_dev_put;
}
+ ret = ethnl_act_cable_test_tdr_cfg(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG],
+ info, &cfg);
+ if (ret)
+ 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);
+ ret = phy_start_cable_test_tdr(dev->phydev, info->extack, &cfg);
ethnl_ops_complete(dev);