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