at v5.9-rc2 446 lines 10 kB view raw
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 14static const struct nla_policy 15cable_test_act_policy[ETHTOOL_A_CABLE_TEST_MAX + 1] = { 16 [ETHTOOL_A_CABLE_TEST_UNSPEC] = { .type = NLA_REJECT }, 17 [ETHTOOL_A_CABLE_TEST_HEADER] = { .type = NLA_NESTED }, 18}; 19 20static 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 50out: 51 nlmsg_free(skb); 52 phydev_err(phydev, "%s: Error %pe\n", __func__, ERR_PTR(err)); 53 54 return err; 55} 56 57int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info) 58{ 59 struct nlattr *tb[ETHTOOL_A_CABLE_TEST_MAX + 1]; 60 struct ethnl_req_info req_info = {}; 61 const struct ethtool_phy_ops *ops; 62 struct net_device *dev; 63 int ret; 64 65 ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb, 66 ETHTOOL_A_CABLE_TEST_MAX, 67 cable_test_act_policy, info->extack); 68 if (ret < 0) 69 return ret; 70 71 ret = ethnl_parse_header_dev_get(&req_info, 72 tb[ETHTOOL_A_CABLE_TEST_HEADER], 73 genl_info_net(info), info->extack, 74 true); 75 if (ret < 0) 76 return ret; 77 78 dev = req_info.dev; 79 if (!dev->phydev) { 80 ret = -EOPNOTSUPP; 81 goto out_dev_put; 82 } 83 84 rtnl_lock(); 85 ops = ethtool_phy_ops; 86 if (!ops || !ops->start_cable_test) { 87 ret = -EOPNOTSUPP; 88 goto out_rtnl; 89 } 90 91 ret = ethnl_ops_begin(dev); 92 if (ret < 0) 93 goto out_rtnl; 94 95 ret = ops->start_cable_test(dev->phydev, info->extack); 96 97 ethnl_ops_complete(dev); 98 99 if (!ret) 100 ethnl_cable_test_started(dev->phydev, 101 ETHTOOL_MSG_CABLE_TEST_NTF); 102 103out_rtnl: 104 rtnl_unlock(); 105out_dev_put: 106 dev_put(dev); 107 return ret; 108} 109 110int 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 146out: 147 nlmsg_free(phydev->skb); 148 phydev->skb = NULL; 149 return err; 150} 151EXPORT_SYMBOL_GPL(ethnl_cable_test_alloc); 152 153void ethnl_cable_test_free(struct phy_device *phydev) 154{ 155 nlmsg_free(phydev->skb); 156 phydev->skb = NULL; 157} 158EXPORT_SYMBOL_GPL(ethnl_cable_test_free); 159 160void 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} 168EXPORT_SYMBOL_GPL(ethnl_cable_test_finished); 169 170int ethnl_cable_test_result(struct phy_device *phydev, u8 pair, u8 result) 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 184 nla_nest_end(phydev->skb, nest); 185 return 0; 186 187err: 188 nla_nest_cancel(phydev->skb, nest); 189 return ret; 190} 191EXPORT_SYMBOL_GPL(ethnl_cable_test_result); 192 193int ethnl_cable_test_fault_length(struct phy_device *phydev, u8 pair, u32 cm) 194{ 195 struct nlattr *nest; 196 int ret = -EMSGSIZE; 197 198 nest = nla_nest_start(phydev->skb, 199 ETHTOOL_A_CABLE_NEST_FAULT_LENGTH); 200 if (!nest) 201 return -EMSGSIZE; 202 203 if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR, pair)) 204 goto err; 205 if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_FAULT_LENGTH_CM, cm)) 206 goto err; 207 208 nla_nest_end(phydev->skb, nest); 209 return 0; 210 211err: 212 nla_nest_cancel(phydev->skb, nest); 213 return ret; 214} 215EXPORT_SYMBOL_GPL(ethnl_cable_test_fault_length); 216 217struct cable_test_tdr_req_info { 218 struct ethnl_req_info base; 219}; 220 221static const struct nla_policy 222cable_test_tdr_act_cfg_policy[ETHTOOL_A_CABLE_TEST_TDR_CFG_MAX + 1] = { 223 [ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST] = { .type = NLA_U32 }, 224 [ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST] = { .type = NLA_U32 }, 225 [ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP] = { .type = NLA_U32 }, 226 [ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR] = { .type = NLA_U8 }, 227}; 228 229static const struct nla_policy 230cable_test_tdr_act_policy[ETHTOOL_A_CABLE_TEST_TDR_MAX + 1] = { 231 [ETHTOOL_A_CABLE_TEST_TDR_UNSPEC] = { .type = NLA_REJECT }, 232 [ETHTOOL_A_CABLE_TEST_TDR_HEADER] = { .type = NLA_NESTED }, 233 [ETHTOOL_A_CABLE_TEST_TDR_CFG] = { .type = NLA_NESTED }, 234}; 235 236/* CABLE_TEST_TDR_ACT */ 237static int ethnl_act_cable_test_tdr_cfg(const struct nlattr *nest, 238 struct genl_info *info, 239 struct phy_tdr_config *cfg) 240{ 241 struct nlattr *tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_MAX + 1]; 242 int ret; 243 244 cfg->first = 100; 245 cfg->step = 100; 246 cfg->last = MAX_CABLE_LENGTH_CM; 247 cfg->pair = PHY_PAIR_ALL; 248 249 if (!nest) 250 return 0; 251 252 ret = nla_parse_nested(tb, ETHTOOL_A_CABLE_TEST_TDR_CFG_MAX, nest, 253 cable_test_tdr_act_cfg_policy, info->extack); 254 if (ret < 0) 255 return ret; 256 257 if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST]) 258 cfg->first = nla_get_u32( 259 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST]); 260 261 if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST]) 262 cfg->last = nla_get_u32(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST]); 263 264 if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP]) 265 cfg->step = nla_get_u32(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP]); 266 267 if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]) { 268 cfg->pair = nla_get_u8(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]); 269 if (cfg->pair > ETHTOOL_A_CABLE_PAIR_D) { 270 NL_SET_ERR_MSG_ATTR( 271 info->extack, 272 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR], 273 "invalid pair parameter"); 274 return -EINVAL; 275 } 276 } 277 278 if (cfg->first > MAX_CABLE_LENGTH_CM) { 279 NL_SET_ERR_MSG_ATTR(info->extack, 280 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST], 281 "invalid first parameter"); 282 return -EINVAL; 283 } 284 285 if (cfg->last > MAX_CABLE_LENGTH_CM) { 286 NL_SET_ERR_MSG_ATTR(info->extack, 287 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST], 288 "invalid last parameter"); 289 return -EINVAL; 290 } 291 292 if (cfg->first > cfg->last) { 293 NL_SET_ERR_MSG(info->extack, "invalid first/last parameter"); 294 return -EINVAL; 295 } 296 297 if (!cfg->step) { 298 NL_SET_ERR_MSG_ATTR(info->extack, 299 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP], 300 "invalid step parameter"); 301 return -EINVAL; 302 } 303 304 if (cfg->step > (cfg->last - cfg->first)) { 305 NL_SET_ERR_MSG_ATTR(info->extack, 306 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP], 307 "step parameter too big"); 308 return -EINVAL; 309 } 310 311 return 0; 312} 313 314int ethnl_act_cable_test_tdr(struct sk_buff *skb, struct genl_info *info) 315{ 316 struct nlattr *tb[ETHTOOL_A_CABLE_TEST_TDR_MAX + 1]; 317 struct ethnl_req_info req_info = {}; 318 const struct ethtool_phy_ops *ops; 319 struct phy_tdr_config cfg; 320 struct net_device *dev; 321 int ret; 322 323 ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb, 324 ETHTOOL_A_CABLE_TEST_TDR_MAX, 325 cable_test_tdr_act_policy, info->extack); 326 if (ret < 0) 327 return 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 if (!dev->phydev) { 338 ret = -EOPNOTSUPP; 339 goto out_dev_put; 340 } 341 342 ret = ethnl_act_cable_test_tdr_cfg(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG], 343 info, &cfg); 344 if (ret) 345 goto out_dev_put; 346 347 rtnl_lock(); 348 ops = ethtool_phy_ops; 349 if (!ops || !ops->start_cable_test_tdr) { 350 ret = -EOPNOTSUPP; 351 goto out_rtnl; 352 } 353 354 ret = ethnl_ops_begin(dev); 355 if (ret < 0) 356 goto out_rtnl; 357 358 ret = ops->start_cable_test_tdr(dev->phydev, info->extack, &cfg); 359 360 ethnl_ops_complete(dev); 361 362 if (!ret) 363 ethnl_cable_test_started(dev->phydev, 364 ETHTOOL_MSG_CABLE_TEST_TDR_NTF); 365 366out_rtnl: 367 rtnl_unlock(); 368out_dev_put: 369 dev_put(dev); 370 return ret; 371} 372 373int ethnl_cable_test_amplitude(struct phy_device *phydev, 374 u8 pair, s16 mV) 375{ 376 struct nlattr *nest; 377 int ret = -EMSGSIZE; 378 379 nest = nla_nest_start(phydev->skb, 380 ETHTOOL_A_CABLE_TDR_NEST_AMPLITUDE); 381 if (!nest) 382 return -EMSGSIZE; 383 384 if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_AMPLITUDE_PAIR, pair)) 385 goto err; 386 if (nla_put_u16(phydev->skb, ETHTOOL_A_CABLE_AMPLITUDE_mV, mV)) 387 goto err; 388 389 nla_nest_end(phydev->skb, nest); 390 return 0; 391 392err: 393 nla_nest_cancel(phydev->skb, nest); 394 return ret; 395} 396EXPORT_SYMBOL_GPL(ethnl_cable_test_amplitude); 397 398int ethnl_cable_test_pulse(struct phy_device *phydev, u16 mV) 399{ 400 struct nlattr *nest; 401 int ret = -EMSGSIZE; 402 403 nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_TDR_NEST_PULSE); 404 if (!nest) 405 return -EMSGSIZE; 406 407 if (nla_put_u16(phydev->skb, ETHTOOL_A_CABLE_PULSE_mV, mV)) 408 goto err; 409 410 nla_nest_end(phydev->skb, nest); 411 return 0; 412 413err: 414 nla_nest_cancel(phydev->skb, nest); 415 return ret; 416} 417EXPORT_SYMBOL_GPL(ethnl_cable_test_pulse); 418 419int ethnl_cable_test_step(struct phy_device *phydev, u32 first, u32 last, 420 u32 step) 421{ 422 struct nlattr *nest; 423 int ret = -EMSGSIZE; 424 425 nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_TDR_NEST_STEP); 426 if (!nest) 427 return -EMSGSIZE; 428 429 if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE, 430 first)) 431 goto err; 432 433 if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_LAST_DISTANCE, last)) 434 goto err; 435 436 if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_STEP_DISTANCE, step)) 437 goto err; 438 439 nla_nest_end(phydev->skb, nest); 440 return 0; 441 442err: 443 nla_nest_cancel(phydev->skb, nest); 444 return ret; 445} 446EXPORT_SYMBOL_GPL(ethnl_cable_test_step);