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 1411ca3c42SAndrew Lunn static const struct nla_policy 1511ca3c42SAndrew Lunn cable_test_act_policy[ETHTOOL_A_CABLE_TEST_MAX + 1] = { 1611ca3c42SAndrew Lunn [ETHTOOL_A_CABLE_TEST_UNSPEC] = { .type = NLA_REJECT }, 1711ca3c42SAndrew Lunn [ETHTOOL_A_CABLE_TEST_HEADER] = { .type = NLA_NESTED }, 1811ca3c42SAndrew Lunn }; 1911ca3c42SAndrew Lunn 201a644de2SAndrew Lunn static int ethnl_cable_test_started(struct phy_device *phydev, u8 cmd) 219896a457SAndrew Lunn { 229896a457SAndrew Lunn struct sk_buff *skb; 239896a457SAndrew Lunn int err = -ENOMEM; 249896a457SAndrew Lunn void *ehdr; 259896a457SAndrew Lunn 269896a457SAndrew Lunn skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); 279896a457SAndrew Lunn if (!skb) 289896a457SAndrew Lunn goto out; 299896a457SAndrew Lunn 301a644de2SAndrew Lunn ehdr = ethnl_bcastmsg_put(skb, cmd); 319896a457SAndrew Lunn if (!ehdr) { 329896a457SAndrew Lunn err = -EMSGSIZE; 339896a457SAndrew Lunn goto out; 349896a457SAndrew Lunn } 359896a457SAndrew Lunn 369896a457SAndrew Lunn err = ethnl_fill_reply_header(skb, phydev->attached_dev, 379896a457SAndrew Lunn ETHTOOL_A_CABLE_TEST_NTF_HEADER); 389896a457SAndrew Lunn if (err) 399896a457SAndrew Lunn goto out; 409896a457SAndrew Lunn 419896a457SAndrew Lunn err = nla_put_u8(skb, ETHTOOL_A_CABLE_TEST_NTF_STATUS, 429896a457SAndrew Lunn ETHTOOL_A_CABLE_TEST_NTF_STATUS_STARTED); 439896a457SAndrew Lunn if (err) 449896a457SAndrew Lunn goto out; 459896a457SAndrew Lunn 469896a457SAndrew Lunn genlmsg_end(skb, ehdr); 479896a457SAndrew Lunn 489896a457SAndrew Lunn return ethnl_multicast(skb, phydev->attached_dev); 499896a457SAndrew Lunn 509896a457SAndrew Lunn out: 519896a457SAndrew Lunn nlmsg_free(skb); 529896a457SAndrew Lunn phydev_err(phydev, "%s: Error %pe\n", __func__, ERR_PTR(err)); 539896a457SAndrew Lunn 549896a457SAndrew Lunn return err; 559896a457SAndrew Lunn } 569896a457SAndrew Lunn 5711ca3c42SAndrew Lunn int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info) 5811ca3c42SAndrew Lunn { 5911ca3c42SAndrew Lunn struct nlattr *tb[ETHTOOL_A_CABLE_TEST_MAX + 1]; 6011ca3c42SAndrew Lunn struct ethnl_req_info req_info = {}; 6111ca3c42SAndrew Lunn struct net_device *dev; 6211ca3c42SAndrew Lunn int ret; 6311ca3c42SAndrew Lunn 6411ca3c42SAndrew Lunn ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb, 6511ca3c42SAndrew Lunn ETHTOOL_A_CABLE_TEST_MAX, 6611ca3c42SAndrew Lunn cable_test_act_policy, info->extack); 6711ca3c42SAndrew Lunn if (ret < 0) 6811ca3c42SAndrew Lunn return ret; 6911ca3c42SAndrew Lunn 7011ca3c42SAndrew Lunn ret = ethnl_parse_header_dev_get(&req_info, 7111ca3c42SAndrew Lunn tb[ETHTOOL_A_CABLE_TEST_HEADER], 7211ca3c42SAndrew Lunn genl_info_net(info), info->extack, 7311ca3c42SAndrew Lunn true); 7411ca3c42SAndrew Lunn if (ret < 0) 7511ca3c42SAndrew Lunn return ret; 7611ca3c42SAndrew Lunn 7711ca3c42SAndrew Lunn dev = req_info.dev; 7811ca3c42SAndrew Lunn if (!dev->phydev) { 7911ca3c42SAndrew Lunn ret = -EOPNOTSUPP; 8011ca3c42SAndrew Lunn goto out_dev_put; 8111ca3c42SAndrew Lunn } 8211ca3c42SAndrew Lunn 8311ca3c42SAndrew Lunn rtnl_lock(); 8411ca3c42SAndrew Lunn ret = ethnl_ops_begin(dev); 8511ca3c42SAndrew Lunn if (ret < 0) 8611ca3c42SAndrew Lunn goto out_rtnl; 8711ca3c42SAndrew Lunn 8811ca3c42SAndrew Lunn ret = phy_start_cable_test(dev->phydev, info->extack); 8911ca3c42SAndrew Lunn 9011ca3c42SAndrew Lunn ethnl_ops_complete(dev); 919896a457SAndrew Lunn 929896a457SAndrew Lunn if (!ret) 931a644de2SAndrew Lunn ethnl_cable_test_started(dev->phydev, 941a644de2SAndrew Lunn ETHTOOL_MSG_CABLE_TEST_NTF); 959896a457SAndrew Lunn 9611ca3c42SAndrew Lunn out_rtnl: 9711ca3c42SAndrew Lunn rtnl_unlock(); 9811ca3c42SAndrew Lunn out_dev_put: 9911ca3c42SAndrew Lunn dev_put(dev); 10011ca3c42SAndrew Lunn return ret; 10111ca3c42SAndrew Lunn } 1021dd3f212SAndrew Lunn 1031a644de2SAndrew Lunn int ethnl_cable_test_alloc(struct phy_device *phydev, u8 cmd) 1041dd3f212SAndrew Lunn { 1051dd3f212SAndrew Lunn int err = -ENOMEM; 1061dd3f212SAndrew Lunn 1076b4a0fc1SAndrew Lunn /* One TDR sample occupies 20 bytes. For a 150 meter cable, 1086b4a0fc1SAndrew Lunn * with four pairs, around 12K is needed. 1096b4a0fc1SAndrew Lunn */ 1106b4a0fc1SAndrew Lunn phydev->skb = genlmsg_new(SZ_16K, GFP_KERNEL); 1111dd3f212SAndrew Lunn if (!phydev->skb) 1121dd3f212SAndrew Lunn goto out; 1131dd3f212SAndrew Lunn 1141a644de2SAndrew Lunn phydev->ehdr = ethnl_bcastmsg_put(phydev->skb, cmd); 1151dd3f212SAndrew Lunn if (!phydev->ehdr) { 1161dd3f212SAndrew Lunn err = -EMSGSIZE; 1171dd3f212SAndrew Lunn goto out; 1181dd3f212SAndrew Lunn } 1191dd3f212SAndrew Lunn 1201dd3f212SAndrew Lunn err = ethnl_fill_reply_header(phydev->skb, phydev->attached_dev, 1211dd3f212SAndrew Lunn ETHTOOL_A_CABLE_TEST_NTF_HEADER); 1221dd3f212SAndrew Lunn if (err) 1231dd3f212SAndrew Lunn goto out; 1241dd3f212SAndrew Lunn 1251dd3f212SAndrew Lunn err = nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_TEST_NTF_STATUS, 1261dd3f212SAndrew Lunn ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED); 1271dd3f212SAndrew Lunn if (err) 1281dd3f212SAndrew Lunn goto out; 1291dd3f212SAndrew Lunn 1301dd3f212SAndrew Lunn phydev->nest = nla_nest_start(phydev->skb, 1311dd3f212SAndrew Lunn ETHTOOL_A_CABLE_TEST_NTF_NEST); 1321e2dc145SAndrew Lunn if (!phydev->nest) { 1331e2dc145SAndrew Lunn err = -EMSGSIZE; 1341dd3f212SAndrew Lunn goto out; 1351e2dc145SAndrew Lunn } 1361dd3f212SAndrew Lunn 1371dd3f212SAndrew Lunn return 0; 1381dd3f212SAndrew Lunn 1391dd3f212SAndrew Lunn out: 1401dd3f212SAndrew Lunn nlmsg_free(phydev->skb); 1411e2dc145SAndrew Lunn phydev->skb = NULL; 1421dd3f212SAndrew Lunn return err; 1431dd3f212SAndrew Lunn } 1441dd3f212SAndrew Lunn EXPORT_SYMBOL_GPL(ethnl_cable_test_alloc); 1451dd3f212SAndrew Lunn 1461dd3f212SAndrew Lunn void ethnl_cable_test_free(struct phy_device *phydev) 1471dd3f212SAndrew Lunn { 1481dd3f212SAndrew Lunn nlmsg_free(phydev->skb); 1491e2dc145SAndrew Lunn phydev->skb = NULL; 1501dd3f212SAndrew Lunn } 1511dd3f212SAndrew Lunn EXPORT_SYMBOL_GPL(ethnl_cable_test_free); 1521dd3f212SAndrew Lunn 1531dd3f212SAndrew Lunn void ethnl_cable_test_finished(struct phy_device *phydev) 1541dd3f212SAndrew Lunn { 1551dd3f212SAndrew Lunn nla_nest_end(phydev->skb, phydev->nest); 1561dd3f212SAndrew Lunn 1571dd3f212SAndrew Lunn genlmsg_end(phydev->skb, phydev->ehdr); 1581dd3f212SAndrew Lunn 1591dd3f212SAndrew Lunn ethnl_multicast(phydev->skb, phydev->attached_dev); 1601dd3f212SAndrew Lunn } 1611dd3f212SAndrew Lunn EXPORT_SYMBOL_GPL(ethnl_cable_test_finished); 1621e2dc145SAndrew Lunn 1631e2dc145SAndrew Lunn int ethnl_cable_test_result(struct phy_device *phydev, u8 pair, u8 result) 1641e2dc145SAndrew Lunn { 1651e2dc145SAndrew Lunn struct nlattr *nest; 1661e2dc145SAndrew Lunn int ret = -EMSGSIZE; 1671e2dc145SAndrew Lunn 1681e2dc145SAndrew Lunn nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_NEST_RESULT); 1691e2dc145SAndrew Lunn if (!nest) 1701e2dc145SAndrew Lunn return -EMSGSIZE; 1711e2dc145SAndrew Lunn 1721e2dc145SAndrew Lunn if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_RESULT_PAIR, pair)) 1731e2dc145SAndrew Lunn goto err; 1741e2dc145SAndrew Lunn if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_RESULT_CODE, result)) 1751e2dc145SAndrew Lunn goto err; 1761e2dc145SAndrew Lunn 1771e2dc145SAndrew Lunn nla_nest_end(phydev->skb, nest); 1781e2dc145SAndrew Lunn return 0; 1791e2dc145SAndrew Lunn 1801e2dc145SAndrew Lunn err: 1811e2dc145SAndrew Lunn nla_nest_cancel(phydev->skb, nest); 1821e2dc145SAndrew Lunn return ret; 1831e2dc145SAndrew Lunn } 1841e2dc145SAndrew Lunn EXPORT_SYMBOL_GPL(ethnl_cable_test_result); 1851e2dc145SAndrew Lunn 1861e2dc145SAndrew Lunn int ethnl_cable_test_fault_length(struct phy_device *phydev, u8 pair, u32 cm) 1871e2dc145SAndrew Lunn { 1881e2dc145SAndrew Lunn struct nlattr *nest; 1891e2dc145SAndrew Lunn int ret = -EMSGSIZE; 1901e2dc145SAndrew Lunn 1911e2dc145SAndrew Lunn nest = nla_nest_start(phydev->skb, 1921e2dc145SAndrew Lunn ETHTOOL_A_CABLE_NEST_FAULT_LENGTH); 1931e2dc145SAndrew Lunn if (!nest) 1941e2dc145SAndrew Lunn return -EMSGSIZE; 1951e2dc145SAndrew Lunn 1961e2dc145SAndrew Lunn if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR, pair)) 1971e2dc145SAndrew Lunn goto err; 1981e2dc145SAndrew Lunn if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_FAULT_LENGTH_CM, cm)) 1991e2dc145SAndrew Lunn goto err; 2001e2dc145SAndrew Lunn 2011e2dc145SAndrew Lunn nla_nest_end(phydev->skb, nest); 2021e2dc145SAndrew Lunn return 0; 2031e2dc145SAndrew Lunn 2041e2dc145SAndrew Lunn err: 2051e2dc145SAndrew Lunn nla_nest_cancel(phydev->skb, nest); 2061e2dc145SAndrew Lunn return ret; 2071e2dc145SAndrew Lunn } 2081e2dc145SAndrew Lunn EXPORT_SYMBOL_GPL(ethnl_cable_test_fault_length); 2091a644de2SAndrew Lunn 210f2bc8ad3SAndrew Lunn struct cable_test_tdr_req_info { 211f2bc8ad3SAndrew Lunn struct ethnl_req_info base; 212f2bc8ad3SAndrew Lunn }; 213f2bc8ad3SAndrew Lunn 214f2bc8ad3SAndrew Lunn static const struct nla_policy 215f2bc8ad3SAndrew Lunn cable_test_tdr_act_cfg_policy[ETHTOOL_A_CABLE_TEST_TDR_CFG_MAX + 1] = { 216f2bc8ad3SAndrew Lunn [ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST] = { .type = NLA_U32 }, 217f2bc8ad3SAndrew Lunn [ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST] = { .type = NLA_U32 }, 218f2bc8ad3SAndrew Lunn [ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP] = { .type = NLA_U32 }, 219f2bc8ad3SAndrew Lunn [ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR] = { .type = NLA_U8 }, 220f2bc8ad3SAndrew Lunn }; 221f2bc8ad3SAndrew Lunn 2221a644de2SAndrew Lunn static const struct nla_policy 2231a644de2SAndrew Lunn cable_test_tdr_act_policy[ETHTOOL_A_CABLE_TEST_TDR_MAX + 1] = { 2241a644de2SAndrew Lunn [ETHTOOL_A_CABLE_TEST_TDR_UNSPEC] = { .type = NLA_REJECT }, 2251a644de2SAndrew Lunn [ETHTOOL_A_CABLE_TEST_TDR_HEADER] = { .type = NLA_NESTED }, 226f2bc8ad3SAndrew Lunn [ETHTOOL_A_CABLE_TEST_TDR_CFG] = { .type = NLA_NESTED }, 2271a644de2SAndrew Lunn }; 2281a644de2SAndrew Lunn 229f2bc8ad3SAndrew Lunn /* CABLE_TEST_TDR_ACT */ 230fd55199dSAndrew Lunn static int ethnl_act_cable_test_tdr_cfg(const struct nlattr *nest, 231f2bc8ad3SAndrew Lunn struct genl_info *info, 232f2bc8ad3SAndrew Lunn struct phy_tdr_config *cfg) 233f2bc8ad3SAndrew Lunn { 234f2bc8ad3SAndrew Lunn struct nlattr *tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_MAX + 1]; 235f2bc8ad3SAndrew Lunn int ret; 236f2bc8ad3SAndrew Lunn 237f2bc8ad3SAndrew Lunn ret = nla_parse_nested(tb, ETHTOOL_A_CABLE_TEST_TDR_CFG_MAX, nest, 238f2bc8ad3SAndrew Lunn cable_test_tdr_act_cfg_policy, info->extack); 239f2bc8ad3SAndrew Lunn if (ret < 0) 240f2bc8ad3SAndrew Lunn return ret; 241f2bc8ad3SAndrew Lunn 242f2bc8ad3SAndrew Lunn if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST]) 243f2bc8ad3SAndrew Lunn cfg->first = nla_get_u32( 244f2bc8ad3SAndrew Lunn tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST]); 245f2bc8ad3SAndrew Lunn else 246f2bc8ad3SAndrew Lunn cfg->first = 100; 247f2bc8ad3SAndrew Lunn if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST]) 248f2bc8ad3SAndrew Lunn cfg->last = nla_get_u32(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST]); 249f2bc8ad3SAndrew Lunn else 250f2bc8ad3SAndrew Lunn cfg->last = MAX_CABLE_LENGTH_CM; 251f2bc8ad3SAndrew Lunn 252f2bc8ad3SAndrew Lunn if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP]) 253f2bc8ad3SAndrew Lunn cfg->step = nla_get_u32(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP]); 254f2bc8ad3SAndrew Lunn else 255f2bc8ad3SAndrew Lunn cfg->step = 100; 256f2bc8ad3SAndrew Lunn 257f2bc8ad3SAndrew Lunn if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]) { 258f2bc8ad3SAndrew Lunn cfg->pair = nla_get_u8(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]); 259f2bc8ad3SAndrew Lunn if (cfg->pair > ETHTOOL_A_CABLE_PAIR_D) { 260f2bc8ad3SAndrew Lunn NL_SET_ERR_MSG_ATTR( 261f2bc8ad3SAndrew Lunn info->extack, 262f2bc8ad3SAndrew Lunn tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR], 263f2bc8ad3SAndrew Lunn "invalid pair parameter"); 264f2bc8ad3SAndrew Lunn return -EINVAL; 265f2bc8ad3SAndrew Lunn } 266f2bc8ad3SAndrew Lunn } else { 267f2bc8ad3SAndrew Lunn cfg->pair = PHY_PAIR_ALL; 268f2bc8ad3SAndrew Lunn } 269f2bc8ad3SAndrew Lunn 270f2bc8ad3SAndrew Lunn if (cfg->first > MAX_CABLE_LENGTH_CM) { 271f2bc8ad3SAndrew Lunn NL_SET_ERR_MSG_ATTR(info->extack, 272f2bc8ad3SAndrew Lunn tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST], 273f2bc8ad3SAndrew Lunn "invalid first parameter"); 274f2bc8ad3SAndrew Lunn return -EINVAL; 275f2bc8ad3SAndrew Lunn } 276f2bc8ad3SAndrew Lunn 277f2bc8ad3SAndrew Lunn if (cfg->last > MAX_CABLE_LENGTH_CM) { 278f2bc8ad3SAndrew Lunn NL_SET_ERR_MSG_ATTR(info->extack, 279f2bc8ad3SAndrew Lunn tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST], 280f2bc8ad3SAndrew Lunn "invalid last parameter"); 281f2bc8ad3SAndrew Lunn return -EINVAL; 282f2bc8ad3SAndrew Lunn } 283f2bc8ad3SAndrew Lunn 284f2bc8ad3SAndrew Lunn if (cfg->first > cfg->last) { 285f2bc8ad3SAndrew Lunn NL_SET_ERR_MSG(info->extack, "invalid first/last parameter"); 286f2bc8ad3SAndrew Lunn return -EINVAL; 287f2bc8ad3SAndrew Lunn } 288f2bc8ad3SAndrew Lunn 289f2bc8ad3SAndrew Lunn if (!cfg->step) { 290f2bc8ad3SAndrew Lunn NL_SET_ERR_MSG_ATTR(info->extack, 291f2bc8ad3SAndrew Lunn tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP], 292f2bc8ad3SAndrew Lunn "invalid step parameter"); 293f2bc8ad3SAndrew Lunn return -EINVAL; 294f2bc8ad3SAndrew Lunn } 295f2bc8ad3SAndrew Lunn 296f2bc8ad3SAndrew Lunn if (cfg->step > (cfg->last - cfg->first)) { 297f2bc8ad3SAndrew Lunn NL_SET_ERR_MSG_ATTR(info->extack, 298f2bc8ad3SAndrew Lunn tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP], 299f2bc8ad3SAndrew Lunn "step parameter too big"); 300f2bc8ad3SAndrew Lunn return -EINVAL; 301f2bc8ad3SAndrew Lunn } 302f2bc8ad3SAndrew Lunn 303f2bc8ad3SAndrew Lunn return 0; 304f2bc8ad3SAndrew Lunn } 305f2bc8ad3SAndrew Lunn 3061a644de2SAndrew Lunn int ethnl_act_cable_test_tdr(struct sk_buff *skb, struct genl_info *info) 3071a644de2SAndrew Lunn { 3081a644de2SAndrew Lunn struct nlattr *tb[ETHTOOL_A_CABLE_TEST_TDR_MAX + 1]; 3091a644de2SAndrew Lunn struct ethnl_req_info req_info = {}; 310f2bc8ad3SAndrew Lunn struct phy_tdr_config cfg; 3111a644de2SAndrew Lunn struct net_device *dev; 3121a644de2SAndrew Lunn int ret; 3131a644de2SAndrew Lunn 3141a644de2SAndrew Lunn ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb, 3151a644de2SAndrew Lunn ETHTOOL_A_CABLE_TEST_TDR_MAX, 3161a644de2SAndrew Lunn cable_test_tdr_act_policy, info->extack); 3171a644de2SAndrew Lunn if (ret < 0) 3181a644de2SAndrew Lunn return ret; 3191a644de2SAndrew Lunn 3201a644de2SAndrew Lunn ret = ethnl_parse_header_dev_get(&req_info, 3211a644de2SAndrew Lunn tb[ETHTOOL_A_CABLE_TEST_TDR_HEADER], 3221a644de2SAndrew Lunn genl_info_net(info), info->extack, 3231a644de2SAndrew Lunn true); 3241a644de2SAndrew Lunn if (ret < 0) 3251a644de2SAndrew Lunn return ret; 3261a644de2SAndrew Lunn 3271a644de2SAndrew Lunn dev = req_info.dev; 3281a644de2SAndrew Lunn if (!dev->phydev) { 3291a644de2SAndrew Lunn ret = -EOPNOTSUPP; 3301a644de2SAndrew Lunn goto out_dev_put; 3311a644de2SAndrew Lunn } 3321a644de2SAndrew Lunn 333f2bc8ad3SAndrew Lunn ret = ethnl_act_cable_test_tdr_cfg(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG], 334f2bc8ad3SAndrew Lunn info, &cfg); 335f2bc8ad3SAndrew Lunn if (ret) 336f2bc8ad3SAndrew Lunn goto out_dev_put; 337f2bc8ad3SAndrew Lunn 3381a644de2SAndrew Lunn rtnl_lock(); 3391a644de2SAndrew Lunn ret = ethnl_ops_begin(dev); 3401a644de2SAndrew Lunn if (ret < 0) 3411a644de2SAndrew Lunn goto out_rtnl; 3421a644de2SAndrew Lunn 343f2bc8ad3SAndrew Lunn ret = phy_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