aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrej Rode <andrej.rode@ettus.com>2017-03-27 18:03:52 -0700
committerMartin Braun <martin.braun@ettus.com>2017-12-22 15:03:45 -0800
commit6a12add1560545438e1bebc05efbafd05aace4f9 (patch)
treec94dbbbd4da0c7ef41fc8849f174875a13f0b511
parentba4fad345d7489b40a7dab83842ac21d13c4f460 (diff)
downloaduhd-6a12add1560545438e1bebc05efbafd05aace4f9.tar.gz
uhd-6a12add1560545438e1bebc05efbafd05aace4f9.tar.bz2
uhd-6a12add1560545438e1bebc05efbafd05aace4f9.zip
mpm: mpm reorganization
-rw-r--r--mpm/include/mpm/CMakeLists.txt2
-rw-r--r--mpm/include/mpm/ad937x/CMakeLists.txt1
-rw-r--r--mpm/include/mpm/lmk04828/lmk04828_spi_iface.hpp2
-rw-r--r--mpm/include/mpm/net_helper.hpp69
-rw-r--r--mpm/include/mpm/udev_helper.hpp62
-rw-r--r--mpm/lib/CMakeLists.txt2
-rw-r--r--mpm/lib/net_helper.cpp118
-rw-r--r--mpm/lib/udev_helper.cpp93
-rw-r--r--mpm/python/CMakeLists.txt6
-rw-r--r--mpm/python/lib_helper.cpp1
-rw-r--r--mpm/python/pyusrp_periphs.cpp7
-rwxr-xr-xmpm/python/setup.py.in8
-rwxr-xr-xmpm/python/usrp_hwd.py33
-rw-r--r--mpm/python/usrp_mpm/CMakeLists.txt2
-rw-r--r--mpm/python/usrp_mpm/__init__.py10
-rw-r--r--mpm/python/usrp_mpm/dboard_manager/CMakeLists.txt1
-rw-r--r--mpm/python/usrp_mpm/dboard_manager/__init__.py10
-rw-r--r--mpm/python/usrp_mpm/dboard_manager/base.py10
-rw-r--r--mpm/python/usrp_mpm/dboard_manager/eiscat.py8
-rw-r--r--mpm/python/usrp_mpm/dboard_manager/magnesium.py32
-rw-r--r--mpm/python/usrp_mpm/dboard_manager/test.py10
-rw-r--r--mpm/python/usrp_mpm/dboard_manager/unknown.py9
-rw-r--r--mpm/python/usrp_mpm/discovery.py44
-rw-r--r--mpm/python/usrp_mpm/helper.py3
-rw-r--r--mpm/python/usrp_mpm/periph_manager/CMakeLists.txt5
-rw-r--r--mpm/python/usrp_mpm/periph_manager/__init__.py.in2
-rw-r--r--mpm/python/usrp_mpm/periph_manager/base.py93
-rw-r--r--mpm/python/usrp_mpm/periph_manager/n310.py65
-rw-r--r--mpm/python/usrp_mpm/periph_manager/net.py63
-rw-r--r--mpm/python/usrp_mpm/periph_manager/test.py33
-rw-r--r--mpm/python/usrp_mpm/periph_manager/udev.py42
-rw-r--r--mpm/python/usrp_mpm/periphs.py38
-rw-r--r--mpm/python/usrp_mpm/rpc_server.py202
-rw-r--r--mpm/python/usrp_mpm/types.py36
-rwxr-xr-xmpm/tools/mpm_debug.py30
35 files changed, 600 insertions, 552 deletions
diff --git a/mpm/include/mpm/CMakeLists.txt b/mpm/include/mpm/CMakeLists.txt
index 8d073b622..a4ce92931 100644
--- a/mpm/include/mpm/CMakeLists.txt
+++ b/mpm/include/mpm/CMakeLists.txt
@@ -15,8 +15,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
INSTALL(FILES
- net_helper.hpp
- udev_helper.hpp
xbar_iface.hpp
DESTINATION ${INCLUDE_DIR}/mpm
)
diff --git a/mpm/include/mpm/ad937x/CMakeLists.txt b/mpm/include/mpm/ad937x/CMakeLists.txt
index 3fe8d1419..834d6759a 100644
--- a/mpm/include/mpm/ad937x/CMakeLists.txt
+++ b/mpm/include/mpm/ad937x/CMakeLists.txt
@@ -15,6 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
INSTALL(FILES
+ adi_ctrl.hpp
ad937x_ctrl.hpp
DESTINATION ${INCLUDE_DIR}/mpm/mykonos
)
diff --git a/mpm/include/mpm/lmk04828/lmk04828_spi_iface.hpp b/mpm/include/mpm/lmk04828/lmk04828_spi_iface.hpp
index fa11029a8..2743977dc 100644
--- a/mpm/include/mpm/lmk04828/lmk04828_spi_iface.hpp
+++ b/mpm/include/mpm/lmk04828/lmk04828_spi_iface.hpp
@@ -1,3 +1,5 @@
+#pragma once
+
#include "lmk04828.hpp"
#include "uhd/types/serial.hpp"
#include <boost/shared_ptr.hpp>
diff --git a/mpm/include/mpm/net_helper.hpp b/mpm/include/mpm/net_helper.hpp
deleted file mode 100644
index b07e43ccc..000000000
--- a/mpm/include/mpm/net_helper.hpp
+++ /dev/null
@@ -1,69 +0,0 @@
-//
-// Copyright 2017 Ettus Research (National Instruments)
-//
-// 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.
-//
-// 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.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-//
-
-#include <map>
-#include <vector>
-#include <string>
-
-namespace mpm {
-namespace network {
-
-/*!
- * A struct describing a single network interface
- */
-using net_iface = struct net_iface {
- /*! MAC address of the interface in the form AABBCCDDEEFF */
- std::string mac_addr;
- /*! vector of associated IP addresses, contains both IPv4 and IPv6 */
- std::vector<std::string> ip_addr;
-};
-
-/*!
- * net_ifaces contains a <interfaces name, net_iface> pair
- * describing mac address and associated ip addresses for
- * each interface
- */
-using net_ifaces = std::map<std::string, net_iface>;
-
-/*!
- * Convenience function to get all ip addresses of one MAC address
- * \param MAC address in the form AABBCCDDEEFF
- * \return vector of strings containing all IP addresses with this MAC address
- */
-std::vector<std::string> get_if_addrs(const std::string& mac_addr);
-
-/*!
- * Get information about all interfaces on this system
- * \return a map with interface names as keys and the interfaces information as value
- */
-net_ifaces get_net_map();
-
-/*!
- * Pretty print net_ifaces in the style of `ip addr`
- * \param interface map net_ifaces to print
- */
-void print_net_ifaces(net_ifaces my_ifaces);
-}
-}
-
-#ifdef LIBMPM_PYTHON
-void export_net_iface(){
- LIBMPM_BOOST_PREAMBLE("network")
- bp::def("get_if_addrs", &mpm::network::get_if_addrs);
-}
-#endif
-
diff --git a/mpm/include/mpm/udev_helper.hpp b/mpm/include/mpm/udev_helper.hpp
deleted file mode 100644
index 055a93cee..000000000
--- a/mpm/include/mpm/udev_helper.hpp
+++ /dev/null
@@ -1,62 +0,0 @@
-//
-// Copyright 2017 Ettus Research (National Instruments)
-//
-// 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.
-//
-// 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.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-//
-
-#include <libudev.h>
-#include <string>
-#include <vector>
-
-namespace mpm {
- /*!
- * The udev_helper class:
- *
- * talks to libudev and holds a udev context. Device enumeration is done
- * once during initialization.
- * On destruction the udev context is unreferenced again.
- */
- class udev_helper{
- public:
- udev_helper();
- ~udev_helper();
- /*!
- * Return the nvmem device associated with the parent address
- * \param address of the parent platform driver
- * \return a string containing the name of file of the device in /sys
- */
- std::string get_eeprom(const std::string &address);
- /*!
- * Find spidevices associated with the spi_master
- * \param address of the parent platform driver
- * \return a vector of string containing the device paths is /dev
- */
- std::vector<std::string> get_spidev_nodes(const std::string &spi_master);
-
- private:
- udev *_udev;
- udev_enumerate *_enumerate;
- };
-}
-
-#ifdef LIBMPM_PYTHON
-void export_udev_helper(){
- LIBMPM_BOOST_PREAMBLE("udev")
- bp::class_<mpm::udev_helper>("udev_helper", bp::init<>())
- .def("get_eeprom", &mpm::udev_helper::get_eeprom)
- .def("get_spidev_nodes", &mpm::udev_helper::get_spidev_nodes)
- ;
-}
-#endif
-
diff --git a/mpm/lib/CMakeLists.txt b/mpm/lib/CMakeLists.txt
index 649d944c9..cfdde2d03 100644
--- a/mpm/lib/CMakeLists.txt
+++ b/mpm/lib/CMakeLists.txt
@@ -25,7 +25,5 @@ ADD_SUBDIRECTORY(mykonos)
ADD_SUBDIRECTORY(lmk04828)
USRP_PERIPHS_ADD_OBJECT(periphs
- net_helper.cpp
- udev_helper.cpp
xbar_iface.cpp
)
diff --git a/mpm/lib/net_helper.cpp b/mpm/lib/net_helper.cpp
deleted file mode 100644
index a40fd742d..000000000
--- a/mpm/lib/net_helper.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
-//
-// Copyright 2017 Ettus Research (National Instruments)
-//
-// 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.
-//
-// 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.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-//
-
-#include <mpm/net_helper.hpp>
-#include <uhd/exception.hpp>
-#include <boost/format.hpp>
-#include <netdb.h>
-#include <ifaddrs.h>
-#include <linux/if_link.h>
-#include <linux/if_packet.h>
-#include <iomanip>
-#include <iostream>
-#include <string>
-#include <sstream>
-#include <cstring>
-#include <cstdint>
-
-namespace mpm {
-namespace network{
-
-template <typename ArrayType>
-std::string bytearray_to_string(const ArrayType array[], size_t elements) {
- std::stringstream result;
- for (size_t i = 0; i < elements; i++) {
- result << std::uppercase << std::setfill('0')
- << std::setw(sizeof(array[i]) *
- 2) // always produce 2 hex values for each byte
- << std::hex << +array[i]; // Implicit integer promotion
- }
- return result.str();
-}
-
-void print_net_ifaces(net_ifaces my_ifaces) {
- /* take in a net_ifaces and pretty print information
- about all detected network interfaces */
- for (const auto& iface : my_ifaces) {
- std::cout << "interface: " << iface.first << std::endl;
- std::cout << "\tMAC: " << iface.second.mac_addr << std::endl;
- for (const auto& addr : iface.second.ip_addr) {
- std::cout << "\tip address: " << addr << std::endl;
- }
- }
-}
-
-net_ifaces get_net_map() {
- /* Get a map containing a string and a net_iface struct
- to describe all adresses assigned to a interface */
- struct ifaddrs *ifaddr, *ifa;
- int family, s;
- char host[NI_MAXHOST];
- net_ifaces net_map;
-
- if (getifaddrs(&ifaddr) == -1) {
- throw uhd::system_error(str(boost::format("Error: %s") % strerror(errno)));
- }
-
- /* Walk through linked list, maintaining head pointer so we
- can free list later */
-
- for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
- if (ifa->ifa_addr == NULL)
- continue;
-
- /* Put the interaface name into the map, if it already exists
- we get an iterator to the existing element */
- auto result = net_map.emplace(
- std::make_pair(std::string(ifa->ifa_name), net_iface()));
- auto current_iface = result.first;
-
- family = ifa->ifa_addr->sa_family;
- if (family == AF_INET || family == AF_INET6) {
- s = getnameinfo(ifa->ifa_addr,
- (family == AF_INET) ? sizeof(struct sockaddr_in)
- : sizeof(struct sockaddr_in6),
- host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
- if (s != 0) {
- printf("getnameinfo() failed: %s\n", gai_strerror(s));
- return net_map;
- }
- current_iface->second.ip_addr.push_back(std::string(host));
- } else if (family == AF_PACKET && ifa->ifa_data != NULL) {
- struct sockaddr_ll* s = (struct sockaddr_ll*)ifa->ifa_addr;
- uint8_t mac_addr[6];
- memcpy(&mac_addr, s->sll_addr, 6);
- current_iface->second.mac_addr = bytearray_to_string(mac_addr, 6);
- }
- }
- freeifaddrs(ifaddr);
- return net_map;
-}
-
-std::vector<std::string> get_if_addrs(const std::string& mac_addr) {
- /* Convenience wrapper to return all adresses associated with one
- mac address */
- net_ifaces my_map = get_net_map();
- for (const auto& iface : my_map) { // find
- if (iface.second.mac_addr == mac_addr) {
- return iface.second.ip_addr;
- }
- }
- return std::vector<std::string>();
-}
-}
-}
diff --git a/mpm/lib/udev_helper.cpp b/mpm/lib/udev_helper.cpp
deleted file mode 100644
index 108b4b07a..000000000
--- a/mpm/lib/udev_helper.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-//
-// Copyright 2017 Ettus Research (National Instruments)
-//
-// 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.
-//
-// 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.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-//
-
-#include "mpm/udev_helper.hpp"
-#include <uhd/exception.hpp>
-#include <boost/format.hpp>
-#include <boost/make_shared.hpp>
-#include <boost/crc.hpp>
-#include <utility>
-#include <iostream>
-#include <string>
-#include <cstring>
-#include <fstream>
-
-using namespace mpm;
-
-udev_helper::udev_helper(){
- _udev = udev_new();
- if (!_udev) {
- throw uhd::os_error("Failed to create udev!");
- }
- _enumerate = udev_enumerate_new(_udev);
-
-}
-
-udev_helper::~udev_helper(){
- udev_enumerate_unref(_enumerate);
- udev_unref(_udev);
-}
-std::string udev_helper::get_eeprom(const std::string &address){
- udev_list_entry *devices, *dev_list_entry;
- udev_device *dev, *parent;
-
- parent = udev_device_new_from_subsystem_sysname(_udev, "platform", address.c_str());
- if (parent == NULL){
- return std::string();
- }
- udev_enumerate_add_match_parent(_enumerate, parent);
- udev_enumerate_add_match_subsystem(_enumerate, "nvmem");
- udev_enumerate_scan_devices(_enumerate);
-
- devices = udev_enumerate_get_list_entry(_enumerate);
- if (devices == NULL){
- return std::string();
- }
- udev_list_entry_foreach(dev_list_entry, devices) {
- const char *path = NULL, *sys_path = NULL;
- path = udev_list_entry_get_name(dev_list_entry);
- dev = udev_device_new_from_syspath(_udev, path);
- sys_path = udev_device_get_syspath(dev);
- udev_device_unref(dev);
- return "/sys" + std::string(sys_path) + "/nvmem";
- }
- return std::string();
-}
-
-std::vector<std::string> udev_helper::get_spidev_nodes(const std::string &spi_master){
- udev_list_entry *devices, *dev_list_entry;
- udev_device *dev, *parent;
-
- parent = udev_device_new_from_subsystem_sysname(_udev, "platform", spi_master.c_str());
- udev_enumerate_add_match_parent(_enumerate, parent);
- udev_enumerate_add_match_subsystem(_enumerate, "spidev");
- udev_enumerate_scan_devices(_enumerate);
-
- devices = udev_enumerate_get_list_entry(_enumerate);
- std::vector<std::string> found_dev_nodes;
- if (devices != NULL){
- udev_list_entry_foreach(dev_list_entry, devices){
- const char *path, *dev_node;
- path = udev_list_entry_get_name(dev_list_entry);
- dev = udev_device_new_from_syspath(_udev, path);
- dev_node = udev_device_get_devnode(dev);
- found_dev_nodes.push_back(std::string(dev_node));
- udev_device_unref(dev);
- }
- }
- return found_dev_nodes;
-}
diff --git a/mpm/python/CMakeLists.txt b/mpm/python/CMakeLists.txt
index 7daa3bbf5..11adef92a 100644
--- a/mpm/python/CMakeLists.txt
+++ b/mpm/python/CMakeLists.txt
@@ -59,12 +59,8 @@ MESSAGE(STATUS "Boost library directories: ${Boost_LIBRARY_DIRS}")
MESSAGE(STATUS "Boost libraries: ${Boost_LIBRARIES}")
SET(UHD_HOST_ROOT ${CMAKE_SOURCE_DIR}/../host)
-LIST(APPEND
- pyusrp_periphs_sources
- pyusrp_periphs.cpp
-)
-ADD_LIBRARY(pyusrp_periphs SHARED ${pyusrp_periphs_sources})
+ADD_LIBRARY(pyusrp_periphs SHARED pyusrp_periphs.cpp)
TARGET_INCLUDE_DIRECTORIES(pyusrp_periphs PUBLIC
${PYTHON_INCLUDE_DIRS}
${CMAKE_SOURCE_DIR}/lib/
diff --git a/mpm/python/lib_helper.cpp b/mpm/python/lib_helper.cpp
index 5948628f8..58d4a5336 100644
--- a/mpm/python/lib_helper.cpp
+++ b/mpm/python/lib_helper.cpp
@@ -32,7 +32,6 @@ void export_helper(){
bp::class_<mpm::udev_helper>("udev_helper", bp::init<>())
.def("get_eeprom", &mpm::udev_helper::get_eeprom)
;
- bp::def("get_if_addrs", &mpm::network::get_if_addrs);
bp::to_python_converter<std::vector< std::string >, iterable_to_python_list<std::vector< std::string > >, false>();
}
diff --git a/mpm/python/pyusrp_periphs.cpp b/mpm/python/pyusrp_periphs.cpp
index b111e40ca..9f6378763 100644
--- a/mpm/python/pyusrp_periphs.cpp
+++ b/mpm/python/pyusrp_periphs.cpp
@@ -27,11 +27,10 @@
//#include "types.hpp"
#include "converters.hpp"
-#include "mpm/net_helper.hpp"
#include "mpm/xbar_iface.hpp"
-#include "mpm/udev_helper.hpp"
#include "mpm/ad937x/ad937x_ctrl.hpp"
#include "mpm/lmk04828//lmk04828_spi_iface.hpp"
+#include "mpm/dboards/magnesium_manager.hpp"
//#include "lib_periphs.hpp"
//#include "dboards.hpp"
#include <boost/noncopyable.hpp>
@@ -43,12 +42,10 @@ BOOST_PYTHON_MODULE(libpyusrp_periphs)
bp::object package = bp::scope();
package.attr("__path__") = "libpyusrp_periphs";
export_converter();
- export_net_iface();
//export_types();
- export_udev_helper();
//export_spi();
//export_lmk();
export_mykonos();
export_xbar();
- //export_dboards();
+ export_dboards();
}
diff --git a/mpm/python/setup.py.in b/mpm/python/setup.py.in
index 2a63d14aa..e09262d44 100755
--- a/mpm/python/setup.py.in
+++ b/mpm/python/setup.py.in
@@ -40,5 +40,9 @@ setup(name='usrp_mpm',
zip_safe=False,
packages=['usrp_mpm', 'usrp_mpm.periph_manager', 'usrp_mpm.dboard_manager'],
install_requires=[
- 'numpy'
- ])
+ 'mprpc',
+ 'systemd-python',
+ 'pyroute2',
+ 'pyudev'
+ ],
+ tests_require=['pytest', 'pylint'])
diff --git a/mpm/python/usrp_hwd.py b/mpm/python/usrp_hwd.py
index a0c66f62b..f0e52e7fb 100755
--- a/mpm/python/usrp_hwd.py
+++ b/mpm/python/usrp_hwd.py
@@ -21,12 +21,13 @@ Main executable for the USRP Hardware Daemon
from __future__ import print_function
from logging import getLogger
from logging import StreamHandler
+from systemd.journal import JournalHandler
from logging import DEBUG
from logging import Formatter
-import signal
+from gevent import signal
import sys
import usrp_mpm as mpm
-from usrp_mpm.types import shared_state
+from usrp_mpm.types import SharedState
from usrp_mpm.periph_manager import periph_manager
log = getLogger("usrp_mpm")
@@ -55,17 +56,29 @@ def main():
"""
# Setup logging
log.setLevel(DEBUG)
- ch = StreamHandler()
- ch.setLevel(DEBUG)
- formatter = Formatter('[%(asctime)s] [%(levelname)s] [%(name)s] [%(message)s]')
- ch.setFormatter(formatter)
- log.addHandler(ch)
+ handler = StreamHandler()
+ journal_handler = JournalHandler(SYSLOG_IDENTIFIER='usrp_hwd')
+ handler.setLevel(DEBUG)
+ formatter = Formatter('[%(asctime)s] [%(levelname)s] [%(module)s] %(message)s')
+ handler.setFormatter(formatter)
+ journal_formatter = Formatter('[%(levelname)s] [%(module)s] %(message)s')
+ journal_handler.setFormatter(journal_formatter)
+ log.addHandler(handler)
+ log.addHandler(journal_handler)
-
- shared = shared_state()
+ shared = SharedState()
+ # Create the periph_manager for this device
+ # This call will be forwarded to the device specific implementation
+ # e.g. in periph_manager/test.py
+ # Which implementation is called will be determined during configuration
+ # with cmake (-DMPM_DEVICE)
mgr = periph_manager()
+ discovery_info = {
+ "type": mgr._get_device_info()["type"],
+ "serial": mgr._get_device_info()["serial"]
+ }
_PROCESSES.append(
- mpm.spawn_discovery_process({'serial': mgr.get_serial()}, shared))
+ mpm.spawn_discovery_process(discovery_info, shared))
_PROCESSES.append(
mpm.spawn_rpc_process(mpm.types.MPM_RPC_PORT, shared, mgr))
signal.signal(signal.SIGTERM, kill_time)
diff --git a/mpm/python/usrp_mpm/CMakeLists.txt b/mpm/python/usrp_mpm/CMakeLists.txt
index 12bcdac11..1ab48de0a 100644
--- a/mpm/python/usrp_mpm/CMakeLists.txt
+++ b/mpm/python/usrp_mpm/CMakeLists.txt
@@ -24,10 +24,8 @@ SET(USRP_MPM_TOP_FILES
${CMAKE_CURRENT_SOURCE_DIR}/types.py
${CMAKE_CURRENT_SOURCE_DIR}/discovery.py
${CMAKE_CURRENT_SOURCE_DIR}/rpc_server.py
- ${CMAKE_CURRENT_SOURCE_DIR}/periphs.py
)
LIST(APPEND USRP_MPM_FILES ${USRP_MPM_TOP_FILES})
ADD_SUBDIRECTORY(periph_manager)
ADD_SUBDIRECTORY(dboard_manager)
-
SET(USRP_MPM_FILES ${USRP_MPM_FILES} PARENT_SCOPE)
diff --git a/mpm/python/usrp_mpm/__init__.py b/mpm/python/usrp_mpm/__init__.py
index c7043c70c..7606f33ce 100644
--- a/mpm/python/usrp_mpm/__init__.py
+++ b/mpm/python/usrp_mpm/__init__.py
@@ -18,8 +18,8 @@
MPM Module
"""
-from discovery import spawn_discovery_process
-from rpc_server import spawn_rpc_process
-import types
-import periph_manager
-import dboard_manager
+from .discovery import spawn_discovery_process
+from .rpc_server import spawn_rpc_process
+from . import types
+from . import periph_manager
+from . import dboard_manager
diff --git a/mpm/python/usrp_mpm/dboard_manager/CMakeLists.txt b/mpm/python/usrp_mpm/dboard_manager/CMakeLists.txt
index b642d506e..9cd65fa08 100644
--- a/mpm/python/usrp_mpm/dboard_manager/CMakeLists.txt
+++ b/mpm/python/usrp_mpm/dboard_manager/CMakeLists.txt
@@ -22,6 +22,7 @@ SET(USRP_MPM_FILES ${USRP_MPM_FILES})
SET(USRP_MPM_DBMGR_FILES
${CMAKE_CURRENT_SOURCE_DIR}/__init__.py
${CMAKE_CURRENT_SOURCE_DIR}/base.py
+ ${CMAKE_CURRENT_SOURCE_DIR}/test.py
${CMAKE_CURRENT_SOURCE_DIR}/magnesium.py
${CMAKE_CURRENT_SOURCE_DIR}/eiscat.py
${CMAKE_CURRENT_SOURCE_DIR}/unknown.py
diff --git a/mpm/python/usrp_mpm/dboard_manager/__init__.py b/mpm/python/usrp_mpm/dboard_manager/__init__.py
index 02746e78f..f21e5258c 100644
--- a/mpm/python/usrp_mpm/dboard_manager/__init__.py
+++ b/mpm/python/usrp_mpm/dboard_manager/__init__.py
@@ -18,12 +18,12 @@
dboards module __init__.py
"""
from .. import libpyusrp_periphs as lib
-from magnesium import magnesium
-from eiscat import eiscat
-from test import test
-from unknown import unknown
+from .magnesium import magnesium
+from .eiscat import eiscat
+from .test import test
+from .unknown import unknown
-hw_pids = {
+HW_PIDS = {
eiscat.hw_pid: eiscat,
magnesium.hw_pid: magnesium,
}
diff --git a/mpm/python/usrp_mpm/dboard_manager/base.py b/mpm/python/usrp_mpm/dboard_manager/base.py
index 257a2424d..37e7dd2b8 100644
--- a/mpm/python/usrp_mpm/dboard_manager/base.py
+++ b/mpm/python/usrp_mpm/dboard_manager/base.py
@@ -17,22 +17,22 @@
"""
dboard base implementation module
"""
-from . import lib
import logging
import struct
-log = logging.Logger("usrp_mpm.dboards")
+LOG = logging.Logger(__name__)
-class dboard_manager(object):
+class DboardManagerBase(object):
"""
Holds shared pointer to wrapped C++ implementation.
Sanitizes arguments before calling C++ functions.
Ties various constants to specific daughterboard class
"""
_eeprom = {}
- def __init__(self, eeprom={}):
- self._eeprom = eeprom
+
+ def __init__(self, eeprom=None):
+ self._eeprom = eeprom or {}
def get_serial(self):
return self._eeprom.get("serial", "")
diff --git a/mpm/python/usrp_mpm/dboard_manager/eiscat.py b/mpm/python/usrp_mpm/dboard_manager/eiscat.py
index f536d307b..101290574 100644
--- a/mpm/python/usrp_mpm/dboard_manager/eiscat.py
+++ b/mpm/python/usrp_mpm/dboard_manager/eiscat.py
@@ -17,12 +17,12 @@
"""
EISCAT rx board implementation module
"""
-from base import dboard_manager
-from base import lib
-from base import log
+from . import lib
+from .base import DboardManagerBase
+from .base import LOG
-class eiscat(dboard_manager):
+class eiscat(DboardManagerBase):
hw_pid = 3
special_eeprom_addrs = {"special0": "something"}
diff --git a/mpm/python/usrp_mpm/dboard_manager/magnesium.py b/mpm/python/usrp_mpm/dboard_manager/magnesium.py
index d48768208..f13f1de77 100644
--- a/mpm/python/usrp_mpm/dboard_manager/magnesium.py
+++ b/mpm/python/usrp_mpm/dboard_manager/magnesium.py
@@ -17,34 +17,52 @@
"""
magnesium dboard implementation module
"""
-from base import dboard_manager
-from base import lib
-from base import log
+from . import lib
+from .base import DboardManagerBase
import struct
+from logging import getLogger
-class magnesium(dboard_manager):
+LOG = getLogger(__name__)
+
+
+class magnesium(DboardManagerBase):
+ """
+ Holds all dboard specific information and methods of the magnesium dboard
+ """
hw_pid = 2
special_eeprom_addrs = {"special0": "something"}
spi_chipselect = {"0": "lmk", "1": "mykonos", "2": "random"}
spidevs = {}
+ lmk = ""
+ mykonos = ""
+ random = ""
def __init__(self, spi_devices, eeprom_data, *args, **kwargs):
# eeprom_data is a tuple (head_dict, raw_data)
if len(spi_devices) != len(self.spi_chipselect):
- log.error("Expected {0} spi devices, found {1} spi devices".format(len(spi_devices), len(self.spi_chipselect)))
+ LOG.error("Expected {0} spi devices, found {1} spi devices".format(len(spi_devices), len(self.spi_chipselect)))
exit(1)
for spi in spi_devices:
device = self.spi_chipselect.get(spi[-1], None)
if self.chipselect is None:
- log.error("Unexpected chipselect {0}".format(spi[-1]))
+ LOG.error("Unexpected chipselect {0}".format(spi[-1]))
exit(1)
setattr(self, device, spi)
super(magnesium, self).__init__(*args, **kwargs)
def init_device(self):
- self._device = lib.db.magnesium(self.lmk, self.mykonos, self.random)
+ """
+ Execute necessary init dance to bring up dboard
+ """
+ LOG.debug("initialize hardware")
+
+ self._device = lib.dboards.magnesium(
+ self.lmk, self.mykonos, self.random)
def read_eeprom_v1(self, data):
+ """
+ read eeprom data version 1
+ """
# magnesium eeprom contains
# nothing
return struct.unpack_from("x", data)
diff --git a/mpm/python/usrp_mpm/dboard_manager/test.py b/mpm/python/usrp_mpm/dboard_manager/test.py
index b2e422bb4..80299ee41 100644
--- a/mpm/python/usrp_mpm/dboard_manager/test.py
+++ b/mpm/python/usrp_mpm/dboard_manager/test.py
@@ -17,8 +17,11 @@
"""
magnesium dboard implementation module
"""
-from base import dboard_manager
-from base import log
+from . import lib
+from .base import DboardManagerBase
+from logging import getLogger
+
+LOG = getLogger(__name__)
class fake_spi(object):
@@ -36,7 +39,7 @@ class test_device(object):
return argument
-class test(dboard_manager):
+class test(DboardManagerBase):
hw_pid = 234
special_eeprom_addrs = {"special0": "something"}
spi_chipselect = {"0": "dev1", "1": "dev2", "2": "dev3"}
@@ -51,6 +54,7 @@ class test(dboard_manager):
self.dev3 = "2"
def init_device(self):
+ LOG.debug("initialize hardware")
self._device = test_device(self.dev1, self.dev2, self.dev3)
diff --git a/mpm/python/usrp_mpm/dboard_manager/unknown.py b/mpm/python/usrp_mpm/dboard_manager/unknown.py
index f33bacc46..de2354f3d 100644
--- a/mpm/python/usrp_mpm/dboard_manager/unknown.py
+++ b/mpm/python/usrp_mpm/dboard_manager/unknown.py
@@ -17,12 +17,13 @@
"""
EISCAT rx board implementation module
"""
-from base import dboard_manager
-from base import lib
-from base import log
+from .base import DboardManagerBase
+from logging import getLogger
+LOG = getLogger(__name__)
-class unknown(dboard_manager):
+
+class unknown(DboardManagerBase):
hw_pid = 0
special_eeprom_addrs = {}
diff --git a/mpm/python/usrp_mpm/discovery.py b/mpm/python/usrp_mpm/discovery.py
index 174866498..595368c76 100644
--- a/mpm/python/usrp_mpm/discovery.py
+++ b/mpm/python/usrp_mpm/discovery.py
@@ -19,19 +19,19 @@ Code to run the discovery port
"""
from __future__ import print_function
-import time
-import ctypes
-from multiprocessing import Process, Value
+from logging import getLogger
+from multiprocessing import Process
from six import iteritems
import socket
-from types import MPM_DISCOVERY_PORT, graceful_exit
+from usrp_mpm.types import MPM_DISCOVERY_PORT
+
+LOG = getLogger(__name__)
RESPONSE_PREAMBLE = "USRP-MPM"
RESPONSE_SEP = ";"
RESPONSE_CLAIMED_KEY = "claimed"
-
def spawn_discovery_process(device_info, shared_state):
"""
Returns a process that contains the device discovery.
@@ -41,9 +41,9 @@ def spawn_discovery_process(device_info, shared_state):
will be included in the response string.
"""
# claim_status = Value(ctypes.c_bool, False)
- p = Process(target=_discovery_process, args=(device_info, shared_state))
- p.start()
- return p
+ proc = Process(target=_discovery_process, args=(device_info, shared_state))
+ proc.start()
+ return proc
def _discovery_process(device_info, state):
@@ -65,12 +65,22 @@ def _discovery_process(device_info, state):
send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- try:
- while True:
- data, sender = sock.recvfrom(4096)
- if data.strip("\0") == "MPM-DISC":
- send_data = create_response_string()
- send_sock.sendto(send_data, sender)
- except:
- sock.close()
- send_sock.close()
+ # try:
+ while True:
+ data, sender = sock.recvfrom(8000)
+ LOG.info("Got poked by: %s", sender[0])
+ if data.strip("\0") == "MPM-DISC":
+ LOG.info("Sending discovery response to %s port: %d",
+ sender[0], sender[1])
+ send_data = create_response_string()
+ send_sock.sendto(send_data, sender)
+ elif data.strip("\0").startswith("MPM-ECHO"):
+ LOG.info("Received echo request")
+ send_data = data
+ send_sock.sendto(send_data, sender)
+
+ # except Exception as err:
+ # LOG.info("Error: %s", err)
+ # LOG.info("Error type: %s", type(err))
+ # sock.close()
+ # send_sock.close()
diff --git a/mpm/python/usrp_mpm/helper.py b/mpm/python/usrp_mpm/helper.py
deleted file mode 100644
index 29864124a..000000000
--- a/mpm/python/usrp_mpm/helper.py
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-class eeprom_helper:
diff --git a/mpm/python/usrp_mpm/periph_manager/CMakeLists.txt b/mpm/python/usrp_mpm/periph_manager/CMakeLists.txt
index 879ac20c1..f4bc1d1d2 100644
--- a/mpm/python/usrp_mpm/periph_manager/CMakeLists.txt
+++ b/mpm/python/usrp_mpm/periph_manager/CMakeLists.txt
@@ -23,6 +23,9 @@ SET(USRP_MPM_PERIPHMGR_FILES
${CMAKE_CURRENT_SOURCE_DIR}/__init__.py.in
${CMAKE_CURRENT_SOURCE_DIR}/base.py
${CMAKE_CURRENT_SOURCE_DIR}/n310.py
+ ${CMAKE_CURRENT_SOURCE_DIR}/test.py
+ ${CMAKE_CURRENT_SOURCE_DIR}/net.py
+ ${CMAKE_CURRENT_SOURCE_DIR}/udev.py
)
-LIST(APPEND USRP_MPM_FILES ${USRP_MPM_TOP_FILES})
+LIST(APPEND USRP_MPM_FILES ${USRP_MPM_PERIPHMGR_FILES})
SET(USRP_MPM_FILES ${USRP_MPM_FILES} PARENT_SCOPE)
diff --git a/mpm/python/usrp_mpm/periph_manager/__init__.py.in b/mpm/python/usrp_mpm/periph_manager/__init__.py.in
index 0956b849e..d8733ba17 100644
--- a/mpm/python/usrp_mpm/periph_manager/__init__.py.in
+++ b/mpm/python/usrp_mpm/periph_manager/__init__.py.in
@@ -24,6 +24,6 @@ from .. import dboard_manager
from .. import types
try:
- from ${MPM_DEVICE} import ${MPM_DEVICE} as periph_manager
+ from .${MPM_DEVICE} import ${MPM_DEVICE} as periph_manager
except ImportError:
raise Exception("Could not import ${MPM_DEVICE}")
diff --git a/mpm/python/usrp_mpm/periph_manager/base.py b/mpm/python/usrp_mpm/periph_manager/base.py
index f19265a05..c84205a76 100644
--- a/mpm/python/usrp_mpm/periph_manager/base.py
+++ b/mpm/python/usrp_mpm/periph_manager/base.py
@@ -18,16 +18,24 @@
Mboard implementation base class
"""
-import re
import os
-from . import lib
-from . import types
-from . import dboard_manager
+from ..types import EEPROM
+from .. import dboard_manager
+from .udev import get_eeprom
+from .udev import get_spidev_nodes
+from six import iteritems
-class periph_manager(object):
+class PeriphManagerBase(object):
+ """"
+ Base class for all motherboards. Common function and API calls should
+ be implemented here. Motherboard specific information can be stored in
+ separate motherboard classes derived from this class
+ """
# stores discovered device information in dicts
+ claimed = False
dboards = {}
+ mboard_info = {"type": "unknown"}
mboard_if_addrs = {}
mboard_overlays = {}
# this information has to be provided by
@@ -35,41 +43,88 @@ class periph_manager(object):
mboard_eeprom_addr = ""
dboard_eeprom_addrs = {}
dboard_spimaster_addrs = {}
+ updateable_components = []
def __init__(self):
# I know my EEPROM address, lets use it
- helper = lib.udev_helper.udev_helper()
- (self._eeprom_head, self._eeprom_rawdata) = types.eeprom().read_eeprom(helper.get_eeprom(self.mboard_eeprom_addr))
+ self.overlays = ""
+ (self._eeprom_head, self._eeprom_rawdata) = EEPROM().read_eeprom(
+ get_eeprom(self.mboard_eeprom_addr))
self._dboard_eeproms = {}
for dboard_slot, eeprom_addr in self.dboard_eeprom_addrs.iteritems():
spi_devices = []
# I know EEPROM adresses for my dboard slots
- eeprom_data = types.eeprom().read(helper.get_eeprom(eeprom_addr))
+ eeprom_data = EEPROM().read_eeprom(get_eeprom(eeprom_addr))
# I know spidev masters on the dboard slots
- hw_pid = eeprom_data.get("hw_pid", 0)
- if hw_pid in dboards.hw_pids:
- spi_devices = helper.get_spidev_nodes(self.dboard_spimaster_addrs.get(dboard_slot))
- dboard = dboards.hw_pids.get(hw_pid, dboards.unknown)
+ hw_pid = eeprom_data[0].get("hw_pid", 0)
+ if hw_pid in dboard_manager.HW_PIDS:
+ spi_devices = get_spidev_nodes(self.dboard_spimaster_addrs.get(dboard_slot))
+ dboard = dboard_manager.HW_PIDS.get(hw_pid, dboard_manager.unknown)
self.dboards.update({dboard_slot: dboard(spi_devices, eeprom_data)})
+ def safe_list_updateable_components(self):
+ """
+ return list of updateable components
+ This method does not require a claim_token in the RPC
+ """
+ return self.updateable_components
+
def get_overlays(self):
+ """
+ get and store the list of available dt overlays
+ """
self.mboard_overlays = []
- for f in os.listdir("/lib/firmware/"):
- if f.endswith(".dtbo"):
- self.mboard_overlays.append(f.strip(".dtbo"))
+ for fw_files in os.listdir("/lib/firmware/"):
+ if fw_files.endswith(".dtbo"):
+ self.mboard_overlays.append(fw_files.strip(".dtbo"))
def check_overlay(self):
- for f in os.listdir("/sys/kernel/device-tree/overlays/"):
- pass #do stuff
+ """
+ check which dt overlay is loaded currently
+ """
+ for overlay_file in os.listdir("/sys/kernel/device-tree/overlays/"):
+ self.overlays = overlay_file
+
+ def _get_device_info(self):
+ """
+ return the mboard_info dict and add a claimed field
+ """
+ result = {"claimed": str(self.claimed)}
+ result.update(self.mboard_info)
+ return result
- def get_serial(self):
- return self._serial
+ def get_dboards(self):
+ """
+ get a dict with slot: hw_pid for each dboard
+ """
+ result = {}
+ for slot, dboard in iteritems(self.dboards):
+ result.update({slot:dboard.hw_pid})
+ return result
def load_fpga_image(self, target=None):
+ """
+ load a new fpga image
+ """
pass
def init_device(self, *args, **kwargs):
+ """
+ Do the real init on the mboard and all dboards
+ """
# Load FPGA
# Init dboards
pass
+ def _probe_interface(self, sender_addr):
+ """
+ Overload this method in actual device implementation
+ """
+ return True
+
+ def get_interfaces(self):
+ """
+ Overload this method in actual device implementation
+ """
+ return []
+
diff --git a/mpm/python/usrp_mpm/periph_manager/n310.py b/mpm/python/usrp_mpm/periph_manager/n310.py
index d1c31540b..1b01ac066 100644
--- a/mpm/python/usrp_mpm/periph_manager/n310.py
+++ b/mpm/python/usrp_mpm/periph_manager/n310.py
@@ -17,24 +17,55 @@
"""
N310 implementation module
"""
-from base import periph_manager
+from __future__ import print_function
import struct
+from .base import PeriphManagerBase
+from .net import get_iface_addrs
+from .net import byte_to_mac
+from .net import get_mac_addr
+from logging import getLogger
+LOG = getLogger(__name__)
-class n310(periph_manager):
+
+class n310(PeriphManagerBase):
+ """
+ Holds N310 specific attributes and methods
+ """
hw_pids = "1"
+ mboard_type = "n310"
mboard_eeprom_addr = "e0007000.spi:ec@0:i2c-tunnel"
dboard_eeprom_addrs = {"A": "something", "B": "else"}
dboard_spimaster_addrs = {"A": "something", "B": "else"}
+ interfaces = {}
def __init__(self, *args, **kwargs):
# First initialize parent class - will populate self._eeprom_head and self._eeprom_rawdata
super(n310, self).__init__(*args, **kwargs)
- data = self.read_eeprom_v1(self._eeprom_rawdata)
+ data = self._read_eeprom_v1(self._eeprom_rawdata)
+ # mac 0: mgmt port, mac1: sfp0, mac2: sfp1
+ self.interfaces["mgmt"] = {
+ "mac_addr": byte_to_mac(data[0]),
+ "addrs": get_iface_addrs(byte_to_mac(data[0]))
+ }
+ self.interfaces["sfp0"] = {
+ "mac_addr": byte_to_mac(data[1]),
+ "addrs": get_iface_addrs(byte_to_mac(data[1]))
+ }
+ self.interfaces["sfp1"] = {
+ "mac_addr": byte_to_mac(data[2]),
+ "addrs": get_iface_addrs(byte_to_mac(data[2]))
+ }
+ self.mboard_info["serial"] = data[3] # some format
+
print(data)
# if header.get("dataversion", 0) == 1:
- def read_eeprom_v1(self, data):
+
+ def _read_eeprom_v1(self, data):
+ """
+ read eeprom with data version 1
+ """
# data_version contains
# 6 bytes mac_addr0
# 2 bytes pad
@@ -44,3 +75,29 @@ class n310(periph_manager):
# 2 bytes pad
# 8 bytes serial
return struct.unpack_from("6s 2x 6s 2x 6s 2x 8s", data)
+
+ def get_interfaces(self):
+ """
+ returns available transport interfaces
+ """
+ return [iface for iface in self.interfaces.keys()
+ if iface.startswith("sfp")]
+
+ def get_interface_addrs(self, interface):
+ """
+ returns discovered ipv4 addresses for a given interface
+ """
+ return self.interfaces.get(interface, {}).get("addrs", [])
+
+ def _probe_interface(self, sender_addr):
+ """
+ Get the MAC address of the sender and store it in the FPGA ARP table
+ """
+ mac_addr = get_mac_addr(sender_addr)
+ if mac_addr is not None:
+ # Do something with mac_address
+ return True
+ return False
+
+
+
diff --git a/mpm/python/usrp_mpm/periph_manager/net.py b/mpm/python/usrp_mpm/periph_manager/net.py
new file mode 100644
index 000000000..2df771549
--- /dev/null
+++ b/mpm/python/usrp_mpm/periph_manager/net.py
@@ -0,0 +1,63 @@
+
+# Copyright 2017 Ettus Research (National Instruments)
+#
+# 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.
+#
+# 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.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+"""
+N310 implementation module
+"""
+import itertools
+import socket
+from pyroute2 import IPRoute
+from logging import getLogger
+
+LOG = getLogger(__name__)
+
+
+def get_iface_addrs(mac_addr):
+ """
+ return ipv4 addresses for a given macaddress
+ input format: "aa:bb:cc:dd:ee:ff"
+ """
+ ip2 = IPRoute()
+ # returns index
+ [link] = ip2.link_lookup(address=mac_addr)
+ # Only get v4 addresses
+ addresses = [addr.get_attrs('IFA_ADDRESS')
+ for addr in ip2.get_addr(family=socket.AF_INET)
+ if addr.get('index', None) == link]
+ # flatten possibly nested list
+ addresses = list(itertools.chain.from_iterable(addresses))
+ return addresses
+
+
+def byte_to_mac(byte_str):
+ """
+ converts a bytestring into nice hex representation
+ """
+ return ':'.join(["%02x" % ord(x) for x in byte_str])
+
+
+def get_mac_addr(remote_addr):
+ """
+ return MAC address of a remote host already discovered
+ or None if no host entry was found
+ """
+ ip2 = IPRoute()
+ addrs = ip2.get_neighbours(dst=remote_addr)
+ if len(addrs) > 1:
+ LOG.warning("More than one device with the same IP address found. Picking entry at random")
+ if not addrs:
+ return None
+ return addrs[0].get_attr('NDA_LLADDR')
diff --git a/mpm/python/usrp_mpm/periph_manager/test.py b/mpm/python/usrp_mpm/periph_manager/test.py
index c9cbc1f3f..4daa876cb 100644
--- a/mpm/python/usrp_mpm/periph_manager/test.py
+++ b/mpm/python/usrp_mpm/periph_manager/test.py
@@ -17,15 +17,19 @@
"""
test periph_manager implementation module
"""
-from base import periph_manager
+from __future__ import print_function
+from .base import PeriphManagerBase
from . import dboard_manager
import random
import string
-import struct
-class test(periph_manager):
+class test(PeriphManagerBase):
+ """
+ Test periph manager class which fakes out all API calls
+ """
hw_pids = "42"
+ mboard_info = {"type": "mpm_test"}
mboard_eeprom_addr = None
dboard_eeprom_addrs = {"A": "something", "B": "else"}
dboard_spimaster_addrs = {"A": "something", "B": "else"}
@@ -34,27 +38,36 @@ class test(periph_manager):
# First initialize parent class - will populate self._eeprom_head and self._eeprom_rawdata
# super(n310, self).__init__(*args, **kwargs)
# if header.get("dataversion", 0) == 1:
- self._eeprom = self.read_eeprom_fake()
- self._serial = "AABBCCDDEEFF"
+ self._eeprom = self._read_eeprom_fake()
+ print(self.mboard_info)
+ self.mboard_info["serial"] = "AABBCCDDEEFF"
+ self.mboard_info["name"] = self._eeprom["name"]
# I'm the test periph_manager, I know I have test dboards attached
self.dboards = {
- "A": dboard_manager.test(self.read_db_eeprom_random()),
- "B": dboard_manager.test(self.read_db_eeprom_random())
+ "A": dboard_manager.test(self._read_db_eeprom_random()),
+ "B": dboard_manager.test(self._read_db_eeprom_random())
}
- def read_eeprom_fake(self):
+ def _read_eeprom_fake(self):
+ """
+ fake eeprom readout function, returns dict with data
+ """
fake_eeprom = {
"magic": 42,
"crc": 4242,
"data_version": 42,
"hw_pid": 42,
- "hw_rev": 5
+ "hw_rev": 5,
+ "name": "foo"
}
return fake_eeprom
- def read_db_eeprom_random(self):
+ def _read_db_eeprom_random(self):
+ """
+ fake db eeprom readout function, returns dict with fake dboard data
+ """
fake_eeprom = {
"serial": ''.join(
random.choice("ABCDEF" + string.digits)
diff --git a/mpm/python/usrp_mpm/periph_manager/udev.py b/mpm/python/usrp_mpm/periph_manager/udev.py
new file mode 100644
index 000000000..014e18ede
--- /dev/null
+++ b/mpm/python/usrp_mpm/periph_manager/udev.py
@@ -0,0 +1,42 @@
+#
+# Copyright 2017 Ettus Research (National Instruments)
+#
+# 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.
+#
+# 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.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import pyudev
+
+
+def get_eeprom(address):
+ """
+ Return EEPROM device path for a given I2C address
+ """
+ context = pyudev.Context()
+ parent = pyudev.Device.from_name(context, "platform", address)
+ paths = [device.dev_node if device.dev_node is not None else device.sys_path
+ for device in context.list_devices(parent=parent, subsystem="nvmem")]
+ if len(paths) != 1:
+ raise Exception("{0} paths to EEPROM found!".format(len(paths)))
+ return paths[0]
+
+
+def get_spidev_nodes(spi_master):
+ """
+ Return found spidev device paths for a given SPI master
+ """
+ context = pyudev.Context()
+ parent = pyudev.Device.from_name(context, "platform", spi_master)
+ paths = [device.dev_node if device.dev_node is not None else device.sys_path
+ for device in context.list_devices(parent=parent, subsystem="spidev")]
+ return paths
diff --git a/mpm/python/usrp_mpm/periphs.py b/mpm/python/usrp_mpm/periphs.py
deleted file mode 100644
index 8c00d8f50..000000000
--- a/mpm/python/usrp_mpm/periphs.py
+++ /dev/null
@@ -1,38 +0,0 @@
-#
-# Copyright 2017 Ettus Research (National Instruments)
-#
-# 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.
-#
-# 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.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-"""
- Module
-"""
-
-import libpyusrp_periphs as lib
-import logging
-import periph_manager
-import dboard_manager
-
-
-log = logging.Logger("usrp_mpm.periphs")
-
-def init_periph_manager(mb_type=None, db_types={}, fpga=None):
- pass
- # Moved to periph_manager/base.py __init__
-
-
- # Next steps implemented in periph_manager/derived class
- #
- # 1. Load FPGA image
- # 2. Use motherboard and daughterboard types to load the FPGA image
- # 3. Create periph_manager object wth given mb_type + db_types information
diff --git a/mpm/python/usrp_mpm/rpc_server.py b/mpm/python/usrp_mpm/rpc_server.py
index ddb588aa6..f712c5c87 100644
--- a/mpm/python/usrp_mpm/rpc_server.py
+++ b/mpm/python/usrp_mpm/rpc_server.py
@@ -18,84 +18,204 @@
Implemented RPC Servers
"""
from __future__ import print_function
+from logging import getLogger
from gevent.server import StreamServer
-from types import graceful_exit, MPM_RPC_PORT
+from gevent.pool import Pool
+from gevent import signal
+from gevent import spawn_later
+from gevent import Greenlet
+from gevent import monkey
+monkey.patch_all()
from mprpc import RPCServer
+from random import choice
from six import iteritems
-import time
-
+from string import ascii_letters, digits
+from threading import Timer
from multiprocessing import Process
+
+LOG = getLogger(__name__)
+
+
class MPMServer(RPCServer):
- _db_methods = {}
- def __init__(self, state, mgr):
+ """
+ Main MPM RPC class which holds the periph_manager object and translates
+ RPC calls to appropiate calls in the periph_manager and dboard_managers.
+
+ Claiming and unclaiming is implemented in python only
+ """
+ _db_methods = []
+ _mb_methods = []
+
+ def __init__(self, state, mgr, *args, **kwargs):
self._state = state
- # Instead do self.mboard = periphs.init_periph_manager(args...)
+ self._timer = Greenlet()
self.periph_manager = mgr
- for db_slot, db in iteritems(mgr.dboards):
- methods = (m for m in dir(db) if not m.startswith('_') and callable(getattr(db, m)))
- for method in methods:
- command_name = 'db_'+ db_slot + '_' + method
- self._add_command(getattr(db,method), command_name)
- db_methods = self._db_methods.get(db_slot, [])
- db_methods.append(command_name)
- self._db_methods.update({db_slot: db_methods})
-
- # When we do init we can just add dboard/periph_manager methods with setattr(self, method)
- # Maybe using partial
- # To remove methods again we also have to remove them from self._methods dict (they're cached)
- super(MPMServer, self).__init__()
+ # add public mboard methods without namespace
+ self._update_component_commands(mgr, '', '_mb_methods')
+ # add public dboard methods in `db_<slot>_` namespace
+ for db_slot, dboard in iteritems(mgr.dboards):
+ self._update_component_commands(dboard, 'db_' + db_slot + '_', '_db_methods')
+ super(MPMServer, self).__init__(*args, **kwargs)
+
+ def _update_component_commands(self, component, namespace, storage):
+ """
+ Detect available methods for an object and add them to the RPC server
+ """
+ for method in (m for m in dir(component)
+ if not m.startswith('_') and callable(getattr(component, m))):
+ if method.startswith('safe_'):
+ command_name = namespace + method.lstrip('safe_')
+ self._add_safe_command(getattr(component, method), command_name)
+ else:
+ command_name = namespace + method
+ self._add_command(getattr(component, method), command_name)
+ getattr(self, storage).append(command_name)
+
def _add_command(self, function, command):
- setattr(self, command, function)
+ """
+ Adds a method with the name command to the RPC server
+ This command will require an acquired claim on the device
+ """
+ LOG.debug("adding command %s pointing to %s", command, function)
+ def new_function(token, *args):
+ if token[:256] != self._state.claim_token.value:
+ return False
+ return function(*args)
+ new_function.__doc__ = function.__doc__
+ setattr(self, command, new_function)
+
+ def _add_safe_command(self, function, command):
+ """
+ Add a safe method which does not require a claim on the
+ device
+ """
+ LOG.debug("adding safe command %s pointing to %s", command, function)
+ setattr(self, command, function)
def list_methods(self):
"""
- Returns all public methods of this RPC server
+ Returns a tuple of public methods and
+ corresponding docs of this RPC server
"""
- methods = filter(lambda entry: not entry.startswith('_'), dir(self)) # Return public methods
- methods_with_docs = map(lambda m: (m, getattr(self, m).__doc__), methods)
- return methods_with_docs
+ return [(met, getattr(self, met).__doc__)
+ for met in dir(self)
+ if not met.startswith('_') and callable(getattr(self, met))]
def ping(self, data=None):
"""
Take in data as argument and send it back
+ This is a safe method which can be called without a claim on the device
"""
+ LOG.debug("I was pinged from: %s:%s", self.client_host, self.client_port)
return data
- def claim(self, token):
+ def claim(self, sender_id):
+ """
+ claim `token` - tries to claim MPM device and provides a human readable sender_id
+ This is a safe method which can be called without a claim on the device
+ """
+ self._state.lock.acquire()
+ if self._state.claim_status.value:
+ return ""
+ LOG.debug("claiming from: %s", self.client_host)
+ self.periph_manager.claimed = True
+ self._state.claim_token.value = ''.join(choice(ascii_letters + digits) for _ in range(256))
+ self._state.claim_status.value = True
+ self._state.lock.release()
+ self.sender_id = sender_id
+ self._reset_timer()
+ LOG.debug("giving token: %s to host: %s", self._state.claim_token.value, self.client_host)
+ return self._state.claim_token.value
+
+ def reclaim(self, token):
"""
- claim `token` - claims the MPM device with given token
+ reclaim a MPM device with a token. This operation will fail
+ if the device is claimed and the token doesn't match.
+ Or if the device is not claimed at all.
"""
+ self._state.lock.acquire()
if self._state.claim_status.value:
- if self._state.claim_token.value == token:
+ if self._state.claim_token.value == token[:256]:
+ self._state.lock.release()
+ LOG.debug("reclaimed from: %s", self.client_host)
+ self._reset_timer()
return True
+ self._state.lock.release()
+ LOG.debug("reclaim failed from: %s", self.client_host)
return False
- self._state.claim_status.value = True
- self._state.claim_token.value = token
- return True
+ LOG.debug("trying to reclaim unclaimed device from: %s", self.client_host)
+ return False
+
+
+
+
+ def _unclaim(self):
+ """
+ unconditional unclaim - for internal use
+ """
+ LOG.debug("releasing claim")
+ self._state.claim_status.value = False
+ self._state.claim_token.value = ""
+ self.sender_id = None
+ self.periph_manager.claimed = False
+ self._timer.kill()
+
+ def _reset_timer(self):
+ """
+ reset unclaim timer
+ """
+ self._timer.kill()
+ self._timer = spawn_later(2.0, self._unclaim)
def unclaim(self, token):
"""
unclaim `token` - unclaims the MPM device if it is claimed with this token
"""
if self._state.claim_status.value and self._state.claim_token.value == token:
- self._state.claim_status.value = False
- self._state.claim_token.value = ""
+ self._unclaim()
return True
return False
+ def get_device_info(self):
+ """
+ get device information
+ This is as safe method which can be called without a claim on the device
+ """
+ info = self.periph_manager._get_device_info()
+ if self.host in ["127.0.0.1", "::1"]:
+ info["connection"] = "local"
+ else:
+ info["connection"] = "remote"
+ return info
+
+ def probe_interface(self, token):
+ """
+ Forwards the call to periph_manager._probe_interface with the client ip addresss
+ as argument. Should be used to probe the data interfaces on the device
+ """
+ if token[:256] != self._state.claim_token.value:
+ return False
+ return self.periph_manager._probe_interface(self.host)
+
+
+
def _rpc_server_process(shared_state, port, mgr):
"""
Start the RPC server
"""
- server = StreamServer(('0.0.0.0', port), handle=MPMServer(shared_state, mgr))
- try:
- server.serve_forever()
- except:
- server.close()
+ connections = Pool(1000)
+ server = StreamServer(
+ ('0.0.0.0', port),
+ handle=MPMServer(shared_state, mgr),
+ spawn=connections)
+ # catch signals and stop the stream server
+ signal(signal.SIGTERM, lambda *args: server.stop())
+ signal(signal.SIGINT, lambda *args: server.stop())
+ server.serve_forever()
def spawn_rpc_process(state, udp_port, mgr):
@@ -103,7 +223,7 @@ def spawn_rpc_process(state, udp_port, mgr):
Returns a process that contains the RPC server
"""
- p_args = [udp_port, state, mgr]
- p = Process(target=_rpc_server_process, args=p_args)
- p.start()
- return p
+ proc_args = [udp_port, state, mgr]
+ proc = Process(target=_rpc_server_process, args=proc_args)
+ proc.start()
+ return proc
diff --git a/mpm/python/usrp_mpm/types.py b/mpm/python/usrp_mpm/types.py
index 31252e0b8..cc8fe8b8d 100644
--- a/mpm/python/usrp_mpm/types.py
+++ b/mpm/python/usrp_mpm/types.py
@@ -20,7 +20,7 @@ MPM types
import ctypes
from multiprocessing import Value
from multiprocessing import Array
-from multiprocessing import Lock
+from multiprocessing import RLock
import struct
MPM_RPC_PORT = 49601
@@ -29,18 +29,26 @@ MPM_DISCOVERY_PORT = 49600
MPM_DISCOVERY_MESSAGE = "MPM-DISC"
-class graceful_exit(Exception):
- pass
+class SharedState(object):
+ """
+ Holds information which should be shared between processes
+ Usage should be kept to a minimum
+ """
-
-class shared_state:
def __init__(self):
- self.lock = Lock()
- self.claim_status = Value(ctypes.c_bool, False, lock=self.lock) # lock
- self.claim_token = Array(ctypes.c_char, 32, lock=self.lock) # String with max length of 32
+ self.lock = RLock()
+ self.claim_status = Value(
+ ctypes.c_bool,
+ False, lock=self.lock) # lock
+ self.claim_token = Array(
+ ctypes.c_char, 256,
+ lock=self.lock) # String with max length of 256
-class eeprom(object):
+class EEPROM(object):
+ """
+ Reads out common properties and rawdata out of a nvmem path
+ """
# eeprom_header contains:
# 4 bytes magic
# 4 bytes CRC
@@ -53,9 +61,13 @@ class eeprom(object):
eeprom_header = struct.Struct("I I H H H 2x")
def read_eeprom(self, nvmem_path):
- with open(nvmem_path, "rb") as f:
- header = f.read(16)
- data = f.read(240)
+ """
+ Read the EEPROM located at nvmem_path and return a tuple (header, body)
+ Header is already parsed in the common header fields
+ """
+ with open(nvmem_path, "rb") as nvmem_file:
+ header = nvmem_file.read(16)
+ data = nvmem_file.read(240)
header = self.eeprom_header.unpack(header)
header = {
"magic": header[0],
diff --git a/mpm/tools/mpm_debug.py b/mpm/tools/mpm_debug.py
index 1a97a103c..dff1c78bd 100755
--- a/mpm/tools/mpm_debug.py
+++ b/mpm/tools/mpm_debug.py
@@ -3,6 +3,7 @@ import socket
from mprpc import RPCClient
import usrp_mpm as mpm
import argparse
+import random
def parse_args():
@@ -16,6 +17,7 @@ def parse_args():
rpc_parser.add_argument("arguments", nargs="*")
disc_parser = sub_parsers.add_parser("disc", help="Issue discovery")
+ echo_parser = sub_parsers.add_parser("echo", help="Issue UDP echo")
return parser.parse_args()
@@ -24,6 +26,7 @@ def rpc(address, port, command, *args):
port = mpm.types.MPM_RPC_PORT
client = RPCClient(address, port)
if args:
+ args = [eval(arg.lstrip("=")) if arg.startswith("=") else arg for arg in args]
result = client.call(command, *args)
else:
result = client.call(command)
@@ -40,13 +43,31 @@ def discovery(address, port):
sock.settimeout(1.0) # wait max 1 second
while True:
try:
- data, sender = sock.recvfrom(4096)
- print("Received respons from: {}".format(sender[0]))
+ data, sender = sock.recvfrom(8000)
+ print("Received response from: {}".format(sender[0]))
print("Dicovery data: {}".format(data))
except:
break
+def echo(address, port):
+ if not port:
+ port = mpm.types.MPM_DISCOVERY_PORT
+ sock = socket.socket(
+ socket.AF_INET,
+ socket.SOCK_DGRAM)
+ message = "MPM-ECHO" + bytearray(random.getrandbits(8) for _ in xrange(8000-8))
+ sock.sendto(message, (address, port))
+ sock.settimeout(0.05) # wait max 50 ms
+ while True:
+ try:
+ data, sender = sock.recvfrom(9000)
+ print("Received response from: {}".format(sender[0]))
+ print("Echo data size: {}".format(len(data)))
+ except:
+ break
+
+
def main():
args = parse_args()
if args.command == "rpc":
@@ -57,6 +78,11 @@ def main():
print(result)
elif args.command == "disc":
discovery(args.address, args.port)
+ result = True
+ elif args.command == "echo":
+ echo(args.address, args.port)
+ result = True
+ return result
if __name__ == "__main__":