aboutsummaryrefslogtreecommitdiffstats
path: root/mpm/python
diff options
context:
space:
mode:
authorMichael Auchter <michael.auchter@ni.com>2020-01-29 14:02:59 -0600
committerAaron Rossetto <aaron.rossetto@ni.com>2021-06-03 09:34:55 -0500
commit15b94c53edc86031c74b27cd3d824d2291c0c91f (patch)
tree1e57c3e87262b8d156336d8c28b6f90e86eda1d5 /mpm/python
parent60a147a6a16ced611ab1a7dfdb233d3bd01a0d65 (diff)
downloaduhd-15b94c53edc86031c74b27cd3d824d2291c0c91f.tar.gz
uhd-15b94c53edc86031c74b27cd3d824d2291c0c91f.tar.bz2
uhd-15b94c53edc86031c74b27cd3d824d2291c0c91f.zip
mpm: sys_utils: add libgpiod-based Gpio helper
This adds a new Gpio helper class, which uses libgpiod under the hood instead of the deprecated sysfs GPIO access. This class provides the ability to get/set a specific GPIO, which is looked up by name.
Diffstat (limited to 'mpm/python')
-rwxr-xr-xmpm/python/tests/base_tests.py9
-rwxr-xr-xmpm/python/tests/sys_utils_tests.py1
-rwxr-xr-xmpm/python/tests/test_utilities.py79
-rw-r--r--mpm/python/usrp_mpm/sys_utils/gpio.py60
4 files changed, 143 insertions, 6 deletions
diff --git a/mpm/python/tests/base_tests.py b/mpm/python/tests/base_tests.py
index ec4b8328a..cb57b3481 100755
--- a/mpm/python/tests/base_tests.py
+++ b/mpm/python/tests/base_tests.py
@@ -7,7 +7,7 @@
Base Test Case classes
"""
-import platform
+from test_utilities import on_linux, on_usrp
import unittest
class TestBase(unittest.TestCase):
@@ -19,7 +19,7 @@ class TestBase(unittest.TestCase):
Test function decorator which skips tests unless the current
execution environment is a linux OS.
"""
- if 'linux' in platform.system().lower():
+ if on_linux():
return lambda func: func
return unittest.skip("This test is only valid when run on a Linux system.")
@@ -27,11 +27,8 @@ class TestBase(unittest.TestCase):
"""
Test function decorator which skips tests unless the current
execution environment is a USRP.
-
- Assumes that 'arm' in the machine name constitutes an ARM
- processor, aka a USRP.
"""
- if 'arm' in platform.machine().lower():
+ if on_usrp():
return lambda func: func
return unittest.skip("This test is only valid when run on the USRP.")
diff --git a/mpm/python/tests/sys_utils_tests.py b/mpm/python/tests/sys_utils_tests.py
index c21cfea07..50a10e1a1 100755
--- a/mpm/python/tests/sys_utils_tests.py
+++ b/mpm/python/tests/sys_utils_tests.py
@@ -9,6 +9,7 @@ Tests related to usrp_mpm.sys_utils
from base_tests import TestBase
import unittest
+import test_utilities
from usrp_mpm.sys_utils import net
import platform
diff --git a/mpm/python/tests/test_utilities.py b/mpm/python/tests/test_utilities.py
index 210e76580..198eda5e8 100755
--- a/mpm/python/tests/test_utilities.py
+++ b/mpm/python/tests/test_utilities.py
@@ -6,6 +6,80 @@
""" Utility classes to facilitate unit testing """
import queue
+import platform
+import os
+
+def on_linux():
+ """
+ Returns True if this is being executed on a Linux system
+ """
+ return 'linux' in platform.system().lower()
+
+def on_usrp():
+ """
+ Returns True if this is being executed on an USRP
+ """
+ # Check device tree standard property for manufacturer info
+ path = '/sys/firmware/devicetree/base/compatible'
+
+ if not os.path.exists(path):
+ return False
+ else:
+ with open(path, 'r') as f:
+ s = f.read()
+ # String returned is actually a list of null-terminated strings,
+ # replace the null-terminations with a separator
+ s.replace('\x00', ';')
+ return 'ettus' in s
+
+def _mock_gpiod_pkg():
+ """
+ Replace the gpiod python package with a mock version if import
+ of the gpiod package fails. This package is not available on all
+ OS versions that we would like to test in.
+ """
+ try:
+ import gpiod
+ except Exception as ex:
+ # The gpiod package should be available if testing on a USRP
+ if on_usrp():
+ raise ex
+ import sys
+ sys.modules["gpiod"] = MockGpiod
+
+class MockGpiod(object):
+ """
+ Mocks a portion of the gpiod python package without actually
+ accessing GPIO hardware.
+ """
+ LINE_REQ_DIR_IN = 0
+ LINE_REQ_DIR_OUT = 1
+
+ _DEFAULT_LINE_VAL = 0
+
+ class MockLine(object):
+ def __init__(self, val):
+ self.val = val
+
+ def request(self):
+ pass
+
+ def release(self):
+ pass
+
+ def get_value(self):
+ return self.val
+
+ def set_value(self, val):
+ self.val = val
+
+ def __init__(self):
+ self.lines = dict()
+
+ def find_line(self, name):
+ if name not in self.lines.keys():
+ self.lines[name] = self.MockLine(self._DEFAULT_LINE_VAL)
+ return self.lines[name]
class MockRegsIface(object):
"""
@@ -111,3 +185,8 @@ class MockLog(object):
return ''
else:
return log_messages.get_nowait()
+
+# importing this utilities package should mock out the gpiod package
+# if necessary so that usrp_mpm can be imported on devices without
+# gpiod support for the OS.
+_mock_gpiod_pkg()
diff --git a/mpm/python/usrp_mpm/sys_utils/gpio.py b/mpm/python/usrp_mpm/sys_utils/gpio.py
new file mode 100644
index 000000000..b609479f1
--- /dev/null
+++ b/mpm/python/usrp_mpm/sys_utils/gpio.py
@@ -0,0 +1,60 @@
+#
+# Copyright 2020 Ettus Research, a National Instruments Brand
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+"""
+Access to GPIOs via libgpiod
+"""
+
+import contextlib
+import gpiod
+
+
+@contextlib.contextmanager
+def request_gpio(line, direction):
+ """
+ Context manager for a GPIO line
+ """
+ line.request(consumer='mpm', type=direction)
+ try:
+ yield line
+ finally:
+ line.release()
+
+
+class Gpio:
+ """
+ Class for accessing a named GPIO line via libgpiod
+ """
+
+ INPUT = gpiod.LINE_REQ_DIR_IN
+ OUTPUT = gpiod.LINE_REQ_DIR_OUT
+
+ def __init__(self, name, direction=INPUT, default_val=None):
+ self._direction = direction
+ self._line = gpiod.find_line(name)
+ self._out_value = False
+ if self._line is None:
+ raise RuntimeError('failed to find gpio with name %s' % name)
+
+ if default_val is not None and direction == Gpio.OUTPUT:
+ self.set(default_val)
+
+ def get(self):
+ """
+ Read the value of this GPIO
+ """
+ if self._direction == self.OUTPUT:
+ return self._out_value
+
+ with request_gpio(self._line, self._direction) as gpio:
+ return bool(gpio.get_value())
+
+ def set(self, value):
+ """
+ Set the value of this GPIO
+ """
+ with request_gpio(self._line, self._direction) as gpio:
+ gpio.set_value(int(value))
+ self._out_value = bool(value)