diff options
Diffstat (limited to 'mpm/python')
| -rw-r--r-- | mpm/python/usrp_mpm/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | mpm/python/usrp_mpm/tlv_eeprom.py | 143 | 
2 files changed, 148 insertions, 1 deletions
diff --git a/mpm/python/usrp_mpm/CMakeLists.txt b/mpm/python/usrp_mpm/CMakeLists.txt index f1637ab03..c5b55283f 100644 --- a/mpm/python/usrp_mpm/CMakeLists.txt +++ b/mpm/python/usrp_mpm/CMakeLists.txt @@ -12,18 +12,22 @@ set(USRP_MPM_TOP_FILES      ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py.in      ${CMAKE_CURRENT_SOURCE_DIR}/aurora_control.py      ${CMAKE_CURRENT_SOURCE_DIR}/bfrfs.py +    ${CMAKE_CURRENT_SOURCE_DIR}/bist.py      ${CMAKE_CURRENT_SOURCE_DIR}/components.py      ${CMAKE_CURRENT_SOURCE_DIR}/discovery.py      ${CMAKE_CURRENT_SOURCE_DIR}/eeprom.py      ${CMAKE_CURRENT_SOURCE_DIR}/e31x_legacy_eeprom.py      ${CMAKE_CURRENT_SOURCE_DIR}/ethdispatch.py +    ${CMAKE_CURRENT_SOURCE_DIR}/fpga_bit_to_bin.py      ${CMAKE_CURRENT_SOURCE_DIR}/gpsd_iface.py      ${CMAKE_CURRENT_SOURCE_DIR}/mpmlog.py      ${CMAKE_CURRENT_SOURCE_DIR}/mpmtypes.py      ${CMAKE_CURRENT_SOURCE_DIR}/mpmutils.py      ${CMAKE_CURRENT_SOURCE_DIR}/prefs.py -    ${CMAKE_CURRENT_SOURCE_DIR}/rpc_server.py      ${CMAKE_CURRENT_SOURCE_DIR}/process_manager.py +    ${CMAKE_CURRENT_SOURCE_DIR}/rpc_server.py +    ${CMAKE_CURRENT_SOURCE_DIR}/tlv_eeprom.py +    ${CMAKE_CURRENT_SOURCE_DIR}/user_eeprom.py  )  list(APPEND USRP_MPM_FILES ${USRP_MPM_TOP_FILES})  add_subdirectory(chips) diff --git a/mpm/python/usrp_mpm/tlv_eeprom.py b/mpm/python/usrp_mpm/tlv_eeprom.py new file mode 100644 index 000000000..671433ffa --- /dev/null +++ b/mpm/python/usrp_mpm/tlv_eeprom.py @@ -0,0 +1,143 @@ +# +# Copyright 2019 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +""" +Tag-Length-Value (TLV) based EEPROM management code +""" + +import struct +import zlib + +class NamedStruct: +    """ +    Helper class for unpacking values from bytes +    """ +    def __init__(self, fmt, keys): +        self.struct = struct.Struct(fmt) +        self.keys = keys + +        # ensure no duplicate keys +        assert len(set(keys) - set([None])) == \ +            len([x for x in keys if x is not None]) +        # ensure same number of keys as elements +        assert len(self.struct.unpack(bytearray(self.size))) == len(keys) + +    def unpack_from(self, buf, offset=0): +        """ +        Unpack values from the struct, returning a dictionary mapping a field +        name to a value. If the field name is None, the field is not included +        in the returned dictionary. + +        buf -- the buffer to unpack from +        offset -- the offset within that buffer +        """ + +        vals = self.struct.unpack_from(buf, offset) +        return {x[0]: x[1] for x in zip(self.keys, vals) if x[0] is not None} + +    @property +    def size(self): +        """ +        number of values to unpack +        """ +        return self.struct.size + + +def tlv_eeprom_validate(eeprom, expected_magic): +    """ +    Validate the contents of the EEPROM and return a tuple (header, tlv) +    Header is a dictionary of the EEPROM header (magic, size, crc) +    Tlv is the raw TLV data + +    eeprom -- raw eeprom data +    expected_magic -- magic value that's expected +    """ +    def crc32(data, initial=0): +        initial = initial ^ 0xFFFFFFFF +        crc = zlib.crc32(data, initial) +        return crc ^ 0xFFFFFFFF + +    size_offset = 8 +    tlv_eeprom_hdr = NamedStruct('< I I I', ['magic', 'crc', 'size']) +    hdr = tlv_eeprom_hdr.unpack_from(eeprom) + +    if hdr['magic'] != expected_magic: +        raise RuntimeError( +            "Received incorrect EEPROM magic. " +            "Read: {:08X} Expected: {:08X}".format( +                hdr['magic'], expected_magic)) + +    if hdr['size'] > (len(eeprom) - tlv_eeprom_hdr.size): +        raise RuntimeError('invalid size') + +    crc = crc32(eeprom[size_offset:tlv_eeprom_hdr.size+hdr['size']]) +    if hdr['crc'] != crc: +        raise RuntimeError( +            "Received incorrect CRC. " +            "Read: {:08X} Expected: {:08X}".format( +                hdr['crc'], crc)) + +    return hdr, eeprom[tlv_eeprom_hdr.size:tlv_eeprom_hdr.size+hdr['size']] + + +def tlv_eeprom_unpack(tlv, tagmap): +    """ +    Parse TLV data and return a dictionary of values found + +    tlv -- raw TLV data from the EEPROM +    tagmap -- dictionary mapping 8-bit tag to a NamedStruct instance +    """ + +    values = {} +    hdr_struct = NamedStruct('< B B', ['tag', 'len']) +    idx = 0 + +    while idx < len(tlv): +        hdr = hdr_struct.unpack_from(tlv, idx) +        idx += hdr_struct.size + +        if hdr['tag'] in tagmap: +            val_struct = tagmap[hdr['tag']] +            if hdr['len'] != val_struct.size: +                raise RuntimeError( +                    "unexpected size: {:d}, expected: {:d}".format( +                        hdr['len'], tagmap[hdr['tag']])) +            unpacked = val_struct.unpack_from(tlv, idx) +            # prohibit clobbering existing values +            assert len(set(values.keys()) & set(unpacked.keys())) == 0 +            values.update(unpacked) + +        idx += hdr['len'] + +    return values + + +def read_eeprom( +        nvmem_path, +        tagmap, +        expected_magic, +        max_size=None +): +    """ +    Read the EEPROM located at nvmem_path and return a tuple (header, data) +    Header is a dictionary of values unpacked from eeprom based upon tagmap +    Data contains the full eeprom contents + +    nvmem_path -- Path to readable file (typically something in sysfs) +    tagmap -- dictionary mapping an 8-bit tag to a NamedStruct instance +    expected_magic -- magic value that is expected +    max_size -- Max number of bytes to read from nvmem, whole file if omitted + +    Tagmap should be a dictionary mapping an 8-bit tag to a NamedStruct +    instance. For each tag that's found in the eeprom, the value at that tag +    will be unpacked using the associated NamedStruct; any named fields within +    that struct will then be added to the returned dictionary. +    """ + +    max_size = max_size or -1 +    with open(nvmem_path, "rb") as nvmem_file: +        data = nvmem_file.read(max_size) +    _, tlv = tlv_eeprom_validate(data, expected_magic) +    return tlv_eeprom_unpack(tlv, tagmap), data  | 
