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