mirror of
https://github.com/saymrwulf/uhd.git
synced 2026-05-16 21:10:10 +00:00
Add support for parsing an eeprom that uses tag-length-value to store contents. Co-authored-by: Michael Auchter <michael.auchter@ni.com> Co-authored-by: Virendra Kakade <virendra.kakade@ni.com> Co-authored-by: Cristina Fuentes <cristina.fuentes@ni.com>
143 lines
4.4 KiB
Python
143 lines
4.4 KiB
Python
#
|
|
# 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
|