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