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 7 #include "common.h" 8 #include "netlink.h" 9 10 /* 802.3 standard allows 100 meters for BaseT cables. However longer 11 * cables might work, depending on the quality of the cables and the 12 * PHY. So allow testing for up to 150 meters. 13 */ 14 #define MAX_CABLE_LENGTH_CM (150 * 100) 15 16 const struct nla_policy ethnl_cable_test_act_policy[] = { 17 [ETHTOOL_A_CABLE_TEST_HEADER] = 18 NLA_POLICY_NESTED(ethnl_header_policy_phy), 19 }; 20 21 static int ethnl_cable_test_started(struct phy_device *phydev, u8 cmd) 22 { 23 struct sk_buff *skb; 24 int err = -ENOMEM; 25 void *ehdr; 26 27 skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); 28 if (!skb) 29 goto out; 30 31 ehdr = ethnl_bcastmsg_put(skb, cmd); 32 if (!ehdr) { 33 err = -EMSGSIZE; 34 goto out; 35 } 36 37 err = ethnl_fill_reply_header(skb, phydev->attached_dev, 38 ETHTOOL_A_CABLE_TEST_NTF_HEADER); 39 if (err) 40 goto out; 41 42 err = nla_put_u8(skb, ETHTOOL_A_CABLE_TEST_NTF_STATUS, 43 ETHTOOL_A_CABLE_TEST_NTF_STATUS_STARTED); 44 if (err) 45 goto out; 46 47 genlmsg_end(skb, ehdr); 48 49 return ethnl_multicast(skb, phydev->attached_dev); 50 51 out: 52 nlmsg_free(skb); 53 phydev_err(phydev, "%s: Error %pe\n", __func__, ERR_PTR(err)); 54 55 return err; 56 } 57 58 int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info) 59 { 60 struct ethnl_req_info req_info = {}; 61 const struct ethtool_phy_ops *ops; 62 struct nlattr **tb = info->attrs; 63 struct phy_device *phydev; 64 struct net_device *dev; 65 int ret; 66 67 ret = ethnl_parse_header_dev_get(&req_info, 68 tb[ETHTOOL_A_CABLE_TEST_HEADER], 69 genl_info_net(info), info->extack, 70 true); 71 if (ret < 0) 72 return ret; 73 74 dev = req_info.dev; 75 76 netdev_lock_ops_compat(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_compat(dev); 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 netdev_lock_ops_compat(dev); 344 phydev = ethnl_req_get_phydev(&req_info, tb, 345 ETHTOOL_A_CABLE_TEST_TDR_HEADER, 346 info->extack); 347 if (IS_ERR_OR_NULL(phydev)) { 348 ret = -EOPNOTSUPP; 349 goto out_unlock; 350 } 351 352 ops = ethtool_phy_ops; 353 if (!ops || !ops->start_cable_test_tdr) { 354 ret = -EOPNOTSUPP; 355 goto out_unlock; 356 } 357 358 ret = ethnl_ops_begin(dev); 359 if (ret < 0) 360 goto out_unlock; 361 362 ret = ops->start_cable_test_tdr(phydev, info->extack, &cfg); 363 364 ethnl_ops_complete(dev); 365 366 if (!ret) 367 ethnl_cable_test_started(phydev, 368 ETHTOOL_MSG_CABLE_TEST_TDR_NTF); 369 370 out_unlock: 371 netdev_unlock_ops_compat(dev); 372 out_dev_put: 373 ethnl_parse_header_dev_put(&req_info); 374 return ret; 375 } 376 377 int ethnl_cable_test_amplitude(struct phy_device *phydev, 378 u8 pair, s16 mV) 379 { 380 struct nlattr *nest; 381 int ret = -EMSGSIZE; 382 383 nest = nla_nest_start(phydev->skb, 384 ETHTOOL_A_CABLE_TDR_NEST_AMPLITUDE); 385 if (!nest) 386 return -EMSGSIZE; 387 388 if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_AMPLITUDE_PAIR, pair)) 389 goto err; 390 if (nla_put_u16(phydev->skb, ETHTOOL_A_CABLE_AMPLITUDE_mV, mV)) 391 goto err; 392 393 nla_nest_end(phydev->skb, nest); 394 return 0; 395 396 err: 397 nla_nest_cancel(phydev->skb, nest); 398 return ret; 399 } 400 EXPORT_SYMBOL_GPL(ethnl_cable_test_amplitude); 401 402 int ethnl_cable_test_pulse(struct phy_device *phydev, u16 mV) 403 { 404 struct nlattr *nest; 405 int ret = -EMSGSIZE; 406 407 nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_TDR_NEST_PULSE); 408 if (!nest) 409 return -EMSGSIZE; 410 411 if (nla_put_u16(phydev->skb, ETHTOOL_A_CABLE_PULSE_mV, mV)) 412 goto err; 413 414 nla_nest_end(phydev->skb, nest); 415 return 0; 416 417 err: 418 nla_nest_cancel(phydev->skb, nest); 419 return ret; 420 } 421 EXPORT_SYMBOL_GPL(ethnl_cable_test_pulse); 422 423 int ethnl_cable_test_step(struct phy_device *phydev, u32 first, u32 last, 424 u32 step) 425 { 426 struct nlattr *nest; 427 int ret = -EMSGSIZE; 428 429 nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_TDR_NEST_STEP); 430 if (!nest) 431 return -EMSGSIZE; 432 433 if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE, 434 first)) 435 goto err; 436 437 if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_LAST_DISTANCE, last)) 438 goto err; 439 440 if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_STEP_DISTANCE, step)) 441 goto err; 442 443 nla_nest_end(phydev->skb, nest); 444 return 0; 445 446 err: 447 nla_nest_cancel(phydev->skb, nest); 448 return ret; 449 } 450 EXPORT_SYMBOL_GPL(ethnl_cable_test_step); 451