diff options
author | Michael Auchter <michael.auchter@ni.com> | 2020-01-29 14:02:59 -0600 |
---|---|---|
committer | Aaron Rossetto <aaron.rossetto@ni.com> | 2021-06-03 09:34:55 -0500 |
commit | 15b94c53edc86031c74b27cd3d824d2291c0c91f (patch) | |
tree | 1e57c3e87262b8d156336d8c28b6f90e86eda1d5 /mpm/python | |
parent | 60a147a6a16ced611ab1a7dfdb233d3bd01a0d65 (diff) | |
download | uhd-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-x | mpm/python/tests/base_tests.py | 9 | ||||
-rwxr-xr-x | mpm/python/tests/sys_utils_tests.py | 1 | ||||
-rwxr-xr-x | mpm/python/tests/test_utilities.py | 79 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/sys_utils/gpio.py | 60 |
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) |