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_dev_put; 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 out_dev_put: 103 ethnl_parse_header_dev_put(&req_info); 104 return ret; 105 } 106 107 int ethnl_cable_test_alloc(struct phy_device *phydev, u8 cmd) 108 { 109 int err = -ENOMEM; 110 111 /* One TDR sample occupies 20 bytes. For a 150 meter cable, 112 * with four pairs, around 12K is needed. 113 */ 114 phydev->skb = genlmsg_new(SZ_16K, GFP_KERNEL); 115 if (!phydev->skb) 116 goto out; 117 118 phydev->ehdr = ethnl_bcastmsg_put(phydev->skb, cmd); 119 if (!phydev->ehdr) { 120 err = -EMSGSIZE; 121 goto out; 122 } 123 124 err = ethnl_fill_reply_header(phydev->skb, phydev->attached_dev, 125 ETHTOOL_A_CABLE_TEST_NTF_HEADER); 126 if (err) 127 goto out; 128 129 err = nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_TEST_NTF_STATUS, 130 ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED); 131 if (err) 132 goto out; 133 134 phydev->nest = nla_nest_start(phydev->skb, 135 ETHTOOL_A_CABLE_TEST_NTF_NEST); 136 if (!phydev->nest) { 137 err = -EMSGSIZE; 138 goto out; 139 } 140 141 return 0; 142 143 out: 144 nlmsg_free(phydev->skb); 145 phydev->skb = NULL; 146 return err; 147 } 148 EXPORT_SYMBOL_GPL(ethnl_cable_test_alloc); 149 150 void ethnl_cable_test_free(struct phy_device *phydev) 151 { 152 nlmsg_free(phydev->skb); 153 phydev->skb = NULL; 154 } 155 EXPORT_SYMBOL_GPL(ethnl_cable_test_free); 156 157 void ethnl_cable_test_finished(struct phy_device *phydev) 158 { 159 nla_nest_end(phydev->skb, phydev->nest); 160 161 genlmsg_end(phydev->skb, phydev->ehdr); 162 163 ethnl_multicast(phydev->skb, phydev->attached_dev); 164 } 165 EXPORT_SYMBOL_GPL(ethnl_cable_test_finished); 166 167 int ethnl_cable_test_result_with_src(struct phy_device *phydev, u8 pair, 168 u8 result, u32 src) 169 { 170 struct nlattr *nest; 171 int ret = -EMSGSIZE; 172 173 nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_NEST_RESULT); 174 if (!nest) 175 return -EMSGSIZE; 176 177 if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_RESULT_PAIR, pair)) 178 goto err; 179 if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_RESULT_CODE, result)) 180 goto err; 181 if (src != ETHTOOL_A_CABLE_INF_SRC_UNSPEC) { 182 if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_RESULT_SRC, src)) 183 goto err; 184 } 185 186 nla_nest_end(phydev->skb, nest); 187 return 0; 188 189 err: 190 nla_nest_cancel(phydev->skb, nest); 191 return ret; 192 } 193 EXPORT_SYMBOL_GPL(ethnl_cable_test_result_with_src); 194 195 int ethnl_cable_test_fault_length_with_src(struct phy_device *phydev, u8 pair, 196 u32 cm, u32 src) 197 { 198 struct nlattr *nest; 199 int ret = -EMSGSIZE; 200 201 nest = nla_nest_start(phydev->skb, 202 ETHTOOL_A_CABLE_NEST_FAULT_LENGTH); 203 if (!nest) 204 return -EMSGSIZE; 205 206 if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR, pair)) 207 goto err; 208 if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_FAULT_LENGTH_CM, cm)) 209 goto err; 210 if (src != ETHTOOL_A_CABLE_INF_SRC_UNSPEC) { 211 if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_FAULT_LENGTH_SRC, 212 src)) 213 goto err; 214 } 215 216 nla_nest_end(phydev->skb, nest); 217 return 0; 218 219 err: 220 nla_nest_cancel(phydev->skb, nest); 221 return ret; 222 } 223 EXPORT_SYMBOL_GPL(ethnl_cable_test_fault_length_with_src); 224 225 static const struct nla_policy cable_test_tdr_act_cfg_policy[] = { 226 [ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST] = { .type = NLA_U32 }, 227 [ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST] = { .type = NLA_U32 }, 228 [ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP] = { .type = NLA_U32 }, 229 [ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR] = { .type = NLA_U8 }, 230 }; 231 232 const struct nla_policy ethnl_cable_test_tdr_act_policy[] = { 233 [ETHTOOL_A_CABLE_TEST_TDR_HEADER] = 234 NLA_POLICY_NESTED(ethnl_header_policy_phy), 235 [ETHTOOL_A_CABLE_TEST_TDR_CFG] = { .type = NLA_NESTED }, 236 }; 237 238 /* CABLE_TEST_TDR_ACT */ 239 static int ethnl_act_cable_test_tdr_cfg(const struct nlattr *nest, 240 struct genl_info *info, 241 struct phy_tdr_config *cfg) 242 { 243 struct nlattr *tb[ARRAY_SIZE(cable_test_tdr_act_cfg_policy)]; 244 int ret; 245 246 cfg->first = 100; 247 cfg->step = 100; 248 cfg->last = MAX_CABLE_LENGTH_CM; 249 cfg->pair = PHY_PAIR_ALL; 250 251 if (!nest) 252 return 0; 253 254 ret = nla_parse_nested(tb, 255 ARRAY_SIZE(cable_test_tdr_act_cfg_policy) - 1, 256 nest, cable_test_tdr_act_cfg_policy, 257 info->extack); 258 if (ret < 0) 259 return ret; 260 261 if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST]) 262 cfg->first = nla_get_u32( 263 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST]); 264 265 if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST]) 266 cfg->last = nla_get_u32(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST]); 267 268 if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP]) 269 cfg->step = nla_get_u32(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP]); 270 271 if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]) { 272 cfg->pair = nla_get_u8(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]); 273 if (cfg->pair > ETHTOOL_A_CABLE_PAIR_D) { 274 NL_SET_ERR_MSG_ATTR( 275 info->extack, 276 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR], 277 "invalid pair parameter"); 278 return -EINVAL; 279 } 280 } 281 282 if (cfg->first > MAX_CABLE_LENGTH_CM) { 283 NL_SET_ERR_MSG_ATTR(info->extack, 284 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST], 285 "invalid first parameter"); 286 return -EINVAL; 287 } 288 289 if (cfg->last > MAX_CABLE_LENGTH_CM) { 290 NL_SET_ERR_MSG_ATTR(info->extack, 291 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST], 292 "invalid last parameter"); 293 return -EINVAL; 294 } 295 296 if (cfg->first > cfg->last) { 297 NL_SET_ERR_MSG(info->extack, "invalid first/last parameter"); 298 return -EINVAL; 299 } 300 301 if (!cfg->step) { 302 NL_SET_ERR_MSG_ATTR(info->extack, 303 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP], 304 "invalid step parameter"); 305 return -EINVAL; 306 } 307 308 if (cfg->step > (cfg->last - cfg->first)) { 309 NL_SET_ERR_MSG_ATTR(info->extack, 310 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP], 311 "step parameter too big"); 312 return -EINVAL; 313 } 314 315 return 0; 316 } 317 318 int ethnl_act_cable_test_tdr(struct sk_buff *skb, struct genl_info *info) 319 { 320 struct ethnl_req_info req_info = {}; 321 const struct ethtool_phy_ops *ops; 322 struct nlattr **tb = info->attrs; 323 struct phy_device *phydev; 324 struct phy_tdr_config cfg; 325 struct net_device *dev; 326 int ret; 327 328 ret = ethnl_parse_header_dev_get(&req_info, 329 tb[ETHTOOL_A_CABLE_TEST_TDR_HEADER], 330 genl_info_net(info), info->extack, 331 true); 332 if (ret < 0) 333 return ret; 334 335 dev = req_info.dev; 336 337 ret = ethnl_act_cable_test_tdr_cfg(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG], 338 info, &cfg); 339 if (ret) 340 goto out_dev_put; 341 342 rtnl_lock(); 343 phydev = ethnl_req_get_phydev(&req_info, 344 tb[ETHTOOL_A_CABLE_TEST_TDR_HEADER], 345 info->extack); 346 if (!IS_ERR_OR_NULL(phydev)) { 347 ret = -EOPNOTSUPP; 348 goto out_dev_put; 349 } 350 351 ops = ethtool_phy_ops; 352 if (!ops || !ops->start_cable_test_tdr) { 353 ret = -EOPNOTSUPP; 354 goto out_rtnl; 355 } 356 357 ret = ethnl_ops_begin(dev); 358 if (ret < 0) 359 goto out_rtnl; 360 361 ret = ops->start_cable_test_tdr(phydev, info->extack, &cfg); 362 363 ethnl_ops_complete(dev); 364 365 if (!ret) 366 ethnl_cable_test_started(phydev, 367 ETHTOOL_MSG_CABLE_TEST_TDR_NTF); 368 369 out_rtnl: 370 rtnl_unlock(); 371 out_dev_put: 372 ethnl_parse_header_dev_put(&req_info); 373 return ret; 374 } 375 376 int ethnl_cable_test_amplitude(struct phy_device *phydev, 377 u8 pair, s16 mV) 378 { 379 struct nlattr *nest; 380 int ret = -EMSGSIZE; 381 382 nest = nla_nest_start(phydev->skb, 383 ETHTOOL_A_CABLE_TDR_NEST_AMPLITUDE); 384 if (!nest) 385 return -EMSGSIZE; 386 387 if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_AMPLITUDE_PAIR, pair)) 388 goto err; 389 if (nla_put_u16(phydev->skb, ETHTOOL_A_CABLE_AMPLITUDE_mV, mV)) 390 goto err; 391 392 nla_nest_end(phydev->skb, nest); 393 return 0; 394 395 err: 396 nla_nest_cancel(phydev->skb, nest); 397 return ret; 398 } 399 EXPORT_SYMBOL_GPL(ethnl_cable_test_amplitude); 400 401 int ethnl_cable_test_pulse(struct phy_device *phydev, u16 mV) 402 { 403 struct nlattr *nest; 404 int ret = -EMSGSIZE; 405 406 nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_TDR_NEST_PULSE); 407 if (!nest) 408 return -EMSGSIZE; 409 410 if (nla_put_u16(phydev->skb, ETHTOOL_A_CABLE_PULSE_mV, mV)) 411 goto err; 412 413 nla_nest_end(phydev->skb, nest); 414 return 0; 415 416 err: 417 nla_nest_cancel(phydev->skb, nest); 418 return ret; 419 } 420 EXPORT_SYMBOL_GPL(ethnl_cable_test_pulse); 421 422 int ethnl_cable_test_step(struct phy_device *phydev, u32 first, u32 last, 423 u32 step) 424 { 425 struct nlattr *nest; 426 int ret = -EMSGSIZE; 427 428 nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_TDR_NEST_STEP); 429 if (!nest) 430 return -EMSGSIZE; 431 432 if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE, 433 first)) 434 goto err; 435 436 if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_LAST_DISTANCE, last)) 437 goto err; 438 439 if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_STEP_DISTANCE, step)) 440 goto err; 441 442 nla_nest_end(phydev->skb, nest); 443 return 0; 444 445 err: 446 nla_nest_cancel(phydev->skb, nest); 447 return ret; 448 } 449 EXPORT_SYMBOL_GPL(ethnl_cable_test_step); 450