aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Amsel <lars.amsel@ni.com>2022-01-13 16:50:11 +0100
committerAaron Rossetto <aaron.rossetto@ni.com>2022-03-23 16:27:04 -0500
commit99ad89609b6c71faff625adbb0a284bc2d405601 (patch)
treebfd93b3747cf8bc46b731be08f0192a8fee68e9f
parent5d20a5a40155e888484f7d57a277bdcebed44207 (diff)
downloaduhd-99ad89609b6c71faff625adbb0a284bc2d405601.tar.gz
uhd-99ad89609b6c71faff625adbb0a284bc2d405601.tar.bz2
uhd-99ad89609b6c71faff625adbb0a284bc2d405601.zip
tools: Add general purpose tool for USRP configuration
Over the years the UHD code base got a whole bunch of tools to control and configure devices. This is an attempt to unify these tools into one. Co-authored-by: Alexander Weber <alexander.weber@ni.com>
-rw-r--r--host/docs/CMakeLists.txt6
-rw-r--r--host/docs/devices.dox4
-rw-r--r--host/docs/uhd_cal_rx_iq_balance.12
-rw-r--r--host/docs/uhd_cal_tx_dc_offset.12
-rw-r--r--host/docs/uhd_cal_tx_iq_balance.12
-rw-r--r--host/docs/uhd_config_info.12
-rw-r--r--host/docs/uhd_find_devices.12
-rw-r--r--host/docs/uhd_image_loader.12
-rw-r--r--host/docs/uhd_images_downloader.12
-rw-r--r--host/docs/uhd_usrp_probe.12
-rw-r--r--host/docs/usrp2_card_burner.12
-rw-r--r--host/docs/usrp_n2xx_simple_net_burner.12
-rw-r--r--host/docs/usrpctl.193
-rw-r--r--host/docs/usrpctl.dox72
-rw-r--r--host/python/uhd/__init__.py1
-rw-r--r--host/python/uhd/usrpctl/__init__.py7
-rw-r--r--host/python/uhd/usrpctl/commands/__init__.py9
-rw-r--r--host/python/uhd/usrpctl/commands/command.py133
-rw-r--r--host/python/uhd/usrpctl/commands/find.py43
-rw-r--r--host/python/uhd/usrpctl/commands/probe.py31
-rw-r--r--host/utils/CMakeLists.txt10
-rw-r--r--host/utils/usrpctl.py91
22 files changed, 510 insertions, 10 deletions
diff --git a/host/docs/CMakeLists.txt b/host/docs/CMakeLists.txt
index 9f6b1eb2b..fc1d2f928 100644
--- a/host/docs/CMakeLists.txt
+++ b/host/docs/CMakeLists.txt
@@ -123,6 +123,12 @@ set(man_page_sources
usrp2_card_burner.1
)
+if (ENABLE_PYTHON_API)
+ set(man_page_sources
+ usrpctl.1
+ )
+endif(ENABLE_PYTHON_API)
+
########################################################################
# Setup man pages
########################################################################
diff --git a/host/docs/devices.dox b/host/docs/devices.dox
index b081a03d5..f65d48960 100644
--- a/host/docs/devices.dox
+++ b/host/docs/devices.dox
@@ -56,5 +56,9 @@ unless stated otherwise, they will still work with this version of UHD.
\li \subpage page_octoclock
+## Usrpctl
+
+\li \subpage page_usrpctl
+
*/
// vim:ft=doxygen:
diff --git a/host/docs/uhd_cal_rx_iq_balance.1 b/host/docs/uhd_cal_rx_iq_balance.1
index df77afb2c..3ca187bd2 100644
--- a/host/docs/uhd_cal_rx_iq_balance.1
+++ b/host/docs/uhd_cal_rx_iq_balance.1
@@ -55,7 +55,7 @@ uhd_cal_tx_dc_offset(1) uhd_cal_tx_iq_balance(1)
This manual page was written by Maitland Bottoms and Nicholas Corgan
for the Debian project (but may be used by others).
.SH COPYRIGHT
-Copyright (c) 2012 Ettus Research LLC
+Copyright (c) 2012-2022 Ettus Research, A National Instruments Brand
.LP
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/host/docs/uhd_cal_tx_dc_offset.1 b/host/docs/uhd_cal_tx_dc_offset.1
index 50d3899e8..4830b7161 100644
--- a/host/docs/uhd_cal_tx_dc_offset.1
+++ b/host/docs/uhd_cal_tx_dc_offset.1
@@ -55,7 +55,7 @@ uhd_cal_rx_iq_balance(1) uhd_cal_tx_iq_balance(1)
This manual page was written by Maitland Bottoms and Nicholas Corgan
for the Debian project (but may be used by others).
.SH COPYRIGHT
-Copyright (c) 2012 Ettus Research LLC
+Copyright (c) 2012-2022 Ettus Research, A National Instruments Brand
.LP
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/host/docs/uhd_cal_tx_iq_balance.1 b/host/docs/uhd_cal_tx_iq_balance.1
index 859cf9a84..accba9f6a 100644
--- a/host/docs/uhd_cal_tx_iq_balance.1
+++ b/host/docs/uhd_cal_tx_iq_balance.1
@@ -55,7 +55,7 @@ uhd_cal_tx_dc_offset(1) uhd_cal_rx_iq_balance(1)
This manual page was written by Maitland Bottoms and Nicholas Corgan
for the Debian project (but may be used by others).
.SH COPYRIGHT
-Copyright (c) 2012 Ettus Research LLC
+Copyright (c) 2012-2022 Ettus Research, A National Instruments Brand
.LP
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/host/docs/uhd_config_info.1 b/host/docs/uhd_config_info.1
index edc1b7532..adc6cb859 100644
--- a/host/docs/uhd_config_info.1
+++ b/host/docs/uhd_config_info.1
@@ -51,7 +51,7 @@ This manual page was written by Nicholas Corgan
for the Debian project (but may be used by others).
.SH COPYRIGHT
-Copyright (c) 2015 National Instruments Corp.
+Copyright (c) 2015-2022 Ettus Research, A National Instruments Brand
.LP
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/host/docs/uhd_find_devices.1 b/host/docs/uhd_find_devices.1
index c628a2d86..15e94a1f1 100644
--- a/host/docs/uhd_find_devices.1
+++ b/host/docs/uhd_find_devices.1
@@ -98,7 +98,7 @@ uhd_usrp_probe(1)
This manual page was written by Maitland Bottoms and Nicholas Corgan
for the Debian project (but may be used by others).
.SH COPYRIGHT
-Copyright (c) 2010 Ettus Research LLC
+Copyright (c) 2010-2022 Ettus Research, A National Instruments Brand
.LP
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/host/docs/uhd_image_loader.1 b/host/docs/uhd_image_loader.1
index 982784f9e..f3b033a70 100644
--- a/host/docs/uhd_image_loader.1
+++ b/host/docs/uhd_image_loader.1
@@ -115,7 +115,7 @@ uhd_images_downloader(1) usrp2_card_burner(1)
This manual page was written by Nicholas Corgan
for the Debian project (but may be used by others).
.SH COPYRIGHT
-Copyright (c) 2015 Ettus Research LLC
+Copyright (c) 2015-2022 Ettus Research, A National Instruments Brand
.LP
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/host/docs/uhd_images_downloader.1 b/host/docs/uhd_images_downloader.1
index 19f109ec5..1f9fbc875 100644
--- a/host/docs/uhd_images_downloader.1
+++ b/host/docs/uhd_images_downloader.1
@@ -38,7 +38,7 @@ usrp2_card_burner(1) usrp_n2xx_simple_net_burner(1) usrp_x3xx_fpga_burner(1) oct
This manual page was written by Maitland Bottoms and Nicholas Corgan
for the Debian project (but may be used by others).
.SH COPYRIGHT
-Copyright (c) 2012,2014 Ettus Research LLC
+Copyright (c) 2012-2022 Ettus Research, A National Instruments Brand
.LP
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/host/docs/uhd_usrp_probe.1 b/host/docs/uhd_usrp_probe.1
index 7d72b32fb..48f2d8667 100644
--- a/host/docs/uhd_usrp_probe.1
+++ b/host/docs/uhd_usrp_probe.1
@@ -112,7 +112,7 @@ uhd_find_devices(1)
This manual page was written by Maitland Bottoms and Nicholas Corgan
for the Debian project (but may be used by others).
.SH COPYRIGHT
-Copyright (c) 2010 Ettus Research LLC
+Copyright (c) 2010-2022 Ettus Research, A National Instruments Brand
.LP
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/host/docs/usrp2_card_burner.1 b/host/docs/usrp2_card_burner.1
index b43484b99..ebf5f560d 100644
--- a/host/docs/usrp2_card_burner.1
+++ b/host/docs/usrp2_card_burner.1
@@ -39,7 +39,7 @@ uhd_images_downloader(1) usrp_n2xx_simple_net_burner(1) usrp_x3xx_fpga_burner(1)
This manual page was written by Nicholas Corgan
for the Debian project (but may be used by others).
.SH COPYRIGHT
-Copyright (c) 2012,2014 Ettus Research LLC
+Copyright (c) 2012-2022 Ettus Research, A National Instruments Brand
.LP
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/host/docs/usrp_n2xx_simple_net_burner.1 b/host/docs/usrp_n2xx_simple_net_burner.1
index 85538ee22..719edc76c 100644
--- a/host/docs/usrp_n2xx_simple_net_burner.1
+++ b/host/docs/usrp_n2xx_simple_net_burner.1
@@ -46,7 +46,7 @@ uhd_images_downloader(1) usrp2_card_burner(1) usrp_x3xx_fpga_burner(1) octoclock
This manual page was written by Nicholas Corgan
for the Debian project (but may be used by others).
.SH COPYRIGHT
-Copyright (c) 2012,2014 Ettus Research LLC
+Copyright (c) 2012-2022 Ettus Research, A National Instruments Brand
.LP
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
diff --git a/host/docs/usrpctl.1 b/host/docs/usrpctl.1
new file mode 100644
index 000000000..1cf0f01eb
--- /dev/null
+++ b/host/docs/usrpctl.1
@@ -0,0 +1,93 @@
+.TH "usrpctl" 1 "4.0.0" UHD "User Commands"
+.SH NAME
+usrpctl \- USRP Hardware Driver Peripheral Configuration Tool
+.SH DESCRIPTION
+Report detailed information on UHD-supported Software Radio Peripherals
+attached by USB, network, or embedded configuration.
+Allows update and configuration of attached devices.
+.LP
+The UHD package is the universal hardware driver for Ettus Research products. The goal
+is to provide a host driver and API for current and future Ettus Research products.
+Users will be able to use the UHD driver standalone or with 3rd party applications.
+.LP
+Details include unit names, revision numbers, and available sensors on all attached
+USRP motherboards and daughterboards.
+.SH SYNOPSIS
+.B usrpctl [ID] COMMAND [OPTIONS]
+.SH ID
+ID is the optional device argument. It is used to define a set
+of USRP devices that COMMAND should be applied to. If ID is omitted
+COMMAND is applied to all reachable devices.
+
+usrpctl understands the device args argument used by other UHD
+tools like uhd_find_devices.
+
+.SH COMMAND
+Is the action the tool is to take. Every command can be either applied to a
+single device or a group of devices. Commands that run on a group of
+devices repeat the command for every device.
+
+- Single device commands:
+ - config: Read/write configuration variables (e.g., IP address)
+ - probe: reads extended information about the USRP
+- Multi device commands:
+ - update: Update binaries (e.g., FPGA image)
+ - reset: Reset the device or parts thereof (e.g., only reset MPM)
+ - find: finds all available USRPs in this network
+
+.SH OPTIONS
+The options are not always mandatory. It depends on the given COMMAND.
+
+.SH find command
+
+The find command takes no further options. If ID is not given it scans the
+system for available, supported devices and prints a list of discovered devices.
+The print out is compatible to uhd_find_devices.
+ID can be used to narrow down the list of discovered devices.
+
+.SH probe command
+.IP "Print a complete property tree:"
+-tree
+.TP
+The probe command can only be applied to a single device so make sure that
+ID identifies exactly one device. Without arguments it displays detailed
+information about the device such as name, serial, revision numbers,
+firmware version sensor information on attached motherboard and daughterboards.
+
+.SH EXAMPLES
+.TP \w'usrpctl\ 'u
+.BI usrpctl\ find
+find all supported devices
+.TP
+.BI usrpctl\ type=x300,product=X310 find
+find all x310 devices
+.TP
+.BI usrpctl\ name=my_usrp\ find
+find a device named my_usrp
+.TP
+.BI usrpctl\ addr=192.168.10.2\ find
+find a device with the given IP.
+.TP
+.BI usrpctl\ addr=192.168.10.2\ probe
+display device information for USRP with the given ID
+.TP
+.BI usprctl\ name=my_usrp\ probe\ \-tree
+display property tree of device with the name my_usrp
+.TP
+
+.SH SEE ALSO
+UHD documentation:
+.B http://files.ettus.com/manual/
+.LP
+.SH COPYRIGHT
+Copyright (c) 2022 Ettus Research, A National Instruments Brand
+.LP
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+.LP
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
diff --git a/host/docs/usrpctl.dox b/host/docs/usrpctl.dox
new file mode 100644
index 000000000..431f2ee66
--- /dev/null
+++ b/host/docs/usrpctl.dox
@@ -0,0 +1,72 @@
+/*! \page page_usrpctl Usrpctl
+
+\tableofcontents
+
+\section usrpctl_description Description
+
+usrpctl is the central and universal tool to
+query, update or configure USRP devices. The command structure is:
+
+ usrpctl $ID $COMMAND $OPTIONS
+
+`usrpctl` will run $COMMAND on the devices identified by $ID. $OPTIONS
+is dependend on the chosen command.
+
+\section usrpctl_id $ID
+
+$ID is the optional device argument. It is used to define a set
+of USRP devices that `$COMMAND` should be applied to. If `$ID` is omitted
+$COMMAND is applied to all reachable devices.
+
+`usrpctl` understands the device args argument used by other UHD
+tools like `uhd_find_devices`.
+
+\section usrpctl_command $COMMAND
+Is the action the tool is to take. Every command can be either applied to a
+single device or a group of devices. Commands that run on a group of
+devices repeat the command for every device.
+
+- Single device commands:
+ - `config`: Read/write configuration variables (e.g., IP address)
+ - `probe`: reads extended information about the USRP
+- Multi device commands:
+ - `update`: Update binaries (e.g., FPGA image)
+ - `reset`: Reset the device or parts thereof (e.g., only reset MPM)
+ - `find`: finds all available USRPs in this network
+
+\section usrpctl_options $OPTIONS
+
+The options depend on the chosen command. Optional arguments are prepended
+with a dash, mandatory are not.
+
+\section usrpctl_commands Available commands
+
+\section usrpctl_find find
+
+The find command takes no further options. If `$ID` is not given it scans the
+system for available, supported devices and prints a list of discovered devices.
+The print out is compatible to \ref id_identifying_cmdline "`uhd_find_devices`".
+`$ID` can be used to narrow down the list of discovered devices.
+
+Examples:
+
+- `usrpctl find` find all supported devices
+- `usrpctl type=x300,product=X310 find` find all x310 devices
+- `usrpctl name=my_usrp find` find a device named my_usrp
+- `usrpctl addr=192.168.10.2 find` find a device with the given IP.
+
+\subsection usrpctl_probe probe
+Arguments:
+-`-tree`: print a list of the device property tree
+
+The probe command can only be applied to a single device so make sure that
+$ID identifies exactly one device. Without arguments it displays detailed
+information about the device such as name, serial, revision numbers,
+firmware version sensor information on attached motherboard and daughterboards.
+
+Examples:
+- `usrpctl addr=192.168.10.2 probe` display device information for USRP with the given ID
+- `usprctl name=my_usrp probe -tree` display property tree of device with the name my_usrp
+
+*/
+// vim:ft=doxygen:
diff --git a/host/python/uhd/__init__.py b/host/python/uhd/__init__.py
index bfbe9a57f..b825cfa2e 100644
--- a/host/python/uhd/__init__.py
+++ b/host/python/uhd/__init__.py
@@ -9,6 +9,7 @@ UHD Python API module
from . import types
from . import usrp
+from . import usrpctl
from . import filters
from . import rfnoc
from . import dsp
diff --git a/host/python/uhd/usrpctl/__init__.py b/host/python/uhd/usrpctl/__init__.py
new file mode 100644
index 000000000..8c16e1823
--- /dev/null
+++ b/host/python/uhd/usrpctl/__init__.py
@@ -0,0 +1,7 @@
+"""
+Copyright (c) 2022 Ettus Research, A National Instruments Brand
+
+SPDX-License-Identifier: GPL-3.0-or-later
+"""
+
+from . import commands
diff --git a/host/python/uhd/usrpctl/commands/__init__.py b/host/python/uhd/usrpctl/commands/__init__.py
new file mode 100644
index 000000000..3a8fd045e
--- /dev/null
+++ b/host/python/uhd/usrpctl/commands/__init__.py
@@ -0,0 +1,9 @@
+"""
+Copyright (c) 2022 Ettus Research, A National Instruments Brand
+
+SPDX-License-Identifier: GPL-3.0-or-later
+"""
+
+from .command import BaseCommand
+from .find import FindCommand
+from .probe import ProbeCommand
diff --git a/host/python/uhd/usrpctl/commands/command.py b/host/python/uhd/usrpctl/commands/command.py
new file mode 100644
index 000000000..9f19d33a5
--- /dev/null
+++ b/host/python/uhd/usrpctl/commands/command.py
@@ -0,0 +1,133 @@
+"""
+Copyright (c) 2022 Ettus Research, A National Instruments Brand
+
+SPDX-License-Identifier: GPL-3.0-or-later
+"""
+
+import functools
+import re
+import subprocess
+
+SSH_COMMAND = 'ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 {user}@{host}'
+SCP_COMMAND = 'scp -o StrictHostKeyChecking=no -o ConnectTimeout=10 {src} {user}@{host}:{dest}'
+
+REBOOT_SCRIPT = r'''
+max_timeout=600
+
+function ssh_cmd() {{
+ ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 {user}@{host} $1
+}}
+
+if ! {{ boot_id=$(ssh_cmd "cat /proc/sys/kernel/random/boot_id") \
+ && [[ $boot_id ]]; }}; then
+ echo "Unable to retrieve boot ID" >&2
+ exit 1
+fi
+
+ssh_cmd "reboot"
+
+timeout_at=$(( SECONDS + max_timeout ))
+until new_boot_id=$(ssh_cmd "cat /proc/sys/kernel/random/boot_id") \
+ && [[ $new_boot_id != "$boot_id" ]]; do
+ if (( SECONDS > timeout_at )); then
+ echo "System did not reboot within timeout" >&2
+ exit 1
+ fi
+ sleep 5
+done
+
+echo "System successfully rebooted" >&2
+'''
+
+class BaseCommand:
+ """
+ Base class for all usrpctl commands
+
+ Implements some helper useful for all command classes.
+ """
+
+ @classmethod
+ def command_name(cls):
+ """
+ By default the command name is the class name (lowercase) without
+ the trailing "Command"
+ """
+ return re.sub("(.*)Command$", r"\1", cls.__name__).lower()
+
+ @classmethod
+ def add_parser(cls, parser):
+ """
+ add_parser is used to tell usrpctl argparse which command line
+ argument the command accepts.
+ """
+
+ def _to_arg_list(self, args):
+ """
+ Takes parsed arguments from argparse and converts it to
+ a flat list of key value pairs where the key is prepended with
+ two dashes. If value is falsy the pair is ommitted.
+ This is suitable to pass parsed arguments into another process.
+ Example: Suppose args is:
+ Namespace(foo='fval', bar=2, baz=None)
+ then this would result in this list:
+ ['--foo', 'fval', '--bar', 2]
+ """
+ arg_list = {f"--{key}": value for key, value in vars(args).items() if value}
+ return list(functools.reduce(
+ lambda left, right: left + right, arg_list.items())) if arg_list else []
+
+ def _external_process(self, command):
+ """
+ Run command in an external process.
+
+ stderr is redirected into stdout and the oupput is
+ yielded while the process is running.
+ throws CalledProcessError on non zero returncode
+ """
+ with subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT, text=True) as process:
+ for line in iter(process.stdout.readline, ""):
+ yield line.rstrip()
+ returncode = process.wait()
+ if returncode != 0:
+ raise subprocess.CalledProcessError(returncode, command)
+
+ def _reboot(self, user, host):
+ cmd = REBOOT_SCRIPT.format(user=user, host=host)
+ yield from self._external_process(["bash", "-c", cmd])
+
+ def _remote_cmd(self, user, host, cmd):
+ """
+ Executes a command via SSH.
+ """
+ ssh_cmd = SSH_COMMAND.format(user=user, host=host)
+ yield from self._external_process(ssh_cmd.split(' ') + [cmd])
+
+ def _remote_copy(self, user, host, src, dest):
+ """
+ Uses scp to copy a file to a remote host
+ """
+ cp_cmd = SCP_COMMAND.format(user=user, host=host, src=src, dest=dest)
+ yield from self._external_process(cp_cmd.split(' '))
+
+ def _process_output(self, line):
+ print(line)
+
+ def _run_commands(self, usrp, args):
+ print(f"{self.__class__.__name__} is not yet implemented."
+ f"arguments usrp: {usrp} args: {args}")
+
+ def is_multi_device_capable(self):
+ """
+ Tell whether this command can serve multiple USRPs at once
+ """
+ return False
+
+ def run(self, usrps, args):
+ """
+ Run the command
+ """
+ for usrp in usrps:
+ print(f'{self.command_name()} {usrp.to_string()}')
+ for line in self._run_commands(usrp, args):
+ self._process_output(line)
diff --git a/host/python/uhd/usrpctl/commands/find.py b/host/python/uhd/usrpctl/commands/find.py
new file mode 100644
index 000000000..7a07a73c3
--- /dev/null
+++ b/host/python/uhd/usrpctl/commands/find.py
@@ -0,0 +1,43 @@
+"""
+Copyright (c) 2022 Ettus Research, A National Instruments Brand
+
+SPDX-License-Identifier: GPL-3.0-or-later
+"""
+
+from .command import BaseCommand
+
+class FindCommand(BaseCommand):
+ """
+ Class that finds USRP devices
+ """
+
+ @classmethod
+ def add_parser(cls, parser):
+ """
+ Adding subparser for find command
+ """
+ parser.add_parser(cls.command_name(),
+ help="print devices found using id parameter")
+
+ def is_multi_device_capable(self):
+ """
+ Can handle multiple USRPs
+ """
+ return True
+
+ def run(self, usrps, args):
+ """
+ Because usrpctl issued find to build the usrps list
+ from the id argument the only thing left to do is
+ print the found devices. Print mimics the behaviour
+ of uhd_find_devices.
+ """
+ for index, usrp in enumerate(usrps):
+ print('--------------------------------------------------')
+ print(f"-- UHD Device {index}")
+ print('--------------------------------------------------')
+ print('Device Address:')
+ for key, value in usrp.to_dict().items():
+ print(f" {key}: {value}")
+ print()
+ print()
diff --git a/host/python/uhd/usrpctl/commands/probe.py b/host/python/uhd/usrpctl/commands/probe.py
new file mode 100644
index 000000000..486844a58
--- /dev/null
+++ b/host/python/uhd/usrpctl/commands/probe.py
@@ -0,0 +1,31 @@
+"""
+Copyright (c) 2022 Ettus Research, A National Instruments Brand
+
+SPDX-License-Identifier: GPL-3.0-or-later
+"""
+
+from .command import BaseCommand
+
+class ProbeCommand(BaseCommand):
+ """
+ Command that uses uhd_usrp_probe to query device properties.
+ Acts on single devices only.
+ """
+ @classmethod
+ def add_parser(cls, parser):
+ """
+ Allow -tree as argument. other uhd_usrp_probe arguments
+ are not supported right now
+ """
+ subparser = parser.add_parser(cls.command_name(),
+ help="report detailed information on USRP device")
+ subparser.add_argument("-tree", const="tree", action="store_const",
+ help="reads the device tree")
+
+ def _run_commands(self, usrp, args):
+ """
+ probe the device
+ """
+ yield from self._external_process(
+ ["uhd_usrp_probe", f"--args={usrp.to_string()}"] +
+ self._to_arg_list(args))
diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt
index 1ac0b1c99..fcb4fd9b0 100644
--- a/host/utils/CMakeLists.txt
+++ b/host/utils/CMakeLists.txt
@@ -38,6 +38,16 @@ UHD_INSTALL(PROGRAMS
DESTINATION ${RUNTIME_DIR}
COMPONENT utilities
)
+configure_file(
+ "${CMAKE_CURRENT_SOURCE_DIR}/usrpctl.py"
+ "${CMAKE_CURRENT_BINARY_DIR}/usrpctl"
+)
+UHD_INSTALL(PROGRAMS
+ ${CMAKE_CURRENT_BINARY_DIR}/usrpctl
+ RENAME usrpctl
+ DESTINATION ${RUNTIME_DIR}
+ COMPONENT utilities
+)
########################################################################
# Utilities that get installed into the share path
diff --git a/host/utils/usrpctl.py b/host/utils/usrpctl.py
new file mode 100644
index 000000000..c184befee
--- /dev/null
+++ b/host/utils/usrpctl.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env python3
+
+"""
+Copyright (c) 2022 Ettus Research, A National Instruments Brand
+
+SPDX-License-Identifier: GPL-3.0-or-later
+"""
+
+import argparse
+import inspect
+import os
+import re
+import sys
+
+import uhd
+import uhd.usrpctl.commands
+
+def get_commands():
+ """
+ Returns a dictionary of all subclasses of BaseCommand
+
+ Classes are searched in uhd.usrpctl.commands module.
+ Key is the command name of the command class.
+ Value is the command class itself.
+
+ BaseCommand is not part of the resulting dictionary
+ """
+ return {command[1].command_name(): command[1] for command in
+ inspect.getmembers(uhd.usrpctl.commands, inspect.isclass)
+ if issubclass(command[1], uhd.usrpctl.commands.BaseCommand)
+ and command[1] != uhd.usrpctl.commands.BaseCommand}
+
+def parse_args(commands):
+ """
+ parse command line arguments and return USRPs to search for as well
+ as command to execute and command arguments
+ """
+ parser = argparse.ArgumentParser()
+ parser.add_argument("id", nargs="?",
+ help="identifies USRPs devices utilizing dev_addr_type")
+ parser.add_argument("-v", action="count", default=0,
+ help="increase verbosity level ranging from -v to -vvvvv")
+
+ subparsers = parser.add_subparsers(dest="command",
+ help="supported commands, detailed help with usrpctl <cmd> --help")
+
+ for command, cls in commands.items():
+ cls.add_parser(subparsers)
+
+ args = parser.parse_args()
+ script_args = argparse.Namespace()
+ script_args.id = args.id
+ script_args.v = args.v
+ command = args.command
+ del args.id
+ del args.v
+ del args.command
+ return (script_args, command, args)
+
+def find_usrps(id_args):
+ """
+ Find USRPs based on the given id_args.
+ """
+ return uhd.find(id_args or "")
+
+def main():
+ """
+ Control all the USRPs!
+ """
+ commands = get_commands()
+ script_args, cmd_name, cmd_args = parse_args(commands)
+ env = os.environ
+ env['UHD_LOG_CONSOLE_LEVEL'] = str(max(0, 5 - script_args.v))
+ usrps = find_usrps(script_args.id)
+
+ if not usrps:
+ print(f"No USRP found to act on (id={script_args.id})")
+ return 1
+
+ command = commands[cmd_name]()
+
+ if not command.is_multi_device_capable():
+ if len(usrps) > 1:
+ print(f"Found {len(usrps)} USRPs but {cmd_name} "
+ "can only act on single device")
+ return 2
+
+ return command.run(usrps, cmd_args)
+
+if __name__ == "__main__":
+ sys.exit(main())