aboutsummaryrefslogtreecommitdiffstats
path: root/mpm/python/usrp_mpm/periph_manager/base.py
diff options
context:
space:
mode:
Diffstat (limited to 'mpm/python/usrp_mpm/periph_manager/base.py')
-rw-r--r--mpm/python/usrp_mpm/periph_manager/base.py278
1 files changed, 212 insertions, 66 deletions
diff --git a/mpm/python/usrp_mpm/periph_manager/base.py b/mpm/python/usrp_mpm/periph_manager/base.py
index 22630dbf6..a40f86d06 100644
--- a/mpm/python/usrp_mpm/periph_manager/base.py
+++ b/mpm/python/usrp_mpm/periph_manager/base.py
@@ -10,6 +10,7 @@ Mboard implementation base class
from __future__ import print_function
import os
+from enum import Enum
from hashlib import md5
from time import sleep
from concurrent import futures
@@ -19,6 +20,7 @@ from six import iteritems, itervalues
from usrp_mpm.mpmlog import get_logger
from usrp_mpm.sys_utils.filesystem_status import get_fs_version
from usrp_mpm.sys_utils.filesystem_status import get_mender_artifact
+from usrp_mpm.sys_utils.udev import get_eeprom_paths_by_symbol
from usrp_mpm.sys_utils.udev import get_eeprom_paths
from usrp_mpm.sys_utils.udev import get_spidev_nodes
from usrp_mpm.sys_utils import dtoverlay
@@ -55,6 +57,12 @@ class PeriphManagerBase(object):
be implemented here. Motherboard specific information can be stored in
separate motherboard classes derived from this class
"""
+ class EepromSearch(Enum):
+ """
+ List supported ways of searching EEPROM files.
+ """
+ LEGACY = 1 # Using EEPROM address
+ SYMBOL = 2 # Using symbol names
#########################################################################
# Overridables
#
@@ -66,6 +74,9 @@ class PeriphManagerBase(object):
pids = {}
# A textual description of this device type
description = "MPM Device"
+ # EEPROM layout used by this class. Defaults to legacy which uses eeprom.py
+ # to read EEPROM data
+ eeprom_search = EepromSearch.LEGACY
# Address of the motherboard EEPROM. This could be something like
# "e0005000.i2c". This value will be passed to get_eeprom_paths() tos
# determine a full path to an EEPROM device.
@@ -86,16 +97,6 @@ class PeriphManagerBase(object):
# read. It's usually safe to not override this, as EEPROMs typically aren't
# that big.
mboard_eeprom_max_len = None
- # lambda expression for motherboard EEPROM readers. path is the only dynamic
- # parameter that is passed to the reader. Subclasses change the lambda
- # expression to use EEPROM readers with different signatures.
- mboard_eeprom_reader = lambda path: eeprom.read_eeprom(
- path,
- PeriphManagerBase.mboard_eeprom_offset,
- eeprom.MboardEEPROM.eeprom_header_format,
- eeprom.MboardEEPROM.eeprom_header_keys,
- PeriphManagerBase.mboard_eeprom_magic,
- PeriphManagerBase.mboard_eeprom_max_len)
# This is the *default* mboard info. The keys from this dict will be copied
# into the current device info before it actually gets initialized. This
# means that keys from this dict could be overwritten during the
@@ -144,16 +145,6 @@ class PeriphManagerBase(object):
# read. It's usually safe to not override this, as EEPROMs typically aren't
# that big.
dboard_eeprom_max_len = None
- # lambda expression for daughterboard EEPROM readers. path is the only
- # dynamic parameter that is passed to the reader. Subclasses change the
- # lambda expression to use EEPROM readers with different signatures.
- dboard_eeprom_reader = lambda path: eeprom.read_eeprom(
- path,
- PeriphManagerBase.dboard_eeprom_offset,
- eeprom.DboardEEPROM.eeprom_header_format,
- eeprom.DboardEEPROM.eeprom_header_keys,
- PeriphManagerBase.dboard_eeprom_magic,
- PeriphManagerBase.dboard_eeprom_max_len)
# If the dboard requires spidev access, the following attribute is a list
# of SPI master addrs (typically something like 'e0006000.spi'). You
# usually want the length of this list to be as long as the number of
@@ -171,6 +162,16 @@ class PeriphManagerBase(object):
# a claim(). Override and set to True in the derived class if desired.
clear_rpc_registry_on_unclaim = False
+ # Symbols are use to find board EEPROM by a symbolic name. Can only be used
+ # on systems that support symbol name under /proc/device-tree/__symbols__.
+ # symbol name for motherboard EEPROM file
+ mboard_eeprom_symbol = "mb_eeprom"
+ # symbol glob for daugtherboard EEPROM files
+ dboard_eeprom_symbols = "db[0,1]_eeprom"
+ # symbol glob fox auxiliary boards
+ auxboard_eeprom_symbols = "*aux_eeprom"
+
+
# Disable checks for unused args in the overridables, because the default
# implementations don't need to use them.
# pylint: disable=unused-argument
@@ -244,18 +245,17 @@ class PeriphManagerBase(object):
self.log = get_logger('PeriphManager')
self.claimed = False
try:
- self._eeprom_head, self._eeprom_rawdata = \
- self._read_mboard_eeprom()
- self.mboard_info = self._get_mboard_info(self._eeprom_head)
+ self.mboard_info = self._get_mboard_info()
self.log.info("Device serial number: {}"
.format(self.mboard_info.get('serial', 'n/a')))
- self.dboard_infos = self._get_dboard_eeprom_info()
+ self.dboard_infos = self._get_dboard_info()
self.device_info = \
self.generate_device_info(
self._eeprom_head,
self.mboard_info,
self.dboard_infos
)
+ self._aux_board_infos = self._get_aux_board_info()
except BaseException as ex:
self.log.error("Failed to initialize device: %s", str(ex))
self._device_initialized = False
@@ -291,57 +291,120 @@ class PeriphManagerBase(object):
self._device_initialized = True
self._initialization_status = "No errors."
+ def _read_mboard_eeprom_data(self, path):
+ return eeprom.read_eeprom(
+ path,
+ self.mboard_eeprom_offset,
+ eeprom.MboardEEPROM.eeprom_header_format,
+ eeprom.MboardEEPROM.eeprom_header_keys,
+ self.mboard_eeprom_magic,
+ self.mboard_eeprom_max_len)
+
+ def _read_mboard_eeprom_legacy(self):
+ """
+ Read out mboard EEPROM.
+ Saves _eeprom_head, _eeprom_rawdata as class members where the the
+ former is a de-serialized dictionary representation of the data, and the
+ latter is a binary string with the raw data.
+
+ If no EEPROM both members are defined and empty.
+ """
+ if not self.mboard_eeprom_addr:
+ self.log.trace("No mboard EEPROM path defined. "
+ "Skipping mboard EEPROM readout.")
+ return
+
+ self.log.trace("Reading EEPROM from address `{}'..."
+ .format(self.mboard_eeprom_addr))
+ eeprom_paths = get_eeprom_paths(self.mboard_eeprom_addr)
+ if not eeprom_paths:
+ self.log.error("Could not identify EEPROM paths for %s!",
+ self.mboard_eeprom_addr)
+ return
+
+ self.log.trace("Found mboard EEPROM path: %s", eeprom_paths[0])
+ (self._eeprom_head, self._eeprom_rawdata) = \
+ self._read_mboard_eeprom_data(eeprom_paths[0])
+
+ def _read_mboard_eeprom_by_symbol(self):
+ """
+ Read out mboard EEPROM.
+ Saves _eeprom_head, _eeprom_rawdata as class members where the the
+ former is a de-serialized dictionary representation of the data, and the
+ latter is a binary string with the raw data.
+
+ If no EEPROM both members are defined and empty.
+ """
+ if not self.mboard_eeprom_symbol:
+ self.log.trace("No mboard EEPROM path defined. "
+ "Skipping mboard EEPROM readout.")
+ return
+
+ self.log.trace("Reading EEPROM from address `{}'..."
+ .format(self.mboard_eeprom_symbol))
+ eeprom_paths = get_eeprom_paths_by_symbol(self.mboard_eeprom_symbol)
+ if not eeprom_paths:
+ self.log.error("Could not identify EEPROM paths for %s!",
+ self.mboard_eeprom_addr)
+ return
+ # There should be exact one item in the dictionary returned by
+ # find_eeprom_paths. If so take the value of it as path for further
+ # processing
+ if not (len(eeprom_paths) == 1):
+ raise RuntimeError("System should contain exact one EEPROM file"
+ "for motherboard but found %d." % len(eeprom_paths))
+ eeprom_path = str(eeprom_paths.popitem()[1])
+
+ self.log.trace("Found mboard EEPROM path: %s", eeprom_path)
+ (self._eeprom_head, self._eeprom_rawdata) = \
+ self._read_mboard_eeprom_data(eeprom_path)
+
def _read_mboard_eeprom(self):
"""
Read out mboard EEPROM.
- Returns a tuple: (eeprom_dict, eeprom_rawdata), where the the former is
- a de-serialized dictionary representation of the data, and the latter
- is a binary string with the raw data.
-
- If no EEPROM is defined, returns empty values.
- """
- if self.mboard_eeprom_addr:
- self.log.trace("Reading EEPROM from address `{}'..."
- .format(self.mboard_eeprom_addr))
- eeprom_paths = get_eeprom_paths(self.mboard_eeprom_addr)
- if not eeprom_paths:
- self.log.error("Could not identify EEPROM paths for %s!",
- self.mboard_eeprom_addr)
- return {}, b''
- self.log.trace("Found mboard EEPROM path: %s", eeprom_paths[0])
- (eeprom_head, eeprom_rawdata) = \
- self.__class__.mboard_eeprom_reader(eeprom_paths[0])
- self.log.trace("Found EEPROM metadata: `{}'"
- .format(str(eeprom_head)))
- self.log.trace("Read {} bytes of EEPROM data."
- .format(len(eeprom_rawdata)))
- return eeprom_head, eeprom_rawdata
- # Nothing defined? Return defaults.
- self.log.trace("No mboard EEPROM path defined. "
- "Skipping mboard EEPROM readout.")
- return {}, b''
-
- def _get_mboard_info(self, eeprom_head):
+
+ This is a wrapper call to switch between the support EEPROM layouts.
+ """
+ if not self.eeprom_search in self.EepromSearch:
+ self.log.warning("%s is not a valid EEPROM layout type. "
+ "Skipping readout.")
+ return
+
+ self._eeprom_head, self._eeprom_rawdata = {}, b""
+ if self.eeprom_search == self.EepromSearch.LEGACY:
+ self._read_mboard_eeprom_legacy()
+ elif self.eeprom_search == self.EepromSearch.SYMBOL:
+ self._read_mboard_eeprom_by_symbol()
+
+ self.log.trace("Found EEPROM metadata: `{}'"
+ .format(str(self._eeprom_head)))
+ self.log.trace("Read {} bytes of EEPROM data."
+ .format(len(self._eeprom_rawdata)))
+
+ def _get_mboard_info(self):
"""
Creates the mboard info dictionary from the EEPROM data.
"""
+ if not hasattr(self, "_eeprom_head"):
+ # read eeprom if not done already
+ self._read_mboard_eeprom()
mboard_info = self.mboard_info
- if not eeprom_head:
+ if not self._eeprom_head:
self.log.debug("No EEPROM info: Can't generate mboard_info")
return mboard_info
for key in ('pid', 'serial', 'rev', 'eeprom_version'):
# In C++, we can only handle dicts if all the values are of the
# same type. So we must convert them all to strings here:
try:
- mboard_info[key] = str(eeprom_head.get(key, ''), 'ascii')
+ mboard_info[key] = str(self._eeprom_head.get(key, ''), 'ascii')
except TypeError:
- mboard_info[key] = str(eeprom_head.get(key, ''))
- if 'pid' in eeprom_head:
- if eeprom_head['pid'] not in self.pids.keys():
+ mboard_info[key] = str(self._eeprom_head.get(key, ''))
+ if 'pid' in self._eeprom_head:
+ if self._eeprom_head['pid'] not in self.pids.keys():
self.log.error(
"Found invalid PID in EEPROM: 0x{:04X}. " \
"Valid PIDs are: {}".format(
- eeprom_head['pid'],
+ self._eeprom_head['pid'],
", ".join(["0x{:04X}".format(x) for x in self.pids]),
)
)
@@ -350,7 +413,7 @@ class PeriphManagerBase(object):
# back to the the rev itself (because every rev is compatible with
# itself).
rev_compat = \
- eeprom_head.get('rev_compat', eeprom_head.get('rev'))
+ self._eeprom_head.get('rev_compat', self._eeprom_head.get('rev'))
try:
rev_compat = int(rev_compat)
except (ValueError, TypeError):
@@ -374,7 +437,16 @@ class PeriphManagerBase(object):
)
return mboard_info
- def _get_dboard_eeprom_info(self):
+ def _read_dboard_eeprom_data(self, path):
+ return eeprom.read_eeprom(
+ path,
+ self.dboard_eeprom_offset,
+ eeprom.DboardEEPROM.eeprom_header_format,
+ eeprom.DboardEEPROM.eeprom_header_keys,
+ self.dboard_eeprom_magic,
+ self.dboard_eeprom_max_len)
+
+ def _get_dboard_info_legacy(self):
"""
Read back EEPROM info from the daughterboards
"""
@@ -399,9 +471,8 @@ class PeriphManagerBase(object):
dboard_info = []
for dboard_idx, dboard_eeprom_path in enumerate(dboard_eeprom_paths):
self.log.debug("Reading EEPROM info for dboard %d...", dboard_idx)
- dboard_eeprom_md, dboard_eeprom_rawdata = self._read_dboard_eeprom(
- dboard_eeprom_path
- )
+ dboard_eeprom_md, dboard_eeprom_rawdata = \
+ self._read_dboard_eeprom_data(dboard_eeprom_path)
self.log.trace("Found dboard EEPROM metadata: `{}'"
.format(str(dboard_eeprom_md)))
self.log.trace("Read %d bytes of dboard EEPROM data.",
@@ -419,8 +490,83 @@ class PeriphManagerBase(object):
})
return dboard_info
- def _read_dboard_eeprom(self, dboard_eeprom_path):
- return self.__class__.dboard_eeprom_reader(dboard_eeprom_path)
+ def _get_board_info_by_symbol(self, symbols):
+ """
+ Collect board info for given symbols.
+ symbols: a (glob) expression identifying EEPROMs to search for
+ returns: dictionary of EEPROM content found with symbol name as key
+ and an dictonary wih metadeta, rawdata and pid as value
+ """
+ result = {}
+ self.log.trace("Identifying EEPROM paths from %s...", symbols)
+ eeprom_paths = get_eeprom_paths_by_symbol(symbols)
+ self.log.trace("Found EEPROM paths: %s", eeprom_paths)
+ for name, path in eeprom_paths.items():
+ self.log.debug("Reading EEPROM info for %s...", name)
+ if not path:
+ self.log.debug("Not present. Skipping board")
+ continue
+ try:
+ eeprom_md, eeprom_rawdata = self._read_dboard_eeprom_data(path)
+ self.log.trace("Found EEPROM metadata: `{}'"
+ .format(str(eeprom_md)))
+ self.log.trace("Read %d bytes of dboard EEPROM data.",
+ len(eeprom_rawdata))
+ pid = eeprom_md.get('pid')
+ if pid is None:
+ self.log.warning("No PID found in EEPROM!")
+ else:
+ self.log.debug("Found PID in EEPROM: 0x{:04X}".format(pid))
+ result[name] = {'eeprom_md': eeprom_md,
+ 'eeprom_rawdata': eeprom_rawdata,
+ 'pid': pid}
+ except RuntimeError as e:
+ self.log.warning("Could not read EEPROM for %s (%s)", name, e)
+
+ return result
+
+ def _get_dboard_info_by_symbol(self):
+ """
+ Read back EEPROM info from the daughterboards
+ """
+ dboard_info = self._get_board_info_by_symbol(self.dboard_eeprom_symbols)
+ if len(dboard_info) > self.max_num_dboards:
+ self.log.warning("Found more EEPROM paths than daughterboards. "
+ "Ignoring some of them.")
+ # dboard_infos keys are sorted so it is safe to remove all items
+ # but the first few. Assumption is that the board names are given
+ # sorted such as db0, db1, db2, …, dbn.
+ dboard_info = {key: val for key, val in dboard_info.items()
+ if key in list(dboard_info.keys())[:self.max_num_dboards]}
+
+ # convert dboard dict back to list for backward compatibility
+ return [value for key, value in dboard_info.items() if value]
+
+ def _get_dboard_info(self):
+ """
+ Read back EEPROM info from the daughterboards
+ """
+ if not self.eeprom_search in self.EepromSearch:
+ self.log.warning("%s is not a valid EEPROM search type. "
+ "Skipping readout.")
+ return []
+
+ if self.eeprom_search == self.EepromSearch.LEGACY:
+ return self._get_dboard_info_legacy()
+ if self.eeprom_search == self.EepromSearch.SYMBOL:
+ return self._get_dboard_info_by_symbol()
+
+ def _get_aux_board_info(self):
+ """
+ Read back EEPROM info from all auxiliary boards
+ """
+ if self.eeprom_search == self.EepromSearch.LEGACY:
+ #legacy has no support for aux board EEPROM read
+ return {}
+ self.log.debug("Read aux boards EEPROMs")
+ result = self._get_board_info_by_symbol(self.auxboard_eeprom_symbols)
+ self.log.trace("Found aux board info for: %s.", ", ".join(result.keys()))
+ return result
def _update_default_args(self, default_args):
"""
@@ -461,7 +607,7 @@ class PeriphManagerBase(object):
Initialize all the daughterboards
dboard_infos -- List of dictionaries as returned from
- _get_dboard_eeprom_info()
+ _get_dboard_info()
override_dboard_pids -- List of dboard PIDs to force
default_args -- Default args
"""