diff options
author | Andrej Rode <andrej.rode@ettus.com> | 2017-03-27 18:03:52 -0700 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2017-12-22 15:03:45 -0800 |
commit | 6a12add1560545438e1bebc05efbafd05aace4f9 (patch) | |
tree | c94dbbbd4da0c7ef41fc8849f174875a13f0b511 /mpm/python/usrp_mpm/periph_manager | |
parent | ba4fad345d7489b40a7dab83842ac21d13c4f460 (diff) | |
download | uhd-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.txt | 5 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/periph_manager/__init__.py.in | 2 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/periph_manager/base.py | 93 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/periph_manager/n310.py | 65 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/periph_manager/net.py | 63 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/periph_manager/test.py | 33 | ||||
-rw-r--r-- | mpm/python/usrp_mpm/periph_manager/udev.py | 42 |
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 |