1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include <linux/phy.h> 4 #include <linux/ethtool_netlink.h> 5 #include <net/netdev_lock.h> 6 7 #include "common.h" 8 #include "netlink.h" 9 10 /* 802.3 standard allows 100 meters for BaseT cables. However longer 11 * cables might work, depending on the quality of the cables and the 12 * PHY. So allow testing for up to 150 meters. 13 */ 14 #define MAX_CABLE_LENGTH_CM (150 * 100) 15 16 const struct nla_policy ethnl_cable_test_act_policy[] = { 17 [ETHTOOL_A_CABLE_TEST_HEADER] = 18 NLA_POLICY_NESTED(ethnl_header_policy_phy), 19 }; 20 21 static int ethnl_cable_test_started(struct phy_device *phydev, u8 cmd) 22 { 23 struct sk_buff *skb; 24 int err = -ENOMEM; 25 void *ehdr; 26 27 skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); 28 if (!skb) 29 goto out; 30 31 ehdr = ethnl_bcastmsg_put(skb, cmd); 32 if (!ehdr) { 33 err = -EMSGSIZE; 34 goto out; 35 } 36 37 err = ethnl_fill_reply_header(skb, phydev->attached_dev, 38 ETHTOOL_A_CABLE_TEST_NTF_HEADER); 39 if (err) 40 goto out; 41 42 err = nla_put_u8(skb, ETHTOOL_A_CABLE_TEST_NTF_STATUS, 43 ETHTOOL_A_CABLE_TEST_NTF_STATUS_STARTED); 44 if (err) 45 goto out; 46 47 genlmsg_end(skb, ehdr); 48 49 return ethnl_multicast(skb, phydev->attached_dev); 50 51 out: 52 nlmsg_free(skb); 53 phydev_err(phydev, "%s: Error %pe\n", __func__, ERR_PTR(err)); 54 55 return err; 56 } 57 58 int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info) 59 { 60 struct ethnl_req_info req_info = {}; 61 const struct ethtool_phy_ops *ops; 62 struct nlattr **tb = info->attrs; 63 struct phy_device *phydev; 64 struct net_device *dev; 65 int ret; 66 67 ret = ethnl_parse_header_dev_get(&req_info, 68 tb[ETHTOOL_A_CABLE_TEST_HEADER], 69 genl_info_net(info), info->extack, 70 true); 71 if (ret < 0) 72 return ret; 73 74 dev = req_info.dev; 75 76 rtnl_lock(); 77 netdev_lock_ops(dev); 78 phydev = ethnl_req_get_phydev(&req_info, tb, 79 ETHTOOL_A_CABLE_TEST_HEADER, 80 info->extack); 81 if (IS_ERR_OR_NULL(phydev)) { 82 ret = -EOPNOTSUPP; 83 goto out_unlock; 84 } 85 86 ops = ethtool_phy_ops; 87 if (!ops || !ops->start_cable_test) { 88 ret = -EOPNOTSUPP; 89 goto out_unlock; 90 } 91 92 ret = ethnl_ops_begin(dev); 93 if (ret < 0) 94 goto out_unlock; 95 96 ret = ops->start_cable_test(phydev, info->extack); 97 98 ethnl_ops_complete(dev); 99 100 if (!ret) 101 ethnl_cable_test_started(phydev, ETHTOOL_MSG_CABLE_TEST_NTF); 102 103 out_unlock: 104 netdev_unlock_ops(dev); 105 rtnl_unlock(); 106 ethnl_parse_header_dev_put(&req_info); 107 return ret; 108 } 109 110 int ethnl_cable_test_alloc(struct phy_device *phydev, u8 cmd) 111 { 112 int err = -ENOMEM; 113 114 /* One TDR sample occupies 20 bytes. For a 150 meter cable, 115 * with four pairs, around 12K is needed. 116 */ 117 phydev->skb = genlmsg_new(SZ_16K, GFP_KERNEL); 118 if (!phydev->skb) 119 goto out; 120 121 phydev->ehdr = ethnl_bcastmsg_put(phydev->skb, cmd); 122 if (!phydev->ehdr) { 123 err = -EMSGSIZE; 124 goto out; 125 } 126 127 err = ethnl_fill_reply_header(phydev->skb, phydev->attached_dev, 128 ETHTOOL_A_CABLE_TEST_NTF_HEADER); 129 if (err) 130 goto out; 131 132 err = nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_TEST_NTF_STATUS, 133 ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED); 134 if (err) 135 goto out; 136 137 phydev->nest = nla_nest_start(phydev->skb, 138 ETHTOOL_A_CABLE_TEST_NTF_NEST); 139 if (!phydev->nest) { 140 err = -EMSGSIZE; 141 goto out; 142 } 143 144 return 0; 145 146 out: 147 nlmsg_free(phydev->skb); 148 phydev->skb = NULL; 149 return err; 150 } 151 EXPORT_SYMBOL_GPL(ethnl_cable_test_alloc); 152 153 void ethnl_cable_test_free(struct phy_device *phydev) 154 { 155 nlmsg_free(phydev->skb); 156 phydev->skb = NULL; 157 } 158 EXPORT_SYMBOL_GPL(ethnl_cable_test_free); 159 160 void ethnl_cable_test_finished(struct phy_device *phydev) 161 { 162 nla_nest_end(phydev->skb, phydev->nest); 163 164 genlmsg_end(phydev->skb, phydev->ehdr); 165 166 ethnl_multicast(phydev->skb, phydev->attached_dev); 167 } 168 EXPORT_SYMBOL_GPL(ethnl_cable_test_finished); 169 170 int ethnl_cable_test_result_with_src(struct phy_device *phydev, u8 pair, 171 u8 result, u32 src) 172 { 173 struct nlattr *nest; 174 int ret = -EMSGSIZE; 175 176 nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_NEST_RESULT); 177 if (!nest) 178 return -EMSGSIZE; 179 180 if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_RESULT_PAIR, pair)) 181 goto err; 182 if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_RESULT_CODE, result)) 183 goto err; 184 if (src != ETHTOOL_A_CABLE_INF_SRC_UNSPEC) { 185 if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_RESULT_SRC, src)) 186 goto err; 187 } 188 189 nla_nest_end(phydev->skb, nest); 190 return 0; 191 192 err: 193 nla_nest_cancel(phydev->skb, nest); 194 return ret; 195 } 196 EXPORT_SYMBOL_GPL(ethnl_cable_test_result_with_src); 197 198 int ethnl_cable_test_fault_length_with_src(struct phy_device *phydev, u8 pair, 199 u32 cm, u32 src) 200 { 201 struct nlattr *nest; 202 int ret = -EMSGSIZE; 203 204 nest = nla_nest_start(phydev->skb, 205 ETHTOOL_A_CABLE_NEST_FAULT_LENGTH); 206 if (!nest) 207 return -EMSGSIZE; 208 209 if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR, pair)) 210 goto err; 211 if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_FAULT_LENGTH_CM, cm)) 212 goto err; 213 if (src != ETHTOOL_A_CABLE_INF_SRC_UNSPEC) { 214 if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_FAULT_LENGTH_SRC, 215 src)) 216 goto err; 217 } 218 219 nla_nest_end(phydev->skb, nest); 220 return 0; 221 222 err: 223 nla_nest_cancel(phydev->skb, nest); 224 return ret; 225 } 226 EXPORT_SYMBOL_GPL(ethnl_cable_test_fault_length_with_src); 227 228 static const struct nla_policy cable_test_tdr_act_cfg_policy[] = { 229 [ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST] = { .type = NLA_U32 }, 230 [ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST] = { .type = NLA_U32 }, 231 [ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP] = { .type = NLA_U32 }, 232 [ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR] = { .type = NLA_U8 }, 233 }; 234 235 const struct nla_policy ethnl_cable_test_tdr_act_policy[] = { 236 [ETHTOOL_A_CABLE_TEST_TDR_HEADER] = 237 NLA_POLICY_NESTED(ethnl_header_policy_phy), 238 [ETHTOOL_A_CABLE_TEST_TDR_CFG] = { .type = NLA_NESTED }, 239 }; 240 241 /* CABLE_TEST_TDR_ACT */ 242 static int ethnl_act_cable_test_tdr_cfg(const struct nlattr *nest, 243 struct genl_info *info, 244 struct phy_tdr_config *cfg) 245 { 246 struct nlattr *tb[ARRAY_SIZE(cable_test_tdr_act_cfg_policy)]; 247 int ret; 248 249 cfg->first = 100; 250 cfg->step = 100; 251 cfg->last = MAX_CABLE_LENGTH_CM; 252 cfg->pair = PHY_PAIR_ALL; 253 254 if (!nest) 255 return 0; 256 257 ret = nla_parse_nested(tb, 258 ARRAY_SIZE(cable_test_tdr_act_cfg_policy) - 1, 259 nest, cable_test_tdr_act_cfg_policy, 260 info->extack); 261 if (ret < 0) 262 return ret; 263 264 if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST]) 265 cfg->first = nla_get_u32( 266 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST]); 267 268 if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST]) 269 cfg->last = nla_get_u32(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST]); 270 271 if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP]) 272 cfg->step = nla_get_u32(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP]); 273 274 if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]) { 275 cfg->pair = nla_get_u8(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]); 276 if (cfg->pair > ETHTOOL_A_CABLE_PAIR_D) { 277 NL_SET_ERR_MSG_ATTR( 278 info->extack, 279 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR], 280 "invalid pair parameter"); 281 return -EINVAL; 282 } 283 } 284 285 if (cfg->first > MAX_CABLE_LENGTH_CM) { 286 NL_SET_ERR_MSG_ATTR(info->extack, 287 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST], 288 "invalid first parameter"); 289 return -EINVAL; 290 } 291 292 if (cfg->last > MAX_CABLE_LENGTH_CM) { 293 NL_SET_ERR_MSG_ATTR(info->extack, 294 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST], 295 "invalid last parameter"); 296 return -EINVAL; 297 } 298 299 if (cfg->first > cfg->last) { 300 NL_SET_ERR_MSG(info->extack, "invalid first/last parameter"); 301 return -EINVAL; 302 } 303 304 if (!cfg->step) { 305 NL_SET_ERR_MSG_ATTR(info->extack, 306 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP], 307 "invalid step parameter"); 308 return -EINVAL; 309 } 310 311 if (cfg->step > (cfg->last - cfg->first)) { 312 NL_SET_ERR_MSG_ATTR(info->extack, 313 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP], 314 "step parameter too big"); 315 return -EINVAL; 316 } 317 318 return 0; 319 } 320 321 int ethnl_act_cable_test_tdr(struct sk_buff *skb, struct genl_info *info) 322 { 323 struct ethnl_req_info req_info = {}; 324 const struct ethtool_phy_ops *ops; 325 struct nlattr **tb = info->attrs; 326 struct phy_device *phydev; 327 struct phy_tdr_config cfg; 328 struct net_device *dev; 329 int ret; 330 331 ret = ethnl_parse_header_dev_get(&req_info, 332 tb[ETHTOOL_A_CABLE_TEST_TDR_HEADER], 333 genl_info_net(info), info->extack, 334 true); 335 if (ret < 0) 336 return ret; 337 338 dev = req_info.dev; 339 340 ret = ethnl_act_cable_test_tdr_cfg(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG], 341 info, &cfg); 342 if (ret) 343 goto out_dev_put; 344 345 rtnl_lock(); 346 netdev_lock_ops(dev); 347 phydev = ethnl_req_get_phydev(&req_info, tb, 348 ETHTOOL_A_CABLE_TEST_TDR_HEADER, 349 info->extack); 350 if (IS_ERR_OR_NULL(phydev)) { 351 ret = -EOPNOTSUPP; 352 goto out_unlock; 353 } 354 355 ops = ethtool_phy_ops; 356 if (!ops || !ops->start_cable_test_tdr) { 357 ret = -EOPNOTSUPP; 358 goto out_unlock; 359 } 360 361 ret = ethnl_ops_begin(dev); 362 if (ret < 0) 363 goto out_unlock; 364 365 ret = ops->start_cable_test_tdr(phydev, info->extack, &cfg); 366 367 ethnl_ops_complete(dev); 368 369 if (!ret) 370 ethnl_cable_test_started(phydev, 371 ETHTOOL_MSG_CABLE_TEST_TDR_NTF); 372 373 out_unlock: 374 netdev_unlock_ops(dev); 375 rtnl_unlock(); 376 out_dev_put: 377 ethnl_parse_header_dev_put(&req_info); 378 return ret; 379 } 380 381 int ethnl_cable_test_amplitude(struct phy_device *phydev, 382 u8 pair, s16 mV) 383 { 384 struct nlattr *nest; 385 int ret = -EMSGSIZE; 386 387 nest = nla_nest_start(phydev->skb, 388 ETHTOOL_A_CABLE_TDR_NEST_AMPLITUDE); 389 if (!nest) 390 return -EMSGSIZE; 391 392 if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_AMPLITUDE_PAIR, pair)) 393 goto err; 394 if (nla_put_u16(phydev->skb, ETHTOOL_A_CABLE_AMPLITUDE_mV, mV)) 395 goto err; 396 397 nla_nest_end(phydev->skb, nest); 398 return 0; 399 400 err: 401 nla_nest_cancel(phydev->skb, nest); 402 return ret; 403 } 404 EXPORT_SYMBOL_GPL(ethnl_cable_test_amplitude); 405 406 int ethnl_cable_test_pulse(struct phy_device *phydev, u16 mV) 407 { 408 struct nlattr *nest; 409 int ret = -EMSGSIZE; 410 411 nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_TDR_NEST_PULSE); 412 if (!nest) 413 return -EMSGSIZE; 414 415 if (nla_put_u16(phydev->skb, ETHTOOL_A_CABLE_PULSE_mV, mV)) 416 goto err; 417 418 nla_nest_end(phydev->skb, nest); 419 return 0; 420 421 err: 422 nla_nest_cancel(phydev->skb, nest); 423 return ret; 424 } 425 EXPORT_SYMBOL_GPL(ethnl_cable_test_pulse); 426 427 int ethnl_cable_test_step(struct phy_device *phydev, u32 first, u32 last, 428 u32 step) 429 { 430 struct nlattr *nest; 431 int ret = -EMSGSIZE; 432 433 nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_TDR_NEST_STEP); 434 if (!nest) 435 return -EMSGSIZE; 436 437 if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE, 438 first)) 439 goto err; 440 441 if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_LAST_DISTANCE, last)) 442 goto err; 443 444 if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_STEP_DISTANCE, step)) 445 goto err; 446 447 nla_nest_end(phydev->skb, nest); 448 return 0; 449 450 err: 451 nla_nest_cancel(phydev->skb, nest); 452 return ret; 453 } 454 EXPORT_SYMBOL_GPL(ethnl_cable_test_step); 455