• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# bash completion for ethtool(8)                          -*- shell-script -*-
2# shellcheck shell=bash disable=SC2207
3
4# Complete a word representing a set of characters.
5# @param $@ chars	Characters which may be present in completed set.
6_ethtool_compgen_letterset()
7{
8	local char
9	for char; do
10		case "$cur" in
11			*"$char"*)
12				# $cur already contains $char
13				;;
14			*)
15				COMPREPLY+=( "$cur$char" )
16				;;
17		esac
18	done
19}
20
21# Generate completions for words matched case-insensitively
22# @param $@ choices	Completion choices.
23_ethtool_compgen_nocase()
24{
25	local reset
26	reset=$( shopt -p nocasematch )
27	shopt -s nocasematch
28
29	local choice
30	for choice; do
31		case "$choice" in
32			"$cur"*) COMPREPLY+=( "$choice" ) ;;
33		esac
34	done
35
36	$reset
37}
38
39# Gets names from a section of ethtool output.
40# @param $1 section_bre	POSIX BRE matching section heading (without : at end).
41# @param $@		ethtool arguments
42_ethtool_get_names_in_section()
43{
44	local section_bre="$1"
45	shift
46
47	PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin" \
48		ethtool "$@" 2>/dev/null |
49		command sed -n "
50# Line is section heading iff it ends with :
51# From requested section heading to next section heading
52/^$section_bre:$/,/:$/ {
53	# If line is section heading, ignore it
54	/:$/d
55	# Remove value and separator, if present
56	s/[[:space:]]*:.*//
57	# Remove leading space, if present
58	s/^[[:space:]]*//
59	# Print the line
60	p
61}"
62}
63
64# Complete an RSS Context ID
65_ethtool_context()
66{
67	COMPREPLY=(
68		$(PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin" \
69			ethtool --show-nfc "${words[2]}" 2>/dev/null |
70			command sed -n 's/^[[:space:]]*RSS Context ID:[[:space:]]*\([0-9]*\)$/\1/p' |
71			sort -u) )
72}
73
74# Complete a network flow traffic type
75# Available OPTIONS:
76#	 --hash  Complete only types suitable for rx hashing
77_ethtool_flow_type()
78{
79	local types='ah4 ah6 esp4 esp6 ether sctp4 sctp6 tcp4 tcp6 udp4 udp6'
80	if [ "${1-}" != --hash ]; then
81		types="$types ip4 ip6"
82	fi
83	COMPREPLY=( $( compgen -W "$types" -- "$cur" ) )
84}
85
86# Completion for ethtool --change
87_ethtool_change()
88{
89	local -A settings=(
90		[advertise]=notseen
91		[autoneg]=notseen
92		[duplex]=notseen
93		[mdix]=notseen
94		[msglvl]=notseen
95		[port]=notseen
96		[phyad]=notseen
97		[speed]=notseen
98		[wol]=notseen
99		[xcvr]=notseen
100	)
101
102	local -A msgtypes=(
103		[drv]=notseen
104		[hw]=notseen
105		[ifdown]=notseen
106		[ifup]=notseen
107		[intr]=notseen
108		[link]=notseen
109		[pktdata]=notseen
110		[probe]=notseen
111		[rx_err]=notseen
112		[rx_status]=notseen
113		[timer]=notseen
114		[tx_done]=notseen
115		[tx_err]=notseen
116		[tx_queued]=notseen
117		[wol]=notseen
118	)
119
120	# Mark seen settings and msgtypes, and whether in msglvl sub-command
121	local in_msglvl=
122	local word
123	for word in "${words[@]:3:${#words[@]}-4}"; do
124		if [ "$in_msglvl" ] && [ "${msgtypes[$word]+set}" ]; then
125			msgtypes[$word]=seen
126		elif [ "${settings[$word]+set}" ]; then
127			settings[$word]=seen
128			if [ "$word" = msglvl ]; then
129				in_msglvl=1
130			else
131				in_msglvl=
132			fi
133		fi
134	done
135
136	if [ "$in_msglvl" ] && [ "${msgtypes[$prev]+set}" ]; then
137		# All msgtypes take an on/off argument
138		COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
139		return
140	fi
141
142	case "$prev" in
143		advertise)
144			# Hex number
145			return ;;
146		autoneg)
147			COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
148			return ;;
149		duplex)
150			COMPREPLY=( $( compgen -W 'half full' -- "$cur" ) )
151			return ;;
152		mdix)
153			COMPREPLY=( $( compgen -W 'auto on off' -- "$cur" ) )
154			return ;;
155		msglvl)
156			# Unsigned integer or msgtype
157			COMPREPLY=( $( compgen -W "${!msgtypes[*]}" -- "$cur" ) )
158			return ;;
159		port)
160			COMPREPLY=( $( compgen -W 'aui bnc fibre mii tp' -- "$cur" ) )
161			return ;;
162		phyad)
163			# Integer
164			return ;;
165		sopass)
166			_mac_addresses
167			return ;;
168		speed)
169			# Number
170			return ;;
171		wol)
172			# $cur is a set of wol type characters.
173			_ethtool_compgen_letterset p u m b a g s f d
174			return ;;
175		xcvr)
176			COMPREPLY=( $( compgen -W 'internal external' -- "$cur" ) )
177			return ;;
178	esac
179
180	local -a comp_words=()
181
182	# Add settings not seen to completions
183	local setting
184	for setting in "${!settings[@]}"; do
185		if [ "${settings[$setting]}" = notseen ]; then
186			comp_words+=( "$setting" )
187		fi
188	done
189
190	# Add settings not seen to completions
191	if [ "$in_msglvl" ]; then
192		local msgtype
193		for msgtype in "${!msgtypes[@]}"; do
194			if [ "${msgtypes[$msgtype]}" = notseen ]; then
195				comp_words+=( "$msgtype" )
196			fi
197		done
198	fi
199
200	COMPREPLY=( $( compgen -W "${comp_words[*]}" -- "$cur" ) )
201}
202
203# Completion for ethtool --change-eeprom
204_ethtool_change_eeprom()
205{
206	local -A settings=(
207		[length]=1
208		[magic]=1
209		[offset]=1
210		[value]=1
211	)
212
213	if [ "${settings[$prev]+set}" ]; then
214		# All settings take an unsigned integer argument
215		return
216	fi
217
218	# Remove settings which have been seen
219	local word
220	for word in "${words[@]:3:${#words[@]}-4}"; do
221		unset "settings[$word]"
222	done
223
224	COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
225}
226
227# Completion for ethtool --coalesce
228_ethtool_coalesce()
229{
230	local -A settings=(
231		[adaptive-rx]=1
232		[adaptive-tx]=1
233		[pkt-rate-high]=1
234		[pkt-rate-low]=1
235		[rx-frames]=1
236		[rx-frames-high]=1
237		[rx-frames-irq]=1
238		[rx-frames-low]=1
239		[rx-usecs]=1
240		[rx-usecs-high]=1
241		[rx-usecs-irq]=1
242		[rx-usecs-low]=1
243		[sample-interval]=1
244		[stats-block-usecs]=1
245		[tx-frames]=1
246		[tx-frames-high]=1
247		[tx-frames-irq]=1
248		[tx-frames-low]=1
249		[tx-usecs]=1
250		[tx-usecs-high]=1
251		[tx-usecs-irq]=1
252		[tx-usecs-low]=1
253	)
254
255	case "$prev" in
256		adaptive-rx|\
257		adaptive-tx)
258			COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
259			return ;;
260	esac
261
262	if [ "${settings[$prev]+set}" ]; then
263		# Unsigned integer
264		return
265	fi
266
267	# Remove settings which have been seen
268	local word
269	for word in "${words[@]:3:${#words[@]}-4}"; do
270		unset "settings[$word]"
271	done
272
273	COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
274}
275
276# Completion for ethtool --config-nfc <devname> flow-type
277_ethtool_config_nfc_flow_type()
278{
279	if [ "$cword" -eq 4 ]; then
280		_ethtool_flow_type --spec
281		return
282	fi
283
284	case "$prev" in
285		context)
286			_ethtool_context
287			return ;;
288		dst|\
289		dst-mac|\
290		src)
291			# TODO: Complete only local for dst and remote for src
292			_mac_addresses
293			return ;;
294		dst-ip)
295			# Note: RX classification, so dst is usually local
296			case "${words[4]}" in
297				*4) _ip_addresses -4 return ;;
298				*6) _ip_addresses -6 return ;;
299			esac
300			return ;;
301		src-ip)
302			# Note: RX classification, so src is usually remote
303			# TODO: Remote IP addresses (ARP cache + /etc/hosts + ?)
304			return ;;
305		m|\
306		*-mask)
307			# MAC, IP, or integer bitmask
308			return ;;
309	esac
310
311	local -A settings=(
312		[action]=1
313		[context]=1
314		[loc]=1
315		[queue]=1
316		[vf]=1
317	)
318
319	if [ "${settings[$prev]+set}" ]; then
320		# Integer
321		return
322	fi
323
324	case "${words[4]}" in
325		ah4|\
326		esp4)
327			local -A fields=(
328				[dst-ip]=1
329				[dst-mac]=1
330				[spi]=1
331				[src-ip]=1
332				[tos]=1
333				[user-def]=1
334				[vlan-etype]=1
335				[vlan]=1
336			)
337			;;
338		ah6|\
339		esp6)
340			local -A fields=(
341				[dst-ip]=1
342				[dst-mac]=1
343				[spi]=1
344				[src-ip]=1
345				[tclass]=1
346				[user-def]=1
347				[vlan-etype]=1
348				[vlan]=1
349			)
350			;;
351		ether)
352			local -A fields=(
353				[dst]=1
354				[proto]=1
355				[src]=1
356				[user-def]=1
357				[vlan-etype]=1
358				[vlan]=1
359			)
360			;;
361		ip4)
362			local -A fields=(
363				[dst-ip]=1
364				[dst-mac]=1
365				[dst-port]=1
366				[l4data]=1
367				[l4proto]=1
368				[spi]=1
369				[src-ip]=1
370				[src-port]=1
371				[tos]=1
372				[user-def]=1
373				[vlan-etype]=1
374				[vlan]=1
375			)
376			;;
377		ip6)
378			local -A fields=(
379				[dst-ip]=1
380				[dst-mac]=1
381				[dst-port]=1
382				[l4data]=1
383				[l4proto]=1
384				[spi]=1
385				[src-ip]=1
386				[src-port]=1
387				[tclass]=1
388				[user-def]=1
389				[vlan-etype]=1
390				[vlan]=1
391			)
392			;;
393		sctp4|\
394		tcp4|\
395		udp4)
396			local -A fields=(
397				[dst-ip]=1
398				[dst-mac]=1
399				[dst-port]=1
400				[src-ip]=1
401				[src-port]=1
402				[tos]=1
403				[user-def]=1
404				[vlan-etype]=1
405				[vlan]=1
406			)
407			;;
408		sctp6|\
409		tcp6|\
410		udp6)
411			local -A fields=(
412				[dst-ip]=1
413				[dst-mac]=1
414				[dst-port]=1
415				[src-ip]=1
416				[src-port]=1
417				[tclass]=1
418				[user-def]=1
419				[vlan-etype]=1
420				[vlan]=1
421			)
422			;;
423		*)
424			return ;;
425	esac
426
427	if [ "${fields[$prev]+set}" ]; then
428		# Integer
429		return
430	fi
431
432	# If the previous 2 words were a field+value, suggest a mask
433	local mask=
434	if [ "${fields[${words[$cword-2]}]+set}" ]; then
435		mask="m ${words[$cword-2]}-mask"
436	fi
437
438	# Remove fields and settings which have been seen
439	local word
440	for word in "${words[@]:5:${#words[@]}-6}"; do
441		unset "fields[$word]" "settings[$word]"
442	done
443
444	# Remove mutually-exclusive options
445	if ! [ "${settings[action]+set}" ]; then
446		unset 'settings[queue]' 'settings[vf]'
447	fi
448	if ! [ "${settings[queue]+set}" ]; then
449		unset 'settings[action]'
450	fi
451	if ! [ "${settings[vf]+set}" ]; then
452		unset 'settings[action]'
453	fi
454
455	COMPREPLY=( $( compgen -W "$mask ${!fields[*]} ${!settings[*]}" -- "$cur" ) )
456}
457
458# Completion for ethtool --config-nfc
459_ethtool_config_nfc()
460{
461	if [ "$cword" -eq 3 ]; then
462		COMPREPLY=( $( compgen -W 'delete flow-type rx-flow-hash' -- "$cur" ) )
463		return
464	fi
465
466	case "${words[3]}" in
467		delete)
468			# Unsigned integer
469			return ;;
470		flow-type)
471			_ethtool_config_nfc_flow_type
472			return ;;
473		rx-flow-hash)
474			case "$cword" in
475				4)
476					_ethtool_flow_type --hash
477					return ;;
478				5)
479					_ethtool_compgen_letterset m v t s d f n r
480					return ;;
481				6)
482					COMPREPLY=( $( compgen -W context -- "$cur" ) )
483					return ;;
484				7)
485					_ethtool_context
486					return ;;
487			esac
488			return ;;
489	esac
490}
491
492# Completion for ethtool --eeprom-dump
493_ethtool_eeprom_dump()
494{
495	local -A settings=(
496		[length]=1
497		[offset]=1
498		[raw]=1
499	)
500
501	if [ "$prev" = raw ]; then
502		COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
503		return
504	fi
505
506	if [ "${settings[$prev]+set}" ]; then
507		# Unsigned integer argument
508		return
509	fi
510
511	# Remove settings which have been seen
512	local word
513	for word in "${words[@]:3:${#words[@]}-4}"; do
514		unset "settings[$word]"
515	done
516
517	COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
518}
519
520# Completion for ethtool --features
521_ethtool_features()
522{
523	local -A abbreviations=(
524		[generic-receive-offload]=gro
525		[generic-segmentation-offload]=gso
526		[large-receive-offload]=lro
527		[ntuple-filters]=ntuple
528		[receive-hashing]=rxhash
529		[rx-checksumming]=rx
530		[rx-vlan-offload]=rxvlan
531		[scatter-gather]=sg
532		[tcp-segmentation-offload]=tso
533		[tx-checksumming]=tx
534		[tx-vlan-offload]=txvlan
535		[udp-fragmentation-offload]=ufo
536	)
537
538	local -A features=()
539	local feature status fixed
540	# shellcheck disable=SC2034
541	while read -r feature status fixed; do
542		if [ -z "$feature" ]; then
543			# Ignore blank line from empty expansion in here-document
544			continue
545		fi
546
547		if [ "$feature" = Features ]; then
548			# Ignore heading
549			continue
550		fi
551
552		if [ "$fixed" = '[fixed]' ]; then
553			# Fixed features can't be changed
554			continue
555		fi
556
557		feature=${feature%:}
558		if [ "${abbreviations[$feature]+set}" ]; then
559			features[${abbreviations[$feature]}]=1
560		else
561			features[$feature]=1
562		fi
563	done <<ETHTOOL_FEATURES
564$(PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin" \
565	ethtool --show-features "${words[2]}" 2>/dev/null)
566ETHTOOL_FEATURES
567
568	if [ "${features[$prev]+set}" ]; then
569		COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
570		return
571	fi
572
573	# Remove features which have been seen
574	local word
575	for word in "${words[@]:3:${#words[@]}-4}"; do
576		unset "features[$word]"
577	done
578
579	COMPREPLY=( $( compgen -W "${!features[*]}" -- "$cur" ) )
580}
581
582# Complete the current word as a kernel firmware file (for request_firmware)
583# See https://www.kernel.org/doc/html/latest/driver-api/firmware/core.html
584_ethtool_firmware()
585{
586	local -a firmware_paths=(
587		/lib/firmware/updates/
588		/lib/firmware/
589	)
590
591	local release
592	if release=$( uname -r 2>/dev/null ); then
593		firmware_paths+=(
594			"/lib/firmware/updates/$release/"
595			"/lib/firmware/$release/"
596		)
597	fi
598
599	local fw_path_para
600	if fw_path_para=$( cat /sys/module/firmware_class/parameters/path 2>/dev/null ) \
601			&& [ -n "$fw_path_para" ]; then
602		firmware_paths+=( "$fw_path_para" )
603	fi
604
605	local -A firmware_files=()
606
607	local firmware_path
608	for firmware_path in "${firmware_paths[@]}"; do
609		local firmware_file
610		for firmware_file in "$firmware_path"*; do
611			if [ -f "$firmware_file" ]; then
612				firmware_files[${firmware_file##*/}]=1
613			fi
614		done
615	done
616
617	local IFS='
618'
619	COMPREPLY=( $( compgen -W "${!firmware_files[*]}" -- "$cur" ) )
620}
621
622# Completion for ethtool --flash
623_ethtool_flash()
624{
625	if [ "$cword" -eq 3 ]; then
626		_ethtool_firmware
627		return
628	fi
629}
630
631# Completion for ethtool --get-dump
632_ethtool_get_dump()
633{
634	case "$cword" in
635		3)
636			COMPREPLY=( $( compgen -W data -- "$cur" ) )
637			return ;;
638		4)
639			# Output filename
640			local IFS='
641'
642			COMPREPLY=( $( compgen -f -- "$cur" ) )
643			return ;;
644	esac
645}
646
647# Completion for ethtool --get-phy-tunable
648_ethtool_get_phy_tunable()
649{
650	if [ "$cword" -eq 3 ]; then
651		COMPREPLY=( $( compgen -W downshift -- "$cur" ) )
652		return
653	fi
654}
655
656# Completion for ethtool --module-info
657_ethtool_module_info()
658{
659	local -A settings=(
660		[hex]=1
661		[length]=1
662		[offset]=1
663		[raw]=1
664	)
665
666	case "$prev" in
667		hex|\
668		raw)
669			COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
670			return ;;
671	esac
672
673	if [ "${settings[$prev]+set}" ]; then
674		# Unsigned integer argument
675		return
676	fi
677
678	# Remove settings which have been seen
679	local word
680	for word in "${words[@]:3:${#words[@]}-4}"; do
681		unset "settings[$word]"
682	done
683
684	COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
685}
686
687# Completion for ethtool --pause
688_ethtool_pause()
689{
690	local -A settings=(
691		[autoneg]=1
692		[rx]=1
693		[tx]=1
694	)
695
696	if [ "${settings[$prev]+set}" ]; then
697		COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
698		return
699	fi
700
701	# Remove settings which have been seen
702	local word
703	for word in "${words[@]:3:${#words[@]}-4}"; do
704		unset "settings[$word]"
705	done
706
707	COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
708}
709
710# Completion for ethtool --per-queue
711_ethtool_per_queue()
712{
713	local -a subcommands=(
714		--coalesce
715		--show-coalesce
716	)
717
718	if [ "$cword" -eq 3 ]; then
719		COMPREPLY=( $( compgen -W "queue_mask ${subcommands[*]}" -- "$cur" ) )
720		return
721	fi
722
723	local sc_start=3
724	if [ "${words[3]}" = queue_mask ] ; then
725		case "$cword" in
726			4)
727				# Hex number
728				return ;;
729			5)
730				COMPREPLY=( $( compgen -W "${subcommands[*]}" -- "$cur" ) )
731				return ;;
732		esac
733
734		sc_start=5
735	fi
736
737	case "${words[$sc_start]}" in
738		--coalesce)
739			# Remove --per-queue args to match normal --coalesce invocation
740			local words=(
741				"${words[0]}"
742				--coalesce
743				"${words[2]}"
744				"${words[@]:$sc_start+1:${#words[@]}-$sc_start-1}"
745			)
746			_ethtool_coalesce
747			return ;;
748		--show-coalesce)
749			# No args
750			return ;;
751	esac
752}
753
754# Completion for ethtool --register-dump
755_ethtool_register_dump()
756{
757	local -A settings=(
758		[file]=1
759		[hex]=1
760		[raw]=1
761	)
762
763	case "$prev" in
764		hex|\
765		raw)
766			COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
767			return ;;
768		file)
769			local IFS='
770'
771			COMPREPLY=( $( compgen -f -- "$cur" ) )
772			return ;;
773	esac
774
775	# Remove settings which have been seen
776	local word
777	for word in "${words[@]:3:${#words[@]}-4}"; do
778		unset "settings[$word]"
779	done
780
781	COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
782}
783
784# Completion for ethtool --reset
785_ethtool_reset()
786{
787	if [ "$prev" = flags ]; then
788		# Unsigned integer
789		return
790	fi
791
792	local -A flag_names=(
793		[ap]=1
794		[dma]=1
795		[filter]=1
796		[irq]=1
797		[mac]=1
798		[mgmt]=1
799		[offload]=1
800		[phy]=1
801		[ram]=1
802	)
803
804	local -A all_flag_names=()
805	local flag_name
806	for flag_name in "${!flag_names[@]}"; do
807		all_flag_names[$flag_name]=1
808		all_flag_names[$flag_name-shared]=1
809	done
810
811	# Remove all_flag_names which have been seen
812	local any_dedicated=
813	local word
814	for word in "${words[@]:3:${#words[@]}-4}"; do
815		case "$word" in
816			all)
817				# Flags are always additive.
818				# Nothing to add after "all".
819				return ;;
820			dedicated)
821				any_dedicated=1
822				# "dedicated" sets all non-shared flags
823				for flag_name in "${!flag_names[@]}"; do
824					unset "all_flag_names[$flag_name]"
825				done
826				continue ;;
827		esac
828
829		if [ "${flag_names[$word]+set}" ]; then
830			any_dedicated=1
831		fi
832
833		unset "all_flag_names[$word]"
834	done
835
836	COMPREPLY=( $( compgen -W "${!all_flag_names[*]}" -- "$cur" ) )
837
838	# Although it is permitted to mix named and un-named flags or duplicate
839	# flags with "all" or "dedicated", it's not likely intentional.
840	# Reconsider if a real use-case (or good consistency argument) is found.
841	if [ "$cword" -eq 3 ]; then
842		COMPREPLY+=( all dedicated flags )
843	elif [ -z "$any_dedicated" ]; then
844		COMPREPLY+=( dedicated )
845	fi
846}
847
848# Completion for ethtool --rxfh
849_ethtool_rxfh()
850{
851	local -A settings=(
852		[context]=1
853		[default]=1
854		[delete]=1
855		[equal]=1
856		[hfunc]=1
857		[hkey]=1
858		[weight]=1
859	)
860
861	case "$prev" in
862		context)
863			_ethtool_context
864			# "new" to create a new context
865			COMPREPLY+=( new )
866			return ;;
867		equal)
868			# Positive integer
869			return ;;
870		hfunc)
871			# Complete available RSS hash functions
872			COMPREPLY=(
873				$(_ethtool_get_names_in_section 'RSS hash function' \
874					--show-rxfh "${words[2]}")
875			)
876			return ;;
877		hkey)
878			# Pairs of hex digits separated by :
879			return ;;
880		weight)
881			# Non-negative integer
882			return ;;
883	esac
884
885	local word
886	for word in "${words[@]:3:${#words[@]}-4}"; do
887		# Remove settings which have been seen
888		unset "settings[$word]"
889
890		# Remove settings which are mutually-exclusive with seen settings
891		case "$word" in
892			context)
893				unset 'settings[default]'
894				;;
895			default)
896				unset \
897					'settings[context]' \
898					'settings[delete]' \
899					'settings[equal]' \
900					'settings[weight]'
901				;;
902			delete)
903				unset \
904					'settings[default]' \
905					'settings[equal]' \
906					'settings[hkey]' \
907					'settings[weight]'
908				;;
909			equal)
910				unset \
911					'settings[default]' \
912					'settings[delete]' \
913					'settings[weight]'
914				;;
915			hkey)
916				unset 'settings[delete]'
917				;;
918			weight)
919				unset \
920					'settings[default]' \
921					'settings[delete]' \
922					'settings[equal]'
923				;;
924		esac
925	done
926
927
928	COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
929}
930
931# Completion for ethtool --set-channels
932_ethtool_set_channels()
933{
934	local -A settings=(
935		[combined]=1
936		[other]=1
937		[rx]=1
938		[tx]=1
939	)
940
941	if [ "${settings[$prev]+set}" ]; then
942		# Unsigned integer argument
943		return
944	fi
945
946	# Remove settings which have been seen
947	local word
948	for word in "${words[@]:3:${#words[@]}-4}"; do
949		unset "settings[$word]"
950	done
951
952	COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
953}
954
955# Completion for ethtool --set-eee
956_ethtool_set_eee()
957{
958	local -A settings=(
959		[advertise]=1
960		[eee]=1
961		[tx-lpi]=1
962		[tx-timer]=1
963	)
964
965	case "$prev" in
966		advertise|\
967		tx-timer)
968			# Unsigned integer
969			return ;;
970		eee|\
971		tx-lpi)
972			COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
973			return ;;
974	esac
975
976	# Remove settings which have been seen
977	local word
978	for word in "${words[@]:3:${#words[@]}-4}"; do
979		unset "settings[$word]"
980	done
981
982	COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
983}
984
985# Completion for ethtool --set-fec
986_ethtool_set_fec()
987{
988	if [ "$cword" -eq 3 ]; then
989		COMPREPLY=( $( compgen -W encoding -- "$cur" ) )
990		return
991	fi
992
993	local -A modes=(
994		[auto]=auto
995		[rs]=RS
996		[off]=off
997		[baser]=BaseR
998	)
999
1000	# Remove modes which have been seen
1001	local word
1002	for word in "${words[@]:3:${#words[@]}-4}"; do
1003		# ethtool recognizes modes case-insensitively
1004		unset "modes[${word,,}]"
1005	done
1006
1007	_ethtool_compgen_nocase "${modes[@]}"
1008}
1009
1010# Completion for ethtool --set-phy-tunable
1011_ethtool_set_phy_tunable()
1012{
1013	case "$cword" in
1014		3)
1015			COMPREPLY=( $( compgen -W downshift -- "$cur" ) )
1016			return ;;
1017		4)
1018			COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
1019			return ;;
1020		5)
1021			COMPREPLY=( $( compgen -W count -- "$cur" ) )
1022			return ;;
1023	esac
1024}
1025
1026# Completion for ethtool --set-priv-flags
1027_ethtool_set_priv_flags()
1028{
1029	if [ $(( cword % 2 )) -eq 0 ]; then
1030		COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
1031		return
1032	fi
1033
1034	# Get available private flags
1035	local -A flags=()
1036	local flag
1037	while IFS= read -r flag; do
1038		# Ignore blank line from empty here-document
1039		if [ -n "$flag" ]; then
1040			flags[$flag]=1
1041		fi
1042	done <<ETHTOOL_PRIV_FLAGS
1043$(_ethtool_get_names_in_section \
1044	'Private flags for [[:graph:]]*' --show-priv-flags "${words[2]}")
1045ETHTOOL_PRIV_FLAGS
1046
1047	# Remove flags which have been seen
1048	local word
1049	for word in "${words[@]:3:${#words[@]}-4}"; do
1050		unset "flags[$word]"
1051	done
1052
1053	COMPREPLY=( $( compgen -W "${!flags[*]}" -- "$cur" ) )
1054}
1055
1056# Completion for ethtool --set-ring
1057_ethtool_set_ring()
1058{
1059	local -A settings=(
1060		[rx-jumbo]=1
1061		[rx-mini]=1
1062		[rx]=1
1063		[tx]=1
1064	)
1065
1066	if [ "${settings[$prev]+set}" ]; then
1067		# Unsigned integer argument
1068		return
1069	fi
1070
1071	# Remove settings which have been seen
1072	local word
1073	for word in "${words[@]:3:${#words[@]}-4}"; do
1074		unset "settings[$word]"
1075	done
1076
1077	COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
1078}
1079
1080# Completion for ethtool --show-nfc
1081_ethtool_show_nfc()
1082{
1083	if [ "$cword" -eq 3 ]; then
1084		COMPREPLY=( $( compgen -W 'rule rx-flow-hash' -- "$cur" ) )
1085		return
1086	fi
1087
1088	case "${words[3]}" in
1089		rule)
1090			if [ "$cword" -eq 4 ]; then
1091				COMPREPLY=(
1092					$(PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin" \
1093						ethtool --show-nfc "${words[2]}" 2>/dev/null |
1094						command sed -n 's/^Filter:[[:space:]]*\([0-9]*\)$/\1/p')
1095				)
1096			fi
1097			return ;;
1098		rx-flow-hash)
1099			case "$cword" in
1100				4)
1101					_ethtool_flow_type --hash
1102					return ;;
1103				5)
1104					COMPREPLY=( $( compgen -W context -- "$cur" ) )
1105					return ;;
1106				6)
1107					_ethtool_context
1108					return ;;
1109			esac
1110			;;
1111	esac
1112}
1113
1114# Completion for ethtool --show-rxfh
1115_ethtool_show_rxfh()
1116{
1117	case "$cword" in
1118		3)
1119			COMPREPLY=( $( compgen -W context -- "$cur" ) )
1120			return ;;
1121		4)
1122			_ethtool_context
1123			return ;;
1124	esac
1125}
1126
1127# Completion for ethtool --test
1128_ethtool_test()
1129{
1130	if [ "$cword" -eq 3 ]; then
1131		COMPREPLY=( $( compgen -W 'external_lb offline online' -- "$cur" ) )
1132		return
1133	fi
1134}
1135
1136
1137# Complete any ethtool command
1138_ethtool()
1139{
1140	local cur prev words cword
1141	_init_completion || return
1142
1143	# Per "Contributing to bash-completion", complete non-duplicate long opts
1144	local -A suggested_funcs=(
1145		[--change-eeprom]=change_eeprom
1146		[--change]=change
1147		[--coalesce]=coalesce
1148		[--config-nfc]=config_nfc
1149		[--driver]=devname
1150		[--dump-module-eeprom]=module_info
1151		[--eeprom-dump]=eeprom_dump
1152		[--features]=features
1153		[--flash]=flash
1154		[--get-dump]=get_dump
1155		[--get-phy-tunable]=get_phy_tunable
1156		[--identify]=devname
1157		[--module-info]=module_info
1158		[--negotiate]=devname
1159		[--offload]=features
1160		[--pause]=pause
1161		[--per-queue]=per_queue
1162		[--phy-statistics]=devname
1163		[--register-dump]=register_dump
1164		[--reset]=reset
1165		[--set-channels]=set_channels
1166		[--set-dump]=devname
1167		[--set-eee]=set_eee
1168		[--set-fec]=set_fec
1169		[--set-phy-tunable]=set_phy_tunable
1170		[--set-priv-flags]=set_priv_flags
1171		[--set-ring]=set_ring
1172		[--set-rxfh-indir]=rxfh
1173		[--show-channels]=devname
1174		[--show-coalesce]=devname
1175		[--show-eee]=devname
1176		[--show-features]=devname
1177		[--show-fec]=devname
1178		[--show-nfc]=show_nfc
1179		[--show-offload]=devname
1180		[--show-pause]=devname
1181		[--show-permaddr]=devname
1182		[--show-priv-flags]=devname
1183		[--show-ring]=devname
1184		[--show-rxfh]=show_rxfh
1185		[--show-time-stamping]=devname
1186		[--statistics]=devname
1187		[--test]=test
1188	)
1189	local -A other_funcs=(
1190		[--config-ntuple]=config_nfc
1191		[--rxfh]=rxfh
1192		[--show-ntuple]=show_nfc
1193		[--show-rxfh-indir]=devname
1194		[-A]=pause
1195		[-C]=coalesce
1196		[-E]=change_eeprom
1197		[-G]=set_ring
1198		[-K]=features
1199		[-L]=set_channels
1200		[-N]=config_nfc
1201		[-P]=devname
1202		[-Q]=per_queue
1203		[-S]=devname
1204		[-T]=devname
1205		[-U]=config_nfc
1206		[-W]=devname
1207		[-X]=rxfh
1208		[-a]=devname
1209		[-c]=devname
1210		[-d]=register_dump
1211		[-e]=eeprom_dump
1212		[-f]=flash
1213		[-g]=devname
1214		[-i]=devname
1215		[-k]=devname
1216		[-l]=devname
1217		[-m]=module_info
1218		[-n]=show_nfc
1219		[-p]=devname
1220		[-r]=devname
1221		[-s]=change
1222		[-t]=test
1223		[-u]=show_nfc
1224		[-w]=get_dump
1225		[-x]=devname
1226	)
1227
1228	if [ "$cword" -le 1 ]; then
1229		_available_interfaces
1230		COMPREPLY+=(
1231			$( compgen -W "--help --version ${!suggested_funcs[*]}" -- "$cur" )
1232		)
1233		return
1234	fi
1235
1236	local func=${suggested_funcs[${words[1]}]-${other_funcs[${words[1]}]-}}
1237	if [ "$func" ]; then
1238		# All sub-commands have devname as their first argument
1239		if [ "$cword" -eq 2 ]; then
1240			_available_interfaces
1241			return
1242		fi
1243
1244		if [ "$func" != devname ]; then
1245			"_ethtool_$func"
1246		fi
1247	fi
1248} &&
1249complete -F _ethtool ethtool
1250
1251# ex: filetype=sh sts=8 sw=8 ts=8 noet
1252