1 // SPDX-License-Identifier: GPL-2.0-only
4 #include <linux/ethtool_netlink.h>
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.
12 #define MAX_CABLE_LENGTH_CM (150 * 100)
14 const struct nla_policy ethnl_cable_test_act_policy
[] = {
15 [ETHTOOL_A_CABLE_TEST_HEADER
] =
16 NLA_POLICY_NESTED(ethnl_header_policy_phy
),
19 static int ethnl_cable_test_started(struct phy_device
*phydev
, u8 cmd
)
25 skb
= genlmsg_new(NLMSG_GOODSIZE
, GFP_KERNEL
);
29 ehdr
= ethnl_bcastmsg_put(skb
, cmd
);
35 err
= ethnl_fill_reply_header(skb
, phydev
->attached_dev
,
36 ETHTOOL_A_CABLE_TEST_NTF_HEADER
);
40 err
= nla_put_u8(skb
, ETHTOOL_A_CABLE_TEST_NTF_STATUS
,
41 ETHTOOL_A_CABLE_TEST_NTF_STATUS_STARTED
);
45 genlmsg_end(skb
, ehdr
);
47 return ethnl_multicast(skb
, phydev
->attached_dev
);
51 phydev_err(phydev
, "%s: Error %pe\n", __func__
, ERR_PTR(err
));
56 int ethnl_act_cable_test(struct sk_buff
*skb
, struct genl_info
*info
)
58 struct ethnl_req_info req_info
= {};
59 const struct ethtool_phy_ops
*ops
;
60 struct nlattr
**tb
= info
->attrs
;
61 struct phy_device
*phydev
;
62 struct net_device
*dev
;
65 ret
= ethnl_parse_header_dev_get(&req_info
,
66 tb
[ETHTOOL_A_CABLE_TEST_HEADER
],
67 genl_info_net(info
), info
->extack
,
75 phydev
= ethnl_req_get_phydev(&req_info
,
76 tb
[ETHTOOL_A_CABLE_TEST_HEADER
],
78 if (IS_ERR_OR_NULL(phydev
)) {
83 ops
= ethtool_phy_ops
;
84 if (!ops
|| !ops
->start_cable_test
) {
89 ret
= ethnl_ops_begin(dev
);
93 ret
= ops
->start_cable_test(phydev
, info
->extack
);
95 ethnl_ops_complete(dev
);
98 ethnl_cable_test_started(phydev
, ETHTOOL_MSG_CABLE_TEST_NTF
);
102 ethnl_parse_header_dev_put(&req_info
);
106 int ethnl_cable_test_alloc(struct phy_device
*phydev
, u8 cmd
)
110 /* One TDR sample occupies 20 bytes. For a 150 meter cable,
111 * with four pairs, around 12K is needed.
113 phydev
->skb
= genlmsg_new(SZ_16K
, GFP_KERNEL
);
117 phydev
->ehdr
= ethnl_bcastmsg_put(phydev
->skb
, cmd
);
123 err
= ethnl_fill_reply_header(phydev
->skb
, phydev
->attached_dev
,
124 ETHTOOL_A_CABLE_TEST_NTF_HEADER
);
128 err
= nla_put_u8(phydev
->skb
, ETHTOOL_A_CABLE_TEST_NTF_STATUS
,
129 ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED
);
133 phydev
->nest
= nla_nest_start(phydev
->skb
,
134 ETHTOOL_A_CABLE_TEST_NTF_NEST
);
143 nlmsg_free(phydev
->skb
);
147 EXPORT_SYMBOL_GPL(ethnl_cable_test_alloc
);
149 void ethnl_cable_test_free(struct phy_device
*phydev
)
151 nlmsg_free(phydev
->skb
);
154 EXPORT_SYMBOL_GPL(ethnl_cable_test_free
);
156 void ethnl_cable_test_finished(struct phy_device
*phydev
)
158 nla_nest_end(phydev
->skb
, phydev
->nest
);
160 genlmsg_end(phydev
->skb
, phydev
->ehdr
);
162 ethnl_multicast(phydev
->skb
, phydev
->attached_dev
);
164 EXPORT_SYMBOL_GPL(ethnl_cable_test_finished
);
166 int ethnl_cable_test_result_with_src(struct phy_device
*phydev
, u8 pair
,
172 nest
= nla_nest_start(phydev
->skb
, ETHTOOL_A_CABLE_NEST_RESULT
);
176 if (nla_put_u8(phydev
->skb
, ETHTOOL_A_CABLE_RESULT_PAIR
, pair
))
178 if (nla_put_u8(phydev
->skb
, ETHTOOL_A_CABLE_RESULT_CODE
, result
))
180 if (src
!= ETHTOOL_A_CABLE_INF_SRC_UNSPEC
) {
181 if (nla_put_u32(phydev
->skb
, ETHTOOL_A_CABLE_RESULT_SRC
, src
))
185 nla_nest_end(phydev
->skb
, nest
);
189 nla_nest_cancel(phydev
->skb
, nest
);
192 EXPORT_SYMBOL_GPL(ethnl_cable_test_result_with_src
);
194 int ethnl_cable_test_fault_length_with_src(struct phy_device
*phydev
, u8 pair
,
200 nest
= nla_nest_start(phydev
->skb
,
201 ETHTOOL_A_CABLE_NEST_FAULT_LENGTH
);
205 if (nla_put_u8(phydev
->skb
, ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR
, pair
))
207 if (nla_put_u32(phydev
->skb
, ETHTOOL_A_CABLE_FAULT_LENGTH_CM
, cm
))
209 if (src
!= ETHTOOL_A_CABLE_INF_SRC_UNSPEC
) {
210 if (nla_put_u32(phydev
->skb
, ETHTOOL_A_CABLE_FAULT_LENGTH_SRC
,
215 nla_nest_end(phydev
->skb
, nest
);
219 nla_nest_cancel(phydev
->skb
, nest
);
222 EXPORT_SYMBOL_GPL(ethnl_cable_test_fault_length_with_src
);
224 static const struct nla_policy cable_test_tdr_act_cfg_policy
[] = {
225 [ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST
] = { .type
= NLA_U32
},
226 [ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST
] = { .type
= NLA_U32
},
227 [ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP
] = { .type
= NLA_U32
},
228 [ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR
] = { .type
= NLA_U8
},
231 const struct nla_policy ethnl_cable_test_tdr_act_policy
[] = {
232 [ETHTOOL_A_CABLE_TEST_TDR_HEADER
] =
233 NLA_POLICY_NESTED(ethnl_header_policy_phy
),
234 [ETHTOOL_A_CABLE_TEST_TDR_CFG
] = { .type
= NLA_NESTED
},
237 /* CABLE_TEST_TDR_ACT */
238 static int ethnl_act_cable_test_tdr_cfg(const struct nlattr
*nest
,
239 struct genl_info
*info
,
240 struct phy_tdr_config
*cfg
)
242 struct nlattr
*tb
[ARRAY_SIZE(cable_test_tdr_act_cfg_policy
)];
247 cfg
->last
= MAX_CABLE_LENGTH_CM
;
248 cfg
->pair
= PHY_PAIR_ALL
;
253 ret
= nla_parse_nested(tb
,
254 ARRAY_SIZE(cable_test_tdr_act_cfg_policy
) - 1,
255 nest
, cable_test_tdr_act_cfg_policy
,
260 if (tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST
])
261 cfg
->first
= nla_get_u32(
262 tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST
]);
264 if (tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST
])
265 cfg
->last
= nla_get_u32(tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST
]);
267 if (tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP
])
268 cfg
->step
= nla_get_u32(tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP
]);
270 if (tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR
]) {
271 cfg
->pair
= nla_get_u8(tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR
]);
272 if (cfg
->pair
> ETHTOOL_A_CABLE_PAIR_D
) {
275 tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR
],
276 "invalid pair parameter");
281 if (cfg
->first
> MAX_CABLE_LENGTH_CM
) {
282 NL_SET_ERR_MSG_ATTR(info
->extack
,
283 tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST
],
284 "invalid first parameter");
288 if (cfg
->last
> MAX_CABLE_LENGTH_CM
) {
289 NL_SET_ERR_MSG_ATTR(info
->extack
,
290 tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST
],
291 "invalid last parameter");
295 if (cfg
->first
> cfg
->last
) {
296 NL_SET_ERR_MSG(info
->extack
, "invalid first/last parameter");
301 NL_SET_ERR_MSG_ATTR(info
->extack
,
302 tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP
],
303 "invalid step parameter");
307 if (cfg
->step
> (cfg
->last
- cfg
->first
)) {
308 NL_SET_ERR_MSG_ATTR(info
->extack
,
309 tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP
],
310 "step parameter too big");
317 int ethnl_act_cable_test_tdr(struct sk_buff
*skb
, struct genl_info
*info
)
319 struct ethnl_req_info req_info
= {};
320 const struct ethtool_phy_ops
*ops
;
321 struct nlattr
**tb
= info
->attrs
;
322 struct phy_device
*phydev
;
323 struct phy_tdr_config cfg
;
324 struct net_device
*dev
;
327 ret
= ethnl_parse_header_dev_get(&req_info
,
328 tb
[ETHTOOL_A_CABLE_TEST_TDR_HEADER
],
329 genl_info_net(info
), info
->extack
,
336 ret
= ethnl_act_cable_test_tdr_cfg(tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG
],
342 phydev
= ethnl_req_get_phydev(&req_info
,
343 tb
[ETHTOOL_A_CABLE_TEST_TDR_HEADER
],
345 if (IS_ERR_OR_NULL(phydev
)) {
350 ops
= ethtool_phy_ops
;
351 if (!ops
|| !ops
->start_cable_test_tdr
) {
356 ret
= ethnl_ops_begin(dev
);
360 ret
= ops
->start_cable_test_tdr(phydev
, info
->extack
, &cfg
);
362 ethnl_ops_complete(dev
);
365 ethnl_cable_test_started(phydev
,
366 ETHTOOL_MSG_CABLE_TEST_TDR_NTF
);
371 ethnl_parse_header_dev_put(&req_info
);
375 int ethnl_cable_test_amplitude(struct phy_device
*phydev
,
381 nest
= nla_nest_start(phydev
->skb
,
382 ETHTOOL_A_CABLE_TDR_NEST_AMPLITUDE
);
386 if (nla_put_u8(phydev
->skb
, ETHTOOL_A_CABLE_AMPLITUDE_PAIR
, pair
))
388 if (nla_put_u16(phydev
->skb
, ETHTOOL_A_CABLE_AMPLITUDE_mV
, mV
))
391 nla_nest_end(phydev
->skb
, nest
);
395 nla_nest_cancel(phydev
->skb
, nest
);
398 EXPORT_SYMBOL_GPL(ethnl_cable_test_amplitude
);
400 int ethnl_cable_test_pulse(struct phy_device
*phydev
, u16 mV
)
405 nest
= nla_nest_start(phydev
->skb
, ETHTOOL_A_CABLE_TDR_NEST_PULSE
);
409 if (nla_put_u16(phydev
->skb
, ETHTOOL_A_CABLE_PULSE_mV
, mV
))
412 nla_nest_end(phydev
->skb
, nest
);
416 nla_nest_cancel(phydev
->skb
, nest
);
419 EXPORT_SYMBOL_GPL(ethnl_cable_test_pulse
);
421 int ethnl_cable_test_step(struct phy_device
*phydev
, u32 first
, u32 last
,
427 nest
= nla_nest_start(phydev
->skb
, ETHTOOL_A_CABLE_TDR_NEST_STEP
);
431 if (nla_put_u32(phydev
->skb
, ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE
,
435 if (nla_put_u32(phydev
->skb
, ETHTOOL_A_CABLE_STEP_LAST_DISTANCE
, last
))
438 if (nla_put_u32(phydev
->skb
, ETHTOOL_A_CABLE_STEP_STEP_DISTANCE
, step
))
441 nla_nest_end(phydev
->skb
, nest
);
445 nla_nest_cancel(phydev
->skb
, nest
);
448 EXPORT_SYMBOL_GPL(ethnl_cable_test_step
);