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