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