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 ethnl_cable_test_act_policy[] = { 15 [ETHTOOL_A_CABLE_TEST_HEADER] = { .type = NLA_NESTED }, 16 }; 17 18 static int ethnl_cable_test_started(struct phy_device *phydev, u8 cmd) 19 { 20 struct sk_buff *skb; 21 int err = -ENOMEM; 22 void *ehdr; 23 24 skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); 25 if (!skb) 26 goto out; 27 28 ehdr = ethnl_bcastmsg_put(skb, cmd); 29 if (!ehdr) { 30 err = -EMSGSIZE; 31 goto out; 32 } 33 34 err = ethnl_fill_reply_header(skb, phydev->attached_dev, 35 ETHTOOL_A_CABLE_TEST_NTF_HEADER); 36 if (err) 37 goto out; 38 39 err = nla_put_u8(skb, ETHTOOL_A_CABLE_TEST_NTF_STATUS, 40 ETHTOOL_A_CABLE_TEST_NTF_STATUS_STARTED); 41 if (err) 42 goto out; 43 44 genlmsg_end(skb, ehdr); 45 46 return ethnl_multicast(skb, phydev->attached_dev); 47 48 out: 49 nlmsg_free(skb); 50 phydev_err(phydev, "%s: Error %pe\n", __func__, ERR_PTR(err)); 51 52 return err; 53 } 54 55 int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info) 56 { 57 struct ethnl_req_info req_info = {}; 58 const struct ethtool_phy_ops *ops; 59 struct nlattr **tb = info->attrs; 60 struct net_device *dev; 61 int ret; 62 63 ret = ethnl_parse_header_dev_get(&req_info, 64 tb[ETHTOOL_A_CABLE_TEST_HEADER], 65 genl_info_net(info), info->extack, 66 true); 67 if (ret < 0) 68 return ret; 69 70 dev = req_info.dev; 71 if (!dev->phydev) { 72 ret = -EOPNOTSUPP; 73 goto out_dev_put; 74 } 75 76 rtnl_lock(); 77 ops = ethtool_phy_ops; 78 if (!ops || !ops->start_cable_test) { 79 ret = -EOPNOTSUPP; 80 goto out_rtnl; 81 } 82 83 ret = ethnl_ops_begin(dev); 84 if (ret < 0) 85 goto out_rtnl; 86 87 ret = ops->start_cable_test(dev->phydev, info->extack); 88 89 ethnl_ops_complete(dev); 90 91 if (!ret) 92 ethnl_cable_test_started(dev->phydev, 93 ETHTOOL_MSG_CABLE_TEST_NTF); 94 95 out_rtnl: 96 rtnl_unlock(); 97 out_dev_put: 98 dev_put(dev); 99 return ret; 100 } 101 102 int ethnl_cable_test_alloc(struct phy_device *phydev, u8 cmd) 103 { 104 int err = -ENOMEM; 105 106 /* One TDR sample occupies 20 bytes. For a 150 meter cable, 107 * with four pairs, around 12K is needed. 108 */ 109 phydev->skb = genlmsg_new(SZ_16K, GFP_KERNEL); 110 if (!phydev->skb) 111 goto out; 112 113 phydev->ehdr = ethnl_bcastmsg_put(phydev->skb, cmd); 114 if (!phydev->ehdr) { 115 err = -EMSGSIZE; 116 goto out; 117 } 118 119 err = ethnl_fill_reply_header(phydev->skb, phydev->attached_dev, 120 ETHTOOL_A_CABLE_TEST_NTF_HEADER); 121 if (err) 122 goto out; 123 124 err = nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_TEST_NTF_STATUS, 125 ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED); 126 if (err) 127 goto out; 128 129 phydev->nest = nla_nest_start(phydev->skb, 130 ETHTOOL_A_CABLE_TEST_NTF_NEST); 131 if (!phydev->nest) { 132 err = -EMSGSIZE; 133 goto out; 134 } 135 136 return 0; 137 138 out: 139 nlmsg_free(phydev->skb); 140 phydev->skb = NULL; 141 return err; 142 } 143 EXPORT_SYMBOL_GPL(ethnl_cable_test_alloc); 144 145 void ethnl_cable_test_free(struct phy_device *phydev) 146 { 147 nlmsg_free(phydev->skb); 148 phydev->skb = NULL; 149 } 150 EXPORT_SYMBOL_GPL(ethnl_cable_test_free); 151 152 void ethnl_cable_test_finished(struct phy_device *phydev) 153 { 154 nla_nest_end(phydev->skb, phydev->nest); 155 156 genlmsg_end(phydev->skb, phydev->ehdr); 157 158 ethnl_multicast(phydev->skb, phydev->attached_dev); 159 } 160 EXPORT_SYMBOL_GPL(ethnl_cable_test_finished); 161 162 int ethnl_cable_test_result(struct phy_device *phydev, u8 pair, u8 result) 163 { 164 struct nlattr *nest; 165 int ret = -EMSGSIZE; 166 167 nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_NEST_RESULT); 168 if (!nest) 169 return -EMSGSIZE; 170 171 if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_RESULT_PAIR, pair)) 172 goto err; 173 if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_RESULT_CODE, result)) 174 goto err; 175 176 nla_nest_end(phydev->skb, nest); 177 return 0; 178 179 err: 180 nla_nest_cancel(phydev->skb, nest); 181 return ret; 182 } 183 EXPORT_SYMBOL_GPL(ethnl_cable_test_result); 184 185 int ethnl_cable_test_fault_length(struct phy_device *phydev, u8 pair, u32 cm) 186 { 187 struct nlattr *nest; 188 int ret = -EMSGSIZE; 189 190 nest = nla_nest_start(phydev->skb, 191 ETHTOOL_A_CABLE_NEST_FAULT_LENGTH); 192 if (!nest) 193 return -EMSGSIZE; 194 195 if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR, pair)) 196 goto err; 197 if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_FAULT_LENGTH_CM, cm)) 198 goto err; 199 200 nla_nest_end(phydev->skb, nest); 201 return 0; 202 203 err: 204 nla_nest_cancel(phydev->skb, nest); 205 return ret; 206 } 207 EXPORT_SYMBOL_GPL(ethnl_cable_test_fault_length); 208 209 struct cable_test_tdr_req_info { 210 struct ethnl_req_info base; 211 }; 212 213 static const struct nla_policy cable_test_tdr_act_cfg_policy[] = { 214 [ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST] = { .type = NLA_U32 }, 215 [ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST] = { .type = NLA_U32 }, 216 [ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP] = { .type = NLA_U32 }, 217 [ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR] = { .type = NLA_U8 }, 218 }; 219 220 const struct nla_policy ethnl_cable_test_tdr_act_policy[] = { 221 [ETHTOOL_A_CABLE_TEST_TDR_HEADER] = { .type = NLA_NESTED }, 222 [ETHTOOL_A_CABLE_TEST_TDR_CFG] = { .type = NLA_NESTED }, 223 }; 224 225 /* CABLE_TEST_TDR_ACT */ 226 static int ethnl_act_cable_test_tdr_cfg(const struct nlattr *nest, 227 struct genl_info *info, 228 struct phy_tdr_config *cfg) 229 { 230 struct nlattr *tb[ARRAY_SIZE(cable_test_tdr_act_cfg_policy)]; 231 int ret; 232 233 cfg->first = 100; 234 cfg->step = 100; 235 cfg->last = MAX_CABLE_LENGTH_CM; 236 cfg->pair = PHY_PAIR_ALL; 237 238 if (!nest) 239 return 0; 240 241 ret = nla_parse_nested(tb, 242 ARRAY_SIZE(cable_test_tdr_act_cfg_policy) - 1, 243 nest, cable_test_tdr_act_cfg_policy, 244 info->extack); 245 if (ret < 0) 246 return ret; 247 248 if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST]) 249 cfg->first = nla_get_u32( 250 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST]); 251 252 if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST]) 253 cfg->last = nla_get_u32(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST]); 254 255 if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP]) 256 cfg->step = nla_get_u32(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP]); 257 258 if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]) { 259 cfg->pair = nla_get_u8(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]); 260 if (cfg->pair > ETHTOOL_A_CABLE_PAIR_D) { 261 NL_SET_ERR_MSG_ATTR( 262 info->extack, 263 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR], 264 "invalid pair parameter"); 265 return -EINVAL; 266 } 267 } 268 269 if (cfg->first > MAX_CABLE_LENGTH_CM) { 270 NL_SET_ERR_MSG_ATTR(info->extack, 271 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST], 272 "invalid first parameter"); 273 return -EINVAL; 274 } 275 276 if (cfg->last > MAX_CABLE_LENGTH_CM) { 277 NL_SET_ERR_MSG_ATTR(info->extack, 278 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST], 279 "invalid last parameter"); 280 return -EINVAL; 281 } 282 283 if (cfg->first > cfg->last) { 284 NL_SET_ERR_MSG(info->extack, "invalid first/last parameter"); 285 return -EINVAL; 286 } 287 288 if (!cfg->step) { 289 NL_SET_ERR_MSG_ATTR(info->extack, 290 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP], 291 "invalid step parameter"); 292 return -EINVAL; 293 } 294 295 if (cfg->step > (cfg->last - cfg->first)) { 296 NL_SET_ERR_MSG_ATTR(info->extack, 297 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP], 298 "step parameter too big"); 299 return -EINVAL; 300 } 301 302 return 0; 303 } 304 305 int ethnl_act_cable_test_tdr(struct sk_buff *skb, struct genl_info *info) 306 { 307 struct ethnl_req_info req_info = {}; 308 const struct ethtool_phy_ops *ops; 309 struct nlattr **tb = info->attrs; 310 struct phy_tdr_config cfg; 311 struct net_device *dev; 312 int ret; 313 314 ret = ethnl_parse_header_dev_get(&req_info, 315 tb[ETHTOOL_A_CABLE_TEST_TDR_HEADER], 316 genl_info_net(info), info->extack, 317 true); 318 if (ret < 0) 319 return ret; 320 321 dev = req_info.dev; 322 if (!dev->phydev) { 323 ret = -EOPNOTSUPP; 324 goto out_dev_put; 325 } 326 327 ret = ethnl_act_cable_test_tdr_cfg(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG], 328 info, &cfg); 329 if (ret) 330 goto out_dev_put; 331 332 rtnl_lock(); 333 ops = ethtool_phy_ops; 334 if (!ops || !ops->start_cable_test_tdr) { 335 ret = -EOPNOTSUPP; 336 goto out_rtnl; 337 } 338 339 ret = ethnl_ops_begin(dev); 340 if (ret < 0) 341 goto out_rtnl; 342 343 ret = ops->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