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