aboutsummaryrefslogtreecommitdiffstats
path: root/mpm/python/usrp_mpm/periph_manager
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 /mpm/python/usrp_mpm/periph_manager
parentba4fad345d7489b40a7dab83842ac21d13c4f460 (diff)
downloaduhd-6a12add1560545438e1bebc05efbafd05aace4f9.tar.gz
uhd-6a12add1560545438e1bebc05efbafd05aace4f9.tar.bz2
uhd-6a12add1560545438e1bebc05efbafd05aace4f9.zip
mpm: mpm reorganization
Diffstat (limited to 'mpm/python/usrp_mpm/periph_manager')
-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
7 files changed, 268 insertions, 35 deletions
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