1 /*
2 * ethtool.c: Linux ethernet device configuration tool.
3 *
4 * Copyright (C) 1998 David S. Miller (davem@dm.cobaltmicro.com)
5 * Portions Copyright 2001 Sun Microsystems
6 * Kernel 2.4 update Copyright 2001 Jeff Garzik <jgarzik@mandrakesoft.com>
7 * Wake-on-LAN,natsemi,misc support by Tim Hockin <thockin@sun.com>
8 * Portions Copyright 2002 Intel
9 * Portions Copyright (C) Sun Microsystems 2008
10 * do_test support by Eli Kupermann <eli.kupermann@intel.com>
11 * ETHTOOL_PHYS_ID support by Chris Leech <christopher.leech@intel.com>
12 * e1000 support by Scott Feldman <scott.feldman@intel.com>
13 * e100 support by Wen Tao <wen-hwa.tao@intel.com>
14 * ixgb support by Nicholas Nunley <Nicholas.d.nunley@intel.com>
15 * amd8111e support by Reeja John <reeja.john@amd.com>
16 * long arguments by Andi Kleen.
17 * SMSC LAN911x support by Steve Glendinning <steve.glendinning@smsc.com>
18 * Rx Network Flow Control configuration support <santwona.behera@sun.com>
19 * Various features by Ben Hutchings <bhutchings@solarflare.com>;
20 * Copyright 2009, 2010 Solarflare Communications
21 * MDI-X set support by Jesse Brandeburg <jesse.brandeburg@intel.com>
22 * Copyright 2012 Intel Corporation
23 * vmxnet3 support by Shrikrishna Khare <skhare@vmware.com>
24 * Various features by Ben Hutchings <ben@decadent.org.uk>;
25 * Copyright 2008-2010, 2013-2016 Ben Hutchings
26 * QSFP+/QSFP28 DOM support by Vidya Sagar Ravipati <vidya@cumulusnetworks.com>
27 *
28 * TODO:
29 * * show settings for all devices
30 */
31
32 #include "internal.h"
33 #include <string.h>
34 #include <strings.h>
35 #include <stdlib.h>
36 #include <sys/stat.h>
37 #include <stdio.h>
38 #include <stddef.h>
39 #include <stdbool.h>
40 #include <errno.h>
41 #include <sys/utsname.h>
42 #include <limits.h>
43 #include <ctype.h>
44 #include <inttypes.h>
45
46 #include <sys/socket.h>
47 #include <netinet/in.h>
48 #include <arpa/inet.h>
49
50 #include <linux/ioctl.h>
51 #include <linux/sockios.h>
52 #include <linux/netlink.h>
53
54 #include "common.h"
55 #include "netlink/extapi.h"
56
57 #ifndef MAX_ADDR_LEN
58 #define MAX_ADDR_LEN 32
59 #endif
60
61 #define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
62
63 static void exit_bad_args(void) __attribute__((noreturn));
64
exit_bad_args(void)65 static void exit_bad_args(void)
66 {
67 fprintf(stderr,
68 "ethtool: bad command line argument(s)\n"
69 "For more information run ethtool -h\n");
70 exit(1);
71 }
72
73 static void exit_bad_args_info(const char *info) __attribute__((noreturn));
74
exit_bad_args_info(const char * info)75 static void exit_bad_args_info(const char *info)
76 {
77 fprintf(stderr,
78 "ethtool: bad command line argument(s)\n"
79 "%s\n"
80 "For more information run ethtool -h\n",
81 info);
82 exit(1);
83 }
84
85 static void exit_nlonly_param(const char *name) __attribute__((noreturn));
86
exit_nlonly_param(const char * name)87 static void exit_nlonly_param(const char *name)
88 {
89 fprintf(stderr,
90 "ethtool: parameter '%s' can be used only with netlink\n",
91 name);
92 exit(1);
93 }
94
95 typedef enum {
96 CMDL_NONE,
97 CMDL_BOOL,
98 CMDL_S32,
99 CMDL_U8,
100 CMDL_U16,
101 CMDL_U32,
102 CMDL_U64,
103 CMDL_BE16,
104 CMDL_IP4,
105 CMDL_STR,
106 CMDL_FLAG,
107 CMDL_MAC,
108 } cmdline_type_t;
109
110 struct cmdline_info {
111 const char *name;
112 cmdline_type_t type;
113 /* Points to int (BOOL), s32, u16, u32 (U32/FLAG/IP4), u64,
114 * char * (STR) or u8[6] (MAC). For FLAG, the value accumulates
115 * all flags to be set. */
116 void *wanted_val;
117 void *ioctl_val;
118 /* For FLAG, the flag value to be set/cleared */
119 u32 flag_val;
120 /* For FLAG, points to u32 and accumulates all flags seen.
121 * For anything else, points to int and is set if the option is
122 * seen. */
123 void *seen_val;
124 };
125
126 struct feature_def {
127 char name[ETH_GSTRING_LEN];
128 int off_flag_index; /* index in off_flag_def; negative if none match */
129 };
130
131 struct feature_defs {
132 size_t n_features;
133 /* Number of features each offload flag is associated with */
134 unsigned int off_flag_matched[OFF_FLAG_DEF_SIZE];
135 /* Name and offload flag index for each feature */
136 struct feature_def def[0];
137 };
138
139 #define FEATURE_BITS_TO_BLOCKS(n_bits) DIV_ROUND_UP(n_bits, 32U)
140 #define FEATURE_WORD(blocks, index, field) ((blocks)[(index) / 32U].field)
141 #define FEATURE_FIELD_FLAG(index) (1U << (index) % 32U)
142 #define FEATURE_BIT_SET(blocks, index, field) \
143 (FEATURE_WORD(blocks, index, field) |= FEATURE_FIELD_FLAG(index))
144 #define FEATURE_BIT_CLEAR(blocks, index, field) \
145 (FEATURE_WORD(blocks, index, filed) &= ~FEATURE_FIELD_FLAG(index))
146 #define FEATURE_BIT_IS_SET(blocks, index, field) \
147 (FEATURE_WORD(blocks, index, field) & FEATURE_FIELD_FLAG(index))
148
149 static long long
get_int_range(char * str,int base,long long min,long long max)150 get_int_range(char *str, int base, long long min, long long max)
151 {
152 long long v;
153 char *endp;
154
155 if (!str)
156 exit_bad_args();
157 errno = 0;
158 v = strtoll(str, &endp, base);
159 if (errno || *endp || v < min || v > max)
160 exit_bad_args();
161 return v;
162 }
163
164 static unsigned long long
get_uint_range(char * str,int base,unsigned long long max)165 get_uint_range(char *str, int base, unsigned long long max)
166 {
167 unsigned long long v;
168 char *endp;
169
170 if (!str)
171 exit_bad_args();
172 errno = 0;
173 v = strtoull(str, &endp, base);
174 if (errno || *endp || v > max)
175 exit_bad_args();
176 return v;
177 }
178
get_int(char * str,int base)179 static int get_int(char *str, int base)
180 {
181 return get_int_range(str, base, INT_MIN, INT_MAX);
182 }
183
get_u32(char * str,int base)184 static u32 get_u32(char *str, int base)
185 {
186 return get_uint_range(str, base, 0xffffffff);
187 }
188
get_mac_addr(char * src,unsigned char * dest)189 static void get_mac_addr(char *src, unsigned char *dest)
190 {
191 int count;
192 int i;
193 int buf[ETH_ALEN];
194
195 count = sscanf(src, "%2x:%2x:%2x:%2x:%2x:%2x",
196 &buf[0], &buf[1], &buf[2], &buf[3], &buf[4], &buf[5]);
197 if (count != ETH_ALEN)
198 exit_bad_args();
199
200 for (i = 0; i < count; i++)
201 dest[i] = buf[i];
202 }
203
parse_hex_u32_bitmap(const char * s,unsigned int nbits,u32 * result)204 static int parse_hex_u32_bitmap(const char *s,
205 unsigned int nbits, u32 *result)
206 {
207 const unsigned int nwords = __KERNEL_DIV_ROUND_UP(nbits, 32);
208 size_t slen = strlen(s);
209 size_t i;
210
211 /* ignore optional '0x' prefix */
212 if ((slen > 2) && (strncasecmp(s, "0x", 2) == 0)) {
213 slen -= 2;
214 s += 2;
215 }
216
217 if (slen > 8 * nwords) /* up to 2 digits per byte */
218 return -1;
219
220 memset(result, 0, 4 * nwords);
221 for (i = 0; i < slen; ++i) {
222 const unsigned int shift = (slen - 1 - i) * 4;
223 u32 *dest = &result[shift / 32];
224 u32 nibble;
225
226 if ('a' <= s[i] && s[i] <= 'f')
227 nibble = 0xa + (s[i] - 'a');
228 else if ('A' <= s[i] && s[i] <= 'F')
229 nibble = 0xa + (s[i] - 'A');
230 else if ('0' <= s[i] && s[i] <= '9')
231 nibble = (s[i] - '0');
232 else
233 return -1;
234
235 *dest |= (nibble << (shift % 32));
236 }
237
238 return 0;
239 }
240
parse_generic_cmdline(struct cmd_context * ctx,int * changed,struct cmdline_info * info,unsigned int n_info)241 static void parse_generic_cmdline(struct cmd_context *ctx,
242 int *changed,
243 struct cmdline_info *info,
244 unsigned int n_info)
245 {
246 unsigned int argc = ctx->argc;
247 char **argp = ctx->argp;
248 unsigned int i, idx;
249 int found;
250
251 for (i = 0; i < argc; i++) {
252 found = 0;
253 for (idx = 0; idx < n_info; idx++) {
254 if (!strcmp(info[idx].name, argp[i])) {
255 found = 1;
256 *changed = 1;
257 if (info[idx].type != CMDL_FLAG &&
258 info[idx].seen_val)
259 *(int *)info[idx].seen_val = 1;
260 i += 1;
261 if (i >= argc)
262 exit_bad_args();
263 switch (info[idx].type) {
264 case CMDL_BOOL: {
265 int *p = info[idx].wanted_val;
266 if (!strcmp(argp[i], "on"))
267 *p = 1;
268 else if (!strcmp(argp[i], "off"))
269 *p = 0;
270 else
271 exit_bad_args();
272 break;
273 }
274 case CMDL_S32: {
275 s32 *p = info[idx].wanted_val;
276 *p = get_int_range(argp[i], 0,
277 -0x80000000LL,
278 0x7fffffff);
279 break;
280 }
281 case CMDL_U8: {
282 u8 *p = info[idx].wanted_val;
283 *p = get_uint_range(argp[i], 0, 0xff);
284 break;
285 }
286 case CMDL_U16: {
287 u16 *p = info[idx].wanted_val;
288 *p = get_uint_range(argp[i], 0, 0xffff);
289 break;
290 }
291 case CMDL_U32: {
292 u32 *p = info[idx].wanted_val;
293 *p = get_uint_range(argp[i], 0,
294 0xffffffff);
295 break;
296 }
297 case CMDL_U64: {
298 u64 *p = info[idx].wanted_val;
299 *p = get_uint_range(
300 argp[i], 0,
301 0xffffffffffffffffLL);
302 break;
303 }
304 case CMDL_BE16: {
305 u16 *p = info[idx].wanted_val;
306 *p = cpu_to_be16(
307 get_uint_range(argp[i], 0,
308 0xffff));
309 break;
310 }
311 case CMDL_IP4: {
312 u32 *p = info[idx].wanted_val;
313 struct in_addr in;
314 if (!inet_pton(AF_INET, argp[i], &in))
315 exit_bad_args();
316 *p = in.s_addr;
317 break;
318 }
319 case CMDL_MAC:
320 get_mac_addr(argp[i],
321 info[idx].wanted_val);
322 break;
323 case CMDL_FLAG: {
324 u32 *p;
325 p = info[idx].seen_val;
326 *p |= info[idx].flag_val;
327 if (!strcmp(argp[i], "on")) {
328 p = info[idx].wanted_val;
329 *p |= info[idx].flag_val;
330 } else if (strcmp(argp[i], "off")) {
331 exit_bad_args();
332 }
333 break;
334 }
335 case CMDL_STR: {
336 char **s = info[idx].wanted_val;
337 *s = strdup(argp[i]);
338 break;
339 }
340 default:
341 exit_bad_args();
342 }
343 break;
344 }
345 }
346 if (!found)
347 exit_bad_args();
348 }
349 }
350
flag_to_cmdline_info(const char * name,u32 value,u32 * wanted,u32 * mask,struct cmdline_info * cli)351 static void flag_to_cmdline_info(const char *name, u32 value,
352 u32 *wanted, u32 *mask,
353 struct cmdline_info *cli)
354 {
355 memset(cli, 0, sizeof(*cli));
356 cli->name = name;
357 cli->type = CMDL_FLAG;
358 cli->flag_val = value;
359 cli->wanted_val = wanted;
360 cli->seen_val = mask;
361 }
362
rxflow_str_to_type(const char * str)363 static int rxflow_str_to_type(const char *str)
364 {
365 int flow_type = 0;
366
367 if (!strcmp(str, "tcp4"))
368 flow_type = TCP_V4_FLOW;
369 else if (!strcmp(str, "udp4"))
370 flow_type = UDP_V4_FLOW;
371 else if (!strcmp(str, "ah4") || !strcmp(str, "esp4"))
372 flow_type = AH_ESP_V4_FLOW;
373 else if (!strcmp(str, "sctp4"))
374 flow_type = SCTP_V4_FLOW;
375 else if (!strcmp(str, "gtpc4"))
376 flow_type = GTPC_V4_FLOW;
377 else if (!strcmp(str, "gtpc4t"))
378 flow_type = GTPC_TEID_V4_FLOW;
379 else if (!strcmp(str, "gtpu4"))
380 flow_type = GTPU_V4_FLOW;
381 else if (!strcmp(str, "gtpu4e"))
382 flow_type = GTPU_EH_V4_FLOW;
383 else if (!strcmp(str, "gtpu4u"))
384 flow_type = GTPU_UL_V4_FLOW;
385 else if (!strcmp(str, "gtpu4d"))
386 flow_type = GTPU_DL_V4_FLOW;
387 else if (!strcmp(str, "tcp6"))
388 flow_type = TCP_V6_FLOW;
389 else if (!strcmp(str, "udp6"))
390 flow_type = UDP_V6_FLOW;
391 else if (!strcmp(str, "ah6") || !strcmp(str, "esp6"))
392 flow_type = AH_ESP_V6_FLOW;
393 else if (!strcmp(str, "sctp6"))
394 flow_type = SCTP_V6_FLOW;
395 else if (!strcmp(str, "ether"))
396 flow_type = ETHER_FLOW;
397 else if (!strcmp(str, "gtpc6"))
398 flow_type = GTPC_V6_FLOW;
399 else if (!strcmp(str, "gtpc6t"))
400 flow_type = GTPC_TEID_V6_FLOW;
401 else if (!strcmp(str, "gtpu6"))
402 flow_type = GTPU_V6_FLOW;
403 else if (!strcmp(str, "gtpu6e"))
404 flow_type = GTPU_EH_V6_FLOW;
405 else if (!strcmp(str, "gtpu6u"))
406 flow_type = GTPU_UL_V6_FLOW;
407 else if (!strcmp(str, "gtpu6d"))
408 flow_type = GTPU_DL_V6_FLOW;
409
410 return flow_type;
411 }
412
do_version(struct cmd_context * ctx __maybe_unused)413 static int do_version(struct cmd_context *ctx __maybe_unused)
414 {
415 fprintf(stdout,
416 PACKAGE " version " VERSION
417 #ifndef ETHTOOL_ENABLE_PRETTY_DUMP
418 " (pretty dumps disabled)"
419 #endif
420 "\n");
421 return 0;
422 }
423
424 /* link mode routines */
425
426 static ETHTOOL_DECLARE_LINK_MODE_MASK(all_advertised_modes);
427 static ETHTOOL_DECLARE_LINK_MODE_MASK(all_advertised_flags);
428
init_global_link_mode_masks(void)429 static void init_global_link_mode_masks(void)
430 {
431 static const enum ethtool_link_mode_bit_indices
432 all_advertised_modes_bits[] = {
433 ETHTOOL_LINK_MODE_10baseT_Half_BIT,
434 ETHTOOL_LINK_MODE_10baseT_Full_BIT,
435 ETHTOOL_LINK_MODE_100baseT_Half_BIT,
436 ETHTOOL_LINK_MODE_100baseT_Full_BIT,
437 ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
438 ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
439 ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
440 ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
441 ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
442 ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
443 ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
444 ETHTOOL_LINK_MODE_10000baseR_FEC_BIT,
445 ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT,
446 ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT,
447 ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
448 ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
449 ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT,
450 ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT,
451 ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT,
452 ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT,
453 ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT,
454 ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT,
455 ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
456 ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
457 ETHTOOL_LINK_MODE_25000baseSR_Full_BIT,
458 ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT,
459 ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT,
460 ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
461 ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
462 ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
463 ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT,
464 ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT,
465 ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
466 ETHTOOL_LINK_MODE_10000baseCR_Full_BIT,
467 ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
468 ETHTOOL_LINK_MODE_10000baseLR_Full_BIT,
469 ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT,
470 ETHTOOL_LINK_MODE_10000baseER_Full_BIT,
471 ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
472 ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
473 ETHTOOL_LINK_MODE_50000baseKR_Full_BIT,
474 ETHTOOL_LINK_MODE_50000baseSR_Full_BIT,
475 ETHTOOL_LINK_MODE_50000baseCR_Full_BIT,
476 ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT,
477 ETHTOOL_LINK_MODE_50000baseDR_Full_BIT,
478 ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT,
479 ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT,
480 ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT,
481 ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT,
482 ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT,
483 ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT,
484 ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT,
485 ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT,
486 ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT,
487 ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT,
488 ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
489 ETHTOOL_LINK_MODE_1000baseT1_Full_BIT,
490 ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT,
491 ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT,
492 ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT,
493 ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT,
494 ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT,
495 ETHTOOL_LINK_MODE_100000baseKR_Full_BIT,
496 ETHTOOL_LINK_MODE_100000baseSR_Full_BIT,
497 ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT,
498 ETHTOOL_LINK_MODE_100000baseCR_Full_BIT,
499 ETHTOOL_LINK_MODE_100000baseDR_Full_BIT,
500 ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT,
501 ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT,
502 ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT,
503 ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT,
504 ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT,
505 ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT,
506 ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT,
507 ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT,
508 ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT,
509 ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT,
510 ETHTOOL_LINK_MODE_100baseFX_Half_BIT,
511 ETHTOOL_LINK_MODE_100baseFX_Full_BIT,
512 ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
513 ETHTOOL_LINK_MODE_800000baseCR8_Full_BIT,
514 ETHTOOL_LINK_MODE_800000baseKR8_Full_BIT,
515 ETHTOOL_LINK_MODE_800000baseDR8_Full_BIT,
516 ETHTOOL_LINK_MODE_800000baseDR8_2_Full_BIT,
517 ETHTOOL_LINK_MODE_800000baseSR8_Full_BIT,
518 ETHTOOL_LINK_MODE_800000baseVR8_Full_BIT,
519 ETHTOOL_LINK_MODE_10baseT1S_Full_BIT,
520 ETHTOOL_LINK_MODE_10baseT1S_Half_BIT,
521 ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT,
522 ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT,
523 };
524 static const enum ethtool_link_mode_bit_indices
525 additional_advertised_flags_bits[] = {
526 ETHTOOL_LINK_MODE_Autoneg_BIT,
527 ETHTOOL_LINK_MODE_TP_BIT,
528 ETHTOOL_LINK_MODE_AUI_BIT,
529 ETHTOOL_LINK_MODE_MII_BIT,
530 ETHTOOL_LINK_MODE_FIBRE_BIT,
531 ETHTOOL_LINK_MODE_BNC_BIT,
532 ETHTOOL_LINK_MODE_Pause_BIT,
533 ETHTOOL_LINK_MODE_Asym_Pause_BIT,
534 ETHTOOL_LINK_MODE_Backplane_BIT,
535 ETHTOOL_LINK_MODE_FEC_NONE_BIT,
536 ETHTOOL_LINK_MODE_FEC_RS_BIT,
537 ETHTOOL_LINK_MODE_FEC_BASER_BIT,
538 ETHTOOL_LINK_MODE_FEC_LLRS_BIT,
539 };
540 unsigned int i;
541
542 ethtool_link_mode_zero(all_advertised_modes);
543 ethtool_link_mode_zero(all_advertised_flags);
544 for (i = 0; i < ARRAY_SIZE(all_advertised_modes_bits); ++i) {
545 ethtool_link_mode_set_bit(all_advertised_modes_bits[i],
546 all_advertised_modes);
547 ethtool_link_mode_set_bit(all_advertised_modes_bits[i],
548 all_advertised_flags);
549 }
550
551 for (i = 0; i < ARRAY_SIZE(additional_advertised_flags_bits); ++i) {
552 ethtool_link_mode_set_bit(
553 additional_advertised_flags_bits[i],
554 all_advertised_flags);
555 }
556 }
557
558 static void dump_link_caps(const char *prefix, const char *an_prefix,
559 const u32 *mask, int link_mode_only);
560
dump_supported(const struct ethtool_link_usettings * link_usettings)561 static void dump_supported(const struct ethtool_link_usettings *link_usettings)
562 {
563 fprintf(stdout, " Supported ports: [ ");
564 if (ethtool_link_mode_test_bit(
565 ETHTOOL_LINK_MODE_TP_BIT,
566 link_usettings->link_modes.supported))
567 fprintf(stdout, "TP ");
568 if (ethtool_link_mode_test_bit(
569 ETHTOOL_LINK_MODE_AUI_BIT,
570 link_usettings->link_modes.supported))
571 fprintf(stdout, "AUI ");
572 if (ethtool_link_mode_test_bit(
573 ETHTOOL_LINK_MODE_BNC_BIT,
574 link_usettings->link_modes.supported))
575 fprintf(stdout, "BNC ");
576 if (ethtool_link_mode_test_bit(
577 ETHTOOL_LINK_MODE_MII_BIT,
578 link_usettings->link_modes.supported))
579 fprintf(stdout, "MII ");
580 if (ethtool_link_mode_test_bit(
581 ETHTOOL_LINK_MODE_FIBRE_BIT,
582 link_usettings->link_modes.supported))
583 fprintf(stdout, "FIBRE ");
584 if (ethtool_link_mode_test_bit(
585 ETHTOOL_LINK_MODE_Backplane_BIT,
586 link_usettings->link_modes.supported))
587 fprintf(stdout, "Backplane ");
588 fprintf(stdout, "]\n");
589
590 dump_link_caps("Supported", "Supports",
591 link_usettings->link_modes.supported, 0);
592 }
593
594 /* Print link capability flags (supported, advertised or lp_advertised).
595 * Assumes that the corresponding SUPPORTED and ADVERTISED flags are equal.
596 */
dump_link_caps(const char * prefix,const char * an_prefix,const u32 * mask,int link_mode_only)597 static void dump_link_caps(const char *prefix, const char *an_prefix,
598 const u32 *mask, int link_mode_only)
599 {
600 static const struct {
601 int same_line; /* print on same line as previous */
602 unsigned int bit_index;
603 const char *name;
604 } mode_defs[] = {
605 { 0, ETHTOOL_LINK_MODE_10baseT_Half_BIT,
606 "10baseT/Half" },
607 { 1, ETHTOOL_LINK_MODE_10baseT_Full_BIT,
608 "10baseT/Full" },
609 { 0, ETHTOOL_LINK_MODE_100baseT_Half_BIT,
610 "100baseT/Half" },
611 { 1, ETHTOOL_LINK_MODE_100baseT_Full_BIT,
612 "100baseT/Full" },
613 { 0, ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
614 "1000baseT/Half" },
615 { 1, ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
616 "1000baseT/Full" },
617 { 0, ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
618 "10000baseT/Full" },
619 { 0, ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
620 "2500baseX/Full" },
621 { 0, ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
622 "1000baseKX/Full" },
623 { 0, ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
624 "10000baseKX4/Full" },
625 { 0, ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
626 "10000baseKR/Full" },
627 { 0, ETHTOOL_LINK_MODE_10000baseR_FEC_BIT,
628 "10000baseR_FEC" },
629 { 0, ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT,
630 "20000baseMLD2/Full" },
631 { 0, ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT,
632 "20000baseKR2/Full" },
633 { 0, ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
634 "40000baseKR4/Full" },
635 { 0, ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
636 "40000baseCR4/Full" },
637 { 0, ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT,
638 "40000baseSR4/Full" },
639 { 0, ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT,
640 "40000baseLR4/Full" },
641 { 0, ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT,
642 "56000baseKR4/Full" },
643 { 0, ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT,
644 "56000baseCR4/Full" },
645 { 0, ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT,
646 "56000baseSR4/Full" },
647 { 0, ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT,
648 "56000baseLR4/Full" },
649 { 0, ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
650 "25000baseCR/Full" },
651 { 0, ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
652 "25000baseKR/Full" },
653 { 0, ETHTOOL_LINK_MODE_25000baseSR_Full_BIT,
654 "25000baseSR/Full" },
655 { 0, ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT,
656 "50000baseCR2/Full" },
657 { 0, ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT,
658 "50000baseKR2/Full" },
659 { 0, ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
660 "100000baseKR4/Full" },
661 { 0, ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
662 "100000baseSR4/Full" },
663 { 0, ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
664 "100000baseCR4/Full" },
665 { 0, ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT,
666 "100000baseLR4_ER4/Full" },
667 { 0, ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT,
668 "50000baseSR2/Full" },
669 { 0, ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
670 "1000baseX/Full" },
671 { 0, ETHTOOL_LINK_MODE_10000baseCR_Full_BIT,
672 "10000baseCR/Full" },
673 { 0, ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
674 "10000baseSR/Full" },
675 { 0, ETHTOOL_LINK_MODE_10000baseLR_Full_BIT,
676 "10000baseLR/Full" },
677 { 0, ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT,
678 "10000baseLRM/Full" },
679 { 0, ETHTOOL_LINK_MODE_10000baseER_Full_BIT,
680 "10000baseER/Full" },
681 { 0, ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
682 "2500baseT/Full" },
683 { 0, ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
684 "5000baseT/Full" },
685 { 0, ETHTOOL_LINK_MODE_50000baseKR_Full_BIT,
686 "50000baseKR/Full" },
687 { 0, ETHTOOL_LINK_MODE_50000baseSR_Full_BIT,
688 "50000baseSR/Full" },
689 { 0, ETHTOOL_LINK_MODE_50000baseCR_Full_BIT,
690 "50000baseCR/Full" },
691 { 0, ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT,
692 "50000baseLR_ER_FR/Full" },
693 { 0, ETHTOOL_LINK_MODE_50000baseDR_Full_BIT,
694 "50000baseDR/Full" },
695 { 0, ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT,
696 "100000baseKR2/Full" },
697 { 0, ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT,
698 "100000baseSR2/Full" },
699 { 0, ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT,
700 "100000baseCR2/Full" },
701 { 0, ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT,
702 "100000baseLR2_ER2_FR2/Full" },
703 { 0, ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT,
704 "100000baseDR2/Full" },
705 { 0, ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT,
706 "200000baseKR4/Full" },
707 { 0, ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT,
708 "200000baseSR4/Full" },
709 { 0, ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT,
710 "200000baseLR4_ER4_FR4/Full" },
711 { 0, ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT,
712 "200000baseDR4/Full" },
713 { 0, ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT,
714 "200000baseCR4/Full" },
715 { 0, ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
716 "100baseT1/Full" },
717 { 0, ETHTOOL_LINK_MODE_1000baseT1_Full_BIT,
718 "1000baseT1/Full" },
719 { 0, ETHTOOL_LINK_MODE_400000baseKR8_Full_BIT,
720 "400000baseKR8/Full" },
721 { 0, ETHTOOL_LINK_MODE_400000baseSR8_Full_BIT,
722 "400000baseSR8/Full" },
723 { 0, ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT,
724 "400000baseLR8_ER8_FR8/Full" },
725 { 0, ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT,
726 "400000baseDR8/Full" },
727 { 0, ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT,
728 "400000baseCR8/Full" },
729 { 0, ETHTOOL_LINK_MODE_100000baseKR_Full_BIT,
730 "100000baseKR/Full" },
731 { 0, ETHTOOL_LINK_MODE_100000baseSR_Full_BIT,
732 "100000baseSR/Full" },
733 { 0, ETHTOOL_LINK_MODE_100000baseLR_ER_FR_Full_BIT,
734 "100000baseLR_ER_FR/Full" },
735 { 0, ETHTOOL_LINK_MODE_100000baseDR_Full_BIT,
736 "100000baseDR/Full" },
737 { 0, ETHTOOL_LINK_MODE_100000baseCR_Full_BIT,
738 "100000baseCR/Full" },
739 { 0, ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT,
740 "200000baseKR2/Full" },
741 { 0, ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT,
742 "200000baseSR2/Full" },
743 { 0, ETHTOOL_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT,
744 "200000baseLR2_ER2_FR2/Full" },
745 { 0, ETHTOOL_LINK_MODE_200000baseDR2_Full_BIT,
746 "200000baseDR2/Full" },
747 { 0, ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT,
748 "200000baseCR2/Full" },
749 { 0, ETHTOOL_LINK_MODE_400000baseKR4_Full_BIT,
750 "400000baseKR4/Full" },
751 { 0, ETHTOOL_LINK_MODE_400000baseSR4_Full_BIT,
752 "400000baseSR4/Full" },
753 { 0, ETHTOOL_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT,
754 "400000baseLR4_ER4_FR4/Full" },
755 { 0, ETHTOOL_LINK_MODE_400000baseDR4_Full_BIT,
756 "400000baseDR4/Full" },
757 { 0, ETHTOOL_LINK_MODE_400000baseCR4_Full_BIT,
758 "400000baseCR4/Full" },
759 { 0, ETHTOOL_LINK_MODE_100baseFX_Half_BIT,
760 "100baseFX/Half" },
761 { 1, ETHTOOL_LINK_MODE_100baseFX_Full_BIT,
762 "100baseFX/Full" },
763 { 0, ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
764 "10baseT1L/Full" },
765 { 0, ETHTOOL_LINK_MODE_800000baseCR8_Full_BIT,
766 "800000baseCR8/Full" },
767 { 0, ETHTOOL_LINK_MODE_800000baseKR8_Full_BIT,
768 "800000baseKR8/Full" },
769 { 0, ETHTOOL_LINK_MODE_800000baseDR8_Full_BIT,
770 "800000baseDR8/Full" },
771 { 0, ETHTOOL_LINK_MODE_800000baseDR8_2_Full_BIT,
772 "800000baseDR8_2/Full" },
773 { 0, ETHTOOL_LINK_MODE_800000baseSR8_Full_BIT,
774 "800000baseSR8/Full" },
775 { 0, ETHTOOL_LINK_MODE_800000baseVR8_Full_BIT,
776 "800000baseVR8/Full" },
777 { 0, ETHTOOL_LINK_MODE_10baseT1S_Full_BIT,
778 "10baseT1S/Full" },
779 { 1, ETHTOOL_LINK_MODE_10baseT1S_Half_BIT,
780 "10baseT1S/Half" },
781 { 0, ETHTOOL_LINK_MODE_10baseT1S_P2MP_Half_BIT,
782 "10baseT1S/Half" },
783 { 0, ETHTOOL_LINK_MODE_10baseT1BRR_Full_BIT,
784 "10baseT1BRR/Full" },
785 };
786 int indent;
787 int did1, new_line_pend;
788 int fecreported = 0;
789 unsigned int i;
790
791 /* Indent just like the separate functions used to */
792 indent = strlen(prefix) + 14;
793 if (indent < 24)
794 indent = 24;
795
796 fprintf(stdout, " %s link modes:%*s", prefix,
797 indent - (int)strlen(prefix) - 12, "");
798 did1 = 0;
799 new_line_pend = 0;
800 for (i = 0; i < ARRAY_SIZE(mode_defs); i++) {
801 if (did1 && !mode_defs[i].same_line)
802 new_line_pend = 1;
803 if (ethtool_link_mode_test_bit(mode_defs[i].bit_index,
804 mask)) {
805 if (new_line_pend) {
806 fprintf(stdout, "\n");
807 fprintf(stdout, " %*s", indent, "");
808 new_line_pend = 0;
809 }
810 did1++;
811 fprintf(stdout, "%s ", mode_defs[i].name);
812 }
813 }
814 if (did1 == 0)
815 fprintf(stdout, "Not reported");
816 fprintf(stdout, "\n");
817
818 if (!link_mode_only) {
819 fprintf(stdout, " %s pause frame use: ", prefix);
820 if (ethtool_link_mode_test_bit(
821 ETHTOOL_LINK_MODE_Pause_BIT, mask)) {
822 fprintf(stdout, "Symmetric");
823 if (ethtool_link_mode_test_bit(
824 ETHTOOL_LINK_MODE_Asym_Pause_BIT, mask))
825 fprintf(stdout, " Receive-only");
826 fprintf(stdout, "\n");
827 } else {
828 if (ethtool_link_mode_test_bit(
829 ETHTOOL_LINK_MODE_Asym_Pause_BIT, mask))
830 fprintf(stdout, "Transmit-only\n");
831 else
832 fprintf(stdout, "No\n");
833 }
834
835 fprintf(stdout, " %s auto-negotiation: ", an_prefix);
836 if (ethtool_link_mode_test_bit(
837 ETHTOOL_LINK_MODE_Autoneg_BIT, mask))
838 fprintf(stdout, "Yes\n");
839 else
840 fprintf(stdout, "No\n");
841
842 fprintf(stdout, " %s FEC modes:", prefix);
843 if (ethtool_link_mode_test_bit(ETHTOOL_LINK_MODE_FEC_NONE_BIT,
844 mask)) {
845 fprintf(stdout, " None");
846 fecreported = 1;
847 }
848 if (ethtool_link_mode_test_bit(ETHTOOL_LINK_MODE_FEC_BASER_BIT,
849 mask)) {
850 fprintf(stdout, " BaseR");
851 fecreported = 1;
852 }
853 if (ethtool_link_mode_test_bit(ETHTOOL_LINK_MODE_FEC_RS_BIT,
854 mask)) {
855 fprintf(stdout, " RS");
856 fecreported = 1;
857 }
858 if (ethtool_link_mode_test_bit(ETHTOOL_LINK_MODE_FEC_LLRS_BIT,
859 mask)) {
860 fprintf(stdout, " LLRS");
861 fecreported = 1;
862 }
863
864 if (!fecreported)
865 fprintf(stdout, " Not reported");
866 fprintf(stdout, "\n");
867 }
868 }
869
870 static int
dump_link_usettings(const struct ethtool_link_usettings * link_usettings)871 dump_link_usettings(const struct ethtool_link_usettings *link_usettings)
872 {
873 dump_supported(link_usettings);
874 dump_link_caps("Advertised", "Advertised",
875 link_usettings->link_modes.advertising, 0);
876 if (!ethtool_link_mode_is_empty(
877 link_usettings->link_modes.lp_advertising))
878 dump_link_caps("Link partner advertised",
879 "Link partner advertised",
880 link_usettings->link_modes.lp_advertising, 0);
881
882 fprintf(stdout, " Speed: ");
883 if (link_usettings->base.speed == 0
884 || link_usettings->base.speed == (u16)(-1)
885 || link_usettings->base.speed == (u32)(-1))
886 fprintf(stdout, "Unknown!\n");
887 else
888 fprintf(stdout, "%uMb/s\n", link_usettings->base.speed);
889
890 fprintf(stdout, " Duplex: ");
891 switch (link_usettings->base.duplex) {
892 case DUPLEX_HALF:
893 fprintf(stdout, "Half\n");
894 break;
895 case DUPLEX_FULL:
896 fprintf(stdout, "Full\n");
897 break;
898 default:
899 fprintf(stdout, "Unknown! (%i)\n", link_usettings->base.duplex);
900 break;
901 };
902
903 fprintf(stdout, " Port: ");
904 switch (link_usettings->base.port) {
905 case PORT_TP:
906 fprintf(stdout, "Twisted Pair\n");
907 break;
908 case PORT_AUI:
909 fprintf(stdout, "AUI\n");
910 break;
911 case PORT_BNC:
912 fprintf(stdout, "BNC\n");
913 break;
914 case PORT_MII:
915 fprintf(stdout, "MII\n");
916 break;
917 case PORT_FIBRE:
918 fprintf(stdout, "FIBRE\n");
919 break;
920 case PORT_DA:
921 fprintf(stdout, "Direct Attach Copper\n");
922 break;
923 case PORT_NONE:
924 fprintf(stdout, "None\n");
925 break;
926 case PORT_OTHER:
927 fprintf(stdout, "Other\n");
928 break;
929 default:
930 fprintf(stdout, "Unknown! (%i)\n", link_usettings->base.port);
931 break;
932 };
933
934 fprintf(stdout, " PHYAD: %d\n", link_usettings->base.phy_address);
935 fprintf(stdout, " Transceiver: ");
936 switch (link_usettings->deprecated.transceiver) {
937 case XCVR_INTERNAL:
938 fprintf(stdout, "internal\n");
939 break;
940 case XCVR_EXTERNAL:
941 fprintf(stdout, "external\n");
942 break;
943 default:
944 fprintf(stdout, "Unknown!\n");
945 break;
946 };
947
948 fprintf(stdout, " Auto-negotiation: %s\n",
949 (link_usettings->base.autoneg == AUTONEG_DISABLE) ?
950 "off" : "on");
951
952 if (link_usettings->base.port == PORT_TP)
953 dump_mdix(link_usettings->base.eth_tp_mdix,
954 link_usettings->base.eth_tp_mdix_ctrl);
955
956 return 0;
957 }
958
dump_drvinfo(struct ethtool_drvinfo * info)959 static int dump_drvinfo(struct ethtool_drvinfo *info)
960 {
961 fprintf(stdout,
962 "driver: %.*s\n"
963 "version: %.*s\n"
964 "firmware-version: %.*s\n"
965 "expansion-rom-version: %.*s\n"
966 "bus-info: %.*s\n"
967 "supports-statistics: %s\n"
968 "supports-test: %s\n"
969 "supports-eeprom-access: %s\n"
970 "supports-register-dump: %s\n"
971 "supports-priv-flags: %s\n",
972 (int)sizeof(info->driver), info->driver,
973 (int)sizeof(info->version), info->version,
974 (int)sizeof(info->fw_version), info->fw_version,
975 (int)sizeof(info->erom_version), info->erom_version,
976 (int)sizeof(info->bus_info), info->bus_info,
977 info->n_stats ? "yes" : "no",
978 info->testinfo_len ? "yes" : "no",
979 info->eedump_len ? "yes" : "no",
980 info->regdump_len ? "yes" : "no",
981 info->n_priv_flags ? "yes" : "no");
982
983 return 0;
984 }
985
parse_wolopts(char * optstr,u32 * data)986 static int parse_wolopts(char *optstr, u32 *data)
987 {
988 *data = 0;
989 while (*optstr) {
990 switch (*optstr) {
991 case 'p':
992 *data |= WAKE_PHY;
993 break;
994 case 'u':
995 *data |= WAKE_UCAST;
996 break;
997 case 'm':
998 *data |= WAKE_MCAST;
999 break;
1000 case 'b':
1001 *data |= WAKE_BCAST;
1002 break;
1003 case 'a':
1004 *data |= WAKE_ARP;
1005 break;
1006 case 'g':
1007 *data |= WAKE_MAGIC;
1008 break;
1009 case 's':
1010 *data |= WAKE_MAGICSECURE;
1011 break;
1012 case 'f':
1013 *data |= WAKE_FILTER;
1014 break;
1015 case 'd':
1016 *data = 0;
1017 break;
1018 default:
1019 return -1;
1020 }
1021 optstr++;
1022 }
1023 return 0;
1024 }
1025
parse_rxfhashopts(char * optstr,u32 * data)1026 static int parse_rxfhashopts(char *optstr, u32 *data)
1027 {
1028 *data = 0;
1029 while (*optstr) {
1030 switch (*optstr) {
1031 case 'm':
1032 *data |= RXH_L2DA;
1033 break;
1034 case 'v':
1035 *data |= RXH_VLAN;
1036 break;
1037 case 't':
1038 *data |= RXH_L3_PROTO;
1039 break;
1040 case 's':
1041 *data |= RXH_IP_SRC;
1042 break;
1043 case 'd':
1044 *data |= RXH_IP_DST;
1045 break;
1046 case 'f':
1047 *data |= RXH_L4_B_0_1;
1048 break;
1049 case 'n':
1050 *data |= RXH_L4_B_2_3;
1051 break;
1052 case 'e':
1053 *data |= RXH_GTP_TEID;
1054 break;
1055 case 'r':
1056 *data |= RXH_DISCARD;
1057 break;
1058 default:
1059 return -1;
1060 }
1061 optstr++;
1062 }
1063 return 0;
1064 }
1065
unparse_rxfhashopts(u64 opts)1066 static char *unparse_rxfhashopts(u64 opts)
1067 {
1068 static char buf[300];
1069
1070 memset(buf, 0, sizeof(buf));
1071
1072 if (opts) {
1073 if (opts & RXH_L2DA)
1074 strcat(buf, "L2DA\n");
1075 if (opts & RXH_VLAN)
1076 strcat(buf, "VLAN tag\n");
1077 if (opts & RXH_L3_PROTO)
1078 strcat(buf, "L3 proto\n");
1079 if (opts & RXH_IP_SRC)
1080 strcat(buf, "IP SA\n");
1081 if (opts & RXH_IP_DST)
1082 strcat(buf, "IP DA\n");
1083 if (opts & RXH_L4_B_0_1)
1084 strcat(buf, "L4 bytes 0 & 1 [TCP/UDP src port]\n");
1085 if (opts & RXH_L4_B_2_3)
1086 strcat(buf, "L4 bytes 2 & 3 [TCP/UDP dst port]\n");
1087 if (opts & RXH_GTP_TEID)
1088 strcat(buf, "GTP TEID\n");
1089 } else {
1090 sprintf(buf, "None");
1091 }
1092
1093 return buf;
1094 }
1095
convert_string_to_hashkey(char * rss_hkey,u32 key_size,const char * rss_hkey_string)1096 static int convert_string_to_hashkey(char *rss_hkey, u32 key_size,
1097 const char *rss_hkey_string)
1098 {
1099 u32 i = 0;
1100 int hex_byte, len;
1101
1102 do {
1103 if (i > (key_size - 1)) {
1104 fprintf(stderr,
1105 "Key is too long for device (%u > %u)\n",
1106 i + 1, key_size);
1107 goto err;
1108 }
1109
1110 if (sscanf(rss_hkey_string, "%2x%n", &hex_byte, &len) < 1 ||
1111 len != 2) {
1112 fprintf(stderr, "Invalid RSS hash key format\n");
1113 goto err;
1114 }
1115
1116 rss_hkey[i++] = hex_byte;
1117 rss_hkey_string += 2;
1118
1119 if (*rss_hkey_string == ':') {
1120 rss_hkey_string++;
1121 } else if (*rss_hkey_string != '\0') {
1122 fprintf(stderr, "Invalid RSS hash key format\n");
1123 goto err;
1124 }
1125
1126 } while (*rss_hkey_string);
1127
1128 if (i != key_size) {
1129 fprintf(stderr, "Key is too short for device (%u < %u)\n",
1130 i, key_size);
1131 goto err;
1132 }
1133
1134 return 0;
1135 err:
1136 return 2;
1137 }
1138
parse_hkey(char ** rss_hkey,u32 key_size,const char * rss_hkey_string)1139 static int parse_hkey(char **rss_hkey, u32 key_size,
1140 const char *rss_hkey_string)
1141 {
1142 if (!key_size) {
1143 fprintf(stderr,
1144 "Cannot set RX flow hash configuration:\n"
1145 " Hash key setting not supported\n");
1146 return 1;
1147 }
1148
1149 *rss_hkey = malloc(key_size);
1150 if (!(*rss_hkey)) {
1151 perror("Cannot allocate memory for RSS hash key");
1152 return 1;
1153 }
1154
1155 if (convert_string_to_hashkey(*rss_hkey, key_size,
1156 rss_hkey_string)) {
1157 free(*rss_hkey);
1158 *rss_hkey = NULL;
1159 return 2;
1160 }
1161 return 0;
1162 }
1163
1164 #ifdef ETHTOOL_ENABLE_PRETTY_DUMP
1165 static const struct {
1166 const char *name;
1167 int (*func)(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
1168
1169 } driver_list[] = {
1170 { "8139cp", realtek_dump_regs },
1171 { "8139too", realtek_dump_regs },
1172 { "r8169", realtek_dump_regs },
1173 { "de2104x", de2104x_dump_regs },
1174 { "e1000", e1000_dump_regs },
1175 { "e1000e", e1000_dump_regs },
1176 { "igb", igb_dump_regs },
1177 { "ixgb", ixgb_dump_regs },
1178 { "ixgbe", ixgbe_dump_regs },
1179 { "ixgbevf", ixgbevf_dump_regs },
1180 { "natsemi", natsemi_dump_regs },
1181 { "e100", e100_dump_regs },
1182 { "amd8111e", amd8111e_dump_regs },
1183 { "pcnet32", pcnet32_dump_regs },
1184 { "fec_8xx", fec_8xx_dump_regs },
1185 { "ibm_emac", ibm_emac_dump_regs },
1186 { "tg3", tg3_dump_regs },
1187 { "skge", skge_dump_regs },
1188 { "sky2", sky2_dump_regs },
1189 { "vioc", vioc_dump_regs },
1190 { "smsc911x", smsc911x_dump_regs },
1191 { "at76c50x-usb", at76c50x_usb_dump_regs },
1192 { "sfc", sfc_dump_regs },
1193 { "st_mac100", st_mac100_dump_regs },
1194 { "st_gmac", st_gmac_dump_regs },
1195 { "et131x", et131x_dump_regs },
1196 { "altera_tse", altera_tse_dump_regs },
1197 { "vmxnet3", vmxnet3_dump_regs },
1198 { "fjes", fjes_dump_regs },
1199 { "lan78xx", lan78xx_dump_regs },
1200 { "dsa", dsa_dump_regs },
1201 { "fec", fec_dump_regs },
1202 { "igc", igc_dump_regs },
1203 { "bnxt_en", bnxt_dump_regs },
1204 { "cpsw-switch", cpsw_dump_regs },
1205 { "lan743x", lan743x_dump_regs },
1206 { "fsl_enetc", fsl_enetc_dump_regs },
1207 { "fsl_enetc_vf", fsl_enetc_dump_regs },
1208 { "hns3", hns3_dump_regs },
1209 };
1210 #endif
1211
dump_hex(FILE * file,const u8 * data,int len,int offset)1212 void dump_hex(FILE *file, const u8 *data, int len, int offset)
1213 {
1214 int i;
1215
1216 fprintf(file, "Offset\t\tValues\n");
1217 fprintf(file, "------\t\t------");
1218 for (i = 0; i < len; i++) {
1219 if (i % 16 == 0)
1220 fprintf(file, "\n0x%04x:\t\t", i + offset);
1221 fprintf(file, "%02x ", data[i]);
1222 }
1223 fprintf(file, "\n");
1224 }
1225
dump_regs(int gregs_dump_raw,int gregs_dump_hex,struct ethtool_drvinfo * info,struct ethtool_regs * regs)1226 static int dump_regs(int gregs_dump_raw, int gregs_dump_hex,
1227 struct ethtool_drvinfo *info, struct ethtool_regs *regs)
1228 {
1229 if (gregs_dump_raw) {
1230 fwrite(regs->data, regs->len, 1, stdout);
1231 goto nested;
1232 }
1233
1234 #ifdef ETHTOOL_ENABLE_PRETTY_DUMP
1235 if (!gregs_dump_hex) {
1236 unsigned int i;
1237
1238 for (i = 0; i < ARRAY_SIZE(driver_list); i++)
1239 if (!strncmp(driver_list[i].name, info->driver,
1240 ETHTOOL_BUSINFO_LEN)) {
1241 if (driver_list[i].func(info, regs) == 0)
1242 goto nested;
1243 /* This version (or some other
1244 * variation in the dump format) is
1245 * not handled; fall back to hex
1246 */
1247 break;
1248 }
1249 }
1250 #endif
1251
1252 dump_hex(stdout, regs->data, regs->len, 0);
1253
1254 nested:
1255 /* Recurse dump if some drvinfo and regs structures are nested */
1256 if (info->regdump_len > regs->len + sizeof(*info) + sizeof(*regs)) {
1257 info = (struct ethtool_drvinfo *)(®s->data[0] + regs->len);
1258 regs = (struct ethtool_regs *)(®s->data[0] + regs->len + sizeof(*info));
1259
1260 return dump_regs(gregs_dump_raw, gregs_dump_hex, info, regs);
1261 }
1262
1263 return 0;
1264 }
1265
dump_eeprom(int geeprom_dump_raw,struct ethtool_drvinfo * info __maybe_unused,struct ethtool_eeprom * ee)1266 static int dump_eeprom(int geeprom_dump_raw,
1267 struct ethtool_drvinfo *info __maybe_unused,
1268 struct ethtool_eeprom *ee)
1269 {
1270 if (geeprom_dump_raw) {
1271 fwrite(ee->data, 1, ee->len, stdout);
1272 return 0;
1273 }
1274 #ifdef ETHTOOL_ENABLE_PRETTY_DUMP
1275 if (!strncmp("natsemi", info->driver, ETHTOOL_BUSINFO_LEN)) {
1276 return natsemi_dump_eeprom(info, ee);
1277 } else if (!strncmp("tg3", info->driver, ETHTOOL_BUSINFO_LEN)) {
1278 return tg3_dump_eeprom(info, ee);
1279 }
1280 #endif
1281 dump_hex(stdout, ee->data, ee->len, ee->offset);
1282
1283 return 0;
1284 }
1285
dump_test(struct ethtool_test * test,struct ethtool_gstrings * strings)1286 static int dump_test(struct ethtool_test *test,
1287 struct ethtool_gstrings *strings)
1288 {
1289 unsigned int i;
1290 int rc;
1291
1292 rc = test->flags & ETH_TEST_FL_FAILED;
1293 fprintf(stdout, "The test result is %s\n", rc ? "FAIL" : "PASS");
1294
1295 if (test->flags & ETH_TEST_FL_EXTERNAL_LB)
1296 fprintf(stdout, "External loopback test was %sexecuted\n",
1297 (test->flags & ETH_TEST_FL_EXTERNAL_LB_DONE) ?
1298 "" : "not ");
1299
1300 if (strings->len)
1301 fprintf(stdout, "The test extra info:\n");
1302
1303 for (i = 0; i < strings->len; i++) {
1304 fprintf(stdout, "%s\t %d\n",
1305 (char *)(strings->data + i * ETH_GSTRING_LEN),
1306 (u32) test->data[i]);
1307 }
1308
1309 fprintf(stdout, "\n");
1310 return rc;
1311 }
1312
dump_pause(const struct ethtool_pauseparam * epause,u32 advertising,u32 lp_advertising)1313 static int dump_pause(const struct ethtool_pauseparam *epause,
1314 u32 advertising, u32 lp_advertising)
1315 {
1316 fprintf(stdout,
1317 "Autonegotiate: %s\n"
1318 "RX: %s\n"
1319 "TX: %s\n",
1320 epause->autoneg ? "on" : "off",
1321 epause->rx_pause ? "on" : "off",
1322 epause->tx_pause ? "on" : "off");
1323
1324 if (lp_advertising) {
1325 int an_rx = 0, an_tx = 0;
1326
1327 /* Work out negotiated pause frame usage per
1328 * IEEE 802.3-2005 table 28B-3.
1329 */
1330 if (advertising & lp_advertising & ADVERTISED_Pause) {
1331 an_tx = 1;
1332 an_rx = 1;
1333 } else if (advertising & lp_advertising &
1334 ADVERTISED_Asym_Pause) {
1335 if (advertising & ADVERTISED_Pause)
1336 an_rx = 1;
1337 else if (lp_advertising & ADVERTISED_Pause)
1338 an_tx = 1;
1339 }
1340
1341 fprintf(stdout,
1342 "RX negotiated: %s\n"
1343 "TX negotiated: %s\n",
1344 an_rx ? "on" : "off",
1345 an_tx ? "on" : "off");
1346 }
1347
1348 fprintf(stdout, "\n");
1349 return 0;
1350 }
1351
dump_ring(const struct ethtool_ringparam * ering)1352 static int dump_ring(const struct ethtool_ringparam *ering)
1353 {
1354 fprintf(stdout,
1355 "Pre-set maximums:\n"
1356 "RX: %u\n"
1357 "RX Mini: %u\n"
1358 "RX Jumbo: %u\n"
1359 "TX: %u\n",
1360 ering->rx_max_pending,
1361 ering->rx_mini_max_pending,
1362 ering->rx_jumbo_max_pending,
1363 ering->tx_max_pending);
1364
1365 fprintf(stdout,
1366 "Current hardware settings:\n"
1367 "RX: %u\n"
1368 "RX Mini: %u\n"
1369 "RX Jumbo: %u\n"
1370 "TX: %u\n",
1371 ering->rx_pending,
1372 ering->rx_mini_pending,
1373 ering->rx_jumbo_pending,
1374 ering->tx_pending);
1375
1376 fprintf(stdout, "\n");
1377 return 0;
1378 }
1379
dump_channels(const struct ethtool_channels * echannels)1380 static int dump_channels(const struct ethtool_channels *echannels)
1381 {
1382 fprintf(stdout,
1383 "Pre-set maximums:\n"
1384 "RX: %u\n"
1385 "TX: %u\n"
1386 "Other: %u\n"
1387 "Combined: %u\n",
1388 echannels->max_rx, echannels->max_tx,
1389 echannels->max_other,
1390 echannels->max_combined);
1391
1392 fprintf(stdout,
1393 "Current hardware settings:\n"
1394 "RX: %u\n"
1395 "TX: %u\n"
1396 "Other: %u\n"
1397 "Combined: %u\n",
1398 echannels->rx_count, echannels->tx_count,
1399 echannels->other_count,
1400 echannels->combined_count);
1401
1402 fprintf(stdout, "\n");
1403 return 0;
1404 }
1405
dump_coalesce(const struct ethtool_coalesce * ecoal)1406 static int dump_coalesce(const struct ethtool_coalesce *ecoal)
1407 {
1408 fprintf(stdout, "Adaptive RX: %s TX: %s\n",
1409 ecoal->use_adaptive_rx_coalesce ? "on" : "off",
1410 ecoal->use_adaptive_tx_coalesce ? "on" : "off");
1411
1412 fprintf(stdout,
1413 "stats-block-usecs: %u\n"
1414 "sample-interval: %u\n"
1415 "pkt-rate-low: %u\n"
1416 "pkt-rate-high: %u\n"
1417 "\n"
1418 "rx-usecs: %u\n"
1419 "rx-frames: %u\n"
1420 "rx-usecs-irq: %u\n"
1421 "rx-frames-irq: %u\n"
1422 "\n"
1423 "tx-usecs: %u\n"
1424 "tx-frames: %u\n"
1425 "tx-usecs-irq: %u\n"
1426 "tx-frames-irq: %u\n"
1427 "\n"
1428 "rx-usecs-low: %u\n"
1429 "rx-frames-low: %u\n"
1430 "tx-usecs-low: %u\n"
1431 "tx-frames-low: %u\n"
1432 "\n"
1433 "rx-usecs-high: %u\n"
1434 "rx-frames-high: %u\n"
1435 "tx-usecs-high: %u\n"
1436 "tx-frames-high: %u\n"
1437 "\n",
1438 ecoal->stats_block_coalesce_usecs,
1439 ecoal->rate_sample_interval,
1440 ecoal->pkt_rate_low,
1441 ecoal->pkt_rate_high,
1442
1443 ecoal->rx_coalesce_usecs,
1444 ecoal->rx_max_coalesced_frames,
1445 ecoal->rx_coalesce_usecs_irq,
1446 ecoal->rx_max_coalesced_frames_irq,
1447
1448 ecoal->tx_coalesce_usecs,
1449 ecoal->tx_max_coalesced_frames,
1450 ecoal->tx_coalesce_usecs_irq,
1451 ecoal->tx_max_coalesced_frames_irq,
1452
1453 ecoal->rx_coalesce_usecs_low,
1454 ecoal->rx_max_coalesced_frames_low,
1455 ecoal->tx_coalesce_usecs_low,
1456 ecoal->tx_max_coalesced_frames_low,
1457
1458 ecoal->rx_coalesce_usecs_high,
1459 ecoal->rx_max_coalesced_frames_high,
1460 ecoal->tx_coalesce_usecs_high,
1461 ecoal->tx_max_coalesced_frames_high);
1462
1463 return 0;
1464 }
1465
dump_per_queue_coalesce(struct ethtool_per_queue_op * per_queue_opt,__u32 * queue_mask,int n_queues)1466 void dump_per_queue_coalesce(struct ethtool_per_queue_op *per_queue_opt,
1467 __u32 *queue_mask, int n_queues)
1468 {
1469 struct ethtool_coalesce *ecoal;
1470 int i, idx = 0;
1471
1472 ecoal = (struct ethtool_coalesce *)(per_queue_opt + 1);
1473 for (i = 0; i < __KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32); i++) {
1474 int queue = i * 32;
1475 __u32 mask = queue_mask[i];
1476
1477 while (mask > 0) {
1478 if (mask & 0x1) {
1479 fprintf(stdout, "Queue: %d\n", queue);
1480 dump_coalesce(ecoal + idx);
1481 idx++;
1482 }
1483 mask = mask >> 1;
1484 queue++;
1485 }
1486 if (idx == n_queues)
1487 break;
1488 }
1489 }
1490
1491 struct feature_state {
1492 u32 off_flags;
1493 struct ethtool_gfeatures features;
1494 };
1495
dump_one_feature(const char * indent,const char * name,const struct feature_state * state,const struct feature_state * ref_state,u32 index)1496 static void dump_one_feature(const char *indent, const char *name,
1497 const struct feature_state *state,
1498 const struct feature_state *ref_state,
1499 u32 index)
1500 {
1501 if (ref_state &&
1502 !(FEATURE_BIT_IS_SET(state->features.features, index, active) ^
1503 FEATURE_BIT_IS_SET(ref_state->features.features, index, active)))
1504 return;
1505
1506 printf("%s%s: %s%s\n",
1507 indent, name,
1508 FEATURE_BIT_IS_SET(state->features.features, index, active) ?
1509 "on" : "off",
1510 (!FEATURE_BIT_IS_SET(state->features.features, index, available)
1511 || FEATURE_BIT_IS_SET(state->features.features, index,
1512 never_changed))
1513 ? " [fixed]"
1514 : (FEATURE_BIT_IS_SET(state->features.features, index, requested)
1515 ^ FEATURE_BIT_IS_SET(state->features.features, index, active))
1516 ? (FEATURE_BIT_IS_SET(state->features.features, index, requested)
1517 ? " [requested on]" : " [requested off]")
1518 : "");
1519 }
1520
linux_version_code(void)1521 static unsigned int linux_version_code(void)
1522 {
1523 struct utsname utsname;
1524 unsigned version, patchlevel, sublevel = 0;
1525
1526 if (uname(&utsname))
1527 return -1;
1528 if (sscanf(utsname.release, "%u.%u.%u", &version, &patchlevel, &sublevel) < 2)
1529 return -1;
1530 return KERNEL_VERSION(version, patchlevel, sublevel);
1531 }
1532
dump_features(const struct feature_defs * defs,const struct feature_state * state,const struct feature_state * ref_state)1533 static void dump_features(const struct feature_defs *defs,
1534 const struct feature_state *state,
1535 const struct feature_state *ref_state)
1536 {
1537 unsigned int kernel_ver = linux_version_code();
1538 unsigned int i, j;
1539 int indent;
1540 u32 value;
1541
1542 for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
1543 /* Don't show features whose state is unknown on this
1544 * kernel version
1545 */
1546 if (defs->off_flag_matched[i] == 0 &&
1547 ((off_flag_def[i].get_cmd == 0 &&
1548 kernel_ver < off_flag_def[i].min_kernel_ver) ||
1549 (off_flag_def[i].get_cmd == ETHTOOL_GUFO &&
1550 kernel_ver >= KERNEL_VERSION(4, 14, 0))))
1551 continue;
1552
1553 value = off_flag_def[i].value;
1554
1555 /* If this offload flag matches exactly one generic
1556 * feature then it's redundant to show the flag and
1557 * feature states separately. Otherwise, show the
1558 * flag state first.
1559 */
1560 if (defs->off_flag_matched[i] != 1 &&
1561 (!ref_state ||
1562 (state->off_flags ^ ref_state->off_flags) & value)) {
1563 printf("%s: %s\n",
1564 off_flag_def[i].long_name,
1565 (state->off_flags & value) ? "on" : "off");
1566 indent = 1;
1567 } else {
1568 indent = 0;
1569 }
1570
1571 /* Show matching features */
1572 for (j = 0; j < defs->n_features; j++) {
1573 if (defs->def[j].off_flag_index != (int)i)
1574 continue;
1575 if (defs->off_flag_matched[i] != 1)
1576 /* Show all matching feature states */
1577 dump_one_feature(indent ? "\t" : "",
1578 defs->def[j].name,
1579 state, ref_state, j);
1580 else
1581 /* Show full state with the old flag name */
1582 dump_one_feature("", off_flag_def[i].long_name,
1583 state, ref_state, j);
1584 }
1585 }
1586
1587 /* Show all unmatched features that have non-null names */
1588 for (j = 0; j < defs->n_features; j++)
1589 if (defs->def[j].off_flag_index < 0 && defs->def[j].name[0])
1590 dump_one_feature("", defs->def[j].name,
1591 state, ref_state, j);
1592 }
1593
dump_rxfhash(int fhash,u64 val)1594 static int dump_rxfhash(int fhash, u64 val)
1595 {
1596 switch (fhash & ~FLOW_RSS) {
1597 case TCP_V4_FLOW:
1598 fprintf(stdout, "TCP over IPV4 flows");
1599 break;
1600 case UDP_V4_FLOW:
1601 fprintf(stdout, "UDP over IPV4 flows");
1602 break;
1603 case SCTP_V4_FLOW:
1604 fprintf(stdout, "SCTP over IPV4 flows");
1605 break;
1606 case GTPC_V4_FLOW:
1607 fprintf(stdout, "GTP-C over IPV4 flows");
1608 break;
1609 case GTPC_TEID_V4_FLOW:
1610 fprintf(stdout, "GTP-C (include TEID) over IPV4 flows");
1611 break;
1612 case GTPU_V4_FLOW:
1613 fprintf(stdout, "GTP-U over IPV4 flows");
1614 break;
1615 case GTPU_EH_V4_FLOW:
1616 fprintf(stdout, "GTP-U and Extension Header over IPV4 flows");
1617 break;
1618 case GTPU_UL_V4_FLOW:
1619 fprintf(stdout, "GTP-U PSC Uplink over IPV4 flows");
1620 break;
1621 case GTPU_DL_V4_FLOW:
1622 fprintf(stdout, "GTP-U PSC Downlink over IPV4 flows");
1623 break;
1624 case AH_ESP_V4_FLOW:
1625 case AH_V4_FLOW:
1626 case ESP_V4_FLOW:
1627 fprintf(stdout, "IPSEC AH/ESP over IPV4 flows");
1628 break;
1629 case TCP_V6_FLOW:
1630 fprintf(stdout, "TCP over IPV6 flows");
1631 break;
1632 case UDP_V6_FLOW:
1633 fprintf(stdout, "UDP over IPV6 flows");
1634 break;
1635 case SCTP_V6_FLOW:
1636 fprintf(stdout, "SCTP over IPV6 flows");
1637 break;
1638 case GTPC_V6_FLOW:
1639 fprintf(stdout, "GTP-C over IPV6 flows");
1640 break;
1641 case GTPC_TEID_V6_FLOW:
1642 fprintf(stdout, "GTP-C (include TEID) over IPV6 flows");
1643 break;
1644 case GTPU_V6_FLOW:
1645 fprintf(stdout, "GTP-U over IPV6 flows");
1646 break;
1647 case GTPU_EH_V6_FLOW:
1648 fprintf(stdout, "GTP-U and Extension Header over IPV6 flows");
1649 break;
1650 case GTPU_UL_V6_FLOW:
1651 fprintf(stdout, "GTP-U PSC Uplink over IPV6 flows");
1652 break;
1653 case GTPU_DL_V6_FLOW:
1654 fprintf(stdout, "GTP-U PSC Downlink over IPV6 flows");
1655 break;
1656 case AH_ESP_V6_FLOW:
1657 case AH_V6_FLOW:
1658 case ESP_V6_FLOW:
1659 fprintf(stdout, "IPSEC AH/ESP over IPV6 flows");
1660 break;
1661 default:
1662 break;
1663 }
1664
1665 if (val & RXH_DISCARD) {
1666 fprintf(stdout, " - All matching flows discarded on RX\n");
1667 return 0;
1668 }
1669 fprintf(stdout, " use these fields for computing Hash flow key:\n");
1670
1671 fprintf(stdout, "%s\n", unparse_rxfhashopts(val));
1672
1673 return 0;
1674 }
1675
dump_eeecmd(struct ethtool_eee * ep)1676 static void dump_eeecmd(struct ethtool_eee *ep)
1677 {
1678 ETHTOOL_DECLARE_LINK_MODE_MASK(link_mode);
1679
1680 fprintf(stdout, " EEE status: ");
1681 if (!ep->supported) {
1682 fprintf(stdout, "not supported\n");
1683 return;
1684 } else if (!ep->eee_enabled) {
1685 fprintf(stdout, "disabled\n");
1686 } else {
1687 fprintf(stdout, "enabled - ");
1688 if (ep->eee_active)
1689 fprintf(stdout, "active\n");
1690 else
1691 fprintf(stdout, "inactive\n");
1692 }
1693
1694 fprintf(stdout, " Tx LPI:");
1695 if (ep->tx_lpi_enabled)
1696 fprintf(stdout, " %d (us)\n", ep->tx_lpi_timer);
1697 else
1698 fprintf(stdout, " disabled\n");
1699
1700 ethtool_link_mode_zero(link_mode);
1701
1702 link_mode[0] = ep->supported;
1703 dump_link_caps("Supported EEE", "", link_mode, 1);
1704
1705 link_mode[0] = ep->advertised;
1706 dump_link_caps("Advertised EEE", "", link_mode, 1);
1707
1708 link_mode[0] = ep->lp_advertised;
1709 dump_link_caps("Link partner advertised EEE", "", link_mode, 1);
1710 }
1711
dump_fec(u32 fec)1712 static void dump_fec(u32 fec)
1713 {
1714 if (fec & ETHTOOL_FEC_NONE)
1715 fprintf(stdout, " None");
1716 if (fec & ETHTOOL_FEC_AUTO)
1717 fprintf(stdout, " Auto");
1718 if (fec & ETHTOOL_FEC_OFF)
1719 fprintf(stdout, " Off");
1720 if (fec & ETHTOOL_FEC_BASER)
1721 fprintf(stdout, " BaseR");
1722 if (fec & ETHTOOL_FEC_RS)
1723 fprintf(stdout, " RS");
1724 if (fec & ETHTOOL_FEC_LLRS)
1725 fprintf(stdout, " LLRS");
1726 }
1727
1728 #define N_SOTS 7
1729
1730 static char *so_timestamping_labels[N_SOTS] = {
1731 "hardware-transmit (SOF_TIMESTAMPING_TX_HARDWARE)",
1732 "software-transmit (SOF_TIMESTAMPING_TX_SOFTWARE)",
1733 "hardware-receive (SOF_TIMESTAMPING_RX_HARDWARE)",
1734 "software-receive (SOF_TIMESTAMPING_RX_SOFTWARE)",
1735 "software-system-clock (SOF_TIMESTAMPING_SOFTWARE)",
1736 "hardware-legacy-clock (SOF_TIMESTAMPING_SYS_HARDWARE)",
1737 "hardware-raw-clock (SOF_TIMESTAMPING_RAW_HARDWARE)",
1738 };
1739
1740 #define N_TX_TYPES (HWTSTAMP_TX_ONESTEP_SYNC + 1)
1741
1742 static char *tx_type_labels[N_TX_TYPES] = {
1743 "off (HWTSTAMP_TX_OFF)",
1744 "on (HWTSTAMP_TX_ON)",
1745 "one-step-sync (HWTSTAMP_TX_ONESTEP_SYNC)",
1746 };
1747
1748 #define N_RX_FILTERS (HWTSTAMP_FILTER_NTP_ALL + 1)
1749
1750 static char *rx_filter_labels[N_RX_FILTERS] = {
1751 "none (HWTSTAMP_FILTER_NONE)",
1752 "all (HWTSTAMP_FILTER_ALL)",
1753 "some (HWTSTAMP_FILTER_SOME)",
1754 "ptpv1-l4-event (HWTSTAMP_FILTER_PTP_V1_L4_EVENT)",
1755 "ptpv1-l4-sync (HWTSTAMP_FILTER_PTP_V1_L4_SYNC)",
1756 "ptpv1-l4-delay-req (HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ)",
1757 "ptpv2-l4-event (HWTSTAMP_FILTER_PTP_V2_L4_EVENT)",
1758 "ptpv2-l4-sync (HWTSTAMP_FILTER_PTP_V2_L4_SYNC)",
1759 "ptpv2-l4-delay-req (HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ)",
1760 "ptpv2-l2-event (HWTSTAMP_FILTER_PTP_V2_L2_EVENT)",
1761 "ptpv2-l2-sync (HWTSTAMP_FILTER_PTP_V2_L2_SYNC)",
1762 "ptpv2-l2-delay-req (HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ)",
1763 "ptpv2-event (HWTSTAMP_FILTER_PTP_V2_EVENT)",
1764 "ptpv2-sync (HWTSTAMP_FILTER_PTP_V2_SYNC)",
1765 "ptpv2-delay-req (HWTSTAMP_FILTER_PTP_V2_DELAY_REQ)",
1766 "ntp-all (HWTSTAMP_FILTER_NTP_ALL)",
1767 };
1768
dump_tsinfo(const struct ethtool_ts_info * info)1769 static int dump_tsinfo(const struct ethtool_ts_info *info)
1770 {
1771 int i;
1772
1773 fprintf(stdout, "Capabilities:\n");
1774
1775 for (i = 0; i < N_SOTS; i++) {
1776 if (info->so_timestamping & (1 << i))
1777 fprintf(stdout, "\t%s\n", so_timestamping_labels[i]);
1778 }
1779
1780 fprintf(stdout, "PTP Hardware Clock: ");
1781
1782 if (info->phc_index < 0)
1783 fprintf(stdout, "none\n");
1784 else
1785 fprintf(stdout, "%d\n", info->phc_index);
1786
1787 fprintf(stdout, "Hardware Transmit Timestamp Modes:");
1788
1789 if (!info->tx_types)
1790 fprintf(stdout, " none\n");
1791 else
1792 fprintf(stdout, "\n");
1793
1794 for (i = 0; i < N_TX_TYPES; i++) {
1795 if (info->tx_types & (1 << i))
1796 fprintf(stdout, "\t%s\n", tx_type_labels[i]);
1797 }
1798
1799 fprintf(stdout, "Hardware Receive Filter Modes:");
1800
1801 if (!info->rx_filters)
1802 fprintf(stdout, " none\n");
1803 else
1804 fprintf(stdout, "\n");
1805
1806 for (i = 0; i < N_RX_FILTERS; i++) {
1807 if (info->rx_filters & (1 << i))
1808 fprintf(stdout, "\t%s\n", rx_filter_labels[i]);
1809 }
1810
1811 return 0;
1812 }
1813
1814 static struct ethtool_gstrings *
get_stringset(struct cmd_context * ctx,enum ethtool_stringset set_id,ptrdiff_t drvinfo_offset,int null_terminate)1815 get_stringset(struct cmd_context *ctx, enum ethtool_stringset set_id,
1816 ptrdiff_t drvinfo_offset, int null_terminate)
1817 {
1818 struct {
1819 struct ethtool_sset_info hdr;
1820 u32 buf[1];
1821 } sset_info;
1822 struct ethtool_drvinfo drvinfo;
1823 u32 len, i;
1824 struct ethtool_gstrings *strings;
1825
1826 sset_info.hdr.cmd = ETHTOOL_GSSET_INFO;
1827 sset_info.hdr.reserved = 0;
1828 sset_info.hdr.sset_mask = 1ULL << set_id;
1829 if (send_ioctl(ctx, &sset_info) == 0) {
1830 const u32 *sset_lengths = sset_info.hdr.data;
1831
1832 len = sset_info.hdr.sset_mask ? sset_lengths[0] : 0;
1833 } else if (errno == EOPNOTSUPP && drvinfo_offset != 0) {
1834 /* Fallback for old kernel versions */
1835 drvinfo.cmd = ETHTOOL_GDRVINFO;
1836 if (send_ioctl(ctx, &drvinfo))
1837 return NULL;
1838 len = *(u32 *)((char *)&drvinfo + drvinfo_offset);
1839 } else {
1840 return NULL;
1841 }
1842
1843 strings = calloc(1, sizeof(*strings) + len * ETH_GSTRING_LEN);
1844 if (!strings)
1845 return NULL;
1846
1847 strings->cmd = ETHTOOL_GSTRINGS;
1848 strings->string_set = set_id;
1849 strings->len = len;
1850 if (len != 0 && send_ioctl(ctx, strings)) {
1851 free(strings);
1852 return NULL;
1853 }
1854
1855 if (null_terminate)
1856 for (i = 0; i < len; i++)
1857 strings->data[(i + 1) * ETH_GSTRING_LEN - 1] = 0;
1858
1859 return strings;
1860 }
1861
get_feature_defs(struct cmd_context * ctx)1862 static struct feature_defs *get_feature_defs(struct cmd_context *ctx)
1863 {
1864 struct ethtool_gstrings *names;
1865 struct feature_defs *defs;
1866 unsigned int i, j;
1867 u32 n_features;
1868
1869 names = get_stringset(ctx, ETH_SS_FEATURES, 0, 1);
1870 if (names) {
1871 n_features = names->len;
1872 } else if (errno == EOPNOTSUPP || errno == EINVAL) {
1873 /* Kernel doesn't support named features; not an error */
1874 n_features = 0;
1875 } else if (errno == EPERM) {
1876 /* Kernel bug: ETHTOOL_GSSET_INFO was privileged.
1877 * Work around it. */
1878 n_features = 0;
1879 } else {
1880 return NULL;
1881 }
1882
1883 defs = malloc(sizeof(*defs) + sizeof(defs->def[0]) * n_features);
1884 if (!defs) {
1885 free(names);
1886 return NULL;
1887 }
1888
1889 defs->n_features = n_features;
1890 memset(defs->off_flag_matched, 0, sizeof(defs->off_flag_matched));
1891
1892 /* Copy out feature names and find those associated with legacy flags */
1893 for (i = 0; i < defs->n_features; i++) {
1894 memcpy(defs->def[i].name, names->data + i * ETH_GSTRING_LEN,
1895 ETH_GSTRING_LEN);
1896 defs->def[i].off_flag_index = -1;
1897
1898 for (j = 0;
1899 j < OFF_FLAG_DEF_SIZE &&
1900 defs->def[i].off_flag_index < 0;
1901 j++) {
1902 const char *pattern =
1903 off_flag_def[j].kernel_name;
1904 const char *name = defs->def[i].name;
1905 for (;;) {
1906 if (*pattern == '*') {
1907 /* There is only one wildcard; so
1908 * switch to a suffix comparison */
1909 size_t pattern_len =
1910 strlen(pattern + 1);
1911 size_t name_len = strlen(name);
1912 if (name_len < pattern_len)
1913 break; /* name is too short */
1914 name += name_len - pattern_len;
1915 ++pattern;
1916 } else if (*pattern != *name) {
1917 break; /* mismatch */
1918 } else if (*pattern == 0) {
1919 defs->def[i].off_flag_index = j;
1920 defs->off_flag_matched[j]++;
1921 break;
1922 } else {
1923 ++name;
1924 ++pattern;
1925 }
1926 }
1927 }
1928 }
1929
1930 free(names);
1931 return defs;
1932 }
1933
do_gdrv(struct cmd_context * ctx)1934 static int do_gdrv(struct cmd_context *ctx)
1935 {
1936 int err;
1937 struct ethtool_drvinfo drvinfo;
1938
1939 if (ctx->argc != 0)
1940 exit_bad_args();
1941
1942 drvinfo.cmd = ETHTOOL_GDRVINFO;
1943 err = send_ioctl(ctx, &drvinfo);
1944 if (err < 0) {
1945 perror("Cannot get driver information");
1946 return 71;
1947 }
1948 return dump_drvinfo(&drvinfo);
1949 }
1950
do_gpause(struct cmd_context * ctx)1951 static int do_gpause(struct cmd_context *ctx)
1952 {
1953 struct ethtool_pauseparam epause;
1954 struct ethtool_cmd ecmd;
1955 int err;
1956
1957 if (ctx->argc != 0)
1958 exit_bad_args();
1959
1960 fprintf(stdout, "Pause parameters for %s:\n", ctx->devname);
1961
1962 epause.cmd = ETHTOOL_GPAUSEPARAM;
1963 err = send_ioctl(ctx, &epause);
1964 if (err) {
1965 perror("Cannot get device pause settings");
1966 return 76;
1967 }
1968
1969 if (epause.autoneg) {
1970 ecmd.cmd = ETHTOOL_GSET;
1971 err = send_ioctl(ctx, &ecmd);
1972 if (err) {
1973 perror("Cannot get device settings");
1974 return 1;
1975 }
1976 dump_pause(&epause, ecmd.advertising, ecmd.lp_advertising);
1977 } else {
1978 dump_pause(&epause, 0, 0);
1979 }
1980
1981 return 0;
1982 }
1983
do_generic_set1(struct cmdline_info * info,int * changed_out)1984 static void do_generic_set1(struct cmdline_info *info, int *changed_out)
1985 {
1986 int wanted, *v1, *v2;
1987
1988 v1 = info->wanted_val;
1989 wanted = *v1;
1990
1991 if (wanted < 0)
1992 return;
1993
1994 v2 = info->ioctl_val;
1995 if (wanted == *v2) {
1996 fprintf(stderr, "%s unmodified, ignoring\n", info->name);
1997 } else {
1998 *v2 = wanted;
1999 *changed_out = 1;
2000 }
2001 }
2002
do_generic_set(struct cmdline_info * info,unsigned int n_info,int * changed_out)2003 static void do_generic_set(struct cmdline_info *info,
2004 unsigned int n_info,
2005 int *changed_out)
2006 {
2007 unsigned int i;
2008
2009 for (i = 0; i < n_info; i++)
2010 do_generic_set1(&info[i], changed_out);
2011 }
2012
do_spause(struct cmd_context * ctx)2013 static int do_spause(struct cmd_context *ctx)
2014 {
2015 struct ethtool_pauseparam epause;
2016 int gpause_changed = 0;
2017 int pause_autoneg_wanted = -1;
2018 int pause_rx_wanted = -1;
2019 int pause_tx_wanted = -1;
2020 struct cmdline_info cmdline_pause[] = {
2021 {
2022 .name = "autoneg",
2023 .type = CMDL_BOOL,
2024 .wanted_val = &pause_autoneg_wanted,
2025 .ioctl_val = &epause.autoneg,
2026 },
2027 {
2028 .name = "rx",
2029 .type = CMDL_BOOL,
2030 .wanted_val = &pause_rx_wanted,
2031 .ioctl_val = &epause.rx_pause,
2032 },
2033 {
2034 .name = "tx",
2035 .type = CMDL_BOOL,
2036 .wanted_val = &pause_tx_wanted,
2037 .ioctl_val = &epause.tx_pause,
2038 },
2039 };
2040 int err, changed = 0;
2041
2042 parse_generic_cmdline(ctx, &gpause_changed,
2043 cmdline_pause, ARRAY_SIZE(cmdline_pause));
2044
2045 epause.cmd = ETHTOOL_GPAUSEPARAM;
2046 err = send_ioctl(ctx, &epause);
2047 if (err) {
2048 perror("Cannot get device pause settings");
2049 return 77;
2050 }
2051
2052 do_generic_set(cmdline_pause, ARRAY_SIZE(cmdline_pause), &changed);
2053
2054 if (!changed) {
2055 fprintf(stderr, "no pause parameters changed, aborting\n");
2056 return 78;
2057 }
2058
2059 epause.cmd = ETHTOOL_SPAUSEPARAM;
2060 err = send_ioctl(ctx, &epause);
2061 if (err) {
2062 perror("Cannot set device pause parameters");
2063 return 79;
2064 }
2065
2066 return 0;
2067 }
2068
do_sring(struct cmd_context * ctx)2069 static int do_sring(struct cmd_context *ctx)
2070 {
2071 struct ethtool_ringparam ering;
2072 int gring_changed = 0;
2073 s32 ring_rx_wanted = -1;
2074 s32 ring_rx_mini_wanted = -1;
2075 s32 ring_rx_jumbo_wanted = -1;
2076 s32 ring_tx_wanted = -1;
2077 struct cmdline_info cmdline_ring[] = {
2078 {
2079 .name = "rx",
2080 .type = CMDL_S32,
2081 .wanted_val = &ring_rx_wanted,
2082 .ioctl_val = &ering.rx_pending,
2083 },
2084 {
2085 .name = "rx-mini",
2086 .type = CMDL_S32,
2087 .wanted_val = &ring_rx_mini_wanted,
2088 .ioctl_val = &ering.rx_mini_pending,
2089 },
2090 {
2091 .name = "rx-jumbo",
2092 .type = CMDL_S32,
2093 .wanted_val = &ring_rx_jumbo_wanted,
2094 .ioctl_val = &ering.rx_jumbo_pending,
2095 },
2096 {
2097 .name = "tx",
2098 .type = CMDL_S32,
2099 .wanted_val = &ring_tx_wanted,
2100 .ioctl_val = &ering.tx_pending,
2101 },
2102 };
2103 int err, changed = 0;
2104
2105 parse_generic_cmdline(ctx, &gring_changed,
2106 cmdline_ring, ARRAY_SIZE(cmdline_ring));
2107
2108 ering.cmd = ETHTOOL_GRINGPARAM;
2109 err = send_ioctl(ctx, &ering);
2110 if (err) {
2111 perror("Cannot get device ring settings");
2112 return 76;
2113 }
2114
2115 do_generic_set(cmdline_ring, ARRAY_SIZE(cmdline_ring), &changed);
2116
2117 if (!changed) {
2118 fprintf(stderr, "no ring parameters changed, aborting\n");
2119 return 80;
2120 }
2121
2122 ering.cmd = ETHTOOL_SRINGPARAM;
2123 err = send_ioctl(ctx, &ering);
2124 if (err) {
2125 perror("Cannot set device ring parameters");
2126 return 81;
2127 }
2128
2129 return 0;
2130 }
2131
do_gring(struct cmd_context * ctx)2132 static int do_gring(struct cmd_context *ctx)
2133 {
2134 struct ethtool_ringparam ering;
2135 int err;
2136
2137 if (ctx->argc != 0)
2138 exit_bad_args();
2139
2140 fprintf(stdout, "Ring parameters for %s:\n", ctx->devname);
2141
2142 ering.cmd = ETHTOOL_GRINGPARAM;
2143 err = send_ioctl(ctx, &ering);
2144 if (err == 0) {
2145 err = dump_ring(&ering);
2146 if (err)
2147 return err;
2148 } else {
2149 perror("Cannot get device ring settings");
2150 return 76;
2151 }
2152
2153 return 0;
2154 }
2155
do_schannels(struct cmd_context * ctx)2156 static int do_schannels(struct cmd_context *ctx)
2157 {
2158 struct ethtool_channels echannels;
2159 int gchannels_changed;
2160 s32 channels_rx_wanted = -1;
2161 s32 channels_tx_wanted = -1;
2162 s32 channels_other_wanted = -1;
2163 s32 channels_combined_wanted = -1;
2164 struct cmdline_info cmdline_channels[] = {
2165 {
2166 .name = "rx",
2167 .type = CMDL_S32,
2168 .wanted_val = &channels_rx_wanted,
2169 .ioctl_val = &echannels.rx_count,
2170 },
2171 {
2172 .name = "tx",
2173 .type = CMDL_S32,
2174 .wanted_val = &channels_tx_wanted,
2175 .ioctl_val = &echannels.tx_count,
2176 },
2177 {
2178 .name = "other",
2179 .type = CMDL_S32,
2180 .wanted_val = &channels_other_wanted,
2181 .ioctl_val = &echannels.other_count,
2182 },
2183 {
2184 .name = "combined",
2185 .type = CMDL_S32,
2186 .wanted_val = &channels_combined_wanted,
2187 .ioctl_val = &echannels.combined_count,
2188 },
2189 };
2190 int err, changed = 0;
2191
2192 parse_generic_cmdline(ctx, &gchannels_changed,
2193 cmdline_channels, ARRAY_SIZE(cmdline_channels));
2194
2195 echannels.cmd = ETHTOOL_GCHANNELS;
2196 err = send_ioctl(ctx, &echannels);
2197 if (err) {
2198 perror("Cannot get device channel parameters");
2199 return 1;
2200 }
2201
2202 do_generic_set(cmdline_channels, ARRAY_SIZE(cmdline_channels),
2203 &changed);
2204
2205 if (!changed) {
2206 fprintf(stderr, "no channel parameters changed.\n");
2207 fprintf(stderr, "current values: rx %u tx %u other %u"
2208 " combined %u\n", echannels.rx_count,
2209 echannels.tx_count, echannels.other_count,
2210 echannels.combined_count);
2211 return 0;
2212 }
2213
2214 echannels.cmd = ETHTOOL_SCHANNELS;
2215 err = send_ioctl(ctx, &echannels);
2216 if (err) {
2217 perror("Cannot set device channel parameters");
2218 return 1;
2219 }
2220
2221 return 0;
2222 }
2223
do_gchannels(struct cmd_context * ctx)2224 static int do_gchannels(struct cmd_context *ctx)
2225 {
2226 struct ethtool_channels echannels;
2227 int err;
2228
2229 if (ctx->argc != 0)
2230 exit_bad_args();
2231
2232 fprintf(stdout, "Channel parameters for %s:\n", ctx->devname);
2233
2234 echannels.cmd = ETHTOOL_GCHANNELS;
2235 err = send_ioctl(ctx, &echannels);
2236 if (err == 0) {
2237 err = dump_channels(&echannels);
2238 if (err)
2239 return err;
2240 } else {
2241 perror("Cannot get device channel parameters");
2242 return 1;
2243 }
2244 return 0;
2245
2246 }
2247
do_gcoalesce(struct cmd_context * ctx)2248 static int do_gcoalesce(struct cmd_context *ctx)
2249 {
2250 struct ethtool_coalesce ecoal = {};
2251 int err;
2252
2253 if (ctx->argc != 0)
2254 exit_bad_args();
2255
2256 fprintf(stdout, "Coalesce parameters for %s:\n", ctx->devname);
2257
2258 ecoal.cmd = ETHTOOL_GCOALESCE;
2259 err = send_ioctl(ctx, &ecoal);
2260 if (err == 0) {
2261 err = dump_coalesce(&ecoal);
2262 if (err)
2263 return err;
2264 } else {
2265 perror("Cannot get device coalesce settings");
2266 return 82;
2267 }
2268
2269 return 0;
2270 }
2271
2272 #define DECLARE_COALESCE_OPTION_VARS() \
2273 s32 coal_stats_wanted = -1; \
2274 int coal_adaptive_rx_wanted = -1; \
2275 int coal_adaptive_tx_wanted = -1; \
2276 s32 coal_sample_rate_wanted = -1; \
2277 s32 coal_pkt_rate_low_wanted = -1; \
2278 s32 coal_pkt_rate_high_wanted = -1; \
2279 s32 coal_rx_usec_wanted = -1; \
2280 s32 coal_rx_frames_wanted = -1; \
2281 s32 coal_rx_usec_irq_wanted = -1; \
2282 s32 coal_rx_frames_irq_wanted = -1; \
2283 s32 coal_tx_usec_wanted = -1; \
2284 s32 coal_tx_frames_wanted = -1; \
2285 s32 coal_tx_usec_irq_wanted = -1; \
2286 s32 coal_tx_frames_irq_wanted = -1; \
2287 s32 coal_rx_usec_low_wanted = -1; \
2288 s32 coal_rx_frames_low_wanted = -1; \
2289 s32 coal_tx_usec_low_wanted = -1; \
2290 s32 coal_tx_frames_low_wanted = -1; \
2291 s32 coal_rx_usec_high_wanted = -1; \
2292 s32 coal_rx_frames_high_wanted = -1; \
2293 s32 coal_tx_usec_high_wanted = -1; \
2294 s32 coal_tx_frames_high_wanted = -1
2295
2296 #define COALESCE_CMDLINE_INFO(__ecoal) \
2297 { \
2298 { \
2299 .name = "adaptive-rx", \
2300 .type = CMDL_BOOL, \
2301 .wanted_val = &coal_adaptive_rx_wanted, \
2302 .ioctl_val = &__ecoal.use_adaptive_rx_coalesce, \
2303 }, \
2304 { \
2305 .name = "adaptive-tx", \
2306 .type = CMDL_BOOL, \
2307 .wanted_val = &coal_adaptive_tx_wanted, \
2308 .ioctl_val = &__ecoal.use_adaptive_tx_coalesce, \
2309 }, \
2310 { \
2311 .name = "sample-interval", \
2312 .type = CMDL_S32, \
2313 .wanted_val = &coal_sample_rate_wanted, \
2314 .ioctl_val = &__ecoal.rate_sample_interval, \
2315 }, \
2316 { \
2317 .name = "stats-block-usecs", \
2318 .type = CMDL_S32, \
2319 .wanted_val = &coal_stats_wanted, \
2320 .ioctl_val = &__ecoal.stats_block_coalesce_usecs, \
2321 }, \
2322 { \
2323 .name = "pkt-rate-low", \
2324 .type = CMDL_S32, \
2325 .wanted_val = &coal_pkt_rate_low_wanted, \
2326 .ioctl_val = &__ecoal.pkt_rate_low, \
2327 }, \
2328 { \
2329 .name = "pkt-rate-high", \
2330 .type = CMDL_S32, \
2331 .wanted_val = &coal_pkt_rate_high_wanted, \
2332 .ioctl_val = &__ecoal.pkt_rate_high, \
2333 }, \
2334 { \
2335 .name = "rx-usecs", \
2336 .type = CMDL_S32, \
2337 .wanted_val = &coal_rx_usec_wanted, \
2338 .ioctl_val = &__ecoal.rx_coalesce_usecs, \
2339 }, \
2340 { \
2341 .name = "rx-frames", \
2342 .type = CMDL_S32, \
2343 .wanted_val = &coal_rx_frames_wanted, \
2344 .ioctl_val = &__ecoal.rx_max_coalesced_frames, \
2345 }, \
2346 { \
2347 .name = "rx-usecs-irq", \
2348 .type = CMDL_S32, \
2349 .wanted_val = &coal_rx_usec_irq_wanted, \
2350 .ioctl_val = &__ecoal.rx_coalesce_usecs_irq, \
2351 }, \
2352 { \
2353 .name = "rx-frames-irq", \
2354 .type = CMDL_S32, \
2355 .wanted_val = &coal_rx_frames_irq_wanted, \
2356 .ioctl_val = &__ecoal.rx_max_coalesced_frames_irq, \
2357 }, \
2358 { \
2359 .name = "tx-usecs", \
2360 .type = CMDL_S32, \
2361 .wanted_val = &coal_tx_usec_wanted, \
2362 .ioctl_val = &__ecoal.tx_coalesce_usecs, \
2363 }, \
2364 { \
2365 .name = "tx-frames", \
2366 .type = CMDL_S32, \
2367 .wanted_val = &coal_tx_frames_wanted, \
2368 .ioctl_val = &__ecoal.tx_max_coalesced_frames, \
2369 }, \
2370 { \
2371 .name = "tx-usecs-irq", \
2372 .type = CMDL_S32, \
2373 .wanted_val = &coal_tx_usec_irq_wanted, \
2374 .ioctl_val = &__ecoal.tx_coalesce_usecs_irq, \
2375 }, \
2376 { \
2377 .name = "tx-frames-irq", \
2378 .type = CMDL_S32, \
2379 .wanted_val = &coal_tx_frames_irq_wanted, \
2380 .ioctl_val = &__ecoal.tx_max_coalesced_frames_irq, \
2381 }, \
2382 { \
2383 .name = "rx-usecs-low", \
2384 .type = CMDL_S32, \
2385 .wanted_val = &coal_rx_usec_low_wanted, \
2386 .ioctl_val = &__ecoal.rx_coalesce_usecs_low, \
2387 }, \
2388 { \
2389 .name = "rx-frames-low", \
2390 .type = CMDL_S32, \
2391 .wanted_val = &coal_rx_frames_low_wanted, \
2392 .ioctl_val = &__ecoal.rx_max_coalesced_frames_low, \
2393 }, \
2394 { \
2395 .name = "tx-usecs-low", \
2396 .type = CMDL_S32, \
2397 .wanted_val = &coal_tx_usec_low_wanted, \
2398 .ioctl_val = &__ecoal.tx_coalesce_usecs_low, \
2399 }, \
2400 { \
2401 .name = "tx-frames-low", \
2402 .type = CMDL_S32, \
2403 .wanted_val = &coal_tx_frames_low_wanted, \
2404 .ioctl_val = &__ecoal.tx_max_coalesced_frames_low, \
2405 }, \
2406 { \
2407 .name = "rx-usecs-high", \
2408 .type = CMDL_S32, \
2409 .wanted_val = &coal_rx_usec_high_wanted, \
2410 .ioctl_val = &__ecoal.rx_coalesce_usecs_high, \
2411 }, \
2412 { \
2413 .name = "rx-frames-high", \
2414 .type = CMDL_S32, \
2415 .wanted_val = &coal_rx_frames_high_wanted, \
2416 .ioctl_val = &__ecoal.rx_max_coalesced_frames_high,\
2417 }, \
2418 { \
2419 .name = "tx-usecs-high", \
2420 .type = CMDL_S32, \
2421 .wanted_val = &coal_tx_usec_high_wanted, \
2422 .ioctl_val = &__ecoal.tx_coalesce_usecs_high, \
2423 }, \
2424 { \
2425 .name = "tx-frames-high", \
2426 .type = CMDL_S32, \
2427 .wanted_val = &coal_tx_frames_high_wanted, \
2428 .ioctl_val = &__ecoal.tx_max_coalesced_frames_high,\
2429 }, \
2430 }
2431
do_scoalesce(struct cmd_context * ctx)2432 static int do_scoalesce(struct cmd_context *ctx)
2433 {
2434 struct ethtool_coalesce ecoal;
2435 int gcoalesce_changed = 0;
2436 DECLARE_COALESCE_OPTION_VARS();
2437 struct cmdline_info cmdline_coalesce[] = COALESCE_CMDLINE_INFO(ecoal);
2438 int err, changed = 0;
2439
2440 parse_generic_cmdline(ctx, &gcoalesce_changed,
2441 cmdline_coalesce, ARRAY_SIZE(cmdline_coalesce));
2442
2443 ecoal.cmd = ETHTOOL_GCOALESCE;
2444 err = send_ioctl(ctx, &ecoal);
2445 if (err) {
2446 perror("Cannot get device coalesce settings");
2447 return 76;
2448 }
2449
2450 do_generic_set(cmdline_coalesce, ARRAY_SIZE(cmdline_coalesce),
2451 &changed);
2452
2453 if (!changed) {
2454 fprintf(stderr, "no coalesce parameters changed, aborting\n");
2455 return 80;
2456 }
2457
2458 ecoal.cmd = ETHTOOL_SCOALESCE;
2459 err = send_ioctl(ctx, &ecoal);
2460 if (err) {
2461 perror("Cannot set device coalesce parameters");
2462 return 81;
2463 }
2464
2465 return 0;
2466 }
2467
2468 static struct feature_state *
get_features(struct cmd_context * ctx,const struct feature_defs * defs)2469 get_features(struct cmd_context *ctx, const struct feature_defs *defs)
2470 {
2471 struct feature_state *state;
2472 struct ethtool_value eval;
2473 int err, allfail = 1;
2474 u32 value;
2475 int i;
2476
2477 state = malloc(sizeof(*state) +
2478 FEATURE_BITS_TO_BLOCKS(defs->n_features) *
2479 sizeof(state->features.features[0]));
2480 if (!state)
2481 return NULL;
2482
2483 state->off_flags = 0;
2484
2485 for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
2486 value = off_flag_def[i].value;
2487 if (!off_flag_def[i].get_cmd)
2488 continue;
2489 eval.cmd = off_flag_def[i].get_cmd;
2490 err = send_ioctl(ctx, &eval);
2491 if (err) {
2492 if (errno == EOPNOTSUPP &&
2493 off_flag_def[i].get_cmd == ETHTOOL_GUFO)
2494 continue;
2495
2496 fprintf(stderr,
2497 "Cannot get device %s settings: %m\n",
2498 off_flag_def[i].long_name);
2499 } else {
2500 if (eval.data)
2501 state->off_flags |= value;
2502 allfail = 0;
2503 }
2504 }
2505
2506 eval.cmd = ETHTOOL_GFLAGS;
2507 err = send_ioctl(ctx, &eval);
2508 if (err) {
2509 perror("Cannot get device flags");
2510 } else {
2511 state->off_flags |= eval.data & ETH_FLAG_EXT_MASK;
2512 allfail = 0;
2513 }
2514
2515 if (defs->n_features) {
2516 state->features.cmd = ETHTOOL_GFEATURES;
2517 state->features.size = FEATURE_BITS_TO_BLOCKS(defs->n_features);
2518 err = send_ioctl(ctx, &state->features);
2519 if (err)
2520 perror("Cannot get device generic features");
2521 else
2522 allfail = 0;
2523 }
2524
2525 if (allfail) {
2526 free(state);
2527 return NULL;
2528 }
2529
2530 return state;
2531 }
2532
do_gfeatures(struct cmd_context * ctx)2533 static int do_gfeatures(struct cmd_context *ctx)
2534 {
2535 struct feature_defs *defs;
2536 struct feature_state *features;
2537
2538 if (ctx->argc != 0)
2539 exit_bad_args();
2540
2541 defs = get_feature_defs(ctx);
2542 if (!defs) {
2543 perror("Cannot get device feature names");
2544 return 1;
2545 }
2546
2547 fprintf(stdout, "Features for %s:\n", ctx->devname);
2548
2549 features = get_features(ctx, defs);
2550 if (!features) {
2551 fprintf(stdout, "no feature info available\n");
2552 free(defs);
2553 return 1;
2554 }
2555
2556 dump_features(defs, features, NULL);
2557 free(features);
2558 free(defs);
2559 return 0;
2560 }
2561
do_sfeatures(struct cmd_context * ctx)2562 static int do_sfeatures(struct cmd_context *ctx)
2563 {
2564 struct feature_defs *defs;
2565 int any_changed = 0, any_mismatch = 0;
2566 u32 off_flags_wanted = 0;
2567 u32 off_flags_mask = 0;
2568 struct ethtool_sfeatures *efeatures = NULL;
2569 struct feature_state *old_state = NULL;
2570 struct feature_state *new_state = NULL;
2571 struct cmdline_info *cmdline_features;
2572 struct ethtool_value eval;
2573 unsigned int i, j;
2574 int err, rc;
2575
2576 defs = get_feature_defs(ctx);
2577 if (!defs) {
2578 perror("Cannot get device feature names");
2579 return 1;
2580 }
2581 if (defs->n_features) {
2582 efeatures = malloc(sizeof(*efeatures) +
2583 FEATURE_BITS_TO_BLOCKS(defs->n_features) *
2584 sizeof(efeatures->features[0]));
2585 if (!efeatures) {
2586 perror("Cannot parse arguments");
2587 rc = 1;
2588 goto err;
2589 }
2590 efeatures->cmd = ETHTOOL_SFEATURES;
2591 efeatures->size = FEATURE_BITS_TO_BLOCKS(defs->n_features);
2592 memset(efeatures->features, 0,
2593 FEATURE_BITS_TO_BLOCKS(defs->n_features) *
2594 sizeof(efeatures->features[0]));
2595 }
2596
2597 /* Generate cmdline_info for legacy flags and kernel-named
2598 * features, and parse our arguments.
2599 */
2600 cmdline_features = calloc(2 * OFF_FLAG_DEF_SIZE + defs->n_features,
2601 sizeof(cmdline_features[0]));
2602 if (!cmdline_features) {
2603 perror("Cannot parse arguments");
2604 rc = 1;
2605 goto err;
2606 }
2607 j = 0;
2608 for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
2609 flag_to_cmdline_info(off_flag_def[i].short_name,
2610 off_flag_def[i].value,
2611 &off_flags_wanted, &off_flags_mask,
2612 &cmdline_features[j++]);
2613 flag_to_cmdline_info(off_flag_def[i].long_name,
2614 off_flag_def[i].value,
2615 &off_flags_wanted, &off_flags_mask,
2616 &cmdline_features[j++]);
2617 }
2618 for (i = 0; i < defs->n_features; i++)
2619 flag_to_cmdline_info(
2620 defs->def[i].name, FEATURE_FIELD_FLAG(i),
2621 &FEATURE_WORD(efeatures->features, i, requested),
2622 &FEATURE_WORD(efeatures->features, i, valid),
2623 &cmdline_features[j++]);
2624 parse_generic_cmdline(ctx, &any_changed, cmdline_features,
2625 2 * OFF_FLAG_DEF_SIZE + defs->n_features);
2626 free(cmdline_features);
2627
2628 if (!any_changed) {
2629 fprintf(stdout, "no features changed\n");
2630 rc = 0;
2631 goto err;
2632 }
2633
2634 old_state = get_features(ctx, defs);
2635 if (!old_state) {
2636 rc = 1;
2637 goto err;
2638 }
2639
2640 if (efeatures) {
2641 /* For each offload that the user specified, update any
2642 * related features that the user did not specify and that
2643 * are not fixed. Warn if all related features are fixed.
2644 */
2645 for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
2646 int fixed = 1;
2647
2648 if (!(off_flags_mask & off_flag_def[i].value))
2649 continue;
2650
2651 for (j = 0; j < defs->n_features; j++) {
2652 if (defs->def[j].off_flag_index != (int)i ||
2653 !FEATURE_BIT_IS_SET(
2654 old_state->features.features,
2655 j, available) ||
2656 FEATURE_BIT_IS_SET(
2657 old_state->features.features,
2658 j, never_changed))
2659 continue;
2660
2661 fixed = 0;
2662 if (!FEATURE_BIT_IS_SET(efeatures->features,
2663 j, valid)) {
2664 FEATURE_BIT_SET(efeatures->features,
2665 j, valid);
2666 if (off_flags_wanted &
2667 off_flag_def[i].value)
2668 FEATURE_BIT_SET(
2669 efeatures->features,
2670 j, requested);
2671 }
2672 }
2673
2674 if (fixed)
2675 fprintf(stderr, "Cannot change %s\n",
2676 off_flag_def[i].long_name);
2677 }
2678
2679 err = send_ioctl(ctx, efeatures);
2680 if (err < 0) {
2681 perror("Cannot set device feature settings");
2682 rc = 1;
2683 goto err;
2684 }
2685 } else {
2686 for (i = 0; i < OFF_FLAG_DEF_SIZE; i++) {
2687 if (!off_flag_def[i].set_cmd)
2688 continue;
2689 if (off_flags_mask & off_flag_def[i].value) {
2690 eval.cmd = off_flag_def[i].set_cmd;
2691 eval.data = !!(off_flags_wanted &
2692 off_flag_def[i].value);
2693 err = send_ioctl(ctx, &eval);
2694 if (err) {
2695 fprintf(stderr,
2696 "Cannot set device %s settings: %m\n",
2697 off_flag_def[i].long_name);
2698 rc = 1;
2699 goto err;
2700 }
2701 }
2702 }
2703
2704 if (off_flags_mask & ETH_FLAG_EXT_MASK) {
2705 eval.cmd = ETHTOOL_SFLAGS;
2706 eval.data = (old_state->off_flags & ~off_flags_mask &
2707 ETH_FLAG_EXT_MASK);
2708 eval.data |= off_flags_wanted & ETH_FLAG_EXT_MASK;
2709
2710 err = send_ioctl(ctx, &eval);
2711 if (err) {
2712 perror("Cannot set device flag settings");
2713 rc = 92;
2714 goto err;
2715 }
2716 }
2717 }
2718
2719 /* Compare new state with requested state */
2720 new_state = get_features(ctx, defs);
2721 if (!new_state) {
2722 rc = 1;
2723 goto err;
2724 }
2725 any_changed = new_state->off_flags != old_state->off_flags;
2726 any_mismatch = (new_state->off_flags !=
2727 ((old_state->off_flags & ~off_flags_mask) |
2728 off_flags_wanted));
2729 for (i = 0; i < FEATURE_BITS_TO_BLOCKS(defs->n_features); i++) {
2730 if (new_state->features.features[i].active !=
2731 old_state->features.features[i].active)
2732 any_changed = 1;
2733 if (new_state->features.features[i].active !=
2734 ((old_state->features.features[i].active &
2735 ~efeatures->features[i].valid) |
2736 efeatures->features[i].requested))
2737 any_mismatch = 1;
2738 }
2739 if (any_mismatch) {
2740 if (!any_changed) {
2741 fprintf(stderr,
2742 "Could not change any device features\n");
2743 rc = 1;
2744 goto err;
2745 }
2746 printf("Actual changes:\n");
2747 dump_features(defs, new_state, old_state);
2748 }
2749
2750 rc = 0;
2751
2752 err:
2753 free(new_state);
2754 free(old_state);
2755 free(defs);
2756 free(efeatures);
2757
2758 return rc;
2759 }
2760
2761 static struct ethtool_link_usettings *
do_ioctl_glinksettings(struct cmd_context * ctx)2762 do_ioctl_glinksettings(struct cmd_context *ctx)
2763 {
2764 int err;
2765 struct {
2766 struct ethtool_link_settings req;
2767 __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
2768 } ecmd;
2769 struct ethtool_link_usettings *link_usettings;
2770 unsigned int u32_offs;
2771
2772 /* Handshake with kernel to determine number of words for link
2773 * mode bitmaps. When requested number of bitmap words is not
2774 * the one expected by kernel, the latter returns the integer
2775 * opposite of what it is expecting. We request length 0 below
2776 * (aka. invalid bitmap length) to get this info.
2777 */
2778 memset(&ecmd, 0, sizeof(ecmd));
2779 ecmd.req.cmd = ETHTOOL_GLINKSETTINGS;
2780 err = send_ioctl(ctx, &ecmd);
2781 if (err < 0)
2782 return NULL;
2783
2784 /* see above: we expect a strictly negative value from kernel.
2785 */
2786 if (ecmd.req.link_mode_masks_nwords >= 0
2787 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
2788 return NULL;
2789
2790 /* got the real ecmd.req.link_mode_masks_nwords,
2791 * now send the real request
2792 */
2793 ecmd.req.cmd = ETHTOOL_GLINKSETTINGS;
2794 ecmd.req.link_mode_masks_nwords = -ecmd.req.link_mode_masks_nwords;
2795 err = send_ioctl(ctx, &ecmd);
2796 if (err < 0)
2797 return NULL;
2798
2799 if (ecmd.req.link_mode_masks_nwords <= 0
2800 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
2801 return NULL;
2802
2803 /* Convert to usettings struct */
2804 link_usettings = calloc(1, sizeof(*link_usettings));
2805 if (link_usettings == NULL)
2806 return NULL;
2807
2808 memcpy(&link_usettings->base, &ecmd.req, sizeof(link_usettings->base));
2809 link_usettings->deprecated.transceiver = ecmd.req.transceiver;
2810
2811 /* copy link mode bitmaps */
2812 u32_offs = 0;
2813 memcpy(link_usettings->link_modes.supported,
2814 &ecmd.link_mode_data[u32_offs],
2815 4 * ecmd.req.link_mode_masks_nwords);
2816
2817 u32_offs += ecmd.req.link_mode_masks_nwords;
2818 memcpy(link_usettings->link_modes.advertising,
2819 &ecmd.link_mode_data[u32_offs],
2820 4 * ecmd.req.link_mode_masks_nwords);
2821
2822 u32_offs += ecmd.req.link_mode_masks_nwords;
2823 memcpy(link_usettings->link_modes.lp_advertising,
2824 &ecmd.link_mode_data[u32_offs],
2825 4 * ecmd.req.link_mode_masks_nwords);
2826
2827 return link_usettings;
2828 }
2829
2830 static int
do_ioctl_slinksettings(struct cmd_context * ctx,const struct ethtool_link_usettings * link_usettings)2831 do_ioctl_slinksettings(struct cmd_context *ctx,
2832 const struct ethtool_link_usettings *link_usettings)
2833 {
2834 struct {
2835 struct ethtool_link_settings req;
2836 __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
2837 } ecmd;
2838 unsigned int u32_offs;
2839
2840 /* refuse to send ETHTOOL_SLINKSETTINGS ioctl if
2841 * link_usettings was retrieved with ETHTOOL_GSET
2842 */
2843 if (link_usettings->base.cmd != ETHTOOL_GLINKSETTINGS)
2844 return -1;
2845
2846 /* refuse to send ETHTOOL_SLINKSETTINGS ioctl if deprecated fields
2847 * were set
2848 */
2849 if (link_usettings->deprecated.transceiver)
2850 return -1;
2851
2852 if (link_usettings->base.link_mode_masks_nwords <= 0)
2853 return -1;
2854
2855 memcpy(&ecmd.req, &link_usettings->base, sizeof(ecmd.req));
2856 ecmd.req.cmd = ETHTOOL_SLINKSETTINGS;
2857
2858 /* copy link mode bitmaps */
2859 u32_offs = 0;
2860 memcpy(&ecmd.link_mode_data[u32_offs],
2861 link_usettings->link_modes.supported,
2862 4 * ecmd.req.link_mode_masks_nwords);
2863
2864 u32_offs += ecmd.req.link_mode_masks_nwords;
2865 memcpy(&ecmd.link_mode_data[u32_offs],
2866 link_usettings->link_modes.advertising,
2867 4 * ecmd.req.link_mode_masks_nwords);
2868
2869 u32_offs += ecmd.req.link_mode_masks_nwords;
2870 memcpy(&ecmd.link_mode_data[u32_offs],
2871 link_usettings->link_modes.lp_advertising,
2872 4 * ecmd.req.link_mode_masks_nwords);
2873
2874 return send_ioctl(ctx, &ecmd);
2875 }
2876
2877 static struct ethtool_link_usettings *
do_ioctl_gset(struct cmd_context * ctx)2878 do_ioctl_gset(struct cmd_context *ctx)
2879 {
2880 int err;
2881 struct ethtool_cmd ecmd;
2882 struct ethtool_link_usettings *link_usettings;
2883
2884 memset(&ecmd, 0, sizeof(ecmd));
2885 ecmd.cmd = ETHTOOL_GSET;
2886 err = send_ioctl(ctx, &ecmd);
2887 if (err < 0)
2888 return NULL;
2889
2890 link_usettings = calloc(1, sizeof(*link_usettings));
2891 if (link_usettings == NULL)
2892 return NULL;
2893
2894 /* remember that ETHTOOL_GSET was used */
2895 link_usettings->base.cmd = ETHTOOL_GSET;
2896
2897 link_usettings->base.link_mode_masks_nwords = 1;
2898 link_usettings->link_modes.supported[0] = ecmd.supported;
2899 link_usettings->link_modes.advertising[0] = ecmd.advertising;
2900 link_usettings->link_modes.lp_advertising[0] = ecmd.lp_advertising;
2901 link_usettings->base.speed = ethtool_cmd_speed(&ecmd);
2902 link_usettings->base.duplex = ecmd.duplex;
2903 link_usettings->base.port = ecmd.port;
2904 link_usettings->base.phy_address = ecmd.phy_address;
2905 link_usettings->deprecated.transceiver = ecmd.transceiver;
2906 link_usettings->base.autoneg = ecmd.autoneg;
2907 link_usettings->base.mdio_support = ecmd.mdio_support;
2908 /* ignored (fully deprecated): maxrxpkt, maxtxpkt */
2909 link_usettings->base.eth_tp_mdix = ecmd.eth_tp_mdix;
2910 link_usettings->base.eth_tp_mdix_ctrl = ecmd.eth_tp_mdix_ctrl;
2911
2912 return link_usettings;
2913 }
2914
ethtool_link_mode_is_backward_compatible(const u32 * mask)2915 static bool ethtool_link_mode_is_backward_compatible(const u32 *mask)
2916 {
2917 unsigned int i;
2918
2919 for (i = 1; i < ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32; ++i)
2920 if (mask[i])
2921 return false;
2922
2923 return true;
2924 }
2925
2926 static int
do_ioctl_sset(struct cmd_context * ctx,const struct ethtool_link_usettings * link_usettings)2927 do_ioctl_sset(struct cmd_context *ctx,
2928 const struct ethtool_link_usettings *link_usettings)
2929 {
2930 struct ethtool_cmd ecmd;
2931
2932 /* refuse to send ETHTOOL_SSET ioctl if link_usettings was
2933 * retrieved with ETHTOOL_GLINKSETTINGS
2934 */
2935 if (link_usettings->base.cmd != ETHTOOL_GSET)
2936 return -1;
2937
2938 if (link_usettings->base.link_mode_masks_nwords <= 0)
2939 return -1;
2940
2941 /* refuse to sset if any bit > 31 is set */
2942 if (!ethtool_link_mode_is_backward_compatible(
2943 link_usettings->link_modes.supported))
2944 return -1;
2945 if (!ethtool_link_mode_is_backward_compatible(
2946 link_usettings->link_modes.advertising))
2947 return -1;
2948 if (!ethtool_link_mode_is_backward_compatible(
2949 link_usettings->link_modes.lp_advertising))
2950 return -1;
2951
2952 memset(&ecmd, 0, sizeof(ecmd));
2953 ecmd.cmd = ETHTOOL_SSET;
2954
2955 ecmd.supported = link_usettings->link_modes.supported[0];
2956 ecmd.advertising = link_usettings->link_modes.advertising[0];
2957 ecmd.lp_advertising = link_usettings->link_modes.lp_advertising[0];
2958 ethtool_cmd_speed_set(&ecmd, link_usettings->base.speed);
2959 ecmd.duplex = link_usettings->base.duplex;
2960 ecmd.port = link_usettings->base.port;
2961 ecmd.phy_address = link_usettings->base.phy_address;
2962 ecmd.transceiver = link_usettings->deprecated.transceiver;
2963 ecmd.autoneg = link_usettings->base.autoneg;
2964 ecmd.mdio_support = link_usettings->base.mdio_support;
2965 /* ignored (fully deprecated): maxrxpkt, maxtxpkt */
2966 ecmd.eth_tp_mdix = link_usettings->base.eth_tp_mdix;
2967 ecmd.eth_tp_mdix_ctrl = link_usettings->base.eth_tp_mdix_ctrl;
2968 return send_ioctl(ctx, &ecmd);
2969 }
2970
do_gset(struct cmd_context * ctx)2971 static int do_gset(struct cmd_context *ctx)
2972 {
2973 int err;
2974 struct ethtool_link_usettings *link_usettings;
2975 struct ethtool_wolinfo wolinfo;
2976 struct ethtool_value edata;
2977 int allfail = 1;
2978
2979 if (ctx->argc != 0)
2980 exit_bad_args();
2981
2982 fprintf(stdout, "Settings for %s:\n", ctx->devname);
2983
2984 link_usettings = do_ioctl_glinksettings(ctx);
2985 if (link_usettings == NULL)
2986 link_usettings = do_ioctl_gset(ctx);
2987 if (link_usettings != NULL) {
2988 err = dump_link_usettings(link_usettings);
2989 free(link_usettings);
2990 if (err)
2991 return err;
2992 allfail = 0;
2993 } else if (errno != EOPNOTSUPP) {
2994 perror("Cannot get device settings");
2995 }
2996
2997 wolinfo.cmd = ETHTOOL_GWOL;
2998 err = send_ioctl(ctx, &wolinfo);
2999 if (err == 0) {
3000 err = dump_wol(&wolinfo);
3001 if (err)
3002 return err;
3003 allfail = 0;
3004 } else if (errno != EOPNOTSUPP) {
3005 perror("Cannot get wake-on-lan settings");
3006 }
3007
3008 edata.cmd = ETHTOOL_GMSGLVL;
3009 err = send_ioctl(ctx, &edata);
3010 if (err == 0) {
3011 fprintf(stdout, " Current message level: 0x%08x (%d)\n"
3012 " ",
3013 edata.data, edata.data);
3014 print_flags(flags_msglvl, n_flags_msglvl, edata.data);
3015 fprintf(stdout, "\n");
3016 allfail = 0;
3017 } else if (errno != EOPNOTSUPP) {
3018 perror("Cannot get message level");
3019 }
3020
3021 edata.cmd = ETHTOOL_GLINK;
3022 err = send_ioctl(ctx, &edata);
3023 if (err == 0) {
3024 fprintf(stdout, " Link detected: %s\n",
3025 edata.data ? "yes":"no");
3026 allfail = 0;
3027 } else if (errno != EOPNOTSUPP) {
3028 perror("Cannot get link status");
3029 }
3030
3031 if (allfail) {
3032 fprintf(stdout, "No data available\n");
3033 return 75;
3034 }
3035 return 0;
3036 }
3037
do_sset(struct cmd_context * ctx)3038 static int do_sset(struct cmd_context *ctx)
3039 {
3040 int speed_wanted = -1;
3041 int duplex_wanted = -1;
3042 int port_wanted = -1;
3043 int mdix_wanted = -1;
3044 int autoneg_wanted = -1;
3045 int phyad_wanted = -1;
3046 int xcvr_wanted = -1;
3047 u32 *full_advertising_wanted = NULL;
3048 u32 *advertising_wanted = NULL;
3049 ETHTOOL_DECLARE_LINK_MODE_MASK(mask_full_advertising_wanted);
3050 ETHTOOL_DECLARE_LINK_MODE_MASK(mask_advertising_wanted);
3051 int gset_changed = 0; /* did anything in GSET change? */
3052 u32 wol_wanted = 0;
3053 int wol_change = 0;
3054 u8 sopass_wanted[SOPASS_MAX];
3055 int sopass_change = 0;
3056 int gwol_changed = 0; /* did anything in GWOL change? */
3057 int msglvl_changed = 0;
3058 u32 msglvl_wanted = 0;
3059 u32 msglvl_mask = 0;
3060 struct cmdline_info cmdline_msglvl[n_flags_msglvl];
3061 unsigned int argc = ctx->argc;
3062 char **argp = ctx->argp;
3063 unsigned int i;
3064 int err = 0;
3065
3066 for (i = 0; i < n_flags_msglvl; i++)
3067 flag_to_cmdline_info(flags_msglvl[i].name,
3068 flags_msglvl[i].value,
3069 &msglvl_wanted, &msglvl_mask,
3070 &cmdline_msglvl[i]);
3071
3072 for (i = 0; i < argc; i++) {
3073 if (!strcmp(argp[i], "speed")) {
3074 gset_changed = 1;
3075 i += 1;
3076 if (i >= argc)
3077 exit_bad_args();
3078 speed_wanted = get_int(argp[i], 10);
3079 } else if (!strcmp(argp[i], "duplex")) {
3080 gset_changed = 1;
3081 i += 1;
3082 if (i >= argc)
3083 exit_bad_args();
3084 if (!strcmp(argp[i], "half"))
3085 duplex_wanted = DUPLEX_HALF;
3086 else if (!strcmp(argp[i], "full"))
3087 duplex_wanted = DUPLEX_FULL;
3088 else
3089 exit_bad_args();
3090 } else if (!strcmp(argp[i], "port")) {
3091 gset_changed = 1;
3092 i += 1;
3093 if (i >= argc)
3094 exit_bad_args();
3095 if (!strcmp(argp[i], "tp"))
3096 port_wanted = PORT_TP;
3097 else if (!strcmp(argp[i], "aui"))
3098 port_wanted = PORT_AUI;
3099 else if (!strcmp(argp[i], "bnc"))
3100 port_wanted = PORT_BNC;
3101 else if (!strcmp(argp[i], "mii"))
3102 port_wanted = PORT_MII;
3103 else if (!strcmp(argp[i], "fibre"))
3104 port_wanted = PORT_FIBRE;
3105 else
3106 exit_bad_args();
3107 } else if (!strcmp(argp[i], "mdix")) {
3108 gset_changed = 1;
3109 i += 1;
3110 if (i >= argc)
3111 exit_bad_args();
3112 if (!strcmp(argp[i], "auto"))
3113 mdix_wanted = ETH_TP_MDI_AUTO;
3114 else if (!strcmp(argp[i], "on"))
3115 mdix_wanted = ETH_TP_MDI_X;
3116 else if (!strcmp(argp[i], "off"))
3117 mdix_wanted = ETH_TP_MDI;
3118 else
3119 exit_bad_args();
3120 } else if (!strcmp(argp[i], "autoneg")) {
3121 i += 1;
3122 if (i >= argc)
3123 exit_bad_args();
3124 if (!strcmp(argp[i], "on")) {
3125 gset_changed = 1;
3126 autoneg_wanted = AUTONEG_ENABLE;
3127 } else if (!strcmp(argp[i], "off")) {
3128 gset_changed = 1;
3129 autoneg_wanted = AUTONEG_DISABLE;
3130 } else {
3131 exit_bad_args();
3132 }
3133 } else if (!strcmp(argp[i], "advertise")) {
3134 gset_changed = 1;
3135 i += 1;
3136 if (i >= argc)
3137 exit_bad_args();
3138 if (parse_hex_u32_bitmap(
3139 argp[i],
3140 ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBITS,
3141 mask_full_advertising_wanted))
3142 exit_bad_args();
3143 full_advertising_wanted = mask_full_advertising_wanted;
3144 } else if (!strcmp(argp[i], "phyad")) {
3145 gset_changed = 1;
3146 i += 1;
3147 if (i >= argc)
3148 exit_bad_args();
3149 phyad_wanted = get_int(argp[i], 0);
3150 } else if (!strcmp(argp[i], "xcvr")) {
3151 gset_changed = 1;
3152 i += 1;
3153 if (i >= argc)
3154 exit_bad_args();
3155 if (!strcmp(argp[i], "internal"))
3156 xcvr_wanted = XCVR_INTERNAL;
3157 else if (!strcmp(argp[i], "external"))
3158 xcvr_wanted = XCVR_EXTERNAL;
3159 else
3160 exit_bad_args();
3161 } else if (!strcmp(argp[i], "wol")) {
3162 gwol_changed = 1;
3163 i++;
3164 if (i >= argc)
3165 exit_bad_args();
3166 if (parse_wolopts(argp[i], &wol_wanted) < 0)
3167 exit_bad_args();
3168 wol_change = 1;
3169 } else if (!strcmp(argp[i], "sopass")) {
3170 gwol_changed = 1;
3171 i++;
3172 if (i >= argc)
3173 exit_bad_args();
3174 get_mac_addr(argp[i], sopass_wanted);
3175 sopass_change = 1;
3176 } else if (!strcmp(argp[i], "msglvl")) {
3177 i++;
3178 if (i >= argc)
3179 exit_bad_args();
3180 if (isdigit((unsigned char)argp[i][0])) {
3181 msglvl_changed = 1;
3182 msglvl_mask = ~0;
3183 msglvl_wanted =
3184 get_uint_range(argp[i], 0,
3185 0xffffffff);
3186 } else {
3187 ctx->argc -= i;
3188 ctx->argp += i;
3189 parse_generic_cmdline(
3190 ctx, &msglvl_changed,
3191 cmdline_msglvl,
3192 ARRAY_SIZE(cmdline_msglvl));
3193 break;
3194 }
3195 } else if (!strcmp(argp[i], "master-slave")) {
3196 exit_nlonly_param(argp[i]);
3197 } else {
3198 exit_bad_args();
3199 }
3200 }
3201
3202 if (full_advertising_wanted == NULL) {
3203 /* User didn't supply a full advertisement bitfield:
3204 * construct one from the specified speed and duplex.
3205 */
3206 int adv_bit = -1;
3207
3208 if (speed_wanted == SPEED_10 && duplex_wanted == DUPLEX_HALF)
3209 adv_bit = ETHTOOL_LINK_MODE_10baseT_Half_BIT;
3210 else if (speed_wanted == SPEED_10 &&
3211 duplex_wanted == DUPLEX_FULL)
3212 adv_bit = ETHTOOL_LINK_MODE_10baseT_Full_BIT;
3213 else if (speed_wanted == SPEED_100 &&
3214 duplex_wanted == DUPLEX_HALF)
3215 adv_bit = ETHTOOL_LINK_MODE_100baseT_Half_BIT;
3216 else if (speed_wanted == SPEED_100 &&
3217 duplex_wanted == DUPLEX_FULL)
3218 adv_bit = ETHTOOL_LINK_MODE_100baseT_Full_BIT;
3219 else if (speed_wanted == SPEED_1000 &&
3220 duplex_wanted == DUPLEX_HALF)
3221 adv_bit = ETHTOOL_LINK_MODE_1000baseT_Half_BIT;
3222 else if (speed_wanted == SPEED_1000 &&
3223 duplex_wanted == DUPLEX_FULL)
3224 adv_bit = ETHTOOL_LINK_MODE_1000baseT_Full_BIT;
3225 else if (speed_wanted == SPEED_2500 &&
3226 duplex_wanted == DUPLEX_FULL)
3227 adv_bit = ETHTOOL_LINK_MODE_2500baseX_Full_BIT;
3228 else if (speed_wanted == SPEED_10000 &&
3229 duplex_wanted == DUPLEX_FULL)
3230 adv_bit = ETHTOOL_LINK_MODE_10000baseT_Full_BIT;
3231
3232 if (adv_bit >= 0) {
3233 advertising_wanted = mask_advertising_wanted;
3234 ethtool_link_mode_zero(advertising_wanted);
3235 ethtool_link_mode_set_bit(
3236 adv_bit, advertising_wanted);
3237 }
3238 /* otherwise: auto negotiate without forcing,
3239 * all supported speed will be assigned below
3240 */
3241 }
3242
3243 if (gset_changed) {
3244 struct ethtool_link_usettings *link_usettings;
3245
3246 link_usettings = do_ioctl_glinksettings(ctx);
3247 if (link_usettings == NULL)
3248 link_usettings = do_ioctl_gset(ctx);
3249 else
3250 memset(&link_usettings->deprecated, 0,
3251 sizeof(link_usettings->deprecated));
3252 if (link_usettings == NULL) {
3253 perror("Cannot get current device settings");
3254 err = -1;
3255 } else {
3256 /* Change everything the user specified. */
3257 if (speed_wanted != -1)
3258 link_usettings->base.speed = speed_wanted;
3259 if (duplex_wanted != -1)
3260 link_usettings->base.duplex = duplex_wanted;
3261 if (port_wanted != -1)
3262 link_usettings->base.port = port_wanted;
3263 if (mdix_wanted != -1) {
3264 /* check driver supports MDI-X */
3265 if (link_usettings->base.eth_tp_mdix_ctrl
3266 != ETH_TP_MDI_INVALID)
3267 link_usettings->base.eth_tp_mdix_ctrl
3268 = mdix_wanted;
3269 else
3270 fprintf(stderr,
3271 "setting MDI not supported\n");
3272 }
3273 if (autoneg_wanted != -1)
3274 link_usettings->base.autoneg = autoneg_wanted;
3275 if (phyad_wanted != -1)
3276 link_usettings->base.phy_address = phyad_wanted;
3277 if (xcvr_wanted != -1)
3278 link_usettings->deprecated.transceiver
3279 = xcvr_wanted;
3280 /* XXX If the user specified speed or duplex
3281 * then we should mask the advertised modes
3282 * accordingly. For now, warn that we aren't
3283 * doing that.
3284 */
3285 if ((speed_wanted != -1 || duplex_wanted != -1) &&
3286 link_usettings->base.autoneg &&
3287 advertising_wanted == NULL) {
3288 fprintf(stderr, "Cannot advertise");
3289 if (speed_wanted >= 0)
3290 fprintf(stderr, " speed %d",
3291 speed_wanted);
3292 if (duplex_wanted >= 0)
3293 fprintf(stderr, " duplex %s",
3294 duplex_wanted ?
3295 "full" : "half");
3296 fprintf(stderr, "\n");
3297 }
3298 if (autoneg_wanted == AUTONEG_ENABLE &&
3299 advertising_wanted == NULL &&
3300 full_advertising_wanted == NULL) {
3301 unsigned int i;
3302
3303 /* Auto negotiation enabled, but with
3304 * unspecified speed and duplex: enable all
3305 * supported speeds and duplexes.
3306 */
3307 ethtool_link_mode_for_each_u32(i) {
3308 u32 sup = link_usettings->link_modes.supported[i];
3309 u32 *adv = link_usettings->link_modes.advertising + i;
3310
3311 *adv = ((*adv & ~all_advertised_modes[i])
3312 | (sup & all_advertised_modes[i]));
3313 }
3314
3315 /* If driver supports unknown flags, we cannot
3316 * be sure that we enable all link modes.
3317 */
3318 ethtool_link_mode_for_each_u32(i) {
3319 u32 sup = link_usettings->link_modes.supported[i];
3320
3321 if ((sup & all_advertised_flags[i]) != sup) {
3322 fprintf(stderr, "Driver supports one or more unknown flags\n");
3323 break;
3324 }
3325 }
3326 } else if (advertising_wanted != NULL) {
3327 unsigned int i;
3328
3329 /* Enable all requested modes */
3330 ethtool_link_mode_for_each_u32(i) {
3331 u32 *adv = link_usettings->link_modes.advertising + i;
3332
3333 *adv = ((*adv & ~all_advertised_modes[i])
3334 | advertising_wanted[i]);
3335 }
3336 } else if (full_advertising_wanted != NULL) {
3337 ethtool_link_mode_copy(
3338 link_usettings->link_modes.advertising,
3339 full_advertising_wanted);
3340 }
3341
3342 /* Try to perform the update. */
3343 if (link_usettings->base.cmd == ETHTOOL_GLINKSETTINGS)
3344 err = do_ioctl_slinksettings(ctx,
3345 link_usettings);
3346 else
3347 err = do_ioctl_sset(ctx, link_usettings);
3348 free(link_usettings);
3349 if (err < 0)
3350 perror("Cannot set new settings");
3351 }
3352 if (err < 0) {
3353 if (speed_wanted != -1)
3354 fprintf(stderr, " not setting speed\n");
3355 if (duplex_wanted != -1)
3356 fprintf(stderr, " not setting duplex\n");
3357 if (port_wanted != -1)
3358 fprintf(stderr, " not setting port\n");
3359 if (autoneg_wanted != -1)
3360 fprintf(stderr, " not setting autoneg\n");
3361 if (phyad_wanted != -1)
3362 fprintf(stderr, " not setting phy_address\n");
3363 if (xcvr_wanted != -1)
3364 fprintf(stderr, " not setting transceiver\n");
3365 if (mdix_wanted != -1)
3366 fprintf(stderr, " not setting mdix\n");
3367 }
3368 }
3369
3370 if (gwol_changed) {
3371 struct ethtool_wolinfo wol;
3372
3373 wol.cmd = ETHTOOL_GWOL;
3374 err = send_ioctl(ctx, &wol);
3375 if (err < 0) {
3376 perror("Cannot get current wake-on-lan settings");
3377 } else {
3378 /* Change everything the user specified. */
3379 if (wol_change)
3380 wol.wolopts = wol_wanted;
3381 if (sopass_change) {
3382 int i;
3383 for (i = 0; i < SOPASS_MAX; i++)
3384 wol.sopass[i] = sopass_wanted[i];
3385 }
3386
3387 /* Try to perform the update. */
3388 wol.cmd = ETHTOOL_SWOL;
3389 err = send_ioctl(ctx, &wol);
3390 if (err < 0)
3391 perror("Cannot set new wake-on-lan settings");
3392 }
3393 if (err < 0) {
3394 if (wol_change)
3395 fprintf(stderr, " not setting wol\n");
3396 if (sopass_change)
3397 fprintf(stderr, " not setting sopass\n");
3398 }
3399 }
3400
3401 if (msglvl_changed) {
3402 struct ethtool_value edata;
3403
3404 edata.cmd = ETHTOOL_GMSGLVL;
3405 err = send_ioctl(ctx, &edata);
3406 if (err < 0) {
3407 perror("Cannot get msglvl");
3408 } else {
3409 edata.cmd = ETHTOOL_SMSGLVL;
3410 edata.data = ((edata.data & ~msglvl_mask) |
3411 msglvl_wanted);
3412 err = send_ioctl(ctx, &edata);
3413 if (err < 0)
3414 perror("Cannot set new msglvl");
3415 }
3416 }
3417
3418 return 0;
3419 }
3420
do_gregs(struct cmd_context * ctx)3421 static int do_gregs(struct cmd_context *ctx)
3422 {
3423 int gregs_changed = 0;
3424 int gregs_dump_raw = 0;
3425 int gregs_dump_hex = 0;
3426 char *gregs_dump_file = NULL;
3427 struct cmdline_info cmdline_gregs[] = {
3428 {
3429 .name = "raw",
3430 .type = CMDL_BOOL,
3431 .wanted_val = &gregs_dump_raw,
3432 },
3433 {
3434 .name = "hex",
3435 .type = CMDL_BOOL,
3436 .wanted_val = &gregs_dump_hex,
3437 },
3438 {
3439 .name = "file",
3440 .type = CMDL_STR,
3441 .wanted_val = &gregs_dump_file,
3442 },
3443 };
3444 int err;
3445 struct ethtool_drvinfo drvinfo;
3446 struct ethtool_regs *regs;
3447
3448 parse_generic_cmdline(ctx, &gregs_changed,
3449 cmdline_gregs, ARRAY_SIZE(cmdline_gregs));
3450
3451 drvinfo.cmd = ETHTOOL_GDRVINFO;
3452 err = send_ioctl(ctx, &drvinfo);
3453 if (err < 0) {
3454 perror("Cannot get driver information");
3455 return 72;
3456 }
3457
3458 regs = calloc(1, sizeof(*regs)+drvinfo.regdump_len);
3459 if (!regs) {
3460 perror("Cannot allocate memory for register dump");
3461 return 73;
3462 }
3463 regs->cmd = ETHTOOL_GREGS;
3464 regs->len = drvinfo.regdump_len;
3465 err = send_ioctl(ctx, regs);
3466 if (err < 0) {
3467 perror("Cannot get register dump");
3468 free(regs);
3469 return 74;
3470 }
3471
3472 if (!gregs_dump_raw && gregs_dump_file != NULL) {
3473 /* overwrite reg values from file dump */
3474 FILE *f = fopen(gregs_dump_file, "r");
3475 struct ethtool_regs *nregs;
3476 struct stat st;
3477 size_t nread;
3478
3479 if (!f || fstat(fileno(f), &st) < 0) {
3480 fprintf(stderr, "Can't open '%s': %s\n",
3481 gregs_dump_file, strerror(errno));
3482 if (f)
3483 fclose(f);
3484 free(regs);
3485 return 75;
3486 }
3487
3488 nregs = realloc(regs, sizeof(*regs) + st.st_size);
3489 if (!nregs) {
3490 perror("Cannot allocate memory for register dump");
3491 free(regs); /* was not freed by realloc */
3492 return 73;
3493 }
3494 regs = nregs;
3495 regs->len = st.st_size;
3496 nread = fread(regs->data, regs->len, 1, f);
3497 fclose(f);
3498 if (nread != 1) {
3499 free(regs);
3500 return 75;
3501 }
3502 }
3503
3504 if (dump_regs(gregs_dump_raw, gregs_dump_hex,
3505 &drvinfo, regs) < 0) {
3506 fprintf(stderr, "Cannot dump registers\n");
3507 free(regs);
3508 return 75;
3509 }
3510 free(regs);
3511
3512 return 0;
3513 }
3514
do_nway_rst(struct cmd_context * ctx)3515 static int do_nway_rst(struct cmd_context *ctx)
3516 {
3517 struct ethtool_value edata;
3518 int err;
3519
3520 if (ctx->argc != 0)
3521 exit_bad_args();
3522
3523 edata.cmd = ETHTOOL_NWAY_RST;
3524 err = send_ioctl(ctx, &edata);
3525 if (err < 0)
3526 perror("Cannot restart autonegotiation");
3527
3528 return err;
3529 }
3530
do_geeprom(struct cmd_context * ctx)3531 static int do_geeprom(struct cmd_context *ctx)
3532 {
3533 int geeprom_changed = 0;
3534 int geeprom_dump_raw = 0;
3535 u32 geeprom_offset = 0;
3536 u32 geeprom_length = 0;
3537 int geeprom_length_seen = 0;
3538 struct cmdline_info cmdline_geeprom[] = {
3539 {
3540 .name = "offset",
3541 .type = CMDL_U32,
3542 .wanted_val = &geeprom_offset,
3543 },
3544 {
3545 .name = "length",
3546 .type = CMDL_U32,
3547 .wanted_val = &geeprom_length,
3548 .seen_val = &geeprom_length_seen,
3549 },
3550 {
3551 .name = "raw",
3552 .type = CMDL_BOOL,
3553 .wanted_val = &geeprom_dump_raw,
3554 },
3555 };
3556 int err;
3557 struct ethtool_drvinfo drvinfo;
3558 struct ethtool_eeprom *eeprom;
3559
3560 parse_generic_cmdline(ctx, &geeprom_changed,
3561 cmdline_geeprom, ARRAY_SIZE(cmdline_geeprom));
3562
3563 drvinfo.cmd = ETHTOOL_GDRVINFO;
3564 err = send_ioctl(ctx, &drvinfo);
3565 if (err < 0) {
3566 perror("Cannot get driver information");
3567 return 74;
3568 }
3569
3570 if (!geeprom_length_seen)
3571 geeprom_length = drvinfo.eedump_len;
3572
3573 if (drvinfo.eedump_len < geeprom_offset + geeprom_length)
3574 geeprom_length = drvinfo.eedump_len - geeprom_offset;
3575
3576 eeprom = calloc(1, sizeof(*eeprom)+geeprom_length);
3577 if (!eeprom) {
3578 perror("Cannot allocate memory for EEPROM data");
3579 return 75;
3580 }
3581 eeprom->cmd = ETHTOOL_GEEPROM;
3582 eeprom->len = geeprom_length;
3583 eeprom->offset = geeprom_offset;
3584 err = send_ioctl(ctx, eeprom);
3585 if (err < 0) {
3586 perror("Cannot get EEPROM data");
3587 free(eeprom);
3588 return 74;
3589 }
3590 err = dump_eeprom(geeprom_dump_raw, &drvinfo, eeprom);
3591 free(eeprom);
3592
3593 return err;
3594 }
3595
do_seeprom(struct cmd_context * ctx)3596 static int do_seeprom(struct cmd_context *ctx)
3597 {
3598 int seeprom_changed = 0;
3599 u32 seeprom_magic = 0;
3600 u32 seeprom_length = 0;
3601 u32 seeprom_offset = 0;
3602 u8 seeprom_value = 0;
3603 int seeprom_length_seen = 0;
3604 int seeprom_value_seen = 0;
3605 struct cmdline_info cmdline_seeprom[] = {
3606 {
3607 .name = "magic",
3608 .type = CMDL_U32,
3609 .wanted_val = &seeprom_magic,
3610 },
3611 {
3612 .name = "offset",
3613 .type = CMDL_U32,
3614 .wanted_val = &seeprom_offset,
3615 },
3616 {
3617 .name = "length",
3618 .type = CMDL_U32,
3619 .wanted_val = &seeprom_length,
3620 .seen_val = &seeprom_length_seen,
3621 },
3622 {
3623 .name = "value",
3624 .type = CMDL_U8,
3625 .wanted_val = &seeprom_value,
3626 .seen_val = &seeprom_value_seen,
3627 },
3628 };
3629 int err;
3630 struct ethtool_drvinfo drvinfo;
3631 struct ethtool_eeprom *eeprom;
3632
3633 parse_generic_cmdline(ctx, &seeprom_changed,
3634 cmdline_seeprom, ARRAY_SIZE(cmdline_seeprom));
3635
3636 drvinfo.cmd = ETHTOOL_GDRVINFO;
3637 err = send_ioctl(ctx, &drvinfo);
3638 if (err < 0) {
3639 perror("Cannot get driver information");
3640 return 74;
3641 }
3642
3643 if (seeprom_value_seen && !seeprom_length_seen)
3644 seeprom_length = 1;
3645 else if (!seeprom_length_seen)
3646 seeprom_length = drvinfo.eedump_len;
3647
3648 if (seeprom_value_seen && (seeprom_length != 1)) {
3649 fprintf(stderr, "value requires length 1\n");
3650 return 1;
3651 }
3652
3653 if (drvinfo.eedump_len < seeprom_offset + seeprom_length) {
3654 fprintf(stderr, "offset & length out of bounds\n");
3655 return 1;
3656 }
3657
3658 eeprom = calloc(1, sizeof(*eeprom)+seeprom_length);
3659 if (!eeprom) {
3660 perror("Cannot allocate memory for EEPROM data");
3661 return 75;
3662 }
3663
3664 eeprom->cmd = ETHTOOL_SEEPROM;
3665 eeprom->len = seeprom_length;
3666 eeprom->offset = seeprom_offset;
3667 eeprom->magic = seeprom_magic;
3668 eeprom->data[0] = seeprom_value;
3669
3670 /* Multi-byte write: read input from stdin */
3671 if (!seeprom_value_seen) {
3672 if (fread(eeprom->data, eeprom->len, 1, stdin) != 1) {
3673 fprintf(stderr, "not enough data from stdin\n");
3674 free(eeprom);
3675 return 75;
3676 }
3677 if ((fgetc(stdin) != EOF) || !feof(stdin)) {
3678 fprintf(stderr, "too much data from stdin\n");
3679 free(eeprom);
3680 return 75;
3681 }
3682 }
3683
3684 err = send_ioctl(ctx, eeprom);
3685 if (err < 0) {
3686 perror("Cannot set EEPROM data");
3687 err = 87;
3688 }
3689 free(eeprom);
3690
3691 return err;
3692 }
3693
do_test(struct cmd_context * ctx)3694 static int do_test(struct cmd_context *ctx)
3695 {
3696 enum {
3697 ONLINE = 0,
3698 OFFLINE,
3699 EXTERNAL_LB,
3700 } test_type;
3701 int err;
3702 struct ethtool_test *test;
3703 struct ethtool_gstrings *strings;
3704
3705 if (ctx->argc > 1)
3706 exit_bad_args();
3707 if (ctx->argc == 1) {
3708 if (!strcmp(ctx->argp[0], "online"))
3709 test_type = ONLINE;
3710 else if (!strcmp(*ctx->argp, "offline"))
3711 test_type = OFFLINE;
3712 else if (!strcmp(*ctx->argp, "external_lb"))
3713 test_type = EXTERNAL_LB;
3714 else
3715 exit_bad_args();
3716 } else {
3717 test_type = OFFLINE;
3718 }
3719
3720 strings = get_stringset(ctx, ETH_SS_TEST,
3721 offsetof(struct ethtool_drvinfo, testinfo_len),
3722 1);
3723 if (!strings) {
3724 perror("Cannot get strings");
3725 return 74;
3726 }
3727
3728 test = calloc(1, sizeof(*test) + strings->len * sizeof(u64));
3729 if (!test) {
3730 perror("Cannot allocate memory for test info");
3731 free(strings);
3732 return 73;
3733 }
3734 memset(test->data, 0, strings->len * sizeof(u64));
3735 test->cmd = ETHTOOL_TEST;
3736 test->len = strings->len;
3737 if (test_type == EXTERNAL_LB)
3738 test->flags = (ETH_TEST_FL_OFFLINE | ETH_TEST_FL_EXTERNAL_LB);
3739 else if (test_type == OFFLINE)
3740 test->flags = ETH_TEST_FL_OFFLINE;
3741 else
3742 test->flags = 0;
3743 err = send_ioctl(ctx, test);
3744 if (err < 0) {
3745 perror("Cannot test");
3746 free(test);
3747 free(strings);
3748 return 74;
3749 }
3750
3751 err = dump_test(test, strings);
3752 free(test);
3753 free(strings);
3754
3755 return err;
3756 }
3757
do_phys_id(struct cmd_context * ctx)3758 static int do_phys_id(struct cmd_context *ctx)
3759 {
3760 int err;
3761 struct ethtool_value edata;
3762 int phys_id_time;
3763
3764 if (ctx->argc > 1)
3765 exit_bad_args();
3766 if (ctx->argc == 1)
3767 phys_id_time = get_int(*ctx->argp, 0);
3768 else
3769 phys_id_time = 0;
3770
3771 edata.cmd = ETHTOOL_PHYS_ID;
3772 edata.data = phys_id_time;
3773 err = send_ioctl(ctx, &edata);
3774 if (err < 0)
3775 perror("Cannot identify NIC");
3776
3777 return err;
3778 }
3779
do_gstats(struct cmd_context * ctx,int cmd,int stringset,const char * name)3780 static int do_gstats(struct cmd_context *ctx, int cmd, int stringset,
3781 const char *name)
3782 {
3783 struct ethtool_gstrings *strings;
3784 struct ethtool_stats *stats;
3785 unsigned int n_stats, sz_stats, i;
3786 int err;
3787
3788 if (ctx->argc != 0)
3789 exit_bad_args();
3790
3791 strings = get_stringset(ctx, stringset,
3792 offsetof(struct ethtool_drvinfo, n_stats),
3793 0);
3794 if (!strings) {
3795 perror("Cannot get stats strings information");
3796 return 96;
3797 }
3798
3799 n_stats = strings->len;
3800 if (n_stats < 1) {
3801 fprintf(stderr, "no stats available\n");
3802 free(strings);
3803 return 94;
3804 }
3805
3806 sz_stats = n_stats * sizeof(u64);
3807
3808 stats = calloc(1, sz_stats + sizeof(struct ethtool_stats));
3809 if (!stats) {
3810 fprintf(stderr, "no memory available\n");
3811 free(strings);
3812 return 95;
3813 }
3814
3815 stats->cmd = cmd;
3816 stats->n_stats = n_stats;
3817 err = send_ioctl(ctx, stats);
3818 if (err < 0) {
3819 perror("Cannot get stats information");
3820 free(strings);
3821 free(stats);
3822 return 97;
3823 }
3824
3825 /* todo - pretty-print the strings per-driver */
3826 fprintf(stdout, "%s statistics:\n", name);
3827 for (i = 0; i < n_stats; i++) {
3828 fprintf(stdout, " %.*s: %llu\n",
3829 ETH_GSTRING_LEN,
3830 &strings->data[i * ETH_GSTRING_LEN],
3831 stats->data[i]);
3832 }
3833 free(strings);
3834 free(stats);
3835
3836 return 0;
3837 }
3838
do_gnicstats(struct cmd_context * ctx)3839 static int do_gnicstats(struct cmd_context *ctx)
3840 {
3841 return do_gstats(ctx, ETHTOOL_GSTATS, ETH_SS_STATS, "NIC");
3842 }
3843
do_gphystats(struct cmd_context * ctx)3844 static int do_gphystats(struct cmd_context *ctx)
3845 {
3846 return do_gstats(ctx, ETHTOOL_GPHYSTATS, ETH_SS_PHY_STATS, "PHY");
3847 }
3848
3849 static int do_srxntuple(struct cmd_context *ctx,
3850 struct ethtool_rx_flow_spec *rx_rule_fs);
3851
do_srxclass(struct cmd_context * ctx)3852 static int do_srxclass(struct cmd_context *ctx)
3853 {
3854 int err;
3855
3856 if (ctx->argc < 2)
3857 exit_bad_args();
3858
3859 if (!strcmp(ctx->argp[0], "rx-flow-hash")) {
3860 int rx_fhash_set;
3861 u32 rx_fhash_val;
3862 struct ethtool_rxnfc nfccmd;
3863 bool flow_rss = false;
3864
3865 if (ctx->argc == 5) {
3866 if (strcmp(ctx->argp[3], "context"))
3867 exit_bad_args();
3868 flow_rss = true;
3869 nfccmd.rss_context = get_u32(ctx->argp[4], 0);
3870 } else if (ctx->argc != 3) {
3871 exit_bad_args();
3872 }
3873 rx_fhash_set = rxflow_str_to_type(ctx->argp[1]);
3874 if (!rx_fhash_set)
3875 exit_bad_args();
3876 if (parse_rxfhashopts(ctx->argp[2], &rx_fhash_val) < 0)
3877 exit_bad_args();
3878
3879 nfccmd.cmd = ETHTOOL_SRXFH;
3880 nfccmd.flow_type = rx_fhash_set;
3881 nfccmd.data = rx_fhash_val;
3882 if (flow_rss)
3883 nfccmd.flow_type |= FLOW_RSS;
3884
3885 err = send_ioctl(ctx, &nfccmd);
3886 if (err < 0) {
3887 perror("Cannot change RX network flow hashing options");
3888 return 1;
3889 }
3890 } else if (!strcmp(ctx->argp[0], "flow-type")) {
3891 struct ethtool_rx_flow_spec rx_rule_fs;
3892 __u32 rss_context = 0;
3893
3894 ctx->argc--;
3895 ctx->argp++;
3896 if (rxclass_parse_ruleopts(ctx, &rx_rule_fs, &rss_context) < 0)
3897 exit_bad_args();
3898
3899 /* attempt to add rule via N-tuple specifier */
3900 err = do_srxntuple(ctx, &rx_rule_fs);
3901 if (!err)
3902 return 0;
3903
3904 /* attempt to add rule via network flow classifier */
3905 err = rxclass_rule_ins(ctx, &rx_rule_fs, rss_context);
3906 if (err < 0) {
3907 fprintf(stderr, "Cannot insert"
3908 " classification rule\n");
3909 return 1;
3910 }
3911 } else if (!strcmp(ctx->argp[0], "delete")) {
3912 int rx_class_rule_del =
3913 get_uint_range(ctx->argp[1], 0, INT_MAX);
3914
3915 err = rxclass_rule_del(ctx, rx_class_rule_del);
3916
3917 if (err < 0) {
3918 fprintf(stderr, "Cannot delete"
3919 " classification rule\n");
3920 return 1;
3921 }
3922 } else {
3923 exit_bad_args();
3924 }
3925
3926 return 0;
3927 }
3928
do_grxclass(struct cmd_context * ctx)3929 static int do_grxclass(struct cmd_context *ctx)
3930 {
3931 struct ethtool_rxnfc nfccmd;
3932 int err;
3933
3934 if (ctx->argc > 0 && !strcmp(ctx->argp[0], "rx-flow-hash")) {
3935 int rx_fhash_get;
3936 bool flow_rss = false;
3937
3938 if (ctx->argc == 4) {
3939 if (strcmp(ctx->argp[2], "context"))
3940 exit_bad_args();
3941 flow_rss = true;
3942 nfccmd.rss_context = get_u32(ctx->argp[3], 0);
3943 } else if (ctx->argc != 2) {
3944 exit_bad_args();
3945 }
3946
3947 rx_fhash_get = rxflow_str_to_type(ctx->argp[1]);
3948 if (!rx_fhash_get)
3949 exit_bad_args();
3950
3951 nfccmd.cmd = ETHTOOL_GRXFH;
3952 nfccmd.flow_type = rx_fhash_get;
3953 if (flow_rss)
3954 nfccmd.flow_type |= FLOW_RSS;
3955 err = send_ioctl(ctx, &nfccmd);
3956 if (err < 0) {
3957 perror("Cannot get RX network flow hashing options");
3958 } else {
3959 if (flow_rss)
3960 fprintf(stdout, "For RSS context %u:\n",
3961 nfccmd.rss_context);
3962 dump_rxfhash(rx_fhash_get, nfccmd.data);
3963 }
3964 } else if (ctx->argc == 2 && !strcmp(ctx->argp[0], "rule")) {
3965 int rx_class_rule_get =
3966 get_uint_range(ctx->argp[1], 0, INT_MAX);
3967
3968 err = rxclass_rule_get(ctx, rx_class_rule_get);
3969 if (err < 0)
3970 fprintf(stderr, "Cannot get RX classification rule\n");
3971 } else if (ctx->argc == 0) {
3972 nfccmd.cmd = ETHTOOL_GRXRINGS;
3973 err = send_ioctl(ctx, &nfccmd);
3974 if (err < 0)
3975 perror("Cannot get RX rings");
3976 else
3977 fprintf(stdout, "%d RX rings available\n",
3978 (int)nfccmd.data);
3979
3980 err = rxclass_rule_getall(ctx);
3981 if (err < 0)
3982 fprintf(stderr, "RX classification rule retrieval failed\n");
3983
3984 } else {
3985 exit_bad_args();
3986 }
3987
3988 return err ? 1 : 0;
3989 }
3990
do_grxfhindir(struct cmd_context * ctx,struct ethtool_rxnfc * ring_count)3991 static int do_grxfhindir(struct cmd_context *ctx,
3992 struct ethtool_rxnfc *ring_count)
3993 {
3994 struct ethtool_rxfh_indir indir_head;
3995 struct ethtool_rxfh_indir *indir;
3996 int err;
3997
3998 indir_head.cmd = ETHTOOL_GRXFHINDIR;
3999 indir_head.size = 0;
4000 err = send_ioctl(ctx, &indir_head);
4001 if (err < 0) {
4002 perror("Cannot get RX flow hash indirection table size");
4003 return 1;
4004 }
4005
4006 indir = malloc(sizeof(*indir) +
4007 indir_head.size * sizeof(*indir->ring_index));
4008 if (!indir) {
4009 perror("Cannot allocate memory for indirection table");
4010 return 1;
4011 }
4012
4013 indir->cmd = ETHTOOL_GRXFHINDIR;
4014 indir->size = indir_head.size;
4015 err = send_ioctl(ctx, indir);
4016 if (err < 0) {
4017 perror("Cannot get RX flow hash indirection table");
4018 free(indir);
4019 return 1;
4020 }
4021
4022 print_indir_table(ctx, ring_count->data, indir->size,
4023 indir->ring_index);
4024
4025 free(indir);
4026 return 0;
4027 }
4028
do_grxfh(struct cmd_context * ctx)4029 static int do_grxfh(struct cmd_context *ctx)
4030 {
4031 struct ethtool_gstrings *hfuncs = NULL;
4032 struct ethtool_rxfh rss_head = {0};
4033 struct ethtool_rxnfc ring_count;
4034 struct ethtool_rxfh *rss;
4035 u32 rss_context = 0;
4036 u32 i, indir_bytes;
4037 unsigned int arg_num = 0;
4038 u8 *hkey;
4039 int err;
4040
4041 while (arg_num < ctx->argc) {
4042 if (!strcmp(ctx->argp[arg_num], "context")) {
4043 ++arg_num;
4044 rss_context = get_int_range(ctx->argp[arg_num], 0, 1,
4045 ETH_RXFH_CONTEXT_ALLOC - 1);
4046 ++arg_num;
4047 } else {
4048 exit_bad_args();
4049 }
4050 }
4051
4052 ring_count.cmd = ETHTOOL_GRXRINGS;
4053 err = send_ioctl(ctx, &ring_count);
4054 if (err < 0) {
4055 perror("Cannot get RX ring count");
4056 return 1;
4057 }
4058
4059 rss_head.cmd = ETHTOOL_GRSSH;
4060 rss_head.rss_context = rss_context;
4061 err = send_ioctl(ctx, &rss_head);
4062 if (err < 0 && errno == EOPNOTSUPP && !rss_context) {
4063 return do_grxfhindir(ctx, &ring_count);
4064 } else if (err < 0) {
4065 perror("Cannot get RX flow hash indir size and/or key size");
4066 return 1;
4067 }
4068
4069 rss = calloc(1, sizeof(*rss) +
4070 rss_head.indir_size * sizeof(rss_head.rss_config[0]) +
4071 rss_head.key_size);
4072 if (!rss) {
4073 perror("Cannot allocate memory for RX flow hash config");
4074 return 1;
4075 }
4076
4077 rss->cmd = ETHTOOL_GRSSH;
4078 rss->rss_context = rss_context;
4079 rss->indir_size = rss_head.indir_size;
4080 rss->key_size = rss_head.key_size;
4081 err = send_ioctl(ctx, rss);
4082 if (err < 0) {
4083 perror("Cannot get RX flow hash configuration");
4084 free(rss);
4085 return 1;
4086 }
4087
4088 print_indir_table(ctx, ring_count.data, rss->indir_size,
4089 rss->rss_config);
4090
4091 indir_bytes = rss->indir_size * sizeof(rss->rss_config[0]);
4092 hkey = ((u8 *)rss->rss_config + indir_bytes);
4093
4094 print_rss_hkey(hkey, rss->key_size);
4095
4096 printf("RSS hash function:\n");
4097 if (!rss->hfunc) {
4098 printf(" Operation not supported\n");
4099 goto out;
4100 }
4101
4102 hfuncs = get_stringset(ctx, ETH_SS_RSS_HASH_FUNCS, 0, 1);
4103 if (!hfuncs) {
4104 perror("Cannot get hash functions names");
4105 free(rss);
4106 return 1;
4107 }
4108
4109 for (i = 0; i < hfuncs->len; i++)
4110 printf(" %s: %s\n",
4111 (const char *)hfuncs->data + i * ETH_GSTRING_LEN,
4112 (rss->hfunc & (1 << i)) ? "on" : "off");
4113
4114 printf("RSS input transformation:\n");
4115 printf(" symmetric-xor: %s\n",
4116 (rss->input_xfrm & RXH_XFRM_SYM_XOR) ? "on" : "off");
4117
4118 out:
4119 free(hfuncs);
4120 free(rss);
4121 return 0;
4122 }
4123
fill_indir_table(u32 * indir_size,u32 * indir,int rxfhindir_default,int rxfhindir_start,int rxfhindir_equal,char ** rxfhindir_weight,u32 num_weights)4124 static int fill_indir_table(u32 *indir_size, u32 *indir, int rxfhindir_default,
4125 int rxfhindir_start, int rxfhindir_equal,
4126 char **rxfhindir_weight, u32 num_weights)
4127 {
4128 u32 i;
4129
4130 if (rxfhindir_equal) {
4131 for (i = 0; i < *indir_size; i++)
4132 indir[i] = rxfhindir_start + (i % rxfhindir_equal);
4133 } else if (rxfhindir_weight) {
4134 u32 j, weight, sum = 0, partial = 0;
4135
4136 for (j = 0; j < num_weights; j++) {
4137 weight = get_u32(rxfhindir_weight[j], 0);
4138 sum += weight;
4139 }
4140
4141 if (sum == 0) {
4142 fprintf(stderr,
4143 "At least one weight must be non-zero\n");
4144 return 2;
4145 }
4146
4147 if (sum > *indir_size) {
4148 fprintf(stderr,
4149 "Total weight exceeds the size of the "
4150 "indirection table\n");
4151 return 2;
4152 }
4153
4154 j = -1;
4155 for (i = 0; i < *indir_size; i++) {
4156 while (i >= (*indir_size) * partial / sum) {
4157 j += 1;
4158 weight = get_u32(rxfhindir_weight[j], 0);
4159 partial += weight;
4160 }
4161 indir[i] = rxfhindir_start + j;
4162 }
4163 } else if (rxfhindir_default) {
4164 /* "*indir_size == 0" ==> reset indir to default */
4165 *indir_size = 0;
4166 } else {
4167 *indir_size = ETH_RXFH_INDIR_NO_CHANGE;
4168 }
4169
4170 return 0;
4171 }
4172
do_srxfhindir(struct cmd_context * ctx,int rxfhindir_default,int rxfhindir_start,int rxfhindir_equal,char ** rxfhindir_weight,u32 num_weights)4173 static int do_srxfhindir(struct cmd_context *ctx, int rxfhindir_default,
4174 int rxfhindir_start, int rxfhindir_equal,
4175 char **rxfhindir_weight, u32 num_weights)
4176 {
4177 struct ethtool_rxfh_indir indir_head;
4178 struct ethtool_rxfh_indir *indir;
4179 int err;
4180
4181 indir_head.cmd = ETHTOOL_GRXFHINDIR;
4182 indir_head.size = 0;
4183 err = send_ioctl(ctx, &indir_head);
4184 if (err < 0) {
4185 perror("Cannot get RX flow hash indirection table size");
4186 return 1;
4187 }
4188
4189 indir = malloc(sizeof(*indir) +
4190 indir_head.size * sizeof(*indir->ring_index));
4191
4192 if (!indir) {
4193 perror("Cannot allocate memory for indirection table");
4194 return 1;
4195 }
4196
4197 indir->cmd = ETHTOOL_SRXFHINDIR;
4198 indir->size = indir_head.size;
4199
4200 if (fill_indir_table(&indir->size, indir->ring_index,
4201 rxfhindir_default, rxfhindir_start,
4202 rxfhindir_equal, rxfhindir_weight, num_weights)) {
4203 free(indir);
4204 return 1;
4205 }
4206
4207 err = send_ioctl(ctx, indir);
4208 if (err < 0) {
4209 perror("Cannot set RX flow hash indirection table");
4210 free(indir);
4211 return 1;
4212 }
4213
4214 free(indir);
4215 return 0;
4216 }
4217
do_srxfh(struct cmd_context * ctx)4218 static int do_srxfh(struct cmd_context *ctx)
4219 {
4220 struct ethtool_rxfh rss_head = {0};
4221 struct ethtool_rxfh *rss = NULL;
4222 struct ethtool_rxnfc ring_count;
4223 int rxfhindir_equal = 0, rxfhindir_default = 0, rxfhindir_start = 0;
4224 struct ethtool_gstrings *hfuncs = NULL;
4225 char **rxfhindir_weight = NULL;
4226 char *rxfhindir_key = NULL;
4227 char *req_hfunc_name = NULL;
4228 char *hfunc_name = NULL;
4229 char *hkey = NULL;
4230 int err = 0;
4231 unsigned int i;
4232 u32 arg_num = 0, indir_bytes = 0;
4233 u32 req_hfunc = 0;
4234 u32 entry_size = sizeof(rss_head.rss_config[0]);
4235 u32 req_input_xfrm = 0xff;
4236 u32 num_weights = 0;
4237 u32 rss_context = 0;
4238 int delete = 0;
4239
4240 if (ctx->argc < 1)
4241 exit_bad_args();
4242
4243 while (arg_num < ctx->argc) {
4244 if (!strcmp(ctx->argp[arg_num], "equal")) {
4245 ++arg_num;
4246 rxfhindir_equal = get_int_range(ctx->argp[arg_num],
4247 0, 1, INT_MAX);
4248 ++arg_num;
4249 } else if (!strcmp(ctx->argp[arg_num], "start")) {
4250 ++arg_num;
4251 rxfhindir_start = get_int_range(ctx->argp[arg_num],
4252 0, 0, INT_MAX);
4253 ++arg_num;
4254 } else if (!strcmp(ctx->argp[arg_num], "weight")) {
4255 ++arg_num;
4256 rxfhindir_weight = ctx->argp + arg_num;
4257 while (arg_num < ctx->argc &&
4258 isdigit((unsigned char)ctx->argp[arg_num][0])) {
4259 ++arg_num;
4260 ++num_weights;
4261 }
4262 if (!num_weights)
4263 exit_bad_args();
4264 } else if (!strcmp(ctx->argp[arg_num], "hkey")) {
4265 ++arg_num;
4266 rxfhindir_key = ctx->argp[arg_num];
4267 if (!rxfhindir_key)
4268 exit_bad_args();
4269 ++arg_num;
4270 } else if (!strcmp(ctx->argp[arg_num], "default")) {
4271 ++arg_num;
4272 rxfhindir_default = 1;
4273 } else if (!strcmp(ctx->argp[arg_num], "hfunc")) {
4274 ++arg_num;
4275 req_hfunc_name = ctx->argp[arg_num];
4276 if (!req_hfunc_name)
4277 exit_bad_args();
4278 ++arg_num;
4279 } else if (!strcmp(ctx->argp[arg_num], "xfrm")) {
4280 ++arg_num;
4281 if (!ctx->argp[arg_num])
4282 exit_bad_args();
4283 if (!strcmp(ctx->argp[arg_num], "symmetric-xor"))
4284 req_input_xfrm = RXH_XFRM_SYM_XOR;
4285 else if (!strcmp(ctx->argp[arg_num], "none"))
4286 req_input_xfrm = 0;
4287 else
4288 exit_bad_args();
4289 ++arg_num;
4290 } else if (!strcmp(ctx->argp[arg_num], "context")) {
4291 ++arg_num;
4292 if (!ctx->argp[arg_num])
4293 exit_bad_args();
4294 if(!strcmp(ctx->argp[arg_num], "new"))
4295 rss_context = ETH_RXFH_CONTEXT_ALLOC;
4296 else
4297 rss_context = get_int_range(
4298 ctx->argp[arg_num], 0, 1,
4299 ETH_RXFH_CONTEXT_ALLOC - 1);
4300 ++arg_num;
4301 } else if (!strcmp(ctx->argp[arg_num], "delete")) {
4302 ++arg_num;
4303 delete = 1;
4304 } else {
4305 exit_bad_args();
4306 }
4307 }
4308
4309 if (rxfhindir_equal && rxfhindir_weight) {
4310 fprintf(stderr,
4311 "Equal and weight options are mutually exclusive\n");
4312 return 1;
4313 }
4314
4315 if (rxfhindir_equal && rxfhindir_default) {
4316 fprintf(stderr,
4317 "Equal and default options are mutually exclusive\n");
4318 return 1;
4319 }
4320
4321 if (rxfhindir_weight && rxfhindir_default) {
4322 fprintf(stderr,
4323 "Weight and default options are mutually exclusive\n");
4324 return 1;
4325 }
4326
4327 if (rxfhindir_start && rxfhindir_default) {
4328 fprintf(stderr,
4329 "Start and default options are mutually exclusive\n");
4330 return 1;
4331 }
4332
4333 if (rxfhindir_start && !(rxfhindir_equal || rxfhindir_weight)) {
4334 fprintf(stderr,
4335 "Start must be used with equal or weight options\n");
4336 return 1;
4337 }
4338
4339 if (rxfhindir_default && rss_context) {
4340 fprintf(stderr,
4341 "Default and context options are mutually exclusive\n");
4342 return 1;
4343 }
4344
4345 if (delete && !rss_context) {
4346 fprintf(stderr, "Delete option requires context option\n");
4347 return 1;
4348 }
4349
4350 if (delete && rxfhindir_weight) {
4351 fprintf(stderr,
4352 "Delete and weight options are mutually exclusive\n");
4353 return 1;
4354 }
4355
4356 if (delete && rxfhindir_equal) {
4357 fprintf(stderr,
4358 "Delete and equal options are mutually exclusive\n");
4359 return 1;
4360 }
4361
4362 if (delete && rxfhindir_default) {
4363 fprintf(stderr,
4364 "Delete and default options are mutually exclusive\n");
4365 return 1;
4366 }
4367
4368 if (delete && rxfhindir_key) {
4369 fprintf(stderr,
4370 "Delete and hkey options are mutually exclusive\n");
4371 return 1;
4372 }
4373
4374 ring_count.cmd = ETHTOOL_GRXRINGS;
4375 err = send_ioctl(ctx, &ring_count);
4376 if (err < 0) {
4377 perror("Cannot get RX ring count");
4378 return 1;
4379 }
4380
4381 rss_head.cmd = ETHTOOL_GRSSH;
4382 err = send_ioctl(ctx, &rss_head);
4383 if (err < 0 && errno == EOPNOTSUPP && !rxfhindir_key &&
4384 !req_hfunc_name && !rss_context) {
4385 return do_srxfhindir(ctx, rxfhindir_default, rxfhindir_start,
4386 rxfhindir_equal, rxfhindir_weight,
4387 num_weights);
4388 } else if (err < 0) {
4389 perror("Cannot get RX flow hash indir size and key size");
4390 return 1;
4391 }
4392
4393 if (rxfhindir_key) {
4394 err = parse_hkey(&hkey, rss_head.key_size,
4395 rxfhindir_key);
4396 if (err)
4397 return err;
4398 }
4399
4400 if (rxfhindir_equal || rxfhindir_weight)
4401 indir_bytes = rss_head.indir_size * entry_size;
4402
4403 if (rss_head.hfunc && req_hfunc_name) {
4404 hfuncs = get_stringset(ctx, ETH_SS_RSS_HASH_FUNCS, 0, 1);
4405 if (!hfuncs) {
4406 perror("Cannot get hash functions names");
4407 err = 1;
4408 goto free;
4409 }
4410
4411 for (i = 0; i < hfuncs->len && !req_hfunc ; i++) {
4412 hfunc_name = (char *)(hfuncs->data +
4413 i * ETH_GSTRING_LEN);
4414 if (!strncmp(hfunc_name, req_hfunc_name,
4415 ETH_GSTRING_LEN))
4416 req_hfunc = (u32)1 << i;
4417 }
4418
4419 if (!req_hfunc) {
4420 fprintf(stderr,
4421 "Unknown hash function: %s\n", req_hfunc_name);
4422 err = 1;
4423 goto free;
4424 }
4425 }
4426
4427 rss = calloc(1, sizeof(*rss) + indir_bytes + rss_head.key_size);
4428 if (!rss) {
4429 perror("Cannot allocate memory for RX flow hash config");
4430 err = 1;
4431 goto free;
4432 }
4433 rss->cmd = ETHTOOL_SRSSH;
4434 rss->rss_context = rss_context;
4435 rss->hfunc = req_hfunc;
4436 rss->input_xfrm = req_input_xfrm;
4437 if (delete) {
4438 rss->indir_size = rss->key_size = 0;
4439 } else {
4440 rss->indir_size = rss_head.indir_size;
4441 rss->key_size = rss_head.key_size;
4442 if (fill_indir_table(&rss->indir_size, rss->rss_config,
4443 rxfhindir_default, rxfhindir_start,
4444 rxfhindir_equal, rxfhindir_weight,
4445 num_weights)) {
4446 err = 1;
4447 goto free;
4448 }
4449 }
4450
4451 if (hkey)
4452 memcpy((char *)rss->rss_config + indir_bytes,
4453 hkey, rss->key_size);
4454 else
4455 rss->key_size = 0;
4456
4457 err = send_ioctl(ctx, rss);
4458 if (err < 0) {
4459 perror("Cannot set RX flow hash configuration");
4460 err = 1;
4461 } else if (rss_context == ETH_RXFH_CONTEXT_ALLOC) {
4462 printf("New RSS context is %d\n", rss->rss_context);
4463 }
4464
4465 free:
4466 free(hkey);
4467 free(rss);
4468 free(hfuncs);
4469 return err;
4470 }
4471
do_flash(struct cmd_context * ctx)4472 static int do_flash(struct cmd_context *ctx)
4473 {
4474 char *flash_file;
4475 int flash_region;
4476 struct ethtool_flash efl;
4477 int err;
4478
4479 if (ctx->argc < 1 || ctx->argc > 2)
4480 exit_bad_args();
4481 flash_file = ctx->argp[0];
4482 if (ctx->argc == 2) {
4483 flash_region = strtol(ctx->argp[1], NULL, 0);
4484 if (flash_region < 0)
4485 exit_bad_args();
4486 } else {
4487 flash_region = -1;
4488 }
4489
4490 if (strlen(flash_file) > ETHTOOL_FLASH_MAX_FILENAME - 1) {
4491 fprintf(stdout, "Filename too long\n");
4492 return 99;
4493 }
4494
4495 efl.cmd = ETHTOOL_FLASHDEV;
4496 strcpy(efl.data, flash_file);
4497
4498 if (flash_region < 0)
4499 efl.region = ETHTOOL_FLASH_ALL_REGIONS;
4500 else
4501 efl.region = flash_region;
4502
4503 err = send_ioctl(ctx, &efl);
4504 if (err < 0)
4505 perror("Flashing failed");
4506
4507 return err;
4508 }
4509
do_permaddr(struct cmd_context * ctx)4510 static int do_permaddr(struct cmd_context *ctx)
4511 {
4512 unsigned int i;
4513 int err;
4514 struct ethtool_perm_addr *epaddr;
4515
4516 epaddr = malloc(sizeof(struct ethtool_perm_addr) + MAX_ADDR_LEN);
4517 if (!epaddr) {
4518 perror("Cannot allocate memory for operation");
4519 return 1;
4520 }
4521
4522 epaddr->cmd = ETHTOOL_GPERMADDR;
4523 epaddr->size = MAX_ADDR_LEN;
4524
4525 err = send_ioctl(ctx, epaddr);
4526 if (err < 0)
4527 perror("Cannot read permanent address");
4528 else {
4529 printf("Permanent address:");
4530 for (i = 0; i < epaddr->size; i++)
4531 printf("%c%02x", (i == 0) ? ' ' : ':',
4532 epaddr->data[i]);
4533 printf("\n");
4534 }
4535 free(epaddr);
4536
4537 return err;
4538 }
4539
flow_type_is_ntuple_supported(__u32 flow_type)4540 static bool flow_type_is_ntuple_supported(__u32 flow_type)
4541 {
4542 switch (flow_type) {
4543 case TCP_V4_FLOW:
4544 case UDP_V4_FLOW:
4545 case SCTP_V4_FLOW:
4546 case AH_V4_FLOW:
4547 case ESP_V4_FLOW:
4548 case IPV4_USER_FLOW:
4549 case ETHER_FLOW:
4550 return true;
4551 default:
4552 return false;
4553 }
4554 }
4555
flow_spec_to_ntuple(struct ethtool_rx_flow_spec * fsp,struct ethtool_rx_ntuple_flow_spec * ntuple)4556 static int flow_spec_to_ntuple(struct ethtool_rx_flow_spec *fsp,
4557 struct ethtool_rx_ntuple_flow_spec *ntuple)
4558 {
4559 size_t i;
4560
4561 /* verify location is not specified */
4562 if (fsp->location != RX_CLS_LOC_ANY)
4563 return -1;
4564
4565 /* destination MAC address in L3/L4 rules is not supported by ntuple */
4566 if (fsp->flow_type & FLOW_MAC_EXT)
4567 return -1;
4568
4569 /* verify ring cookie can transfer to action */
4570 if (fsp->ring_cookie > INT_MAX && fsp->ring_cookie < (u64)(-2))
4571 return -1;
4572
4573 /* verify only one field is setting data field */
4574 if ((fsp->flow_type & FLOW_EXT) &&
4575 (fsp->m_ext.data[0] || fsp->m_ext.data[1]) &&
4576 fsp->m_ext.vlan_etype)
4577 return -1;
4578
4579 /* IPv6 flow types are not supported by ntuple */
4580 if (!flow_type_is_ntuple_supported(fsp->flow_type & ~FLOW_EXT))
4581 return -1;
4582
4583 /* Set entire ntuple to ~0 to guarantee all masks are set */
4584 memset(ntuple, ~0, sizeof(*ntuple));
4585
4586 /* set non-filter values */
4587 ntuple->flow_type = fsp->flow_type;
4588 ntuple->action = fsp->ring_cookie;
4589
4590 /*
4591 * Copy over header union, they are identical in layout however
4592 * the ntuple union contains additional padding on the end
4593 */
4594 memcpy(&ntuple->h_u, &fsp->h_u, sizeof(fsp->h_u));
4595
4596 /*
4597 * The same rule mentioned above applies to the mask union. However,
4598 * in addition we need to invert the mask bits to match the ntuple
4599 * mask which is 1 for masked, versus 0 for masked as seen in nfc.
4600 */
4601 memcpy(&ntuple->m_u, &fsp->m_u, sizeof(fsp->m_u));
4602 for (i = 0; i < sizeof(fsp->m_u); i++)
4603 ntuple->m_u.hdata[i] ^= 0xFF;
4604
4605 /* copy extended fields */
4606 if (fsp->flow_type & FLOW_EXT) {
4607 ntuple->vlan_tag =
4608 ntohs(fsp->h_ext.vlan_tci);
4609 ntuple->vlan_tag_mask =
4610 ~ntohs(fsp->m_ext.vlan_tci);
4611 if (fsp->m_ext.vlan_etype) {
4612 /*
4613 * vlan_etype and user data are mutually exclusive
4614 * in ntuple configuration as they occupy the same
4615 * space.
4616 */
4617 if (fsp->m_ext.data[0] || fsp->m_ext.data[1])
4618 return -1;
4619 ntuple->data =
4620 ntohl(fsp->h_ext.vlan_etype);
4621 ntuple->data_mask =
4622 ~(u64)ntohl(fsp->m_ext.vlan_etype);
4623 } else {
4624 ntuple->data =
4625 (u64)ntohl(fsp->h_ext.data[0]) << 32;
4626 ntuple->data |=
4627 (u64)ntohl(fsp->h_ext.data[1]);
4628 ntuple->data_mask =
4629 (u64)ntohl(~fsp->m_ext.data[0]) << 32;
4630 ntuple->data_mask |=
4631 (u64)ntohl(~fsp->m_ext.data[1]);
4632 }
4633 }
4634
4635 /* Mask out the extended bit, because ntuple does not know it! */
4636 ntuple->flow_type &= ~FLOW_EXT;
4637
4638 return 0;
4639 }
4640
do_srxntuple(struct cmd_context * ctx,struct ethtool_rx_flow_spec * rx_rule_fs)4641 static int do_srxntuple(struct cmd_context *ctx,
4642 struct ethtool_rx_flow_spec *rx_rule_fs)
4643 {
4644 struct ethtool_rx_ntuple ntuplecmd;
4645 struct ethtool_value eval;
4646 int err;
4647
4648 /* attempt to convert the flow classifier to an ntuple classifier */
4649 err = flow_spec_to_ntuple(rx_rule_fs, &ntuplecmd.fs);
4650 if (err)
4651 return -1;
4652
4653 /*
4654 * Check to see if the flag is set for N-tuple, this allows
4655 * us to avoid the possible EINVAL response for the N-tuple
4656 * flag not being set on the device
4657 */
4658 eval.cmd = ETHTOOL_GFLAGS;
4659 err = send_ioctl(ctx, &eval);
4660 if (err || !(eval.data & ETH_FLAG_NTUPLE))
4661 return -1;
4662
4663 /* send rule via N-tuple */
4664 ntuplecmd.cmd = ETHTOOL_SRXNTUPLE;
4665 err = send_ioctl(ctx, &ntuplecmd);
4666
4667 /*
4668 * Display error only if response is something other than op not
4669 * supported. It is possible that the interface uses the network
4670 * flow classifier interface instead of N-tuple.
4671 */
4672 if (err < 0) {
4673 if (errno != EOPNOTSUPP)
4674 perror("Cannot add new rule via N-tuple");
4675 return -1;
4676 }
4677
4678 return 0;
4679 }
4680
do_writefwdump(struct ethtool_dump * dump,const char * dump_file)4681 static int do_writefwdump(struct ethtool_dump *dump, const char *dump_file)
4682 {
4683 int err = 0;
4684 FILE *f;
4685 size_t bytes;
4686
4687 f = fopen(dump_file, "wb+");
4688
4689 if (!f) {
4690 fprintf(stderr, "Can't open file %s: %s\n",
4691 dump_file, strerror(errno));
4692 return 1;
4693 }
4694 bytes = fwrite(dump->data, 1, dump->len, f);
4695 if (bytes != dump->len) {
4696 fprintf(stderr, "Can not write all of dump data\n");
4697 err = 1;
4698 }
4699 if (fclose(f)) {
4700 fprintf(stderr, "Can't close file %s: %s\n",
4701 dump_file, strerror(errno));
4702 err = 1;
4703 }
4704 return err;
4705 }
4706
do_getfwdump(struct cmd_context * ctx)4707 static int do_getfwdump(struct cmd_context *ctx)
4708 {
4709 u32 dump_flag;
4710 char *dump_file;
4711 int err;
4712 struct ethtool_dump edata;
4713 struct ethtool_dump *data;
4714
4715 if (ctx->argc == 2 && !strcmp(ctx->argp[0], "data")) {
4716 dump_flag = ETHTOOL_GET_DUMP_DATA;
4717 dump_file = ctx->argp[1];
4718 } else if (ctx->argc == 0) {
4719 dump_flag = 0;
4720 dump_file = NULL;
4721 } else {
4722 exit_bad_args();
4723 }
4724
4725 edata.cmd = ETHTOOL_GET_DUMP_FLAG;
4726
4727 err = send_ioctl(ctx, &edata);
4728 if (err < 0) {
4729 perror("Can not get dump level");
4730 return 1;
4731 }
4732 if (dump_flag != ETHTOOL_GET_DUMP_DATA) {
4733 fprintf(stdout, "flag: %u, version: %u, length: %u\n",
4734 edata.flag, edata.version, edata.len);
4735 return 0;
4736 }
4737 data = calloc(1, offsetof(struct ethtool_dump, data) + edata.len);
4738 if (!data) {
4739 perror("Can not allocate enough memory");
4740 return 1;
4741 }
4742 data->cmd = ETHTOOL_GET_DUMP_DATA;
4743 data->len = edata.len;
4744 err = send_ioctl(ctx, data);
4745 if (err < 0) {
4746 perror("Can not get dump data");
4747 err = 1;
4748 goto free;
4749 }
4750 err = do_writefwdump(data, dump_file);
4751 free:
4752 free(data);
4753 return err;
4754 }
4755
do_setfwdump(struct cmd_context * ctx)4756 static int do_setfwdump(struct cmd_context *ctx)
4757 {
4758 u32 dump_flag;
4759 int err;
4760 struct ethtool_dump dump;
4761
4762 if (ctx->argc != 1)
4763 exit_bad_args();
4764 dump_flag = get_u32(ctx->argp[0], 0);
4765
4766 dump.cmd = ETHTOOL_SET_DUMP;
4767 dump.flag = dump_flag;
4768 err = send_ioctl(ctx, &dump);
4769 if (err < 0) {
4770 perror("Can not set dump level");
4771 return 1;
4772 }
4773 return 0;
4774 }
4775
do_gprivflags(struct cmd_context * ctx)4776 static int do_gprivflags(struct cmd_context *ctx)
4777 {
4778 struct ethtool_gstrings *strings;
4779 struct ethtool_value flags;
4780 unsigned int i;
4781 int max_len = 0, cur_len, rc;
4782
4783 if (ctx->argc != 0)
4784 exit_bad_args();
4785
4786 strings = get_stringset(ctx, ETH_SS_PRIV_FLAGS,
4787 offsetof(struct ethtool_drvinfo, n_priv_flags),
4788 1);
4789 if (!strings) {
4790 perror("Cannot get private flag names");
4791 return 1;
4792 }
4793 if (strings->len == 0) {
4794 fprintf(stderr, "No private flags defined\n");
4795 rc = 1;
4796 goto err;
4797 }
4798 if (strings->len > 32) {
4799 /* ETHTOOL_GPFLAGS can only cover 32 flags */
4800 fprintf(stderr, "Only showing first 32 private flags\n");
4801 strings->len = 32;
4802 }
4803
4804 flags.cmd = ETHTOOL_GPFLAGS;
4805 if (send_ioctl(ctx, &flags)) {
4806 perror("Cannot get private flags");
4807 rc = 1;
4808 goto err;
4809 }
4810
4811 /* Find longest string and align all strings accordingly */
4812 for (i = 0; i < strings->len; i++) {
4813 cur_len = strlen((const char *)strings->data +
4814 i * ETH_GSTRING_LEN);
4815 if (cur_len > max_len)
4816 max_len = cur_len;
4817 }
4818
4819 printf("Private flags for %s:\n", ctx->devname);
4820 for (i = 0; i < strings->len; i++)
4821 printf("%-*s: %s\n",
4822 max_len,
4823 (const char *)strings->data + i * ETH_GSTRING_LEN,
4824 (flags.data & (1U << i)) ? "on" : "off");
4825
4826 rc = 0;
4827
4828 err:
4829 free(strings);
4830 return rc;
4831 }
4832
do_sprivflags(struct cmd_context * ctx)4833 static int do_sprivflags(struct cmd_context *ctx)
4834 {
4835 struct ethtool_gstrings *strings;
4836 struct cmdline_info *cmdline;
4837 struct ethtool_value flags;
4838 u32 wanted_flags = 0, seen_flags = 0;
4839 int any_changed, rc;
4840 unsigned int i;
4841
4842 strings = get_stringset(ctx, ETH_SS_PRIV_FLAGS,
4843 offsetof(struct ethtool_drvinfo, n_priv_flags),
4844 1);
4845 if (!strings) {
4846 perror("Cannot get private flag names");
4847 return 1;
4848 }
4849 if (strings->len == 0) {
4850 fprintf(stderr, "No private flags defined\n");
4851 rc = 1;
4852 goto err;
4853 }
4854 if (strings->len > 32) {
4855 /* ETHTOOL_{G,S}PFLAGS can only cover 32 flags */
4856 fprintf(stderr, "Only setting first 32 private flags\n");
4857 strings->len = 32;
4858 }
4859
4860 cmdline = calloc(strings->len, sizeof(*cmdline));
4861 if (!cmdline) {
4862 perror("Cannot parse arguments");
4863 rc = 1;
4864 goto err;
4865 }
4866 for (i = 0; i < strings->len; i++) {
4867 cmdline[i].name = ((const char *)strings->data +
4868 i * ETH_GSTRING_LEN);
4869 cmdline[i].type = CMDL_FLAG;
4870 cmdline[i].wanted_val = &wanted_flags;
4871 cmdline[i].flag_val = 1U << i;
4872 cmdline[i].seen_val = &seen_flags;
4873 }
4874 parse_generic_cmdline(ctx, &any_changed, cmdline, strings->len);
4875 free(cmdline);
4876
4877 flags.cmd = ETHTOOL_GPFLAGS;
4878 if (send_ioctl(ctx, &flags)) {
4879 perror("Cannot get private flags");
4880 rc = 1;
4881 goto err;
4882 }
4883
4884 flags.cmd = ETHTOOL_SPFLAGS;
4885 flags.data = (flags.data & ~seen_flags) | wanted_flags;
4886 if (send_ioctl(ctx, &flags)) {
4887 perror("Cannot set private flags");
4888 rc = 1;
4889 goto err;
4890 }
4891
4892 rc = 0;
4893 err:
4894 free(strings);
4895 return rc;
4896 }
4897
do_tsinfo(struct cmd_context * ctx)4898 static int do_tsinfo(struct cmd_context *ctx)
4899 {
4900 struct ethtool_ts_info info;
4901
4902 if (ctx->argc != 0)
4903 exit_bad_args();
4904
4905 fprintf(stdout, "Time stamping parameters for %s:\n", ctx->devname);
4906 info.cmd = ETHTOOL_GET_TS_INFO;
4907 if (send_ioctl(ctx, &info)) {
4908 perror("Cannot get device time stamping settings");
4909 return -1;
4910 }
4911 dump_tsinfo(&info);
4912 return 0;
4913 }
4914
do_getmodule(struct cmd_context * ctx)4915 static int do_getmodule(struct cmd_context *ctx)
4916 {
4917 struct ethtool_modinfo modinfo;
4918 struct ethtool_eeprom *eeprom;
4919 u32 geeprom_offset = 0;
4920 u32 geeprom_length = 0;
4921 int geeprom_changed = 0;
4922 int geeprom_dump_raw = 0;
4923 int geeprom_dump_hex = 0;
4924 int geeprom_length_seen = 0;
4925 int err;
4926
4927 struct cmdline_info cmdline_geeprom[] = {
4928 {
4929 .name = "offset",
4930 .type = CMDL_U32,
4931 .wanted_val = &geeprom_offset,
4932 },
4933 {
4934 .name = "length",
4935 .type = CMDL_U32,
4936 .wanted_val = &geeprom_length,
4937 .seen_val = &geeprom_length_seen,
4938 },
4939 {
4940 .name = "raw",
4941 .type = CMDL_BOOL,
4942 .wanted_val = &geeprom_dump_raw,
4943 },
4944 {
4945 .name = "hex",
4946 .type = CMDL_BOOL,
4947 .wanted_val = &geeprom_dump_hex,
4948 },
4949 };
4950
4951 parse_generic_cmdline(ctx, &geeprom_changed,
4952 cmdline_geeprom, ARRAY_SIZE(cmdline_geeprom));
4953
4954 if (geeprom_dump_raw && geeprom_dump_hex) {
4955 printf("Hex and raw dump cannot be specified together\n");
4956 return 1;
4957 }
4958
4959 modinfo.cmd = ETHTOOL_GMODULEINFO;
4960 err = send_ioctl(ctx, &modinfo);
4961 if (err < 0) {
4962 perror("Cannot get module EEPROM information");
4963 return 1;
4964 }
4965
4966 if (!geeprom_length_seen)
4967 geeprom_length = modinfo.eeprom_len;
4968
4969 if (modinfo.eeprom_len < geeprom_offset + geeprom_length)
4970 geeprom_length = modinfo.eeprom_len - geeprom_offset;
4971
4972 eeprom = calloc(1, sizeof(*eeprom)+geeprom_length);
4973 if (!eeprom) {
4974 perror("Cannot allocate memory for Module EEPROM data");
4975 return 1;
4976 }
4977
4978 eeprom->cmd = ETHTOOL_GMODULEEEPROM;
4979 eeprom->len = geeprom_length;
4980 eeprom->offset = geeprom_offset;
4981 err = send_ioctl(ctx, eeprom);
4982 if (err < 0) {
4983 int saved_errno = errno;
4984
4985 perror("Cannot get Module EEPROM data");
4986 if (saved_errno == ENODEV || saved_errno == EIO ||
4987 saved_errno == ENXIO)
4988 fprintf(stderr, "SFP module not in cage?\n");
4989 free(eeprom);
4990 return 1;
4991 }
4992
4993 /*
4994 * SFF-8079 EEPROM layout contains the memory available at A0 address on
4995 * the PHY EEPROM.
4996 * SFF-8472 defines a virtual extension of the EEPROM, where the
4997 * microcontroller on the SFP/SFP+ generates a page at the A2 address,
4998 * which contains data relative to optical diagnostics.
4999 * The current kernel implementation returns a blob, which contains:
5000 * - ETH_MODULE_SFF_8079 => The A0 page only.
5001 * - ETH_MODULE_SFF_8472 => The A0 and A2 page concatenated.
5002 */
5003 if (geeprom_dump_raw) {
5004 fwrite(eeprom->data, 1, eeprom->len, stdout);
5005 } else {
5006 if (eeprom->offset != 0 ||
5007 (eeprom->len != modinfo.eeprom_len)) {
5008 geeprom_dump_hex = 1;
5009 } else if (!geeprom_dump_hex) {
5010 switch (modinfo.type) {
5011 #ifdef ETHTOOL_ENABLE_PRETTY_DUMP
5012 case ETH_MODULE_SFF_8079:
5013 sff8079_show_all_ioctl(eeprom->data);
5014 break;
5015 case ETH_MODULE_SFF_8472:
5016 sff8079_show_all_ioctl(eeprom->data);
5017 sff8472_show_all(eeprom->data);
5018 break;
5019 case ETH_MODULE_SFF_8436:
5020 case ETH_MODULE_SFF_8636:
5021 sff8636_show_all_ioctl(eeprom->data,
5022 modinfo.eeprom_len);
5023 break;
5024 #endif
5025 default:
5026 geeprom_dump_hex = 1;
5027 break;
5028 }
5029 }
5030 if (geeprom_dump_hex)
5031 dump_hex(stdout, eeprom->data,
5032 eeprom->len, eeprom->offset);
5033 }
5034
5035 free(eeprom);
5036
5037 return 0;
5038 }
5039
do_geee(struct cmd_context * ctx)5040 static int do_geee(struct cmd_context *ctx)
5041 {
5042 struct ethtool_eee eeecmd;
5043
5044 if (ctx->argc != 0)
5045 exit_bad_args();
5046
5047 eeecmd.cmd = ETHTOOL_GEEE;
5048 if (send_ioctl(ctx, &eeecmd)) {
5049 perror("Cannot get EEE settings");
5050 return 1;
5051 }
5052
5053 fprintf(stdout, "EEE Settings for %s:\n", ctx->devname);
5054 dump_eeecmd(&eeecmd);
5055
5056 return 0;
5057 }
5058
do_seee(struct cmd_context * ctx)5059 static int do_seee(struct cmd_context *ctx)
5060 {
5061 int adv_c = -1, lpi_c = -1, lpi_time_c = -1, eee_c = -1;
5062 int change = -1, change2 = 0;
5063 struct ethtool_eee eeecmd;
5064 struct cmdline_info cmdline_eee[] = {
5065 {
5066 .name = "advertise",
5067 .type = CMDL_U32,
5068 .wanted_val = &adv_c,
5069 .ioctl_val = &eeecmd.advertised,
5070 },
5071 {
5072 .name = "tx-lpi",
5073 .type = CMDL_BOOL,
5074 .wanted_val = &lpi_c,
5075 .ioctl_val = &eeecmd.tx_lpi_enabled,
5076 },
5077 {
5078 .name = "tx-timer",
5079 .type = CMDL_U32,
5080 .wanted_val = &lpi_time_c,
5081 .ioctl_val = &eeecmd.tx_lpi_timer,
5082 },
5083 {
5084 .name = "eee",
5085 .type = CMDL_BOOL,
5086 .wanted_val = &eee_c,
5087 .ioctl_val = &eeecmd.eee_enabled,
5088 },
5089 };
5090
5091 if (ctx->argc == 0)
5092 exit_bad_args();
5093
5094 parse_generic_cmdline(ctx, &change, cmdline_eee,
5095 ARRAY_SIZE(cmdline_eee));
5096
5097 eeecmd.cmd = ETHTOOL_GEEE;
5098 if (send_ioctl(ctx, &eeecmd)) {
5099 perror("Cannot get EEE settings");
5100 return 1;
5101 }
5102
5103 do_generic_set(cmdline_eee, ARRAY_SIZE(cmdline_eee), &change2);
5104
5105 if (change2) {
5106 eeecmd.cmd = ETHTOOL_SEEE;
5107 if (send_ioctl(ctx, &eeecmd)) {
5108 perror("Cannot set EEE settings");
5109 return 1;
5110 }
5111 }
5112
5113 return 0;
5114 }
5115
5116 /* copy of net/ethtool/common.c */
5117 char
5118 tunable_strings[__ETHTOOL_TUNABLE_COUNT][ETH_GSTRING_LEN] = {
5119 [ETHTOOL_ID_UNSPEC] = "Unspec",
5120 [ETHTOOL_RX_COPYBREAK] = "rx-copybreak",
5121 [ETHTOOL_TX_COPYBREAK] = "tx-copybreak",
5122 [ETHTOOL_TX_COPYBREAK_BUF_SIZE] = "tx-buf-size",
5123 [ETHTOOL_PFC_PREVENTION_TOUT] = "pfc-prevention-tout",
5124 };
5125
5126 union ethtool_tunable_info_val {
5127 uint8_t u8;
5128 uint16_t u16;
5129 uint32_t u32;
5130 uint64_t u64;
5131 int8_t s8;
5132 int16_t s16;
5133 int32_t s32;
5134 int64_t s64;
5135 };
5136
5137 struct ethtool_tunable_info {
5138 enum tunable_id t_id;
5139 enum tunable_type_id t_type_id;
5140 size_t size;
5141 cmdline_type_t type;
5142 union ethtool_tunable_info_val wanted;
5143 int seen;
5144 };
5145
5146 static struct ethtool_tunable_info tunables_info[] = {
5147 { .t_id = ETHTOOL_RX_COPYBREAK,
5148 .t_type_id = ETHTOOL_TUNABLE_U32,
5149 .size = sizeof(u32),
5150 .type = CMDL_U32,
5151 },
5152 { .t_id = ETHTOOL_TX_COPYBREAK,
5153 .t_type_id = ETHTOOL_TUNABLE_U32,
5154 .size = sizeof(u32),
5155 .type = CMDL_U32,
5156 },
5157 { .t_id = ETHTOOL_PFC_PREVENTION_TOUT,
5158 .t_type_id = ETHTOOL_TUNABLE_U16,
5159 .size = sizeof(u16),
5160 .type = CMDL_U16,
5161 },
5162 { .t_id = ETHTOOL_TX_COPYBREAK_BUF_SIZE,
5163 .t_type_id = ETHTOOL_TUNABLE_U32,
5164 .size = sizeof(u32),
5165 .type = CMDL_U32,
5166 },
5167 };
5168 #define TUNABLES_INFO_SIZE ARRAY_SIZE(tunables_info)
5169
do_stunable(struct cmd_context * ctx)5170 static int do_stunable(struct cmd_context *ctx)
5171 {
5172 struct cmdline_info cmdline_tunable[TUNABLES_INFO_SIZE];
5173 struct ethtool_tunable_info *tinfo = tunables_info;
5174 int changed = 0;
5175 unsigned int i;
5176
5177 for (i = 0; i < TUNABLES_INFO_SIZE; i++) {
5178 cmdline_tunable[i].name = tunable_strings[tinfo[i].t_id];
5179 cmdline_tunable[i].type = tinfo[i].type;
5180 cmdline_tunable[i].wanted_val = &tinfo[i].wanted;
5181 cmdline_tunable[i].seen_val = &tinfo[i].seen;
5182 }
5183
5184 parse_generic_cmdline(ctx, &changed, cmdline_tunable, TUNABLES_INFO_SIZE);
5185 if (!changed)
5186 exit_bad_args();
5187
5188 for (i = 0; i < TUNABLES_INFO_SIZE; i++) {
5189 struct ethtool_tunable *tuna;
5190 size_t size;
5191 int ret;
5192
5193 if (!tinfo[i].seen)
5194 continue;
5195
5196 size = sizeof(*tuna) + tinfo[i].size;
5197 tuna = calloc(1, size);
5198 if (!tuna) {
5199 perror(tunable_strings[tinfo[i].t_id]);
5200 return 1;
5201 }
5202 tuna->cmd = ETHTOOL_STUNABLE;
5203 tuna->id = tinfo[i].t_id;
5204 tuna->type_id = tinfo[i].t_type_id;
5205 tuna->len = tinfo[i].size;
5206 memcpy(tuna->data, &tinfo[i].wanted, tuna->len);
5207 ret = send_ioctl(ctx, tuna);
5208 if (ret) {
5209 perror(tunable_strings[tuna->id]);
5210 free(tuna);
5211 return ret;
5212 }
5213 free(tuna);
5214 }
5215 return 0;
5216 }
5217
print_tunable(struct ethtool_tunable * tuna)5218 static void print_tunable(struct ethtool_tunable *tuna)
5219 {
5220 char *name = tunable_strings[tuna->id];
5221 union ethtool_tunable_info_val *val;
5222
5223 val = (union ethtool_tunable_info_val *)tuna->data;
5224 switch (tuna->type_id) {
5225 case ETHTOOL_TUNABLE_U8:
5226 fprintf(stdout, "%s: %" PRIu8 "\n", name, val->u8);
5227 break;
5228 case ETHTOOL_TUNABLE_U16:
5229 fprintf(stdout, "%s: %" PRIu16 "\n", name, val->u16);
5230 break;
5231 case ETHTOOL_TUNABLE_U32:
5232 fprintf(stdout, "%s: %" PRIu32 "\n", name, val->u32);
5233 break;
5234 case ETHTOOL_TUNABLE_U64:
5235 fprintf(stdout, "%s: %" PRIu64 "\n", name, val->u64);
5236 break;
5237 case ETHTOOL_TUNABLE_S8:
5238 fprintf(stdout, "%s: %" PRId8 "\n", name, val->s8);
5239 break;
5240 case ETHTOOL_TUNABLE_S16:
5241 fprintf(stdout, "%s: %" PRId16 "\n", name, val->s16);
5242 break;
5243 case ETHTOOL_TUNABLE_S32:
5244 fprintf(stdout, "%s: %" PRId32 "\n", name, val->s32);
5245 break;
5246 case ETHTOOL_TUNABLE_S64:
5247 fprintf(stdout, "%s: %" PRId64 "\n", name, val->s64);
5248 break;
5249 default:
5250 fprintf(stdout, "%s: Unknown format\n", name);
5251 }
5252 }
5253
do_gtunable(struct cmd_context * ctx)5254 static int do_gtunable(struct cmd_context *ctx)
5255 {
5256 struct ethtool_tunable_info *tinfo = tunables_info;
5257 char **argp = ctx->argp;
5258 unsigned int argc = ctx->argc;
5259 unsigned int i, j;
5260
5261 if (argc < 1)
5262 exit_bad_args();
5263
5264 for (i = 0; i < argc; i++) {
5265 int valid = 0;
5266
5267 for (j = 0; j < TUNABLES_INFO_SIZE; j++) {
5268 char *ts = tunable_strings[tinfo[j].t_id];
5269 struct ethtool_tunable *tuna;
5270 int ret;
5271
5272 if (strcmp(argp[i], ts))
5273 continue;
5274 valid = 1;
5275
5276 tuna = calloc(1, sizeof(*tuna) + tinfo[j].size);
5277 if (!tuna) {
5278 perror(ts);
5279 return 1;
5280 }
5281 tuna->cmd = ETHTOOL_GTUNABLE;
5282 tuna->id = tinfo[j].t_id;
5283 tuna->type_id = tinfo[j].t_type_id;
5284 tuna->len = tinfo[j].size;
5285 ret = send_ioctl(ctx, tuna);
5286 if (ret) {
5287 fprintf(stderr, "%s: Cannot get tunable\n", ts);
5288 free(tuna);
5289 return ret;
5290 }
5291 print_tunable(tuna);
5292 free(tuna);
5293 }
5294 if (!valid)
5295 exit_bad_args();
5296 }
5297 return 0;
5298 }
5299
do_get_phy_tunable(struct cmd_context * ctx)5300 static int do_get_phy_tunable(struct cmd_context *ctx)
5301 {
5302 unsigned int argc = ctx->argc;
5303 char **argp = ctx->argp;
5304
5305 if (argc < 1)
5306 exit_bad_args();
5307
5308 if (!strcmp(argp[0], "downshift")) {
5309 struct {
5310 struct ethtool_tunable ds;
5311 u8 count;
5312 } cont;
5313
5314 cont.ds.cmd = ETHTOOL_PHY_GTUNABLE;
5315 cont.ds.id = ETHTOOL_PHY_DOWNSHIFT;
5316 cont.ds.type_id = ETHTOOL_TUNABLE_U8;
5317 cont.ds.len = 1;
5318 if (send_ioctl(ctx, &cont.ds) < 0) {
5319 perror("Cannot Get PHY downshift count");
5320 return 87;
5321 }
5322 if (cont.count)
5323 fprintf(stdout, "Downshift count: %d\n", cont.count);
5324 else
5325 fprintf(stdout, "Downshift disabled\n");
5326 } else if (!strcmp(argp[0], "fast-link-down")) {
5327 struct {
5328 struct ethtool_tunable fld;
5329 u8 msecs;
5330 } cont;
5331
5332 cont.fld.cmd = ETHTOOL_PHY_GTUNABLE;
5333 cont.fld.id = ETHTOOL_PHY_FAST_LINK_DOWN;
5334 cont.fld.type_id = ETHTOOL_TUNABLE_U8;
5335 cont.fld.len = 1;
5336 if (send_ioctl(ctx, &cont.fld) < 0) {
5337 perror("Cannot Get PHY Fast Link Down value");
5338 return 87;
5339 }
5340
5341 if (cont.msecs == ETHTOOL_PHY_FAST_LINK_DOWN_ON)
5342 fprintf(stdout, "Fast Link Down enabled\n");
5343 else if (cont.msecs == ETHTOOL_PHY_FAST_LINK_DOWN_OFF)
5344 fprintf(stdout, "Fast Link Down disabled\n");
5345 else
5346 fprintf(stdout, "Fast Link Down enabled, %d msecs\n",
5347 cont.msecs);
5348 } else if (!strcmp(argp[0], "energy-detect-power-down")) {
5349 struct {
5350 struct ethtool_tunable ds;
5351 u16 msecs;
5352 } cont;
5353
5354 cont.ds.cmd = ETHTOOL_PHY_GTUNABLE;
5355 cont.ds.id = ETHTOOL_PHY_EDPD;
5356 cont.ds.type_id = ETHTOOL_TUNABLE_U16;
5357 cont.ds.len = 2;
5358 if (send_ioctl(ctx, &cont.ds) < 0) {
5359 perror("Cannot Get PHY Energy Detect Power Down value");
5360 return 87;
5361 }
5362
5363 if (cont.msecs == ETHTOOL_PHY_EDPD_DISABLE)
5364 fprintf(stdout, "Energy Detect Power Down: disabled\n");
5365 else if (cont.msecs == ETHTOOL_PHY_EDPD_NO_TX)
5366 fprintf(stdout,
5367 "Energy Detect Power Down: enabled, TX disabled\n");
5368 else
5369 fprintf(stdout,
5370 "Energy Detect Power Down: enabled, TX %u msecs\n",
5371 cont.msecs);
5372 } else {
5373 exit_bad_args();
5374 }
5375
5376 return 0;
5377 }
5378
parse_reset(char * val,__u32 bitset,char * arg,__u32 * data)5379 static __u32 parse_reset(char *val, __u32 bitset, char *arg, __u32 *data)
5380 {
5381 __u32 bitval = 0;
5382 int i;
5383
5384 /* Check for component match */
5385 for (i = 0; val[i] != '\0'; i++)
5386 if (arg[i] != val[i])
5387 return 0;
5388
5389 /* Check if component has -shared specified or not */
5390 if (arg[i] == '\0')
5391 bitval = bitset;
5392 else if (!strcmp(arg+i, "-shared"))
5393 bitval = bitset << ETH_RESET_SHARED_SHIFT;
5394
5395 if (bitval) {
5396 *data |= bitval;
5397 return 1;
5398 }
5399 return 0;
5400 }
5401
do_reset(struct cmd_context * ctx)5402 static int do_reset(struct cmd_context *ctx)
5403 {
5404 struct ethtool_value resetinfo;
5405 __u32 data;
5406 unsigned int argc = ctx->argc;
5407 char **argp = ctx->argp;
5408 unsigned int i;
5409
5410 if (argc == 0)
5411 exit_bad_args();
5412
5413 data = 0;
5414
5415 for (i = 0; i < argc; i++) {
5416 if (!strcmp(argp[i], "flags")) {
5417 __u32 flags;
5418
5419 i++;
5420 if (i >= argc)
5421 exit_bad_args();
5422 flags = strtoul(argp[i], NULL, 0);
5423 if (flags == 0)
5424 exit_bad_args();
5425 else
5426 data |= flags;
5427 } else if (parse_reset("mgmt", ETH_RESET_MGMT,
5428 argp[i], &data)) {
5429 } else if (parse_reset("irq", ETH_RESET_IRQ,
5430 argp[i], &data)) {
5431 } else if (parse_reset("dma", ETH_RESET_DMA,
5432 argp[i], &data)) {
5433 } else if (parse_reset("filter", ETH_RESET_FILTER,
5434 argp[i], &data)) {
5435 } else if (parse_reset("offload", ETH_RESET_OFFLOAD,
5436 argp[i], &data)) {
5437 } else if (parse_reset("mac", ETH_RESET_MAC,
5438 argp[i], &data)) {
5439 } else if (parse_reset("phy", ETH_RESET_PHY,
5440 argp[i], &data)) {
5441 } else if (parse_reset("ram", ETH_RESET_RAM,
5442 argp[i], &data)) {
5443 } else if (parse_reset("ap", ETH_RESET_AP,
5444 argp[i], &data)) {
5445 } else if (!strcmp(argp[i], "dedicated")) {
5446 data |= ETH_RESET_DEDICATED;
5447 } else if (!strcmp(argp[i], "all")) {
5448 data |= ETH_RESET_ALL;
5449 } else {
5450 exit_bad_args();
5451 }
5452 }
5453
5454 resetinfo.cmd = ETHTOOL_RESET;
5455 resetinfo.data = data;
5456 fprintf(stdout, "ETHTOOL_RESET 0x%x\n", resetinfo.data);
5457
5458 if (send_ioctl(ctx, &resetinfo)) {
5459 perror("Cannot issue ETHTOOL_RESET");
5460 return 1;
5461 }
5462
5463 fprintf(stdout, "Components reset: 0x%x\n", data & ~resetinfo.data);
5464 if (resetinfo.data)
5465 fprintf(stdout, "Components not reset: 0x%x\n", resetinfo.data);
5466
5467 return 0;
5468 }
5469
parse_named_bool(struct cmd_context * ctx,const char * name,u8 * on)5470 static int parse_named_bool(struct cmd_context *ctx, const char *name, u8 *on)
5471 {
5472 if (ctx->argc < 2)
5473 return 0;
5474
5475 if (strcmp(*ctx->argp, name))
5476 return 0;
5477
5478 if (!strcmp(*(ctx->argp + 1), "on")) {
5479 *on = 1;
5480 } else if (!strcmp(*(ctx->argp + 1), "off")) {
5481 *on = 0;
5482 } else {
5483 fprintf(stderr, "Invalid boolean\n");
5484 exit_bad_args();
5485 }
5486
5487 ctx->argc -= 2;
5488 ctx->argp += 2;
5489
5490 return 1;
5491 }
5492
parse_named_uint(struct cmd_context * ctx,const char * name,unsigned long long * val,unsigned long long max)5493 static int parse_named_uint(struct cmd_context *ctx,
5494 const char *name,
5495 unsigned long long *val,
5496 unsigned long long max)
5497 {
5498 if (ctx->argc < 2)
5499 return 0;
5500
5501 if (strcmp(*ctx->argp, name))
5502 return 0;
5503
5504 *val = get_uint_range(*(ctx->argp + 1), 0, max);
5505
5506 ctx->argc -= 2;
5507 ctx->argp += 2;
5508
5509 return 1;
5510 }
5511
parse_named_u8(struct cmd_context * ctx,const char * name,u8 * val)5512 static int parse_named_u8(struct cmd_context *ctx, const char *name, u8 *val)
5513 {
5514 unsigned long long val1;
5515 int ret;
5516
5517 ret = parse_named_uint(ctx, name, &val1, 0xff);
5518 if (ret)
5519 *val = val1;
5520
5521 return ret;
5522 }
5523
parse_named_u16(struct cmd_context * ctx,const char * name,u16 * val)5524 static int parse_named_u16(struct cmd_context *ctx, const char *name, u16 *val)
5525 {
5526 unsigned long long val1;
5527 int ret;
5528
5529 ret = parse_named_uint(ctx, name, &val1, 0xffff);
5530 if (ret)
5531 *val = val1;
5532
5533 return ret;
5534 }
5535
do_set_phy_tunable(struct cmd_context * ctx)5536 static int do_set_phy_tunable(struct cmd_context *ctx)
5537 {
5538 int err = 0;
5539 u8 ds_cnt = DOWNSHIFT_DEV_DEFAULT_COUNT;
5540 u8 ds_changed = 0, ds_has_cnt = 0, ds_enable = 0;
5541 u8 fld_changed = 0, fld_enable = 0;
5542 u8 fld_msecs = ETHTOOL_PHY_FAST_LINK_DOWN_ON;
5543 u8 edpd_changed = 0, edpd_enable = 0;
5544 u16 edpd_tx_interval = ETHTOOL_PHY_EDPD_DFLT_TX_MSECS;
5545
5546 /* Parse arguments */
5547 if (parse_named_bool(ctx, "downshift", &ds_enable)) {
5548 ds_changed = 1;
5549 ds_has_cnt = parse_named_u8(ctx, "count", &ds_cnt);
5550 } else if (parse_named_bool(ctx, "fast-link-down", &fld_enable)) {
5551 fld_changed = 1;
5552 if (fld_enable)
5553 parse_named_u8(ctx, "msecs", &fld_msecs);
5554 } else if (parse_named_bool(ctx, "energy-detect-power-down",
5555 &edpd_enable)) {
5556 edpd_changed = 1;
5557 if (edpd_enable)
5558 parse_named_u16(ctx, "msecs", &edpd_tx_interval);
5559 } else {
5560 exit_bad_args();
5561 }
5562
5563 /* Validate parameters */
5564 if (ds_changed) {
5565 if (!ds_enable && ds_has_cnt) {
5566 fprintf(stderr, "'count' may not be set when downshift "
5567 "is off.\n");
5568 exit_bad_args();
5569 }
5570
5571 if (ds_enable && ds_has_cnt && ds_cnt == 0) {
5572 fprintf(stderr, "'count' may not be zero.\n");
5573 exit_bad_args();
5574 }
5575
5576 if (!ds_enable)
5577 ds_cnt = DOWNSHIFT_DEV_DISABLE;
5578 } else if (fld_changed) {
5579 if (!fld_enable)
5580 fld_msecs = ETHTOOL_PHY_FAST_LINK_DOWN_OFF;
5581 else if (fld_msecs == ETHTOOL_PHY_FAST_LINK_DOWN_OFF)
5582 exit_bad_args();
5583 } else if (edpd_changed) {
5584 if (!edpd_enable)
5585 edpd_tx_interval = ETHTOOL_PHY_EDPD_DISABLE;
5586 else if (edpd_tx_interval == 0)
5587 edpd_tx_interval = ETHTOOL_PHY_EDPD_NO_TX;
5588 else if (edpd_tx_interval > ETHTOOL_PHY_EDPD_NO_TX) {
5589 fprintf(stderr, "'msecs' max value is %d.\n",
5590 (ETHTOOL_PHY_EDPD_NO_TX - 1));
5591 exit_bad_args();
5592 }
5593 }
5594
5595 /* Do it */
5596 if (ds_changed) {
5597 struct {
5598 struct ethtool_tunable ds;
5599 u8 count;
5600 } cont;
5601
5602 cont.ds.cmd = ETHTOOL_PHY_STUNABLE;
5603 cont.ds.id = ETHTOOL_PHY_DOWNSHIFT;
5604 cont.ds.type_id = ETHTOOL_TUNABLE_U8;
5605 cont.ds.len = 1;
5606 cont.count = ds_cnt;
5607 err = send_ioctl(ctx, &cont.ds);
5608 if (err < 0) {
5609 perror("Cannot Set PHY downshift count");
5610 err = 87;
5611 }
5612 } else if (fld_changed) {
5613 struct {
5614 struct ethtool_tunable fld;
5615 u8 msecs;
5616 } cont;
5617
5618 cont.fld.cmd = ETHTOOL_PHY_STUNABLE;
5619 cont.fld.id = ETHTOOL_PHY_FAST_LINK_DOWN;
5620 cont.fld.type_id = ETHTOOL_TUNABLE_U8;
5621 cont.fld.len = 1;
5622 cont.msecs = fld_msecs;
5623 err = send_ioctl(ctx, &cont.fld);
5624 if (err < 0) {
5625 perror("Cannot Set PHY Fast Link Down value");
5626 err = 87;
5627 }
5628 } else if (edpd_changed) {
5629 struct {
5630 struct ethtool_tunable fld;
5631 u16 msecs;
5632 } cont;
5633
5634 cont.fld.cmd = ETHTOOL_PHY_STUNABLE;
5635 cont.fld.id = ETHTOOL_PHY_EDPD;
5636 cont.fld.type_id = ETHTOOL_TUNABLE_U16;
5637 cont.fld.len = 2;
5638 cont.msecs = edpd_tx_interval;
5639 err = send_ioctl(ctx, &cont.fld);
5640 if (err < 0) {
5641 perror("Cannot Set PHY Energy Detect Power Down");
5642 err = 87;
5643 }
5644 }
5645
5646 return err;
5647 }
5648
fecmode_str_to_type(const char * str)5649 static int fecmode_str_to_type(const char *str)
5650 {
5651 if (!strcasecmp(str, "auto"))
5652 return ETHTOOL_FEC_AUTO;
5653 if (!strcasecmp(str, "off"))
5654 return ETHTOOL_FEC_OFF;
5655 if (!strcasecmp(str, "rs"))
5656 return ETHTOOL_FEC_RS;
5657 if (!strcasecmp(str, "baser"))
5658 return ETHTOOL_FEC_BASER;
5659 if (!strcasecmp(str, "llrs"))
5660 return ETHTOOL_FEC_LLRS;
5661 return 0;
5662 }
5663
do_gfec(struct cmd_context * ctx)5664 static int do_gfec(struct cmd_context *ctx)
5665 {
5666 struct ethtool_fecparam feccmd = { 0 };
5667 int rv;
5668
5669 if (ctx->argc != 0)
5670 exit_bad_args();
5671
5672 feccmd.cmd = ETHTOOL_GFECPARAM;
5673 rv = send_ioctl(ctx, &feccmd);
5674 if (rv != 0) {
5675 perror("Cannot get FEC settings");
5676 return rv;
5677 }
5678
5679 fprintf(stdout, "FEC parameters for %s:\n", ctx->devname);
5680 fprintf(stdout, "Supported/Configured FEC encodings:");
5681 dump_fec(feccmd.fec);
5682 fprintf(stdout, "\n");
5683
5684 fprintf(stdout, "Active FEC encoding:");
5685 dump_fec(feccmd.active_fec);
5686 fprintf(stdout, "\n");
5687
5688 return 0;
5689 }
5690
do_sfec(struct cmd_context * ctx)5691 static int do_sfec(struct cmd_context *ctx)
5692 {
5693 enum { ARG_NONE, ARG_ENCODING } state = ARG_NONE;
5694 struct ethtool_fecparam feccmd;
5695 int fecmode = 0, newmode;
5696 unsigned int i;
5697 int rv;
5698
5699 for (i = 0; i < ctx->argc; i++) {
5700 if (!strcmp(ctx->argp[i], "encoding")) {
5701 state = ARG_ENCODING;
5702 continue;
5703 }
5704 if (state == ARG_ENCODING) {
5705 newmode = fecmode_str_to_type(ctx->argp[i]);
5706 if (!newmode)
5707 exit_bad_args();
5708 fecmode |= newmode;
5709 continue;
5710 }
5711 exit_bad_args();
5712 }
5713
5714 if (!fecmode)
5715 exit_bad_args();
5716
5717 feccmd.cmd = ETHTOOL_SFECPARAM;
5718 feccmd.fec = fecmode;
5719 rv = send_ioctl(ctx, &feccmd);
5720 if (rv != 0) {
5721 perror("Cannot set FEC settings");
5722 return rv;
5723 }
5724
5725 return 0;
5726 }
5727
5728 static int do_perqueue(struct cmd_context *ctx);
5729
5730 #ifndef TEST_ETHTOOL
send_ioctl(struct cmd_context * ctx,void * cmd)5731 int send_ioctl(struct cmd_context *ctx, void *cmd)
5732 {
5733 ctx->ifr.ifr_data = cmd;
5734 return ioctl(ctx->fd, SIOCETHTOOL, &ctx->ifr);
5735 }
5736 #endif
5737
5738 static int show_usage(struct cmd_context *ctx);
5739
5740 struct option {
5741 const char *opts;
5742 bool no_dev;
5743 bool json;
5744 bool targets_phy;
5745 int (*func)(struct cmd_context *);
5746 nl_chk_t nlchk;
5747 nl_func_t nlfunc;
5748 const char *help;
5749 const char *xhelp;
5750 };
5751
5752 static const struct option args[] = {
5753 {
5754 /* "default" entry when no switch is used */
5755 .opts = "",
5756 .json = true,
5757 .func = do_gset,
5758 .nlfunc = nl_gset,
5759 .help = "Display standard information about device",
5760 },
5761 {
5762 .opts = "-s|--change",
5763 .func = do_sset,
5764 .nlfunc = nl_sset,
5765 .help = "Change generic options",
5766 .xhelp = " [ speed %d ]\n"
5767 " [ lanes %d ]\n"
5768 " [ duplex half|full ]\n"
5769 " [ port tp|aui|bnc|mii|fibre|da ]\n"
5770 " [ mdix auto|on|off ]\n"
5771 " [ autoneg on|off ]\n"
5772 " [ advertise %x[/%x] | mode on|off ... [--] ]\n"
5773 " [ phyad %d ]\n"
5774 " [ xcvr internal|external ]\n"
5775 " [ wol %d[/%d] | p|u|m|b|a|g|s|f|d... ]\n"
5776 " [ sopass %x:%x:%x:%x:%x:%x ]\n"
5777 " [ msglvl %d[/%d] | type on|off ... [--] ]\n"
5778 " [ master-slave preferred-master|preferred-slave|forced-master|forced-slave ]\n"
5779 },
5780 {
5781 .opts = "-a|--show-pause",
5782 .json = true,
5783 .func = do_gpause,
5784 .nlfunc = nl_gpause,
5785 .help = "Show pause options",
5786 .xhelp = " [ --src aggregate | emac | pmac ]\n"
5787 },
5788 {
5789 .opts = "-A|--pause",
5790 .func = do_spause,
5791 .nlfunc = nl_spause,
5792 .help = "Set pause options",
5793 .xhelp = " [ autoneg on|off ]\n"
5794 " [ rx on|off ]\n"
5795 " [ tx on|off ]\n"
5796 },
5797 {
5798 .opts = "-c|--show-coalesce",
5799 .json = true,
5800 .func = do_gcoalesce,
5801 .nlfunc = nl_gcoalesce,
5802 .help = "Show coalesce options"
5803 },
5804 {
5805 .opts = "-C|--coalesce",
5806 .func = do_scoalesce,
5807 .nlfunc = nl_scoalesce,
5808 .help = "Set coalesce options",
5809 .xhelp = " [adaptive-rx on|off]\n"
5810 " [adaptive-tx on|off]\n"
5811 " [rx-usecs N]\n"
5812 " [rx-frames N]\n"
5813 " [rx-usecs-irq N]\n"
5814 " [rx-frames-irq N]\n"
5815 " [tx-usecs N]\n"
5816 " [tx-frames N]\n"
5817 " [tx-usecs-irq N]\n"
5818 " [tx-frames-irq N]\n"
5819 " [stats-block-usecs N]\n"
5820 " [pkt-rate-low N]\n"
5821 " [rx-usecs-low N]\n"
5822 " [rx-frames-low N]\n"
5823 " [tx-usecs-low N]\n"
5824 " [tx-frames-low N]\n"
5825 " [pkt-rate-high N]\n"
5826 " [rx-usecs-high N]\n"
5827 " [rx-frames-high N]\n"
5828 " [tx-usecs-high N]\n"
5829 " [tx-frames-high N]\n"
5830 " [sample-interval N]\n"
5831 " [cqe-mode-rx on|off]\n"
5832 " [cqe-mode-tx on|off]\n"
5833 " [tx-aggr-max-bytes N]\n"
5834 " [tx-aggr-max-frames N]\n"
5835 " [tx-aggr-time-usecs N]\n"
5836 },
5837 {
5838 .opts = "-g|--show-ring",
5839 .json = true,
5840 .func = do_gring,
5841 .nlfunc = nl_gring,
5842 .help = "Query RX/TX ring parameters"
5843 },
5844 {
5845 .opts = "-G|--set-ring",
5846 .func = do_sring,
5847 .nlfunc = nl_sring,
5848 .help = "Set RX/TX ring parameters",
5849 .xhelp = " [ rx N ]\n"
5850 " [ rx-mini N ]\n"
5851 " [ rx-jumbo N ]\n"
5852 " [ tx N ]\n"
5853 " [ rx-buf-len N ]\n"
5854 " [ tcp-data-split auto|on|off ]\n"
5855 " [ cqe-size N ]\n"
5856 " [ tx-push on|off ]\n"
5857 " [ rx-push on|off ]\n"
5858 " [ tx-push-buf-len N]\n"
5859 },
5860 {
5861 .opts = "-k|--show-features|--show-offload",
5862 .json = true,
5863 .func = do_gfeatures,
5864 .nlfunc = nl_gfeatures,
5865 .help = "Get state of protocol offload and other features"
5866 },
5867 {
5868 .opts = "-K|--features|--offload",
5869 .func = do_sfeatures,
5870 .nlfunc = nl_sfeatures,
5871 .help = "Set protocol offload and other features",
5872 .xhelp = " FEATURE on|off ...\n"
5873 },
5874 {
5875 .opts = "-i|--driver",
5876 .func = do_gdrv,
5877 .help = "Show driver information"
5878 },
5879 {
5880 .opts = "-d|--register-dump",
5881 .func = do_gregs,
5882 .help = "Do a register dump",
5883 .xhelp = " [ raw on|off ]\n"
5884 " [ file FILENAME ]\n"
5885 },
5886 {
5887 .opts = "-e|--eeprom-dump",
5888 .func = do_geeprom,
5889 .help = "Do a EEPROM dump",
5890 .xhelp = " [ raw on|off ]\n"
5891 " [ offset N ]\n"
5892 " [ length N ]\n"
5893 },
5894 {
5895 .opts = "-E|--change-eeprom",
5896 .func = do_seeprom,
5897 .help = "Change bytes in device EEPROM",
5898 .xhelp = " [ magic N ]\n"
5899 " [ offset N ]\n"
5900 " [ length N ]\n"
5901 " [ value N ]\n"
5902 },
5903 {
5904 .opts = "-r|--negotiate",
5905 .func = do_nway_rst,
5906 .help = "Restart N-WAY negotiation"
5907 },
5908 {
5909 .opts = "-p|--identify",
5910 .func = do_phys_id,
5911 .help = "Show visible port identification (e.g. blinking)",
5912 .xhelp = " [ TIME-IN-SECONDS ]\n"
5913 },
5914 {
5915 .opts = "-t|--test",
5916 .func = do_test,
5917 .help = "Execute adapter self test",
5918 .xhelp = " [ online | offline | external_lb ]\n"
5919 },
5920 {
5921 .opts = "-S|--statistics",
5922 .json = true,
5923 .func = do_gnicstats,
5924 .nlchk = nl_gstats_chk,
5925 .nlfunc = nl_gstats,
5926 .help = "Show adapter statistics",
5927 .xhelp = " [ --all-groups | --groups [eth-phy] [eth-mac] [eth-ctrl] [rmon] ]\n"
5928 " [ --src aggregate | emac | pmac ]\n"
5929 },
5930 {
5931 .opts = "--phy-statistics",
5932 .func = do_gphystats,
5933 .help = "Show phy statistics"
5934 },
5935 {
5936 .opts = "-n|-u|--show-nfc|--show-ntuple",
5937 .func = do_grxclass,
5938 .help = "Show Rx network flow classification options or rules",
5939 .xhelp = " [ rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|"
5940 "gtpc4|gtpc4t|gtpu4|gtpu4e|gtpu4u|gtpu4d|tcp6|udp6|ah6|esp6|sctp6|"
5941 "gtpc6|gtpc6t|gtpu6|gtpu6e|gtpu6u|gtpu6d [context %d] |\n"
5942 " rule %d ]\n"
5943 },
5944 {
5945 .opts = "-N|-U|--config-nfc|--config-ntuple",
5946 .func = do_srxclass,
5947 .help = "Configure Rx network flow classification options or rules",
5948 .xhelp = " rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|"
5949 "gtpc4|gtpc4t|gtpu4|gtpu4e|gtpu4u|gtpu4d|tcp6|udp6|ah6|esp6|sctp6"
5950 "|gtpc6|gtpc6t|gtpu6|gtpu6e|gtpu6u|gtpu6d m|v|t|s|d|f|n|r|e... [context %d] |\n"
5951 " flow-type ether|ip4|tcp4|udp4|sctp4|ah4|esp4|"
5952 "ip6|tcp6|udp6|ah6|esp6|sctp6\n"
5953 " [ src %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n"
5954 " [ dst %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n"
5955 " [ proto %d [m %x] ]\n"
5956 " [ src-ip IP-ADDRESS [m IP-ADDRESS] ]\n"
5957 " [ dst-ip IP-ADDRESS [m IP-ADDRESS] ]\n"
5958 " [ tos %d [m %x] ]\n"
5959 " [ tclass %d [m %x] ]\n"
5960 " [ l4proto %d [m %x] ]\n"
5961 " [ src-port %d [m %x] ]\n"
5962 " [ dst-port %d [m %x] ]\n"
5963 " [ spi %d [m %x] ]\n"
5964 " [ vlan-etype %x [m %x] ]\n"
5965 " [ vlan %x [m %x] ]\n"
5966 " [ user-def %x [m %x] ]\n"
5967 " [ dst-mac %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n"
5968 " [ action %d ] | [ vf %d queue %d ]\n"
5969 " [ context %d ]\n"
5970 " [ loc %d ] |\n"
5971 " delete %d\n"
5972 },
5973 {
5974 .opts = "-T|--show-time-stamping",
5975 .func = do_tsinfo,
5976 .nlfunc = nl_tsinfo,
5977 .help = "Show time stamping capabilities"
5978 },
5979 {
5980 .opts = "-x|--show-rxfh-indir|--show-rxfh",
5981 .json = true,
5982 .func = do_grxfh,
5983 .nlfunc = nl_grss,
5984 .help = "Show Rx flow hash indirection table and/or RSS hash key",
5985 .xhelp = " [ context %d ]\n"
5986 },
5987 {
5988 .opts = "-X|--set-rxfh-indir|--rxfh",
5989 .func = do_srxfh,
5990 .help = "Set Rx flow hash indirection table and/or RSS hash key",
5991 .xhelp = " [ context %d|new ]\n"
5992 " [ equal N | weight W0 W1 ... | default ]\n"
5993 " [ hkey %x:%x:%x:%x:%x:.... ]\n"
5994 " [ hfunc FUNC ]\n"
5995 " [ xfrm symmetric-xor|none ]\n"
5996 " [ delete ]\n"
5997 },
5998 {
5999 .opts = "-f|--flash",
6000 .func = do_flash,
6001 .help = "Flash firmware image from the specified file to a region on the device",
6002 .xhelp = " FILENAME [ REGION-NUMBER-TO-FLASH ]\n"
6003 },
6004 {
6005 .opts = "-P|--show-permaddr",
6006 .func = do_permaddr,
6007 .nlfunc = nl_permaddr,
6008 .help = "Show permanent hardware address"
6009 },
6010 {
6011 .opts = "-w|--get-dump",
6012 .func = do_getfwdump,
6013 .help = "Get dump flag, data",
6014 .xhelp = " [ data FILENAME ]\n"
6015 },
6016 {
6017 .opts = "-W|--set-dump",
6018 .func = do_setfwdump,
6019 .help = "Set dump flag of the device",
6020 .xhelp = " N\n"
6021 },
6022 {
6023 .opts = "-l|--show-channels",
6024 .func = do_gchannels,
6025 .nlfunc = nl_gchannels,
6026 .help = "Query Channels"
6027 },
6028 {
6029 .opts = "-L|--set-channels",
6030 .func = do_schannels,
6031 .nlfunc = nl_schannels,
6032 .help = "Set Channels",
6033 .xhelp = " [ rx N ]\n"
6034 " [ tx N ]\n"
6035 " [ other N ]\n"
6036 " [ combined N ]\n"
6037 },
6038 {
6039 .opts = "--show-priv-flags",
6040 .func = do_gprivflags,
6041 .nlfunc = nl_gprivflags,
6042 .help = "Query private flags"
6043 },
6044 {
6045 .opts = "--set-priv-flags",
6046 .func = do_sprivflags,
6047 .nlfunc = nl_sprivflags,
6048 .help = "Set private flags",
6049 .xhelp = " FLAG on|off ...\n"
6050 },
6051 {
6052 .opts = "-m|--dump-module-eeprom|--module-info",
6053 .func = do_getmodule,
6054 .nlfunc = nl_getmodule,
6055 .help = "Query/Decode Module EEPROM information and optical diagnostics if available",
6056 .xhelp = " [ raw on|off ]\n"
6057 " [ hex on|off ]\n"
6058 " [ offset N ]\n"
6059 " [ length N ]\n"
6060 " [ page N ]\n"
6061 " [ bank N ]\n"
6062 " [ i2c N ]\n"
6063 },
6064 {
6065 .opts = "--show-eee",
6066 .json = true,
6067 .func = do_geee,
6068 .nlfunc = nl_geee,
6069 .help = "Show EEE settings",
6070 },
6071 {
6072 .opts = "--set-eee",
6073 .func = do_seee,
6074 .nlfunc = nl_seee,
6075 .help = "Set EEE settings",
6076 .xhelp = " [ eee on|off ]\n"
6077 " [ advertise %x ]\n"
6078 " [ tx-lpi on|off ]\n"
6079 " [ tx-timer %d ]\n"
6080 },
6081 {
6082 .opts = "--set-phy-tunable",
6083 .func = do_set_phy_tunable,
6084 .help = "Set PHY tunable",
6085 .xhelp = " [ downshift on|off [count N] ]\n"
6086 " [ fast-link-down on|off [msecs N] ]\n"
6087 " [ energy-detect-power-down on|off [msecs N] ]\n"
6088 },
6089 {
6090 .opts = "--get-phy-tunable",
6091 .func = do_get_phy_tunable,
6092 .help = "Get PHY tunable",
6093 .xhelp = " [ downshift ]\n"
6094 " [ fast-link-down ]\n"
6095 " [ energy-detect-power-down ]\n"
6096 },
6097 {
6098 .opts = "--get-tunable",
6099 .func = do_gtunable,
6100 .help = "Get tunable",
6101 .xhelp = " [ rx-copybreak ]\n"
6102 " [ tx-copybreak ]\n"
6103 " [ tx-buf-size ]\n"
6104 " [ pfc-prevention-tout ]\n"
6105 },
6106 {
6107 .opts = "--set-tunable",
6108 .func = do_stunable,
6109 .help = "Set tunable",
6110 .xhelp = " [ rx-copybreak N ]\n"
6111 " [ tx-copybreak N ]\n"
6112 " [ tx-buf-size N ]\n"
6113 " [ pfc-prevention-tout N ]\n"
6114 },
6115 {
6116 .opts = "--reset",
6117 .func = do_reset,
6118 .help = "Reset components",
6119 .xhelp = " [ flags %x ]\n"
6120 " [ mgmt ]\n"
6121 " [ mgmt-shared ]\n"
6122 " [ irq ]\n"
6123 " [ irq-shared ]\n"
6124 " [ dma ]\n"
6125 " [ dma-shared ]\n"
6126 " [ filter ]\n"
6127 " [ filter-shared ]\n"
6128 " [ offload ]\n"
6129 " [ offload-shared ]\n"
6130 " [ mac ]\n"
6131 " [ mac-shared ]\n"
6132 " [ phy ]\n"
6133 " [ phy-shared ]\n"
6134 " [ ram ]\n"
6135 " [ ram-shared ]\n"
6136 " [ ap ]\n"
6137 " [ ap-shared ]\n"
6138 " [ dedicated ]\n"
6139 " [ all ]\n"
6140 },
6141 {
6142 .opts = "--show-fec",
6143 .json = true,
6144 .func = do_gfec,
6145 .nlfunc = nl_gfec,
6146 .help = "Show FEC settings",
6147 },
6148 {
6149 .opts = "--set-fec",
6150 .func = do_sfec,
6151 .nlfunc = nl_sfec,
6152 .help = "Set FEC settings",
6153 .xhelp = " [ encoding auto|off|rs|baser|llrs [...] ]\n"
6154 },
6155 {
6156 .opts = "-Q|--per-queue",
6157 .func = do_perqueue,
6158 .help = "Apply per-queue command. ",
6159 .xhelp = "The supported sub commands include --show-coalesce, --coalesce"
6160 " [queue_mask %x] SUB_COMMAND\n",
6161 },
6162 {
6163 .opts = "--cable-test",
6164 .targets_phy = true,
6165 .json = true,
6166 .nlfunc = nl_cable_test,
6167 .help = "Perform a cable test",
6168 },
6169 {
6170 .opts = "--cable-test-tdr",
6171 .targets_phy = true,
6172 .json = true,
6173 .nlfunc = nl_cable_test_tdr,
6174 .help = "Print cable test time domain reflectrometery data",
6175 .xhelp = " [ first N ]\n"
6176 " [ last N ]\n"
6177 " [ step N ]\n"
6178 " [ pair N ]\n"
6179 },
6180 {
6181 .opts = "--show-tunnels",
6182 .nlfunc = nl_gtunnels,
6183 .help = "Show NIC tunnel offload information",
6184 },
6185 {
6186 .opts = "--show-module",
6187 .json = true,
6188 .nlfunc = nl_gmodule,
6189 .help = "Show transceiver module settings",
6190 },
6191 {
6192 .opts = "--set-module",
6193 .nlfunc = nl_smodule,
6194 .help = "Set transceiver module settings",
6195 .xhelp = " [ power-mode-policy high|auto ]\n"
6196 },
6197 {
6198 .opts = "--get-plca-cfg",
6199 .targets_phy = true,
6200 .nlfunc = nl_plca_get_cfg,
6201 .help = "Get PLCA configuration",
6202 },
6203 {
6204 .opts = "--set-plca-cfg",
6205 .targets_phy = true,
6206 .nlfunc = nl_plca_set_cfg,
6207 .help = "Set PLCA configuration",
6208 .xhelp = " [ enable on|off ]\n"
6209 " [ node-id N ]\n"
6210 " [ node-cnt N ]\n"
6211 " [ to-tmr N ]\n"
6212 " [ burst-cnt N ]\n"
6213 " [ burst-tmr N ]\n"
6214 },
6215 {
6216 .opts = "--get-plca-status",
6217 .targets_phy = true,
6218 .nlfunc = nl_plca_get_status,
6219 .help = "Get PLCA status information",
6220 },
6221 {
6222 .opts = "--show-mm",
6223 .json = true,
6224 .nlfunc = nl_get_mm,
6225 .help = "Show MAC merge layer state",
6226 },
6227 {
6228 .opts = "--set-mm",
6229 .nlfunc = nl_set_mm,
6230 .help = "Set MAC merge layer parameters",
6231 " [ verify-enabled on|off ]\n"
6232 " [ verify-time N ]\n"
6233 " [ tx-enabled on|off ]\n"
6234 " [ pmac-enabled on|off ]\n"
6235 " [ tx-min-frag-size 60-252 ]\n"
6236 },
6237 {
6238 .opts = "--show-pse",
6239 .targets_phy = true,
6240 .json = true,
6241 .nlfunc = nl_gpse,
6242 .help = "Show settings for Power Sourcing Equipment",
6243 },
6244 {
6245 .opts = "--set-pse",
6246 .targets_phy = true,
6247 .nlfunc = nl_spse,
6248 .help = "Set Power Sourcing Equipment settings",
6249 .xhelp = " [ podl-pse-admin-control enable|disable ]\n"
6250 " [ c33-pse-admin-control enable|disable ]\n"
6251 " [ c33-pse-avail-pw-limit N ]\n"
6252 },
6253 {
6254 .opts = "--flash-module-firmware",
6255 .nlfunc = nl_flash_module_fw,
6256 .help = "Flash transceiver module firmware",
6257 .xhelp = " file FILE\n"
6258 " [ pass PASS ]\n"
6259 },
6260 {
6261 .opts = "--show-phys",
6262 .nlfunc = nl_get_phy,
6263 .help = "List PHYs"
6264 },
6265 {
6266 .opts = "-h|--help",
6267 .no_dev = true,
6268 .func = show_usage,
6269 .help = "Show this help"
6270 },
6271 {
6272 .opts = "--version",
6273 .no_dev = true,
6274 .func = do_version,
6275 .help = "Show version number"
6276 },
6277 {}
6278 };
6279
show_usage(struct cmd_context * ctx __maybe_unused)6280 static int show_usage(struct cmd_context *ctx __maybe_unused)
6281 {
6282 int i;
6283
6284 /* ethtool -h */
6285 fprintf(stdout, PACKAGE " version " VERSION "\n");
6286 fprintf(stdout, "Usage:\n");
6287 for (i = 0; args[i].opts; i++) {
6288 fputs(" ethtool [ FLAGS ] ", stdout);
6289 fprintf(stdout, "%s%s %s\t%s\n",
6290 args[i].targets_phy ? "[ --phy PHY ] " : "",
6291 args[i].opts,
6292 args[i].no_dev ? "\t" : "DEVNAME",
6293 args[i].help);
6294 if (args[i].xhelp)
6295 fputs(args[i].xhelp, stdout);
6296 }
6297 nl_monitor_usage();
6298 fprintf(stdout, "\n");
6299 fprintf(stdout, "FLAGS:\n");
6300 fprintf(stdout, " --debug MASK turn on debugging messages\n");
6301 fprintf(stdout, " --json enable JSON output format (not supported by all commands)\n");
6302 fprintf(stdout, " -I|--include-statistics request device statistics related to the command (not supported by all commands)\n");
6303
6304 return 0;
6305 }
6306
find_option(char * arg)6307 static int find_option(char *arg)
6308 {
6309 const char *opt;
6310 size_t len;
6311 int k;
6312
6313 for (k = 1; args[k].opts; k++) {
6314 opt = args[k].opts;
6315 for (;;) {
6316 len = strcspn(opt, "|");
6317 if (strncmp(arg, opt, len) == 0 && arg[len] == 0)
6318 return k;
6319
6320 if (opt[len] == 0)
6321 break;
6322 opt += len + 1;
6323 }
6324 }
6325
6326 return -1;
6327 }
6328
6329 #define MAX(x, y) (x > y ? x : y)
6330
find_max_num_queues(struct cmd_context * ctx)6331 static int find_max_num_queues(struct cmd_context *ctx)
6332 {
6333 struct ethtool_channels echannels;
6334
6335 echannels.cmd = ETHTOOL_GCHANNELS;
6336 if (send_ioctl(ctx, &echannels))
6337 return -1;
6338
6339 return MAX(echannels.rx_count, echannels.tx_count) +
6340 echannels.combined_count;
6341 }
6342
6343 static struct ethtool_per_queue_op *
get_per_queue_coalesce(struct cmd_context * ctx,__u32 * queue_mask,int n_queues)6344 get_per_queue_coalesce(struct cmd_context *ctx, __u32 *queue_mask, int n_queues)
6345 {
6346 struct ethtool_per_queue_op *per_queue_opt;
6347
6348 per_queue_opt = malloc(sizeof(*per_queue_opt) + n_queues *
6349 sizeof(struct ethtool_coalesce));
6350 if (!per_queue_opt)
6351 return NULL;
6352
6353 memcpy(per_queue_opt->queue_mask, queue_mask,
6354 __KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32) * sizeof(__u32));
6355 per_queue_opt->cmd = ETHTOOL_PERQUEUE;
6356 per_queue_opt->sub_command = ETHTOOL_GCOALESCE;
6357 if (send_ioctl(ctx, per_queue_opt)) {
6358 free(per_queue_opt);
6359 perror("Cannot get device per queue parameters");
6360 return NULL;
6361 }
6362
6363 return per_queue_opt;
6364 }
6365
set_per_queue_coalesce(struct cmd_context * ctx,struct ethtool_per_queue_op * per_queue_opt,int n_queues)6366 static void set_per_queue_coalesce(struct cmd_context *ctx,
6367 struct ethtool_per_queue_op *per_queue_opt,
6368 int n_queues)
6369 {
6370 struct ethtool_coalesce ecoal;
6371 DECLARE_COALESCE_OPTION_VARS();
6372 struct cmdline_info cmdline_coalesce[] = COALESCE_CMDLINE_INFO(ecoal);
6373 __u32 *queue_mask = per_queue_opt->queue_mask;
6374 struct ethtool_coalesce *ecoal_q;
6375 int gcoalesce_changed = 0;
6376 int i, idx = 0;
6377
6378 parse_generic_cmdline(ctx, &gcoalesce_changed,
6379 cmdline_coalesce, ARRAY_SIZE(cmdline_coalesce));
6380
6381 ecoal_q = (struct ethtool_coalesce *)(per_queue_opt + 1);
6382 for (i = 0; i < __KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32); i++) {
6383 int queue = i * 32;
6384 __u32 mask = queue_mask[i];
6385
6386 while (mask > 0) {
6387 if (mask & 0x1) {
6388 int changed = 0;
6389
6390 memcpy(&ecoal, ecoal_q + idx,
6391 sizeof(struct ethtool_coalesce));
6392 do_generic_set(cmdline_coalesce,
6393 ARRAY_SIZE(cmdline_coalesce),
6394 &changed);
6395 if (!changed)
6396 fprintf(stderr,
6397 "Queue %d, no coalesce parameters changed\n",
6398 queue);
6399 memcpy(ecoal_q + idx, &ecoal,
6400 sizeof(struct ethtool_coalesce));
6401 idx++;
6402 }
6403 mask = mask >> 1;
6404 queue++;
6405 }
6406 if (idx == n_queues)
6407 break;
6408 }
6409
6410 per_queue_opt->cmd = ETHTOOL_PERQUEUE;
6411 per_queue_opt->sub_command = ETHTOOL_SCOALESCE;
6412
6413 if (send_ioctl(ctx, per_queue_opt))
6414 perror("Cannot set device per queue parameters");
6415 }
6416
do_perqueue(struct cmd_context * ctx)6417 static int do_perqueue(struct cmd_context *ctx)
6418 {
6419 struct ethtool_per_queue_op *per_queue_opt;
6420 __u32 queue_mask[__KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32)] = {0};
6421 int i, n_queues = 0;
6422
6423 if (ctx->argc == 0)
6424 exit_bad_args();
6425
6426 /*
6427 * The sub commands will be applied to
6428 * all queues if no queue_mask set
6429 */
6430 if (strncmp(*ctx->argp, "queue_mask", 11)) {
6431 n_queues = find_max_num_queues(ctx);
6432 if (n_queues < 0) {
6433 perror("Cannot get number of queues");
6434 return -EFAULT;
6435 } else if (n_queues > MAX_NUM_QUEUE) {
6436 n_queues = MAX_NUM_QUEUE;
6437 }
6438 for (i = 0; i < n_queues / 32; i++)
6439 queue_mask[i] = ~0;
6440 if (n_queues % 32)
6441 queue_mask[i] = (1 << (n_queues - i * 32)) - 1;
6442 fprintf(stdout,
6443 "The sub commands will be applied to all %d queues\n",
6444 n_queues);
6445 } else {
6446 if (ctx->argc <= 2)
6447 exit_bad_args();
6448 ctx->argc--;
6449 ctx->argp++;
6450 if (parse_hex_u32_bitmap(*ctx->argp, MAX_NUM_QUEUE,
6451 queue_mask)) {
6452 fprintf(stdout, "Invalid queue mask\n");
6453 return -1;
6454 }
6455 for (i = 0; i < __KERNEL_DIV_ROUND_UP(MAX_NUM_QUEUE, 32); i++) {
6456 __u32 mask = queue_mask[i];
6457
6458 while (mask > 0) {
6459 if (mask & 0x1)
6460 n_queues++;
6461 mask = mask >> 1;
6462 }
6463 }
6464 ctx->argc--;
6465 ctx->argp++;
6466 }
6467
6468 i = find_option(ctx->argp[0]);
6469 if (i < 0)
6470 exit_bad_args();
6471
6472 if (strstr(args[i].opts, "--show-coalesce") != NULL) {
6473 per_queue_opt = get_per_queue_coalesce(ctx, queue_mask,
6474 n_queues);
6475 if (per_queue_opt == NULL) {
6476 perror("Cannot get device per queue parameters");
6477 return -EFAULT;
6478 }
6479 dump_per_queue_coalesce(per_queue_opt, queue_mask, n_queues);
6480 free(per_queue_opt);
6481 } else if (strstr(args[i].opts, "--coalesce") != NULL) {
6482 ctx->argc--;
6483 ctx->argp++;
6484 per_queue_opt = get_per_queue_coalesce(ctx, queue_mask,
6485 n_queues);
6486 if (per_queue_opt == NULL) {
6487 perror("Cannot get device per queue parameters");
6488 return -EFAULT;
6489 }
6490 set_per_queue_coalesce(ctx, per_queue_opt, n_queues);
6491 free(per_queue_opt);
6492 } else {
6493 perror("The subcommand is not supported yet");
6494 return -EOPNOTSUPP;
6495 }
6496
6497 return 0;
6498 }
6499
ioctl_init(struct cmd_context * ctx,bool no_dev)6500 int ioctl_init(struct cmd_context *ctx, bool no_dev)
6501 {
6502 if (no_dev) {
6503 ctx->fd = -1;
6504 return 0;
6505 }
6506 if (strlen(ctx->devname) >= IFNAMSIZ) {
6507 fprintf(stderr, "Device name longer than %u characters\n",
6508 IFNAMSIZ - 1);
6509 exit_bad_args();
6510 }
6511
6512 /* Setup our control structures. */
6513 memset(&ctx->ifr, 0, sizeof(ctx->ifr));
6514 strcpy(ctx->ifr.ifr_name, ctx->devname);
6515
6516 /* Open control socket. */
6517 ctx->fd = socket(AF_INET, SOCK_DGRAM, 0);
6518 if (ctx->fd < 0)
6519 ctx->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
6520 if (ctx->fd < 0) {
6521 perror("Cannot get control socket");
6522 return 70;
6523 }
6524
6525 return 0;
6526 }
6527
main(int argc,char ** argp)6528 int main(int argc, char **argp)
6529 {
6530 struct cmd_context ctx = {};
6531 int ret;
6532 int k;
6533
6534 init_global_link_mode_masks();
6535
6536 if (argc < 2)
6537 exit_bad_args();
6538
6539 /* Skip command name */
6540 argp++;
6541 argc--;
6542
6543 while (true) {
6544 if (*argp && !strcmp(*argp, "--debug")) {
6545 char *eptr;
6546
6547 if (argc < 2)
6548 exit_bad_args();
6549 ctx.debug = strtoul(argp[1], &eptr, 0);
6550 if (!argp[1][0] || *eptr)
6551 exit_bad_args();
6552
6553 argp += 2;
6554 argc -= 2;
6555 continue;
6556 }
6557 if (*argp && !strcmp(*argp, "--disable-netlink")) {
6558 ctx.nl_disable = true;
6559 argp += 1;
6560 argc -= 1;
6561 continue;
6562 }
6563 if (*argp && !strcmp(*argp, "--json")) {
6564 ctx.json = true;
6565 argp += 1;
6566 argc -= 1;
6567 continue;
6568 }
6569 if (*argp && (!strcmp(*argp, "--include-statistics") ||
6570 !strcmp(*argp, "-I"))) {
6571 ctx.show_stats = true;
6572 argp += 1;
6573 argc -= 1;
6574 continue;
6575 }
6576 if (*argp && !strcmp(*argp, "--phy")) {
6577 char *eptr;
6578
6579 if (argc < 2)
6580 exit_bad_args_info("--phy parameters expects a phy index");
6581
6582 ctx.phy_index = strtoul(argp[1], &eptr, 0);
6583 if (!argp[1][0] || *eptr)
6584 exit_bad_args_info("invalid phy index");
6585 argp += 2;
6586 argc -= 2;
6587 continue;
6588 }
6589 break;
6590 }
6591 if (*argp && !strcmp(*argp, "--monitor")) {
6592 ctx.argp = ++argp;
6593 ctx.argc = --argc;
6594 ret = nl_monitor(&ctx);
6595 return ret ? 1 : 0;
6596 }
6597
6598 /* First argument must be either a valid option or a device
6599 * name to get settings for (which we don't expect to begin
6600 * with '-').
6601 */
6602 if (!*argp)
6603 exit_bad_args();
6604
6605 k = find_option(*argp);
6606 if (k > 0) {
6607 argp++;
6608 argc--;
6609 } else {
6610 if ((*argp)[0] == '-')
6611 exit_bad_args();
6612 k = 0;
6613 }
6614
6615 if (!args[k].no_dev) {
6616 ctx.devname = *argp++;
6617 argc--;
6618
6619 if (!ctx.devname)
6620 exit_bad_args();
6621 }
6622 if (ctx.json && !args[k].json)
6623 exit_bad_args_info("JSON output not available for this subcommand");
6624
6625 if (!args[k].targets_phy && ctx.phy_index)
6626 exit_bad_args_info("Unexpected --phy parameter");
6627
6628 ctx.argc = argc;
6629 ctx.argp = argp;
6630 netlink_run_handler(&ctx, args[k].nlchk, args[k].nlfunc, !args[k].func);
6631
6632 if (ctx.json) /* no IOCTL command supports JSON output */
6633 exit_nlonly_param("--json");
6634
6635 ret = ioctl_init(&ctx, args[k].no_dev);
6636 if (ret)
6637 return ret;
6638
6639 return args[k].func(&ctx);
6640 }
6641