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 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
},
20 static int ethnl_cable_test_started(struct phy_device
*phydev
, u8 cmd
)
26 skb
= genlmsg_new(NLMSG_GOODSIZE
, GFP_KERNEL
);
30 ehdr
= ethnl_bcastmsg_put(skb
, cmd
);
36 err
= ethnl_fill_reply_header(skb
, phydev
->attached_dev
,
37 ETHTOOL_A_CABLE_TEST_NTF_HEADER
);
41 err
= nla_put_u8(skb
, ETHTOOL_A_CABLE_TEST_NTF_STATUS
,
42 ETHTOOL_A_CABLE_TEST_NTF_STATUS_STARTED
);
46 genlmsg_end(skb
, ehdr
);
48 return ethnl_multicast(skb
, phydev
->attached_dev
);
52 phydev_err(phydev
, "%s: Error %pe\n", __func__
, ERR_PTR(err
));
57 int ethnl_act_cable_test(struct sk_buff
*skb
, struct genl_info
*info
)
59 struct nlattr
*tb
[ETHTOOL_A_CABLE_TEST_MAX
+ 1];
60 struct ethnl_req_info req_info
= {};
61 const struct ethtool_phy_ops
*ops
;
62 struct net_device
*dev
;
65 ret
= nlmsg_parse(info
->nlhdr
, GENL_HDRLEN
, tb
,
66 ETHTOOL_A_CABLE_TEST_MAX
,
67 cable_test_act_policy
, info
->extack
);
71 ret
= ethnl_parse_header_dev_get(&req_info
,
72 tb
[ETHTOOL_A_CABLE_TEST_HEADER
],
73 genl_info_net(info
), info
->extack
,
85 ops
= ethtool_phy_ops
;
86 if (!ops
|| !ops
->start_cable_test
) {
91 ret
= ethnl_ops_begin(dev
);
95 ret
= ops
->start_cable_test(dev
->phydev
, info
->extack
);
97 ethnl_ops_complete(dev
);
100 ethnl_cable_test_started(dev
->phydev
,
101 ETHTOOL_MSG_CABLE_TEST_NTF
);
110 int ethnl_cable_test_alloc(struct phy_device
*phydev
, u8 cmd
)
114 /* One TDR sample occupies 20 bytes. For a 150 meter cable,
115 * with four pairs, around 12K is needed.
117 phydev
->skb
= genlmsg_new(SZ_16K
, GFP_KERNEL
);
121 phydev
->ehdr
= ethnl_bcastmsg_put(phydev
->skb
, cmd
);
127 err
= ethnl_fill_reply_header(phydev
->skb
, phydev
->attached_dev
,
128 ETHTOOL_A_CABLE_TEST_NTF_HEADER
);
132 err
= nla_put_u8(phydev
->skb
, ETHTOOL_A_CABLE_TEST_NTF_STATUS
,
133 ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED
);
137 phydev
->nest
= nla_nest_start(phydev
->skb
,
138 ETHTOOL_A_CABLE_TEST_NTF_NEST
);
147 nlmsg_free(phydev
->skb
);
151 EXPORT_SYMBOL_GPL(ethnl_cable_test_alloc
);
153 void ethnl_cable_test_free(struct phy_device
*phydev
)
155 nlmsg_free(phydev
->skb
);
158 EXPORT_SYMBOL_GPL(ethnl_cable_test_free
);
160 void ethnl_cable_test_finished(struct phy_device
*phydev
)
162 nla_nest_end(phydev
->skb
, phydev
->nest
);
164 genlmsg_end(phydev
->skb
, phydev
->ehdr
);
166 ethnl_multicast(phydev
->skb
, phydev
->attached_dev
);
168 EXPORT_SYMBOL_GPL(ethnl_cable_test_finished
);
170 int ethnl_cable_test_result(struct phy_device
*phydev
, u8 pair
, u8 result
)
175 nest
= nla_nest_start(phydev
->skb
, ETHTOOL_A_CABLE_NEST_RESULT
);
179 if (nla_put_u8(phydev
->skb
, ETHTOOL_A_CABLE_RESULT_PAIR
, pair
))
181 if (nla_put_u8(phydev
->skb
, ETHTOOL_A_CABLE_RESULT_CODE
, result
))
184 nla_nest_end(phydev
->skb
, nest
);
188 nla_nest_cancel(phydev
->skb
, nest
);
191 EXPORT_SYMBOL_GPL(ethnl_cable_test_result
);
193 int ethnl_cable_test_fault_length(struct phy_device
*phydev
, u8 pair
, u32 cm
)
198 nest
= nla_nest_start(phydev
->skb
,
199 ETHTOOL_A_CABLE_NEST_FAULT_LENGTH
);
203 if (nla_put_u8(phydev
->skb
, ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR
, pair
))
205 if (nla_put_u32(phydev
->skb
, ETHTOOL_A_CABLE_FAULT_LENGTH_CM
, cm
))
208 nla_nest_end(phydev
->skb
, nest
);
212 nla_nest_cancel(phydev
->skb
, nest
);
215 EXPORT_SYMBOL_GPL(ethnl_cable_test_fault_length
);
217 struct cable_test_tdr_req_info
{
218 struct ethnl_req_info base
;
221 static const struct nla_policy
222 cable_test_tdr_act_cfg_policy
[ETHTOOL_A_CABLE_TEST_TDR_CFG_MAX
+ 1] = {
223 [ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST
] = { .type
= NLA_U32
},
224 [ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST
] = { .type
= NLA_U32
},
225 [ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP
] = { .type
= NLA_U32
},
226 [ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR
] = { .type
= NLA_U8
},
229 static const struct nla_policy
230 cable_test_tdr_act_policy
[ETHTOOL_A_CABLE_TEST_TDR_MAX
+ 1] = {
231 [ETHTOOL_A_CABLE_TEST_TDR_UNSPEC
] = { .type
= NLA_REJECT
},
232 [ETHTOOL_A_CABLE_TEST_TDR_HEADER
] = { .type
= NLA_NESTED
},
233 [ETHTOOL_A_CABLE_TEST_TDR_CFG
] = { .type
= NLA_NESTED
},
236 /* CABLE_TEST_TDR_ACT */
237 static int ethnl_act_cable_test_tdr_cfg(const struct nlattr
*nest
,
238 struct genl_info
*info
,
239 struct phy_tdr_config
*cfg
)
241 struct nlattr
*tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_MAX
+ 1];
246 cfg
->last
= MAX_CABLE_LENGTH_CM
;
247 cfg
->pair
= PHY_PAIR_ALL
;
252 ret
= nla_parse_nested(tb
, ETHTOOL_A_CABLE_TEST_TDR_CFG_MAX
, nest
,
253 cable_test_tdr_act_cfg_policy
, info
->extack
);
257 if (tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST
])
258 cfg
->first
= nla_get_u32(
259 tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST
]);
261 if (tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST
])
262 cfg
->last
= nla_get_u32(tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST
]);
264 if (tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP
])
265 cfg
->step
= nla_get_u32(tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP
]);
267 if (tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR
]) {
268 cfg
->pair
= nla_get_u8(tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR
]);
269 if (cfg
->pair
> ETHTOOL_A_CABLE_PAIR_D
) {
272 tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR
],
273 "invalid pair parameter");
278 if (cfg
->first
> MAX_CABLE_LENGTH_CM
) {
279 NL_SET_ERR_MSG_ATTR(info
->extack
,
280 tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST
],
281 "invalid first parameter");
285 if (cfg
->last
> MAX_CABLE_LENGTH_CM
) {
286 NL_SET_ERR_MSG_ATTR(info
->extack
,
287 tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST
],
288 "invalid last parameter");
292 if (cfg
->first
> cfg
->last
) {
293 NL_SET_ERR_MSG(info
->extack
, "invalid first/last parameter");
298 NL_SET_ERR_MSG_ATTR(info
->extack
,
299 tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP
],
300 "invalid step parameter");
304 if (cfg
->step
> (cfg
->last
- cfg
->first
)) {
305 NL_SET_ERR_MSG_ATTR(info
->extack
,
306 tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP
],
307 "step parameter too big");
314 int ethnl_act_cable_test_tdr(struct sk_buff
*skb
, struct genl_info
*info
)
316 struct nlattr
*tb
[ETHTOOL_A_CABLE_TEST_TDR_MAX
+ 1];
317 struct ethnl_req_info req_info
= {};
318 const struct ethtool_phy_ops
*ops
;
319 struct phy_tdr_config cfg
;
320 struct net_device
*dev
;
323 ret
= nlmsg_parse(info
->nlhdr
, GENL_HDRLEN
, tb
,
324 ETHTOOL_A_CABLE_TEST_TDR_MAX
,
325 cable_test_tdr_act_policy
, info
->extack
);
329 ret
= ethnl_parse_header_dev_get(&req_info
,
330 tb
[ETHTOOL_A_CABLE_TEST_TDR_HEADER
],
331 genl_info_net(info
), info
->extack
,
342 ret
= ethnl_act_cable_test_tdr_cfg(tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG
],
348 ops
= ethtool_phy_ops
;
349 if (!ops
|| !ops
->start_cable_test_tdr
) {
354 ret
= ethnl_ops_begin(dev
);
358 ret
= ops
->start_cable_test_tdr(dev
->phydev
, info
->extack
, &cfg
);
360 ethnl_ops_complete(dev
);
363 ethnl_cable_test_started(dev
->phydev
,
364 ETHTOOL_MSG_CABLE_TEST_TDR_NTF
);
373 int ethnl_cable_test_amplitude(struct phy_device
*phydev
,
379 nest
= nla_nest_start(phydev
->skb
,
380 ETHTOOL_A_CABLE_TDR_NEST_AMPLITUDE
);
384 if (nla_put_u8(phydev
->skb
, ETHTOOL_A_CABLE_AMPLITUDE_PAIR
, pair
))
386 if (nla_put_u16(phydev
->skb
, ETHTOOL_A_CABLE_AMPLITUDE_mV
, mV
))
389 nla_nest_end(phydev
->skb
, nest
);
393 nla_nest_cancel(phydev
->skb
, nest
);
396 EXPORT_SYMBOL_GPL(ethnl_cable_test_amplitude
);
398 int ethnl_cable_test_pulse(struct phy_device
*phydev
, u16 mV
)
403 nest
= nla_nest_start(phydev
->skb
, ETHTOOL_A_CABLE_TDR_NEST_PULSE
);
407 if (nla_put_u16(phydev
->skb
, ETHTOOL_A_CABLE_PULSE_mV
, mV
))
410 nla_nest_end(phydev
->skb
, nest
);
414 nla_nest_cancel(phydev
->skb
, nest
);
417 EXPORT_SYMBOL_GPL(ethnl_cable_test_pulse
);
419 int ethnl_cable_test_step(struct phy_device
*phydev
, u32 first
, u32 last
,
425 nest
= nla_nest_start(phydev
->skb
, ETHTOOL_A_CABLE_TDR_NEST_STEP
);
429 if (nla_put_u32(phydev
->skb
, ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE
,
433 if (nla_put_u32(phydev
->skb
, ETHTOOL_A_CABLE_STEP_LAST_DISTANCE
, last
))
436 if (nla_put_u32(phydev
->skb
, ETHTOOL_A_CABLE_STEP_STEP_DISTANCE
, step
))
439 nla_nest_end(phydev
->skb
, nest
);
443 nla_nest_cancel(phydev
->skb
, nest
);
446 EXPORT_SYMBOL_GPL(ethnl_cable_test_step
);