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