111ca3c42SAndrew Lunn // SPDX-License-Identifier: GPL-2.0-only 211ca3c42SAndrew Lunn 311ca3c42SAndrew Lunn #include <linux/phy.h> 41dd3f212SAndrew Lunn #include <linux/ethtool_netlink.h> 511ca3c42SAndrew Lunn #include "netlink.h" 611ca3c42SAndrew Lunn #include "common.h" 711ca3c42SAndrew Lunn 8f2bc8ad3SAndrew Lunn /* 802.3 standard allows 100 meters for BaseT cables. However longer 9f2bc8ad3SAndrew Lunn * cables might work, depending on the quality of the cables and the 10f2bc8ad3SAndrew Lunn * PHY. So allow testing for up to 150 meters. 11f2bc8ad3SAndrew Lunn */ 12f2bc8ad3SAndrew Lunn #define MAX_CABLE_LENGTH_CM (150 * 100) 1311ca3c42SAndrew Lunn 14ff419afaSJakub Kicinski const struct nla_policy ethnl_cable_test_act_policy[] = { 1511ca3c42SAndrew Lunn [ETHTOOL_A_CABLE_TEST_HEADER] = { .type = NLA_NESTED }, 1611ca3c42SAndrew Lunn }; 1711ca3c42SAndrew Lunn 181a644de2SAndrew Lunn static int ethnl_cable_test_started(struct phy_device *phydev, u8 cmd) 199896a457SAndrew Lunn { 209896a457SAndrew Lunn struct sk_buff *skb; 219896a457SAndrew Lunn int err = -ENOMEM; 229896a457SAndrew Lunn void *ehdr; 239896a457SAndrew Lunn 249896a457SAndrew Lunn skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); 259896a457SAndrew Lunn if (!skb) 269896a457SAndrew Lunn goto out; 279896a457SAndrew Lunn 281a644de2SAndrew Lunn ehdr = ethnl_bcastmsg_put(skb, cmd); 299896a457SAndrew Lunn if (!ehdr) { 309896a457SAndrew Lunn err = -EMSGSIZE; 319896a457SAndrew Lunn goto out; 329896a457SAndrew Lunn } 339896a457SAndrew Lunn 349896a457SAndrew Lunn err = ethnl_fill_reply_header(skb, phydev->attached_dev, 359896a457SAndrew Lunn ETHTOOL_A_CABLE_TEST_NTF_HEADER); 369896a457SAndrew Lunn if (err) 379896a457SAndrew Lunn goto out; 389896a457SAndrew Lunn 399896a457SAndrew Lunn err = nla_put_u8(skb, ETHTOOL_A_CABLE_TEST_NTF_STATUS, 409896a457SAndrew Lunn ETHTOOL_A_CABLE_TEST_NTF_STATUS_STARTED); 419896a457SAndrew Lunn if (err) 429896a457SAndrew Lunn goto out; 439896a457SAndrew Lunn 449896a457SAndrew Lunn genlmsg_end(skb, ehdr); 459896a457SAndrew Lunn 469896a457SAndrew Lunn return ethnl_multicast(skb, phydev->attached_dev); 479896a457SAndrew Lunn 489896a457SAndrew Lunn out: 499896a457SAndrew Lunn nlmsg_free(skb); 509896a457SAndrew Lunn phydev_err(phydev, "%s: Error %pe\n", __func__, ERR_PTR(err)); 519896a457SAndrew Lunn 529896a457SAndrew Lunn return err; 539896a457SAndrew Lunn } 549896a457SAndrew Lunn 5511ca3c42SAndrew Lunn int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info) 5611ca3c42SAndrew Lunn { 5711ca3c42SAndrew Lunn struct ethnl_req_info req_info = {}; 58f3631ab0SFlorian Fainelli const struct ethtool_phy_ops *ops; 595028588bSJakub Kicinski struct nlattr **tb = info->attrs; 6011ca3c42SAndrew Lunn struct net_device *dev; 6111ca3c42SAndrew Lunn int ret; 6211ca3c42SAndrew Lunn 6311ca3c42SAndrew Lunn ret = ethnl_parse_header_dev_get(&req_info, 6411ca3c42SAndrew Lunn tb[ETHTOOL_A_CABLE_TEST_HEADER], 6511ca3c42SAndrew Lunn genl_info_net(info), info->extack, 6611ca3c42SAndrew Lunn true); 6711ca3c42SAndrew Lunn if (ret < 0) 6811ca3c42SAndrew Lunn return ret; 6911ca3c42SAndrew Lunn 7011ca3c42SAndrew Lunn dev = req_info.dev; 7111ca3c42SAndrew Lunn if (!dev->phydev) { 7211ca3c42SAndrew Lunn ret = -EOPNOTSUPP; 7311ca3c42SAndrew Lunn goto out_dev_put; 7411ca3c42SAndrew Lunn } 7511ca3c42SAndrew Lunn 7611ca3c42SAndrew Lunn rtnl_lock(); 77f3631ab0SFlorian Fainelli ops = ethtool_phy_ops; 78f3631ab0SFlorian Fainelli if (!ops || !ops->start_cable_test) { 79f3631ab0SFlorian Fainelli ret = -EOPNOTSUPP; 80f3631ab0SFlorian Fainelli goto out_rtnl; 81f3631ab0SFlorian Fainelli } 82f3631ab0SFlorian Fainelli 8311ca3c42SAndrew Lunn ret = ethnl_ops_begin(dev); 8411ca3c42SAndrew Lunn if (ret < 0) 8511ca3c42SAndrew Lunn goto out_rtnl; 8611ca3c42SAndrew Lunn 87f3631ab0SFlorian Fainelli ret = ops->start_cable_test(dev->phydev, info->extack); 8811ca3c42SAndrew Lunn 8911ca3c42SAndrew Lunn ethnl_ops_complete(dev); 909896a457SAndrew Lunn 919896a457SAndrew Lunn if (!ret) 921a644de2SAndrew Lunn ethnl_cable_test_started(dev->phydev, 931a644de2SAndrew Lunn ETHTOOL_MSG_CABLE_TEST_NTF); 949896a457SAndrew Lunn 9511ca3c42SAndrew Lunn out_rtnl: 9611ca3c42SAndrew Lunn rtnl_unlock(); 9711ca3c42SAndrew Lunn out_dev_put: 9811ca3c42SAndrew Lunn dev_put(dev); 9911ca3c42SAndrew Lunn return ret; 10011ca3c42SAndrew Lunn } 1011dd3f212SAndrew Lunn 1021a644de2SAndrew Lunn int ethnl_cable_test_alloc(struct phy_device *phydev, u8 cmd) 1031dd3f212SAndrew Lunn { 1041dd3f212SAndrew Lunn int err = -ENOMEM; 1051dd3f212SAndrew Lunn 1066b4a0fc1SAndrew Lunn /* One TDR sample occupies 20 bytes. For a 150 meter cable, 1076b4a0fc1SAndrew Lunn * with four pairs, around 12K is needed. 1086b4a0fc1SAndrew Lunn */ 1096b4a0fc1SAndrew Lunn phydev->skb = genlmsg_new(SZ_16K, GFP_KERNEL); 1101dd3f212SAndrew Lunn if (!phydev->skb) 1111dd3f212SAndrew Lunn goto out; 1121dd3f212SAndrew Lunn 1131a644de2SAndrew Lunn phydev->ehdr = ethnl_bcastmsg_put(phydev->skb, cmd); 1141dd3f212SAndrew Lunn if (!phydev->ehdr) { 1151dd3f212SAndrew Lunn err = -EMSGSIZE; 1161dd3f212SAndrew Lunn goto out; 1171dd3f212SAndrew Lunn } 1181dd3f212SAndrew Lunn 1191dd3f212SAndrew Lunn err = ethnl_fill_reply_header(phydev->skb, phydev->attached_dev, 1201dd3f212SAndrew Lunn ETHTOOL_A_CABLE_TEST_NTF_HEADER); 1211dd3f212SAndrew Lunn if (err) 1221dd3f212SAndrew Lunn goto out; 1231dd3f212SAndrew Lunn 1241dd3f212SAndrew Lunn err = nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_TEST_NTF_STATUS, 1251dd3f212SAndrew Lunn ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED); 1261dd3f212SAndrew Lunn if (err) 1271dd3f212SAndrew Lunn goto out; 1281dd3f212SAndrew Lunn 1291dd3f212SAndrew Lunn phydev->nest = nla_nest_start(phydev->skb, 1301dd3f212SAndrew Lunn ETHTOOL_A_CABLE_TEST_NTF_NEST); 1311e2dc145SAndrew Lunn if (!phydev->nest) { 1321e2dc145SAndrew Lunn err = -EMSGSIZE; 1331dd3f212SAndrew Lunn goto out; 1341e2dc145SAndrew Lunn } 1351dd3f212SAndrew Lunn 1361dd3f212SAndrew Lunn return 0; 1371dd3f212SAndrew Lunn 1381dd3f212SAndrew Lunn out: 1391dd3f212SAndrew Lunn nlmsg_free(phydev->skb); 1401e2dc145SAndrew Lunn phydev->skb = NULL; 1411dd3f212SAndrew Lunn return err; 1421dd3f212SAndrew Lunn } 1431dd3f212SAndrew Lunn EXPORT_SYMBOL_GPL(ethnl_cable_test_alloc); 1441dd3f212SAndrew Lunn 1451dd3f212SAndrew Lunn void ethnl_cable_test_free(struct phy_device *phydev) 1461dd3f212SAndrew Lunn { 1471dd3f212SAndrew Lunn nlmsg_free(phydev->skb); 1481e2dc145SAndrew Lunn phydev->skb = NULL; 1491dd3f212SAndrew Lunn } 1501dd3f212SAndrew Lunn EXPORT_SYMBOL_GPL(ethnl_cable_test_free); 1511dd3f212SAndrew Lunn 1521dd3f212SAndrew Lunn void ethnl_cable_test_finished(struct phy_device *phydev) 1531dd3f212SAndrew Lunn { 1541dd3f212SAndrew Lunn nla_nest_end(phydev->skb, phydev->nest); 1551dd3f212SAndrew Lunn 1561dd3f212SAndrew Lunn genlmsg_end(phydev->skb, phydev->ehdr); 1571dd3f212SAndrew Lunn 1581dd3f212SAndrew Lunn ethnl_multicast(phydev->skb, phydev->attached_dev); 1591dd3f212SAndrew Lunn } 1601dd3f212SAndrew Lunn EXPORT_SYMBOL_GPL(ethnl_cable_test_finished); 1611e2dc145SAndrew Lunn 1621e2dc145SAndrew Lunn int ethnl_cable_test_result(struct phy_device *phydev, u8 pair, u8 result) 1631e2dc145SAndrew Lunn { 1641e2dc145SAndrew Lunn struct nlattr *nest; 1651e2dc145SAndrew Lunn int ret = -EMSGSIZE; 1661e2dc145SAndrew Lunn 1671e2dc145SAndrew Lunn nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_NEST_RESULT); 1681e2dc145SAndrew Lunn if (!nest) 1691e2dc145SAndrew Lunn return -EMSGSIZE; 1701e2dc145SAndrew Lunn 1711e2dc145SAndrew Lunn if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_RESULT_PAIR, pair)) 1721e2dc145SAndrew Lunn goto err; 1731e2dc145SAndrew Lunn if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_RESULT_CODE, result)) 1741e2dc145SAndrew Lunn goto err; 1751e2dc145SAndrew Lunn 1761e2dc145SAndrew Lunn nla_nest_end(phydev->skb, nest); 1771e2dc145SAndrew Lunn return 0; 1781e2dc145SAndrew Lunn 1791e2dc145SAndrew Lunn err: 1801e2dc145SAndrew Lunn nla_nest_cancel(phydev->skb, nest); 1811e2dc145SAndrew Lunn return ret; 1821e2dc145SAndrew Lunn } 1831e2dc145SAndrew Lunn EXPORT_SYMBOL_GPL(ethnl_cable_test_result); 1841e2dc145SAndrew Lunn 1851e2dc145SAndrew Lunn int ethnl_cable_test_fault_length(struct phy_device *phydev, u8 pair, u32 cm) 1861e2dc145SAndrew Lunn { 1871e2dc145SAndrew Lunn struct nlattr *nest; 1881e2dc145SAndrew Lunn int ret = -EMSGSIZE; 1891e2dc145SAndrew Lunn 1901e2dc145SAndrew Lunn nest = nla_nest_start(phydev->skb, 1911e2dc145SAndrew Lunn ETHTOOL_A_CABLE_NEST_FAULT_LENGTH); 1921e2dc145SAndrew Lunn if (!nest) 1931e2dc145SAndrew Lunn return -EMSGSIZE; 1941e2dc145SAndrew Lunn 1951e2dc145SAndrew Lunn if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR, pair)) 1961e2dc145SAndrew Lunn goto err; 1971e2dc145SAndrew Lunn if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_FAULT_LENGTH_CM, cm)) 1981e2dc145SAndrew Lunn goto err; 1991e2dc145SAndrew Lunn 2001e2dc145SAndrew Lunn nla_nest_end(phydev->skb, nest); 2011e2dc145SAndrew Lunn return 0; 2021e2dc145SAndrew Lunn 2031e2dc145SAndrew Lunn err: 2041e2dc145SAndrew Lunn nla_nest_cancel(phydev->skb, nest); 2051e2dc145SAndrew Lunn return ret; 2061e2dc145SAndrew Lunn } 2071e2dc145SAndrew Lunn EXPORT_SYMBOL_GPL(ethnl_cable_test_fault_length); 2081a644de2SAndrew Lunn 209f2bc8ad3SAndrew Lunn struct cable_test_tdr_req_info { 210f2bc8ad3SAndrew Lunn struct ethnl_req_info base; 211f2bc8ad3SAndrew Lunn }; 212f2bc8ad3SAndrew Lunn 213ff419afaSJakub Kicinski static const struct nla_policy cable_test_tdr_act_cfg_policy[] = { 214f2bc8ad3SAndrew Lunn [ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST] = { .type = NLA_U32 }, 215f2bc8ad3SAndrew Lunn [ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST] = { .type = NLA_U32 }, 216f2bc8ad3SAndrew Lunn [ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP] = { .type = NLA_U32 }, 217f2bc8ad3SAndrew Lunn [ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR] = { .type = NLA_U8 }, 218f2bc8ad3SAndrew Lunn }; 219f2bc8ad3SAndrew Lunn 220ff419afaSJakub Kicinski const struct nla_policy ethnl_cable_test_tdr_act_policy[] = { 2211a644de2SAndrew Lunn [ETHTOOL_A_CABLE_TEST_TDR_HEADER] = { .type = NLA_NESTED }, 222f2bc8ad3SAndrew Lunn [ETHTOOL_A_CABLE_TEST_TDR_CFG] = { .type = NLA_NESTED }, 2231a644de2SAndrew Lunn }; 2241a644de2SAndrew Lunn 225f2bc8ad3SAndrew Lunn /* CABLE_TEST_TDR_ACT */ 226fd55199dSAndrew Lunn static int ethnl_act_cable_test_tdr_cfg(const struct nlattr *nest, 227f2bc8ad3SAndrew Lunn struct genl_info *info, 228f2bc8ad3SAndrew Lunn struct phy_tdr_config *cfg) 229f2bc8ad3SAndrew Lunn { 230ff419afaSJakub Kicinski struct nlattr *tb[ARRAY_SIZE(cable_test_tdr_act_cfg_policy)]; 231f2bc8ad3SAndrew Lunn int ret; 232f2bc8ad3SAndrew Lunn 2334b973f49SAndrew Lunn cfg->first = 100; 2344b973f49SAndrew Lunn cfg->step = 100; 2354b973f49SAndrew Lunn cfg->last = MAX_CABLE_LENGTH_CM; 2364b973f49SAndrew Lunn cfg->pair = PHY_PAIR_ALL; 2374b973f49SAndrew Lunn 2384b973f49SAndrew Lunn if (!nest) 2394b973f49SAndrew Lunn return 0; 2404b973f49SAndrew Lunn 241ff419afaSJakub Kicinski ret = nla_parse_nested(tb, 242ff419afaSJakub Kicinski ARRAY_SIZE(cable_test_tdr_act_cfg_policy) - 1, 243ff419afaSJakub Kicinski nest, cable_test_tdr_act_cfg_policy, 244ff419afaSJakub Kicinski info->extack); 245f2bc8ad3SAndrew Lunn if (ret < 0) 246f2bc8ad3SAndrew Lunn return ret; 247f2bc8ad3SAndrew Lunn 248f2bc8ad3SAndrew Lunn if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST]) 249f2bc8ad3SAndrew Lunn cfg->first = nla_get_u32( 250f2bc8ad3SAndrew Lunn tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST]); 2514b973f49SAndrew Lunn 252f2bc8ad3SAndrew Lunn if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST]) 253f2bc8ad3SAndrew Lunn cfg->last = nla_get_u32(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST]); 254f2bc8ad3SAndrew Lunn 255f2bc8ad3SAndrew Lunn if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP]) 256f2bc8ad3SAndrew Lunn cfg->step = nla_get_u32(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP]); 257f2bc8ad3SAndrew Lunn 258f2bc8ad3SAndrew Lunn if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]) { 259f2bc8ad3SAndrew Lunn cfg->pair = nla_get_u8(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]); 260f2bc8ad3SAndrew Lunn if (cfg->pair > ETHTOOL_A_CABLE_PAIR_D) { 261f2bc8ad3SAndrew Lunn NL_SET_ERR_MSG_ATTR( 262f2bc8ad3SAndrew Lunn info->extack, 263f2bc8ad3SAndrew Lunn tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR], 264f2bc8ad3SAndrew Lunn "invalid pair parameter"); 265f2bc8ad3SAndrew Lunn return -EINVAL; 266f2bc8ad3SAndrew Lunn } 267f2bc8ad3SAndrew Lunn } 268f2bc8ad3SAndrew Lunn 269f2bc8ad3SAndrew Lunn if (cfg->first > MAX_CABLE_LENGTH_CM) { 270f2bc8ad3SAndrew Lunn NL_SET_ERR_MSG_ATTR(info->extack, 271f2bc8ad3SAndrew Lunn tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST], 272f2bc8ad3SAndrew Lunn "invalid first parameter"); 273f2bc8ad3SAndrew Lunn return -EINVAL; 274f2bc8ad3SAndrew Lunn } 275f2bc8ad3SAndrew Lunn 276f2bc8ad3SAndrew Lunn if (cfg->last > MAX_CABLE_LENGTH_CM) { 277f2bc8ad3SAndrew Lunn NL_SET_ERR_MSG_ATTR(info->extack, 278f2bc8ad3SAndrew Lunn tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST], 279f2bc8ad3SAndrew Lunn "invalid last parameter"); 280f2bc8ad3SAndrew Lunn return -EINVAL; 281f2bc8ad3SAndrew Lunn } 282f2bc8ad3SAndrew Lunn 283f2bc8ad3SAndrew Lunn if (cfg->first > cfg->last) { 284f2bc8ad3SAndrew Lunn NL_SET_ERR_MSG(info->extack, "invalid first/last parameter"); 285f2bc8ad3SAndrew Lunn return -EINVAL; 286f2bc8ad3SAndrew Lunn } 287f2bc8ad3SAndrew Lunn 288f2bc8ad3SAndrew Lunn if (!cfg->step) { 289f2bc8ad3SAndrew Lunn NL_SET_ERR_MSG_ATTR(info->extack, 290f2bc8ad3SAndrew Lunn tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP], 291f2bc8ad3SAndrew Lunn "invalid step parameter"); 292f2bc8ad3SAndrew Lunn return -EINVAL; 293f2bc8ad3SAndrew Lunn } 294f2bc8ad3SAndrew Lunn 295f2bc8ad3SAndrew Lunn if (cfg->step > (cfg->last - cfg->first)) { 296f2bc8ad3SAndrew Lunn NL_SET_ERR_MSG_ATTR(info->extack, 297f2bc8ad3SAndrew Lunn tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP], 298f2bc8ad3SAndrew Lunn "step parameter too big"); 299f2bc8ad3SAndrew Lunn return -EINVAL; 300f2bc8ad3SAndrew Lunn } 301f2bc8ad3SAndrew Lunn 302f2bc8ad3SAndrew Lunn return 0; 303f2bc8ad3SAndrew Lunn } 304f2bc8ad3SAndrew Lunn 3051a644de2SAndrew Lunn int ethnl_act_cable_test_tdr(struct sk_buff *skb, struct genl_info *info) 3061a644de2SAndrew Lunn { 3071a644de2SAndrew Lunn struct ethnl_req_info req_info = {}; 308f3631ab0SFlorian Fainelli const struct ethtool_phy_ops *ops; 3095028588bSJakub Kicinski struct nlattr **tb = info->attrs; 310f2bc8ad3SAndrew Lunn struct phy_tdr_config cfg; 3111a644de2SAndrew Lunn struct net_device *dev; 3121a644de2SAndrew Lunn int ret; 3131a644de2SAndrew Lunn 3141a644de2SAndrew Lunn ret = ethnl_parse_header_dev_get(&req_info, 3151a644de2SAndrew Lunn tb[ETHTOOL_A_CABLE_TEST_TDR_HEADER], 3161a644de2SAndrew Lunn genl_info_net(info), info->extack, 3171a644de2SAndrew Lunn true); 3181a644de2SAndrew Lunn if (ret < 0) 3191a644de2SAndrew Lunn return ret; 3201a644de2SAndrew Lunn 3211a644de2SAndrew Lunn dev = req_info.dev; 3221a644de2SAndrew Lunn if (!dev->phydev) { 3231a644de2SAndrew Lunn ret = -EOPNOTSUPP; 3241a644de2SAndrew Lunn goto out_dev_put; 3251a644de2SAndrew Lunn } 3261a644de2SAndrew Lunn 327f2bc8ad3SAndrew Lunn ret = ethnl_act_cable_test_tdr_cfg(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG], 328f2bc8ad3SAndrew Lunn info, &cfg); 329f2bc8ad3SAndrew Lunn if (ret) 330f2bc8ad3SAndrew Lunn goto out_dev_put; 331f2bc8ad3SAndrew Lunn 3321a644de2SAndrew Lunn rtnl_lock(); 333f3631ab0SFlorian Fainelli ops = ethtool_phy_ops; 334f3631ab0SFlorian Fainelli if (!ops || !ops->start_cable_test_tdr) { 335f3631ab0SFlorian Fainelli ret = -EOPNOTSUPP; 336f3631ab0SFlorian Fainelli goto out_rtnl; 337f3631ab0SFlorian Fainelli } 338f3631ab0SFlorian Fainelli 3391a644de2SAndrew Lunn ret = ethnl_ops_begin(dev); 3401a644de2SAndrew Lunn if (ret < 0) 3411a644de2SAndrew Lunn goto out_rtnl; 3421a644de2SAndrew Lunn 343f3631ab0SFlorian Fainelli ret = ops->start_cable_test_tdr(dev->phydev, info->extack, &cfg); 3441a644de2SAndrew Lunn 3451a644de2SAndrew Lunn ethnl_ops_complete(dev); 3461a644de2SAndrew Lunn 3471a644de2SAndrew Lunn if (!ret) 3481a644de2SAndrew Lunn ethnl_cable_test_started(dev->phydev, 3491a644de2SAndrew Lunn ETHTOOL_MSG_CABLE_TEST_TDR_NTF); 3501a644de2SAndrew Lunn 3511a644de2SAndrew Lunn out_rtnl: 3521a644de2SAndrew Lunn rtnl_unlock(); 3531a644de2SAndrew Lunn out_dev_put: 3541a644de2SAndrew Lunn dev_put(dev); 3551a644de2SAndrew Lunn return ret; 3561a644de2SAndrew Lunn } 3576b4a0fc1SAndrew Lunn 3586b4a0fc1SAndrew Lunn int ethnl_cable_test_amplitude(struct phy_device *phydev, 3596b4a0fc1SAndrew Lunn u8 pair, s16 mV) 3606b4a0fc1SAndrew Lunn { 3616b4a0fc1SAndrew Lunn struct nlattr *nest; 3626b4a0fc1SAndrew Lunn int ret = -EMSGSIZE; 3636b4a0fc1SAndrew Lunn 3646b4a0fc1SAndrew Lunn nest = nla_nest_start(phydev->skb, 3656b4a0fc1SAndrew Lunn ETHTOOL_A_CABLE_TDR_NEST_AMPLITUDE); 3666b4a0fc1SAndrew Lunn if (!nest) 3676b4a0fc1SAndrew Lunn return -EMSGSIZE; 3686b4a0fc1SAndrew Lunn 3696b4a0fc1SAndrew Lunn if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_AMPLITUDE_PAIR, pair)) 3706b4a0fc1SAndrew Lunn goto err; 3716b4a0fc1SAndrew Lunn if (nla_put_u16(phydev->skb, ETHTOOL_A_CABLE_AMPLITUDE_mV, mV)) 3726b4a0fc1SAndrew Lunn goto err; 3736b4a0fc1SAndrew Lunn 3746b4a0fc1SAndrew Lunn nla_nest_end(phydev->skb, nest); 3756b4a0fc1SAndrew Lunn return 0; 3766b4a0fc1SAndrew Lunn 3776b4a0fc1SAndrew Lunn err: 3786b4a0fc1SAndrew Lunn nla_nest_cancel(phydev->skb, nest); 3796b4a0fc1SAndrew Lunn return ret; 3806b4a0fc1SAndrew Lunn } 3816b4a0fc1SAndrew Lunn EXPORT_SYMBOL_GPL(ethnl_cable_test_amplitude); 3826b4a0fc1SAndrew Lunn 3836b4a0fc1SAndrew Lunn int ethnl_cable_test_pulse(struct phy_device *phydev, u16 mV) 3846b4a0fc1SAndrew Lunn { 3856b4a0fc1SAndrew Lunn struct nlattr *nest; 3866b4a0fc1SAndrew Lunn int ret = -EMSGSIZE; 3876b4a0fc1SAndrew Lunn 3886b4a0fc1SAndrew Lunn nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_TDR_NEST_PULSE); 3896b4a0fc1SAndrew Lunn if (!nest) 3906b4a0fc1SAndrew Lunn return -EMSGSIZE; 3916b4a0fc1SAndrew Lunn 3926b4a0fc1SAndrew Lunn if (nla_put_u16(phydev->skb, ETHTOOL_A_CABLE_PULSE_mV, mV)) 3936b4a0fc1SAndrew Lunn goto err; 3946b4a0fc1SAndrew Lunn 3956b4a0fc1SAndrew Lunn nla_nest_end(phydev->skb, nest); 3966b4a0fc1SAndrew Lunn return 0; 3976b4a0fc1SAndrew Lunn 3986b4a0fc1SAndrew Lunn err: 3996b4a0fc1SAndrew Lunn nla_nest_cancel(phydev->skb, nest); 4006b4a0fc1SAndrew Lunn return ret; 4016b4a0fc1SAndrew Lunn } 4026b4a0fc1SAndrew Lunn EXPORT_SYMBOL_GPL(ethnl_cable_test_pulse); 4036b4a0fc1SAndrew Lunn 4046b4a0fc1SAndrew Lunn int ethnl_cable_test_step(struct phy_device *phydev, u32 first, u32 last, 4056b4a0fc1SAndrew Lunn u32 step) 4066b4a0fc1SAndrew Lunn { 4076b4a0fc1SAndrew Lunn struct nlattr *nest; 4086b4a0fc1SAndrew Lunn int ret = -EMSGSIZE; 4096b4a0fc1SAndrew Lunn 4106b4a0fc1SAndrew Lunn nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_TDR_NEST_STEP); 4116b4a0fc1SAndrew Lunn if (!nest) 4126b4a0fc1SAndrew Lunn return -EMSGSIZE; 4136b4a0fc1SAndrew Lunn 4146b4a0fc1SAndrew Lunn if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE, 4156b4a0fc1SAndrew Lunn first)) 4166b4a0fc1SAndrew Lunn goto err; 4176b4a0fc1SAndrew Lunn 4186b4a0fc1SAndrew Lunn if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_LAST_DISTANCE, last)) 4196b4a0fc1SAndrew Lunn goto err; 4206b4a0fc1SAndrew Lunn 4216b4a0fc1SAndrew Lunn if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_STEP_DISTANCE, step)) 4226b4a0fc1SAndrew Lunn goto err; 4236b4a0fc1SAndrew Lunn 4246b4a0fc1SAndrew Lunn nla_nest_end(phydev->skb, nest); 4256b4a0fc1SAndrew Lunn return 0; 4266b4a0fc1SAndrew Lunn 4276b4a0fc1SAndrew Lunn err: 4286b4a0fc1SAndrew Lunn nla_nest_cancel(phydev->skb, nest); 4296b4a0fc1SAndrew Lunn return ret; 4306b4a0fc1SAndrew Lunn } 4316b4a0fc1SAndrew Lunn EXPORT_SYMBOL_GPL(ethnl_cable_test_step); 432