Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 
0003 #include <linux/phy.h>
0004 #include <linux/ethtool_netlink.h>
0005 #include "netlink.h"
0006 #include "common.h"
0007 
0008 /* 802.3 standard allows 100 meters for BaseT cables. However longer
0009  * cables might work, depending on the quality of the cables and the
0010  * PHY. So allow testing for up to 150 meters.
0011  */
0012 #define MAX_CABLE_LENGTH_CM (150 * 100)
0013 
0014 const struct nla_policy ethnl_cable_test_act_policy[] = {
0015     [ETHTOOL_A_CABLE_TEST_HEADER]       =
0016         NLA_POLICY_NESTED(ethnl_header_policy),
0017 };
0018 
0019 static int ethnl_cable_test_started(struct phy_device *phydev, u8 cmd)
0020 {
0021     struct sk_buff *skb;
0022     int err = -ENOMEM;
0023     void *ehdr;
0024 
0025     skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
0026     if (!skb)
0027         goto out;
0028 
0029     ehdr = ethnl_bcastmsg_put(skb, cmd);
0030     if (!ehdr) {
0031         err = -EMSGSIZE;
0032         goto out;
0033     }
0034 
0035     err = ethnl_fill_reply_header(skb, phydev->attached_dev,
0036                       ETHTOOL_A_CABLE_TEST_NTF_HEADER);
0037     if (err)
0038         goto out;
0039 
0040     err = nla_put_u8(skb, ETHTOOL_A_CABLE_TEST_NTF_STATUS,
0041              ETHTOOL_A_CABLE_TEST_NTF_STATUS_STARTED);
0042     if (err)
0043         goto out;
0044 
0045     genlmsg_end(skb, ehdr);
0046 
0047     return ethnl_multicast(skb, phydev->attached_dev);
0048 
0049 out:
0050     nlmsg_free(skb);
0051     phydev_err(phydev, "%s: Error %pe\n", __func__, ERR_PTR(err));
0052 
0053     return err;
0054 }
0055 
0056 int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info)
0057 {
0058     struct ethnl_req_info req_info = {};
0059     const struct ethtool_phy_ops *ops;
0060     struct nlattr **tb = info->attrs;
0061     struct net_device *dev;
0062     int ret;
0063 
0064     ret = ethnl_parse_header_dev_get(&req_info,
0065                      tb[ETHTOOL_A_CABLE_TEST_HEADER],
0066                      genl_info_net(info), info->extack,
0067                      true);
0068     if (ret < 0)
0069         return ret;
0070 
0071     dev = req_info.dev;
0072     if (!dev->phydev) {
0073         ret = -EOPNOTSUPP;
0074         goto out_dev_put;
0075     }
0076 
0077     rtnl_lock();
0078     ops = ethtool_phy_ops;
0079     if (!ops || !ops->start_cable_test) {
0080         ret = -EOPNOTSUPP;
0081         goto out_rtnl;
0082     }
0083 
0084     ret = ethnl_ops_begin(dev);
0085     if (ret < 0)
0086         goto out_rtnl;
0087 
0088     ret = ops->start_cable_test(dev->phydev, info->extack);
0089 
0090     ethnl_ops_complete(dev);
0091 
0092     if (!ret)
0093         ethnl_cable_test_started(dev->phydev,
0094                      ETHTOOL_MSG_CABLE_TEST_NTF);
0095 
0096 out_rtnl:
0097     rtnl_unlock();
0098 out_dev_put:
0099     ethnl_parse_header_dev_put(&req_info);
0100     return ret;
0101 }
0102 
0103 int ethnl_cable_test_alloc(struct phy_device *phydev, u8 cmd)
0104 {
0105     int err = -ENOMEM;
0106 
0107     /* One TDR sample occupies 20 bytes. For a 150 meter cable,
0108      * with four pairs, around 12K is needed.
0109      */
0110     phydev->skb = genlmsg_new(SZ_16K, GFP_KERNEL);
0111     if (!phydev->skb)
0112         goto out;
0113 
0114     phydev->ehdr = ethnl_bcastmsg_put(phydev->skb, cmd);
0115     if (!phydev->ehdr) {
0116         err = -EMSGSIZE;
0117         goto out;
0118     }
0119 
0120     err = ethnl_fill_reply_header(phydev->skb, phydev->attached_dev,
0121                       ETHTOOL_A_CABLE_TEST_NTF_HEADER);
0122     if (err)
0123         goto out;
0124 
0125     err = nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_TEST_NTF_STATUS,
0126              ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED);
0127     if (err)
0128         goto out;
0129 
0130     phydev->nest = nla_nest_start(phydev->skb,
0131                       ETHTOOL_A_CABLE_TEST_NTF_NEST);
0132     if (!phydev->nest) {
0133         err = -EMSGSIZE;
0134         goto out;
0135     }
0136 
0137     return 0;
0138 
0139 out:
0140     nlmsg_free(phydev->skb);
0141     phydev->skb = NULL;
0142     return err;
0143 }
0144 EXPORT_SYMBOL_GPL(ethnl_cable_test_alloc);
0145 
0146 void ethnl_cable_test_free(struct phy_device *phydev)
0147 {
0148     nlmsg_free(phydev->skb);
0149     phydev->skb = NULL;
0150 }
0151 EXPORT_SYMBOL_GPL(ethnl_cable_test_free);
0152 
0153 void ethnl_cable_test_finished(struct phy_device *phydev)
0154 {
0155     nla_nest_end(phydev->skb, phydev->nest);
0156 
0157     genlmsg_end(phydev->skb, phydev->ehdr);
0158 
0159     ethnl_multicast(phydev->skb, phydev->attached_dev);
0160 }
0161 EXPORT_SYMBOL_GPL(ethnl_cable_test_finished);
0162 
0163 int ethnl_cable_test_result(struct phy_device *phydev, u8 pair, u8 result)
0164 {
0165     struct nlattr *nest;
0166     int ret = -EMSGSIZE;
0167 
0168     nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_NEST_RESULT);
0169     if (!nest)
0170         return -EMSGSIZE;
0171 
0172     if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_RESULT_PAIR, pair))
0173         goto err;
0174     if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_RESULT_CODE, result))
0175         goto err;
0176 
0177     nla_nest_end(phydev->skb, nest);
0178     return 0;
0179 
0180 err:
0181     nla_nest_cancel(phydev->skb, nest);
0182     return ret;
0183 }
0184 EXPORT_SYMBOL_GPL(ethnl_cable_test_result);
0185 
0186 int ethnl_cable_test_fault_length(struct phy_device *phydev, u8 pair, u32 cm)
0187 {
0188     struct nlattr *nest;
0189     int ret = -EMSGSIZE;
0190 
0191     nest = nla_nest_start(phydev->skb,
0192                   ETHTOOL_A_CABLE_NEST_FAULT_LENGTH);
0193     if (!nest)
0194         return -EMSGSIZE;
0195 
0196     if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR, pair))
0197         goto err;
0198     if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_FAULT_LENGTH_CM, cm))
0199         goto err;
0200 
0201     nla_nest_end(phydev->skb, nest);
0202     return 0;
0203 
0204 err:
0205     nla_nest_cancel(phydev->skb, nest);
0206     return ret;
0207 }
0208 EXPORT_SYMBOL_GPL(ethnl_cable_test_fault_length);
0209 
0210 struct cable_test_tdr_req_info {
0211     struct ethnl_req_info       base;
0212 };
0213 
0214 static const struct nla_policy cable_test_tdr_act_cfg_policy[] = {
0215     [ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST]    = { .type = NLA_U32 },
0216     [ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST] = { .type = NLA_U32 },
0217     [ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP] = { .type = NLA_U32 },
0218     [ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR] = { .type = NLA_U8 },
0219 };
0220 
0221 const struct nla_policy ethnl_cable_test_tdr_act_policy[] = {
0222     [ETHTOOL_A_CABLE_TEST_TDR_HEADER]   =
0223         NLA_POLICY_NESTED(ethnl_header_policy),
0224     [ETHTOOL_A_CABLE_TEST_TDR_CFG]      = { .type = NLA_NESTED },
0225 };
0226 
0227 /* CABLE_TEST_TDR_ACT */
0228 static int ethnl_act_cable_test_tdr_cfg(const struct nlattr *nest,
0229                     struct genl_info *info,
0230                     struct phy_tdr_config *cfg)
0231 {
0232     struct nlattr *tb[ARRAY_SIZE(cable_test_tdr_act_cfg_policy)];
0233     int ret;
0234 
0235     cfg->first = 100;
0236     cfg->step = 100;
0237     cfg->last = MAX_CABLE_LENGTH_CM;
0238     cfg->pair = PHY_PAIR_ALL;
0239 
0240     if (!nest)
0241         return 0;
0242 
0243     ret = nla_parse_nested(tb,
0244                    ARRAY_SIZE(cable_test_tdr_act_cfg_policy) - 1,
0245                    nest, cable_test_tdr_act_cfg_policy,
0246                    info->extack);
0247     if (ret < 0)
0248         return ret;
0249 
0250     if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST])
0251         cfg->first = nla_get_u32(
0252             tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST]);
0253 
0254     if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST])
0255         cfg->last = nla_get_u32(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST]);
0256 
0257     if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP])
0258         cfg->step = nla_get_u32(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP]);
0259 
0260     if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]) {
0261         cfg->pair = nla_get_u8(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]);
0262         if (cfg->pair > ETHTOOL_A_CABLE_PAIR_D) {
0263             NL_SET_ERR_MSG_ATTR(
0264                 info->extack,
0265                 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR],
0266                 "invalid pair parameter");
0267             return -EINVAL;
0268         }
0269     }
0270 
0271     if (cfg->first > MAX_CABLE_LENGTH_CM) {
0272         NL_SET_ERR_MSG_ATTR(info->extack,
0273                     tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST],
0274                     "invalid first parameter");
0275         return -EINVAL;
0276     }
0277 
0278     if (cfg->last > MAX_CABLE_LENGTH_CM) {
0279         NL_SET_ERR_MSG_ATTR(info->extack,
0280                     tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST],
0281                     "invalid last parameter");
0282         return -EINVAL;
0283     }
0284 
0285     if (cfg->first > cfg->last) {
0286         NL_SET_ERR_MSG(info->extack, "invalid first/last parameter");
0287         return -EINVAL;
0288     }
0289 
0290     if (!cfg->step) {
0291         NL_SET_ERR_MSG_ATTR(info->extack,
0292                     tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP],
0293                     "invalid step parameter");
0294         return -EINVAL;
0295     }
0296 
0297     if (cfg->step > (cfg->last - cfg->first)) {
0298         NL_SET_ERR_MSG_ATTR(info->extack,
0299                     tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP],
0300                     "step parameter too big");
0301         return -EINVAL;
0302     }
0303 
0304     return 0;
0305 }
0306 
0307 int ethnl_act_cable_test_tdr(struct sk_buff *skb, struct genl_info *info)
0308 {
0309     struct ethnl_req_info req_info = {};
0310     const struct ethtool_phy_ops *ops;
0311     struct nlattr **tb = info->attrs;
0312     struct phy_tdr_config cfg;
0313     struct net_device *dev;
0314     int ret;
0315 
0316     ret = ethnl_parse_header_dev_get(&req_info,
0317                      tb[ETHTOOL_A_CABLE_TEST_TDR_HEADER],
0318                      genl_info_net(info), info->extack,
0319                      true);
0320     if (ret < 0)
0321         return ret;
0322 
0323     dev = req_info.dev;
0324     if (!dev->phydev) {
0325         ret = -EOPNOTSUPP;
0326         goto out_dev_put;
0327     }
0328 
0329     ret = ethnl_act_cable_test_tdr_cfg(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG],
0330                        info, &cfg);
0331     if (ret)
0332         goto out_dev_put;
0333 
0334     rtnl_lock();
0335     ops = ethtool_phy_ops;
0336     if (!ops || !ops->start_cable_test_tdr) {
0337         ret = -EOPNOTSUPP;
0338         goto out_rtnl;
0339     }
0340 
0341     ret = ethnl_ops_begin(dev);
0342     if (ret < 0)
0343         goto out_rtnl;
0344 
0345     ret = ops->start_cable_test_tdr(dev->phydev, info->extack, &cfg);
0346 
0347     ethnl_ops_complete(dev);
0348 
0349     if (!ret)
0350         ethnl_cable_test_started(dev->phydev,
0351                      ETHTOOL_MSG_CABLE_TEST_TDR_NTF);
0352 
0353 out_rtnl:
0354     rtnl_unlock();
0355 out_dev_put:
0356     ethnl_parse_header_dev_put(&req_info);
0357     return ret;
0358 }
0359 
0360 int ethnl_cable_test_amplitude(struct phy_device *phydev,
0361                    u8 pair, s16 mV)
0362 {
0363     struct nlattr *nest;
0364     int ret = -EMSGSIZE;
0365 
0366     nest = nla_nest_start(phydev->skb,
0367                   ETHTOOL_A_CABLE_TDR_NEST_AMPLITUDE);
0368     if (!nest)
0369         return -EMSGSIZE;
0370 
0371     if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_AMPLITUDE_PAIR, pair))
0372         goto err;
0373     if (nla_put_u16(phydev->skb, ETHTOOL_A_CABLE_AMPLITUDE_mV, mV))
0374         goto err;
0375 
0376     nla_nest_end(phydev->skb, nest);
0377     return 0;
0378 
0379 err:
0380     nla_nest_cancel(phydev->skb, nest);
0381     return ret;
0382 }
0383 EXPORT_SYMBOL_GPL(ethnl_cable_test_amplitude);
0384 
0385 int ethnl_cable_test_pulse(struct phy_device *phydev, u16 mV)
0386 {
0387     struct nlattr *nest;
0388     int ret = -EMSGSIZE;
0389 
0390     nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_TDR_NEST_PULSE);
0391     if (!nest)
0392         return -EMSGSIZE;
0393 
0394     if (nla_put_u16(phydev->skb, ETHTOOL_A_CABLE_PULSE_mV, mV))
0395         goto err;
0396 
0397     nla_nest_end(phydev->skb, nest);
0398     return 0;
0399 
0400 err:
0401     nla_nest_cancel(phydev->skb, nest);
0402     return ret;
0403 }
0404 EXPORT_SYMBOL_GPL(ethnl_cable_test_pulse);
0405 
0406 int ethnl_cable_test_step(struct phy_device *phydev, u32 first, u32 last,
0407               u32 step)
0408 {
0409     struct nlattr *nest;
0410     int ret = -EMSGSIZE;
0411 
0412     nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_TDR_NEST_STEP);
0413     if (!nest)
0414         return -EMSGSIZE;
0415 
0416     if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE,
0417             first))
0418         goto err;
0419 
0420     if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_LAST_DISTANCE, last))
0421         goto err;
0422 
0423     if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_STEP_DISTANCE, step))
0424         goto err;
0425 
0426     nla_nest_end(phydev->skb, nest);
0427     return 0;
0428 
0429 err:
0430     nla_nest_cancel(phydev->skb, nest);
0431     return ret;
0432 }
0433 EXPORT_SYMBOL_GPL(ethnl_cable_test_step);