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