2 # SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
12 sys
.path
.append(pathlib
.Path(__file__
).resolve().parent
.as_posix())
13 from lib
import YnlFamily
15 def args_to_req(ynl
, op_name
, args
, req
):
17 Verify and convert command-line arguments to the ynl-compatible request.
19 valid_attrs
= ynl
.operation_do_attributes(op_name
)
20 valid_attrs
.remove('header') # not user-provided
23 print(f
'no attributes, expected: {valid_attrs}')
29 if i
+ 1 >= len(args
):
30 print(f
'expected value for \'{attr}\'')
33 if attr
not in valid_attrs
:
34 print(f
'invalid attribute \'{attr}\', expected: {valid_attrs}')
42 def print_field(reply
, *desc
):
44 Pretty-print a set of fields from the reply. desc specifies the
45 fields and the optional type (bool/yn).
48 return print_field(reply
, *zip(reply
.keys(), reply
.keys()))
52 field
, name
, tp
= spec
57 value
= reply
.get(field
, None)
59 value
= 'yes' if value
else 'no'
60 elif tp
== 'bool' or isinstance(value
, bool):
61 value
= 'on' if value
else 'off'
63 value
= 'n/a' if value
is None else value
65 print(f
'{name}: {value}')
67 def print_speed(name
, value
):
69 Print out the speed-like strings from the value dict.
71 speed_re
= re
.compile(r
'[0-9]+base[^/]+/.+')
72 speed
= [ k
for k
, v
in value
.items() if v
and speed_re
.match(k
) ]
73 print(f
'{name}: {" ".join(speed)}')
75 def doit(ynl
, args
, op_name
):
77 Prepare request header, parse arguments and doit.
81 'dev-name': args
.device
,
85 args_to_req(ynl
, op_name
, args
.args
, req
)
88 def dumpit(ynl
, args
, op_name
, extra
= {}):
90 Prepare request header, parse arguments and dumpit (filtering out the
91 devices we're not interested in).
93 reply
= ynl
.dump(op_name
, { 'header': {} } | extra
)
98 if msg
['header']['dev-name'] == args
.device
:
100 pprint
.PrettyPrinter().pprint(msg
)
102 msg
.pop('header', None)
105 print(f
"Not supported for device {args.device}")
108 def bits_to_dict(attr
):
110 Convert ynl-formatted bitmask to a dict of bit=value.
113 if 'bits' not in attr
:
115 if 'bit' not in attr
['bits']:
117 for bit
in attr
['bits']['bit']:
118 if bit
['name'] == '':
121 value
= bit
.get('value', False)
126 parser
= argparse
.ArgumentParser(description
='ethtool wannabe')
127 parser
.add_argument('--json', action
=argparse
.BooleanOptionalAction
)
128 parser
.add_argument('--show-priv-flags', action
=argparse
.BooleanOptionalAction
)
129 parser
.add_argument('--set-priv-flags', action
=argparse
.BooleanOptionalAction
)
130 parser
.add_argument('--show-eee', action
=argparse
.BooleanOptionalAction
)
131 parser
.add_argument('--set-eee', action
=argparse
.BooleanOptionalAction
)
132 parser
.add_argument('-a', '--show-pause', action
=argparse
.BooleanOptionalAction
)
133 parser
.add_argument('-A', '--set-pause', action
=argparse
.BooleanOptionalAction
)
134 parser
.add_argument('-c', '--show-coalesce', action
=argparse
.BooleanOptionalAction
)
135 parser
.add_argument('-C', '--set-coalesce', action
=argparse
.BooleanOptionalAction
)
136 parser
.add_argument('-g', '--show-ring', action
=argparse
.BooleanOptionalAction
)
137 parser
.add_argument('-G', '--set-ring', action
=argparse
.BooleanOptionalAction
)
138 parser
.add_argument('-k', '--show-features', action
=argparse
.BooleanOptionalAction
)
139 parser
.add_argument('-K', '--set-features', action
=argparse
.BooleanOptionalAction
)
140 parser
.add_argument('-l', '--show-channels', action
=argparse
.BooleanOptionalAction
)
141 parser
.add_argument('-L', '--set-channels', action
=argparse
.BooleanOptionalAction
)
142 parser
.add_argument('-T', '--show-time-stamping', action
=argparse
.BooleanOptionalAction
)
143 parser
.add_argument('-S', '--statistics', action
=argparse
.BooleanOptionalAction
)
144 # TODO: --show-tunnels tunnel-info-get
145 # TODO: --show-module module-get
146 # TODO: --get-plca-cfg plca-get
147 # TODO: --get-plca-status plca-get-status
148 # TODO: --show-mm mm-get
149 # TODO: --show-fec fec-get
150 # TODO: --dump-module-eerpom module-eeprom-get
153 parser
.add_argument('device', metavar
='device', type=str)
154 parser
.add_argument('args', metavar
='args', type=str, nargs
='*')
156 args
= parser
.parse_args()
158 script_abs_dir
= os
.path
.dirname(os
.path
.abspath(sys
.argv
[0]))
159 spec
= os
.path
.join(script_abs_dir
,
160 '../../../Documentation/netlink/specs/ethtool.yaml')
161 schema
= os
.path
.join(script_abs_dir
,
162 '../../../Documentation/netlink/genetlink-legacy.yaml')
164 ynl
= YnlFamily(spec
, schema
)
166 if args
.set_priv_flags
:
167 # TODO: parse the bitmask
168 print("not implemented")
172 return doit(ynl
, args
, 'eee-set')
175 return doit(ynl
, args
, 'pause-set')
177 if args
.set_coalesce
:
178 return doit(ynl
, args
, 'coalesce-set')
180 if args
.set_features
:
181 # TODO: parse the bitmask
182 print("not implemented")
185 if args
.set_channels
:
186 return doit(ynl
, args
, 'channels-set')
189 return doit(ynl
, args
, 'rings-set')
191 if args
.show_priv_flags
:
192 flags
= bits_to_dict(dumpit(ynl
, args
, 'privflags-get')['flags'])
197 eee
= dumpit(ynl
, args
, 'eee-get')
198 ours
= bits_to_dict(eee
['modes-ours'])
199 peer
= bits_to_dict(eee
['modes-peer'])
202 status
= 'enabled' if eee
['enabled'] else 'disabled'
203 if 'active' in eee
and eee
['active']:
204 status
= status
+ ' - active'
206 status
= status
+ ' - inactive'
208 status
= 'not supported'
210 print(f
'EEE status: {status}')
211 print_field(eee
, ('tx-lpi-timer', 'Tx LPI'))
212 print_speed('Advertised EEE link modes', ours
)
213 print_speed('Link partner advertised EEE link modes', peer
)
218 print_field(dumpit(ynl
, args
, 'pause-get'),
219 ('autoneg', 'Autonegotiate', 'bool'),
220 ('rx', 'RX', 'bool'),
221 ('tx', 'TX', 'bool'))
224 if args
.show_coalesce
:
225 print_field(dumpit(ynl
, args
, 'coalesce-get'))
228 if args
.show_features
:
229 reply
= dumpit(ynl
, args
, 'features-get')
230 available
= bits_to_dict(reply
['hw'])
231 requested
= bits_to_dict(reply
['wanted']).keys()
232 active
= bits_to_dict(reply
['active']).keys()
233 never_changed
= bits_to_dict(reply
['nochange']).keys()
235 for f
in sorted(available
):
241 if f
not in available
or f
in never_changed
:
247 req
= " [requested on]"
249 req
= " [requested off]"
251 print(f
'{f}: {value}{fixed}{req}')
255 if args
.show_channels
:
256 reply
= dumpit(ynl
, args
, 'channels-get')
257 print(f
'Channel parameters for {args.device}:')
259 print(f
'Pre-set maximums:')
263 ('other-max', 'Other'),
264 ('combined-max', 'Combined'))
266 print(f
'Current hardware settings:')
270 ('other-count', 'Other'),
271 ('combined-count', 'Combined'))
276 reply
= dumpit(ynl
, args
, 'channels-get')
278 print(f
'Ring parameters for {args.device}:')
280 print(f
'Pre-set maximums:')
283 ('rx-mini-max', 'RX Mini'),
284 ('rx-jumbo-max', 'RX Jumbo'),
287 print(f
'Current hardware settings:')
290 ('rx-mini', 'RX Mini'),
291 ('rx-jumbo', 'RX Jumbo'),
295 ('rx-buf-len', 'RX Buf Len'),
296 ('cqe-size', 'CQE Size'),
297 ('tx-push', 'TX Push', 'bool'))
302 print(f
'NIC statistics:')
305 strset
= dumpit(ynl
, args
, 'strset-get')
306 pprint
.PrettyPrinter().pprint(strset
)
313 # TODO: support passing the bitmask
315 #{ 'name': 'eth-phy', 'value': True },
316 { 'name': 'eth-mac', 'value': True },
317 #{ 'name': 'eth-ctrl', 'value': True },
318 #{ 'name': 'rmon', 'value': True },
324 rsp
= dumpit(ynl
, args
, 'stats-get', req
)
325 pprint
.PrettyPrinter().pprint(rsp
)
328 if args
.show_time_stamping
:
335 tsinfo
= dumpit(ynl
, args
, 'tsinfo-get', req
)
337 print(f
'Time stamping parameters for {args.device}:')
339 print('Capabilities:')
340 [print(f
'\t{v}') for v
in bits_to_dict(tsinfo
['timestamping'])]
342 print(f
'PTP Hardware Clock: {tsinfo["phc-index"]}')
344 print('Hardware Transmit Timestamp Modes:')
345 [print(f
'\t{v}') for v
in bits_to_dict(tsinfo
['tx-types'])]
347 print('Hardware Receive Filter Modes:')
348 [print(f
'\t{v}') for v
in bits_to_dict(tsinfo
['rx-filters'])]
351 [print(f
'\t{k}: {v}') for k
, v
in tsinfo
['stats'].items()]
354 print(f
'Settings for {args.device}:')
355 linkmodes
= dumpit(ynl
, args
, 'linkmodes-get')
356 ours
= bits_to_dict(linkmodes
['ours'])
358 supported_ports
= ('TP', 'AUI', 'BNC', 'MII', 'FIBRE', 'Backplane')
359 ports
= [ p
for p
in supported_ports
if ours
.get(p
, False)]
360 print(f
'Supported ports: [ {" ".join(ports)} ]')
362 print_speed('Supported link modes', ours
)
364 print_field(ours
, ('Pause', 'Supported pause frame use', 'yn'))
365 print_field(ours
, ('Autoneg', 'Supports auto-negotiation', 'yn'))
367 supported_fec
= ('None', 'PS', 'BASER', 'LLRS')
368 fec
= [ p
for p
in supported_fec
if ours
.get(p
, False)]
369 fec_str
= " ".join(fec
)
371 fec_str
= "Not reported"
373 print(f
'Supported FEC modes: {fec_str}')
376 if linkmodes
['speed'] > 0 and linkmodes
['speed'] < 0xffffffff:
377 speed
= f
'{linkmodes["speed"]}Mb/s'
378 print(f
'Speed: {speed}')
384 duplex
= duplex_modes
.get(linkmodes
["duplex"], None)
386 duplex
= f
'Unknown! ({linkmodes["duplex"]})'
387 print(f
'Duplex: {duplex}')
390 if linkmodes
.get("autoneg", 0) != 0:
392 print(f
'Auto-negotiation: {autoneg}')
400 5: 'Directly Attached Copper',
403 linkinfo
= dumpit(ynl
, args
, 'linkinfo-get')
404 print(f
'Port: {ports.get(linkinfo["port"], "Other")}')
406 print_field(linkinfo
, ('phyaddr', 'PHYAD'))
412 print(f
'Transceiver: {transceiver.get(linkinfo["transceiver"], "Unknown")}')
418 mdix
= mdix_ctrl
.get(linkinfo
['tp-mdix-ctrl'], None)
420 mdix
= mdix
+ ' (forced)'
422 mdix
= mdix_ctrl
.get(linkinfo
['tp-mdix'], 'Unknown (auto)')
423 print(f
'MDI-X: {mdix}')
425 debug
= dumpit(ynl
, args
, 'debug-get')
426 msgmask
= bits_to_dict(debug
.get("msgmask", [])).keys()
427 print(f
'Current message level: {" ".join(msgmask)}')
429 linkstate
= dumpit(ynl
, args
, 'linkstate-get')
435 detected
= detected_states
.get(linkstate
['link'], 'unknown')
436 print(f
'Link detected: {detected}')
438 if __name__
== '__main__':