~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/net/ethtool/cabletest.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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]           =
 16                 NLA_POLICY_NESTED(ethnl_header_policy),
 17 };
 18 
 19 static int ethnl_cable_test_started(struct phy_device *phydev, u8 cmd)
 20 {
 21         struct sk_buff *skb;
 22         int err = -ENOMEM;
 23         void *ehdr;
 24 
 25         skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
 26         if (!skb)
 27                 goto out;
 28 
 29         ehdr = ethnl_bcastmsg_put(skb, cmd);
 30         if (!ehdr) {
 31                 err = -EMSGSIZE;
 32                 goto out;
 33         }
 34 
 35         err = ethnl_fill_reply_header(skb, phydev->attached_dev,
 36                                       ETHTOOL_A_CABLE_TEST_NTF_HEADER);
 37         if (err)
 38                 goto out;
 39 
 40         err = nla_put_u8(skb, ETHTOOL_A_CABLE_TEST_NTF_STATUS,
 41                          ETHTOOL_A_CABLE_TEST_NTF_STATUS_STARTED);
 42         if (err)
 43                 goto out;
 44 
 45         genlmsg_end(skb, ehdr);
 46 
 47         return ethnl_multicast(skb, phydev->attached_dev);
 48 
 49 out:
 50         nlmsg_free(skb);
 51         phydev_err(phydev, "%s: Error %pe\n", __func__, ERR_PTR(err));
 52 
 53         return err;
 54 }
 55 
 56 int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info)
 57 {
 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;
 62         int ret;
 63 
 64         ret = ethnl_parse_header_dev_get(&req_info,
 65                                          tb[ETHTOOL_A_CABLE_TEST_HEADER],
 66                                          genl_info_net(info), info->extack,
 67                                          true);
 68         if (ret < 0)
 69                 return ret;
 70 
 71         dev = req_info.dev;
 72         if (!dev->phydev) {
 73                 ret = -EOPNOTSUPP;
 74                 goto out_dev_put;
 75         }
 76 
 77         rtnl_lock();
 78         ops = ethtool_phy_ops;
 79         if (!ops || !ops->start_cable_test) {
 80                 ret = -EOPNOTSUPP;
 81                 goto out_rtnl;
 82         }
 83 
 84         ret = ethnl_ops_begin(dev);
 85         if (ret < 0)
 86                 goto out_rtnl;
 87 
 88         ret = ops->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         ethnl_parse_header_dev_put(&req_info);
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 static const struct nla_policy cable_test_tdr_act_cfg_policy[] = {
211         [ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST]    = { .type = NLA_U32 },
212         [ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST]     = { .type = NLA_U32 },
213         [ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP]     = { .type = NLA_U32 },
214         [ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]     = { .type = NLA_U8 },
215 };
216 
217 const struct nla_policy ethnl_cable_test_tdr_act_policy[] = {
218         [ETHTOOL_A_CABLE_TEST_TDR_HEADER]       =
219                 NLA_POLICY_NESTED(ethnl_header_policy),
220         [ETHTOOL_A_CABLE_TEST_TDR_CFG]          = { .type = NLA_NESTED },
221 };
222 
223 /* CABLE_TEST_TDR_ACT */
224 static int ethnl_act_cable_test_tdr_cfg(const struct nlattr *nest,
225                                         struct genl_info *info,
226                                         struct phy_tdr_config *cfg)
227 {
228         struct nlattr *tb[ARRAY_SIZE(cable_test_tdr_act_cfg_policy)];
229         int ret;
230 
231         cfg->first = 100;
232         cfg->step = 100;
233         cfg->last = MAX_CABLE_LENGTH_CM;
234         cfg->pair = PHY_PAIR_ALL;
235 
236         if (!nest)
237                 return 0;
238 
239         ret = nla_parse_nested(tb,
240                                ARRAY_SIZE(cable_test_tdr_act_cfg_policy) - 1,
241                                nest, cable_test_tdr_act_cfg_policy,
242                                info->extack);
243         if (ret < 0)
244                 return ret;
245 
246         if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST])
247                 cfg->first = nla_get_u32(
248                         tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST]);
249 
250         if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST])
251                 cfg->last = nla_get_u32(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST]);
252 
253         if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP])
254                 cfg->step = nla_get_u32(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP]);
255 
256         if (tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]) {
257                 cfg->pair = nla_get_u8(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]);
258                 if (cfg->pair > ETHTOOL_A_CABLE_PAIR_D) {
259                         NL_SET_ERR_MSG_ATTR(
260                                 info->extack,
261                                 tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR],
262                                 "invalid pair parameter");
263                         return -EINVAL;
264                 }
265         }
266 
267         if (cfg->first > MAX_CABLE_LENGTH_CM) {
268                 NL_SET_ERR_MSG_ATTR(info->extack,
269                                     tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST],
270                                     "invalid first parameter");
271                 return -EINVAL;
272         }
273 
274         if (cfg->last > MAX_CABLE_LENGTH_CM) {
275                 NL_SET_ERR_MSG_ATTR(info->extack,
276                                     tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST],
277                                     "invalid last parameter");
278                 return -EINVAL;
279         }
280 
281         if (cfg->first > cfg->last) {
282                 NL_SET_ERR_MSG(info->extack, "invalid first/last parameter");
283                 return -EINVAL;
284         }
285 
286         if (!cfg->step) {
287                 NL_SET_ERR_MSG_ATTR(info->extack,
288                                     tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP],
289                                     "invalid step parameter");
290                 return -EINVAL;
291         }
292 
293         if (cfg->step > (cfg->last - cfg->first)) {
294                 NL_SET_ERR_MSG_ATTR(info->extack,
295                                     tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP],
296                                     "step parameter too big");
297                 return -EINVAL;
298         }
299 
300         return 0;
301 }
302 
303 int ethnl_act_cable_test_tdr(struct sk_buff *skb, struct genl_info *info)
304 {
305         struct ethnl_req_info req_info = {};
306         const struct ethtool_phy_ops *ops;
307         struct nlattr **tb = info->attrs;
308         struct phy_tdr_config cfg;
309         struct net_device *dev;
310         int ret;
311 
312         ret = ethnl_parse_header_dev_get(&req_info,
313                                          tb[ETHTOOL_A_CABLE_TEST_TDR_HEADER],
314                                          genl_info_net(info), info->extack,
315                                          true);
316         if (ret < 0)
317                 return ret;
318 
319         dev = req_info.dev;
320         if (!dev->phydev) {
321                 ret = -EOPNOTSUPP;
322                 goto out_dev_put;
323         }
324 
325         ret = ethnl_act_cable_test_tdr_cfg(tb[ETHTOOL_A_CABLE_TEST_TDR_CFG],
326                                            info, &cfg);
327         if (ret)
328                 goto out_dev_put;
329 
330         rtnl_lock();
331         ops = ethtool_phy_ops;
332         if (!ops || !ops->start_cable_test_tdr) {
333                 ret = -EOPNOTSUPP;
334                 goto out_rtnl;
335         }
336 
337         ret = ethnl_ops_begin(dev);
338         if (ret < 0)
339                 goto out_rtnl;
340 
341         ret = ops->start_cable_test_tdr(dev->phydev, info->extack, &cfg);
342 
343         ethnl_ops_complete(dev);
344 
345         if (!ret)
346                 ethnl_cable_test_started(dev->phydev,
347                                          ETHTOOL_MSG_CABLE_TEST_TDR_NTF);
348 
349 out_rtnl:
350         rtnl_unlock();
351 out_dev_put:
352         ethnl_parse_header_dev_put(&req_info);
353         return ret;
354 }
355 
356 int ethnl_cable_test_amplitude(struct phy_device *phydev,
357                                u8 pair, s16 mV)
358 {
359         struct nlattr *nest;
360         int ret = -EMSGSIZE;
361 
362         nest = nla_nest_start(phydev->skb,
363                               ETHTOOL_A_CABLE_TDR_NEST_AMPLITUDE);
364         if (!nest)
365                 return -EMSGSIZE;
366 
367         if (nla_put_u8(phydev->skb, ETHTOOL_A_CABLE_AMPLITUDE_PAIR, pair))
368                 goto err;
369         if (nla_put_u16(phydev->skb, ETHTOOL_A_CABLE_AMPLITUDE_mV, mV))
370                 goto err;
371 
372         nla_nest_end(phydev->skb, nest);
373         return 0;
374 
375 err:
376         nla_nest_cancel(phydev->skb, nest);
377         return ret;
378 }
379 EXPORT_SYMBOL_GPL(ethnl_cable_test_amplitude);
380 
381 int ethnl_cable_test_pulse(struct phy_device *phydev, u16 mV)
382 {
383         struct nlattr *nest;
384         int ret = -EMSGSIZE;
385 
386         nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_TDR_NEST_PULSE);
387         if (!nest)
388                 return -EMSGSIZE;
389 
390         if (nla_put_u16(phydev->skb, ETHTOOL_A_CABLE_PULSE_mV, mV))
391                 goto err;
392 
393         nla_nest_end(phydev->skb, nest);
394         return 0;
395 
396 err:
397         nla_nest_cancel(phydev->skb, nest);
398         return ret;
399 }
400 EXPORT_SYMBOL_GPL(ethnl_cable_test_pulse);
401 
402 int ethnl_cable_test_step(struct phy_device *phydev, u32 first, u32 last,
403                           u32 step)
404 {
405         struct nlattr *nest;
406         int ret = -EMSGSIZE;
407 
408         nest = nla_nest_start(phydev->skb, ETHTOOL_A_CABLE_TDR_NEST_STEP);
409         if (!nest)
410                 return -EMSGSIZE;
411 
412         if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_FIRST_DISTANCE,
413                         first))
414                 goto err;
415 
416         if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_LAST_DISTANCE, last))
417                 goto err;
418 
419         if (nla_put_u32(phydev->skb, ETHTOOL_A_CABLE_STEP_STEP_DISTANCE, step))
420                 goto err;
421 
422         nla_nest_end(phydev->skb, nest);
423         return 0;
424 
425 err:
426         nla_nest_cancel(phydev->skb, nest);
427         return ret;
428 }
429 EXPORT_SYMBOL_GPL(ethnl_cable_test_step);
430 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php