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 ret = nla_parse_nested(tb, ETHTOOL_A_CABLE_TEST_TDR_CFG_MAX, nest, 238 cable_test_tdr_act_cfg_policy, info->extack); 239 if (ret < 0) 240 return ret; 241 242 if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST]) 243 cfg->first = nla_get_u32( 244 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST]); 245 else 246 cfg->first = 100; 247 if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST]) 248 cfg->last = nla_get_u32(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST]); 249 else 250 cfg->last = MAX_CABLE_LENGTH_CM; 251 252 if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP]) 253 cfg->step = nla_get_u32(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP]); 254 else 255 cfg->step = 100; 256 257 if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]) { 258 cfg->pair = nla_get_u8(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]); 259 if (cfg->pair > ETHTOOL_A_CABLE_PAIR_D) { 260 NL_SET_ERR_MSG_ATTR( 261 info->extack, 262 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR], 263 "invalid pair parameter"); 264 return -EINVAL; 265 } 266 } else { 267 cfg->pair = PHY_PAIR_ALL; 268 } 269 270 if (cfg->first > MAX_CABLE_LENGTH_CM) { 271 NL_SET_ERR_MSG_ATTR(info->extack, 272 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST], 273 "invalid first parameter"); 274 return -EINVAL; 275 } 276 277 if (cfg->last > MAX_CABLE_LENGTH_CM) { 278 NL_SET_ERR_MSG_ATTR(info->extack, 279 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST], 280 "invalid last parameter"); 281 return -EINVAL; 282 } 283 284 if (cfg->first > cfg->last) { 285 NL_SET_ERR_MSG(info->extack, "invalid first/last parameter"); 286 return -EINVAL; 287 } 288 289 if (!cfg->step) { 290 NL_SET_ERR_MSG_ATTR(info->extack, 291 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP], 292 "invalid step parameter"); 293 return -EINVAL; 294 } 295 296 if (cfg->step > (cfg->last - cfg->first)) { 297 NL_SET_ERR_MSG_ATTR(info->extack, 298 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP], 299 "step parameter too big"); 300 return -EINVAL; 301 } 302 303 return 0; 304 } 305 306 int ethnl_act_cable_test_tdr(struct sk_buff *skb, struct genl_info *info) 307 { 308 struct nlattr *tb[ETHTOOL_A_CABLE_TEST_TDR_MAX + 1]; 309 struct ethnl_req_info req_info = {}; 310 struct phy_tdr_config cfg; 311 struct net_device *dev; 312 int ret; 313 314 ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb, 315 ETHTOOL_A_CABLE_TEST_TDR_MAX, 316 cable_test_tdr_act_policy, info->extack); 317 if (ret < 0) 318 return ret; 319 320 ret = ethnl_parse_header_dev_get(&req_info, 321 tb[ETHTOOL_A_CABLE_TEST_TDR_HEADER], 322 genl_info_net(info), info->extack, 323 true); 324 if (ret < 0) 325 return ret; 326 327 dev = req_info.dev; 328 if (!dev->phydev) { 329 ret = -EOPNOTSUPP; 330 goto out_dev_put; 331 } 332 333 ret = ethnl_act_cable_test_tdr_cfg(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG], 334 info, &cfg); 335 if (ret) 336 goto out_dev_put; 337 338 rtnl_lock(); 339 ret = ethnl_ops_begin(dev); 340 if (ret < 0) 341 goto out_rtnl; 342 343 ret = phy_start_cable_test_tdr(dev->phydev, info->extack, &cfg); 344 345 ethnl_ops_complete(dev); 346 347 if (!ret) 348 ethnl_cable_test_started(dev->phydev, 349 ETHTOOL_MSG_CABLE_TEST_TDR_NTF); 350 351 out_rtnl: 352 rtnl_unlock(); 353 out_dev_put: 354 dev_put(dev); 355 return ret; 356 } 357 358 int ethnl_cable_test_amplitude(struct phy_device *phydev, 359 u8 pair, s16 mV) 360 { 361 struct nlattr *nest; 362 int ret = -EMSGSIZE; 363 364 nest = nla_nest_start(phydev->skb, 365 ETHTOOL_A_CABLE_TDR_NEST_AMPLITUDE); 366 if (!nest) 367 return -EMSGSIZE; 368 369 if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_AMPLITUDE_PAIR, pair)) 370 goto err; 371 if (nla_put_u16(phydev->skb, ETHTOOL_A_CABLE_AMPLITUDE_mV, mV)) 372 goto err; 373 374 nla_nest_end(phydev->skb, nest); 375 return 0; 376 377 err: 378 nla_nest_cancel(phydev->skb, nest); 379 return ret; 380 } 381 EXPORT_SYMBOL_GPL(ethnl_cable_test_amplitude); 382 383 int ethnl_cable_test_pulse(struct phy_device *phydev, u16 mV) 384 { 385 struct nlattr *nest; 386 int ret = -EMSGSIZE; 387 388 nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_TDR_NEST_PULSE); 389 if (!nest) 390 return -EMSGSIZE; 391 392 if (nla_put_u16(phydev->skb, ETHTOOL_A_CABLE_PULSE_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_pulse); 403 404 int ethnl_cable_test_step(struct phy_device *phydev, u32 first, u32 last, 405 u32 step) 406 { 407 struct nlattr *nest; 408 int ret = -EMSGSIZE; 409 410 nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_TDR_NEST_STEP); 411 if (!nest) 412 return -EMSGSIZE; 413 414 if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE, 415 first)) 416 goto err; 417 418 if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_LAST_DISTANCE, last)) 419 goto err; 420 421 if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_STEP_DISTANCE, step)) 422 goto err; 423 424 nla_nest_end(phydev->skb, nest); 425 return 0; 426 427 err: 428 nla_nest_cancel(phydev->skb, nest); 429 return ret; 430 } 431 EXPORT_SYMBOL_GPL(ethnl_cable_test_step); 432