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
),
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 net_device
*dev
;
64 ret
= ethnl_parse_header_dev_get(&req_info
,
65 tb
[ETHTOOL_A_CABLE_TEST_HEADER
],
66 genl_info_net(info
), info
->extack
,
78 ops
= ethtool_phy_ops
;
79 if (!ops
|| !ops
->start_cable_test
) {
84 ret
= ethnl_ops_begin(dev
);
88 ret
= ops
->start_cable_test(dev
->phydev
, info
->extack
);
90 ethnl_ops_complete(dev
);
93 ethnl_cable_test_started(dev
->phydev
,
94 ETHTOOL_MSG_CABLE_TEST_NTF
);
103 int ethnl_cable_test_alloc(struct phy_device
*phydev
, u8 cmd
)
107 /* One TDR sample occupies 20 bytes. For a 150 meter cable,
108 * with four pairs, around 12K is needed.
110 phydev
->skb
= genlmsg_new(SZ_16K
, GFP_KERNEL
);
114 phydev
->ehdr
= ethnl_bcastmsg_put(phydev
->skb
, cmd
);
120 err
= ethnl_fill_reply_header(phydev
->skb
, phydev
->attached_dev
,
121 ETHTOOL_A_CABLE_TEST_NTF_HEADER
);
125 err
= nla_put_u8(phydev
->skb
, ETHTOOL_A_CABLE_TEST_NTF_STATUS
,
126 ETHTOOL_A_CABLE_TEST_NTF_STATUS_COMPLETED
);
130 phydev
->nest
= nla_nest_start(phydev
->skb
,
131 ETHTOOL_A_CABLE_TEST_NTF_NEST
);
140 nlmsg_free(phydev
->skb
);
144 EXPORT_SYMBOL_GPL(ethnl_cable_test_alloc
);
146 void ethnl_cable_test_free(struct phy_device
*phydev
)
148 nlmsg_free(phydev
->skb
);
151 EXPORT_SYMBOL_GPL(ethnl_cable_test_free
);
153 void ethnl_cable_test_finished(struct phy_device
*phydev
)
155 nla_nest_end(phydev
->skb
, phydev
->nest
);
157 genlmsg_end(phydev
->skb
, phydev
->ehdr
);
159 ethnl_multicast(phydev
->skb
, phydev
->attached_dev
);
161 EXPORT_SYMBOL_GPL(ethnl_cable_test_finished
);
163 int ethnl_cable_test_result(struct phy_device
*phydev
, u8 pair
, u8 result
)
168 nest
= nla_nest_start(phydev
->skb
, ETHTOOL_A_CABLE_NEST_RESULT
);
172 if (nla_put_u8(phydev
->skb
, ETHTOOL_A_CABLE_RESULT_PAIR
, pair
))
174 if (nla_put_u8(phydev
->skb
, ETHTOOL_A_CABLE_RESULT_CODE
, result
))
177 nla_nest_end(phydev
->skb
, nest
);
181 nla_nest_cancel(phydev
->skb
, nest
);
184 EXPORT_SYMBOL_GPL(ethnl_cable_test_result
);
186 int ethnl_cable_test_fault_length(struct phy_device
*phydev
, u8 pair
, u32 cm
)
191 nest
= nla_nest_start(phydev
->skb
,
192 ETHTOOL_A_CABLE_NEST_FAULT_LENGTH
);
196 if (nla_put_u8(phydev
->skb
, ETHTOOL_A_CABLE_FAULT_LENGTH_PAIR
, pair
))
198 if (nla_put_u32(phydev
->skb
, ETHTOOL_A_CABLE_FAULT_LENGTH_CM
, cm
))
201 nla_nest_end(phydev
->skb
, nest
);
205 nla_nest_cancel(phydev
->skb
, nest
);
208 EXPORT_SYMBOL_GPL(ethnl_cable_test_fault_length
);
210 struct cable_test_tdr_req_info
{
211 struct ethnl_req_info base
;
214 static const struct nla_policy cable_test_tdr_act_cfg_policy
[] = {
215 [ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST
] = { .type
= NLA_U32
},
216 [ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST
] = { .type
= NLA_U32
},
217 [ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP
] = { .type
= NLA_U32
},
218 [ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR
] = { .type
= NLA_U8
},
221 const struct nla_policy ethnl_cable_test_tdr_act_policy
[] = {
222 [ETHTOOL_A_CABLE_TEST_TDR_HEADER
] =
223 NLA_POLICY_NESTED(ethnl_header_policy
),
224 [ETHTOOL_A_CABLE_TEST_TDR_CFG
] = { .type
= NLA_NESTED
},
227 /* CABLE_TEST_TDR_ACT */
228 static int ethnl_act_cable_test_tdr_cfg(const struct nlattr
*nest
,
229 struct genl_info
*info
,
230 struct phy_tdr_config
*cfg
)
232 struct nlattr
*tb
[ARRAY_SIZE(cable_test_tdr_act_cfg_policy
)];
237 cfg
->last
= MAX_CABLE_LENGTH_CM
;
238 cfg
->pair
= PHY_PAIR_ALL
;
243 ret
= nla_parse_nested(tb
,
244 ARRAY_SIZE(cable_test_tdr_act_cfg_policy
) - 1,
245 nest
, cable_test_tdr_act_cfg_policy
,
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
]);
254 if (tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST
])
255 cfg
->last
= nla_get_u32(tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST
]);
257 if (tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP
])
258 cfg
->step
= nla_get_u32(tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP
]);
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
) {
265 tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR
],
266 "invalid pair parameter");
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");
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");
285 if (cfg
->first
> cfg
->last
) {
286 NL_SET_ERR_MSG(info
->extack
, "invalid first/last parameter");
291 NL_SET_ERR_MSG_ATTR(info
->extack
,
292 tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP
],
293 "invalid step parameter");
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");
307 int ethnl_act_cable_test_tdr(struct sk_buff
*skb
, struct genl_info
*info
)
309 struct ethnl_req_info req_info
= {};
310 const struct ethtool_phy_ops
*ops
;
311 struct nlattr
**tb
= info
->attrs
;
312 struct phy_tdr_config cfg
;
313 struct net_device
*dev
;
316 ret
= ethnl_parse_header_dev_get(&req_info
,
317 tb
[ETHTOOL_A_CABLE_TEST_TDR_HEADER
],
318 genl_info_net(info
), info
->extack
,
329 ret
= ethnl_act_cable_test_tdr_cfg(tb
[ETHTOOL_A_CABLE_TEST_TDR_CFG
],
335 ops
= ethtool_phy_ops
;
336 if (!ops
|| !ops
->start_cable_test_tdr
) {
341 ret
= ethnl_ops_begin(dev
);
345 ret
= ops
->start_cable_test_tdr(dev
->phydev
, info
->extack
, &cfg
);
347 ethnl_ops_complete(dev
);
350 ethnl_cable_test_started(dev
->phydev
,
351 ETHTOOL_MSG_CABLE_TEST_TDR_NTF
);
360 int ethnl_cable_test_amplitude(struct phy_device
*phydev
,
366 nest
= nla_nest_start(phydev
->skb
,
367 ETHTOOL_A_CABLE_TDR_NEST_AMPLITUDE
);
371 if (nla_put_u8(phydev
->skb
, ETHTOOL_A_CABLE_AMPLITUDE_PAIR
, pair
))
373 if (nla_put_u16(phydev
->skb
, ETHTOOL_A_CABLE_AMPLITUDE_mV
, mV
))
376 nla_nest_end(phydev
->skb
, nest
);
380 nla_nest_cancel(phydev
->skb
, nest
);
383 EXPORT_SYMBOL_GPL(ethnl_cable_test_amplitude
);
385 int ethnl_cable_test_pulse(struct phy_device
*phydev
, u16 mV
)
390 nest
= nla_nest_start(phydev
->skb
, ETHTOOL_A_CABLE_TDR_NEST_PULSE
);
394 if (nla_put_u16(phydev
->skb
, ETHTOOL_A_CABLE_PULSE_mV
, mV
))
397 nla_nest_end(phydev
->skb
, nest
);
401 nla_nest_cancel(phydev
->skb
, nest
);
404 EXPORT_SYMBOL_GPL(ethnl_cable_test_pulse
);
406 int ethnl_cable_test_step(struct phy_device
*phydev
, u32 first
, u32 last
,
412 nest
= nla_nest_start(phydev
->skb
, ETHTOOL_A_CABLE_TDR_NEST_STEP
);
416 if (nla_put_u32(phydev
->skb
, ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE
,
420 if (nla_put_u32(phydev
->skb
, ETHTOOL_A_CABLE_STEP_LAST_DISTANCE
, last
))
423 if (nla_put_u32(phydev
->skb
, ETHTOOL_A_CABLE_STEP_STEP_DISTANCE
, step
))
426 nla_nest_end(phydev
->skb
, nest
);
430 nla_nest_cancel(phydev
->skb
, nest
);
433 EXPORT_SYMBOL_GPL(ethnl_cable_test_step
);