2017-05-23 23:48:39 +00:00
#
2018-02-20 00:53:15 +00:00
# Copyright 2017 Ettus Research, a National Instruments Company
2017-05-23 23:48:39 +00:00
#
2018-02-20 00:53:15 +00:00
# SPDX-License-Identifier: GPL-3.0-or-later
2017-05-23 23:48:39 +00:00
#
"""
EEPROM management code
"""
import struct
2017-07-24 21:19:32 +00:00
import zlib
2017-05-23 23:48:39 +00:00
EEPROM_DEFAULT_HEADER = struct . Struct ( " !I I " )
2022-01-28 10:55:14 +00:00
class MboardEEPROM :
2017-05-23 23:48:39 +00:00
"""
Given a nvmem path , read out EEPROM values from the motherboard ' s EEPROM.
The format of data in the EEPROM must follow the following standard :
- 4 bytes magic . This will always be the same value ; checking this value is
a sanity check for if the read was successful .
- 4 bytes version . This is the version of the EEPROM format .
The following bytes are version - dependent :
Version 1 :
- 4 x4 bytes mcu_flags - > throw them away
- 2 bytes hw_pid
- 2 bytes hw_rev ( starting at 0 )
- 8 bytes serial number ( zero - terminated string of 7 characters )
- 6 bytes MAC address for eth0
- 2 bytes padding
- 6 bytes MAC address for eth1
- 2 bytes padding
- 6 bytes MAC address for eth2
- 2 bytes padding
- 4 bytes CRC
2018-04-26 01:01:09 +00:00
Version 2 : ( FWIW MPM doesn ' t care about the extra bytes)
- 4 x4 bytes mcu_flags - > throw them away
- 2 bytes hw_pid
- 2 bytes hw_rev ( starting at 0 )
- 8 bytes serial number ( zero - terminated string of 7 characters )
- 6 bytes MAC address for eth0
- 2 bytes Devicetree compatible - > throw them away
- 6 bytes MAC address for eth1
- 2 bytes EC compatible - > throw them away
- 6 bytes MAC address for eth2
- 2 bytes padding
- 4 bytes CRC
2019-05-09 23:25:11 +00:00
Version 3 : ( FWIW MPM doesn ' t care about the extra bytes)
- 4 x4 bytes mcu_flags - > throw them away
- 2 bytes hw_pid
- 2 bytes hw_rev ( starting at 0 )
- 8 bytes serial number ( zero - terminated string of 7 characters )
- 6 bytes MAC address for eth0
- 2 bytes Devicetree compatible - > throw them away
- 6 bytes MAC address for eth1
- 2 bytes EC compatible - > throw them away
- 6 bytes MAC address for eth2
- 2 bytes rev_compat
- 4 bytes CRC
2017-05-23 23:48:39 +00:00
MAC addresses are ignored here ; they are read elsewhere . If we really need
to know the MAC address of an interface , we can fish it out the raw data ,
or ask the system .
"""
# Create one of these for every version of the EEPROM format:
eeprom_header_format = (
None , # For laziness, we start at version 1 and thus index 0 stays empty
2022-01-28 10:55:14 +00:00
# Version 1
" !I I 16s H H 7s 1x 24s I " ,
# Version 2 (Ignore the extra fields, it doesn't matter to MPM)
" !I I 16s H H 7s 1x 6s H 6s H 6s 2x I " ,
# Version 3 (Ignore the extra fields, it doesn't matter to MPM)
" !I I 16s H H 7s 1x 6s H 6s H 6s H I " ,
2017-05-23 23:48:39 +00:00
)
eeprom_header_keys = (
None , # For laziness, we start at version 1 and thus index 0 stays empty
2018-04-26 01:01:09 +00:00
( ' magic ' , ' eeprom_version ' , ' mcu_flags ' , ' pid ' , ' rev ' , ' serial ' , ' mac_addresses ' , ' CRC ' ) , # Version 1
2019-05-09 23:25:11 +00:00
( ' magic ' , ' eeprom_version ' , ' mcu_flags ' , ' pid ' , ' rev ' , ' serial ' , ' mac_eth0 ' , ' dt_compat ' , ' mac_eth1 ' , ' ec_compat ' , ' mac_eth2 ' , ' CRC ' ) , # Version 2 (Ignore the extra fields, it doesn't matter to MPM)
( ' magic ' , ' eeprom_version ' , ' mcu_flags ' , ' pid ' , ' rev ' , ' serial ' , ' mac_eth0 ' , ' dt_compat ' , ' mac_eth1 ' , ' ec_compat ' , ' mac_eth2 ' , ' rev_compat ' , ' CRC ' ) , # Version 3
2017-05-23 23:48:39 +00:00
)
2022-01-28 10:55:14 +00:00
class DboardEEPROM :
2017-05-23 23:48:39 +00:00
"""
Given a nvmem path , read out EEPROM values from the daughterboard ' s EEPROM.
The format of data in the EEPROM must follow the following standard :
- 4 bytes magic . This will always be the same value ; checking this value is
a sanity check for if the read was successful .
- 4 bytes version . This is the version of the EEPROM format .
The following bytes are version - dependent :
Version 1 :
- 2 bytes hw_pid
- 2 bytes hw_rev ( starting at 0 )
- 8 bytes serial number ( zero - terminated string of 7 characters )
- 4 bytes CRC
2018-04-26 01:01:09 +00:00
Version 2 :
- 2 bytes hw_pid
- 1 byte hw_rev ( starting at 0 )
- 1 byte dt_compat ( starting at 0 , MPM can ignore that )
- 8 bytes serial number ( zero - terminated string of 7 characters )
- 4 bytes CRC
2017-05-23 23:48:39 +00:00
MAC addresses are ignored here ; they are read elsewhere . If we really need
to know the MAC address of an interface , we can fish it out the raw data ,
or ask the system .
"""
# Create one of these for every version of the EEPROM format:
eeprom_header_format = (
None , # For laziness, we start at version 1 and thus index 0 stays empty
" !I I H H 7s 1x I " , # Version 1
2018-04-26 01:01:09 +00:00
" !I I H B 1x 7s 1x I " , # Version 2
2017-05-23 23:48:39 +00:00
)
eeprom_header_keys = (
None , # For laziness, we start at version 1 and thus index 0 stays empty
2022-01-28 10:55:14 +00:00
# Version 1
( ' magic ' , ' eeprom_version ' , ' pid ' , ' rev ' , ' serial ' , ' CRC ' ) ,
# Version 2 (Ignore the extra field, it doesn't matter to MPM)
( ' magic ' , ' eeprom_version ' , ' pid ' , ' rev ' , ' serial ' , ' CRC ' ) ,
2017-05-23 23:48:39 +00:00
)
def read_eeprom (
nvmem_path ,
2018-03-20 22:13:14 +00:00
offset ,
2017-05-23 23:48:39 +00:00
eeprom_header_format ,
eeprom_header_keys ,
expected_magic ,
max_size = None
) :
"""
Read the EEPROM located at nvmem_path and return a tuple ( header , data )
Header is already parsed in the common header fields
Data contains the full eeprom data structure
nvmem_path - - Path to readable file ( typically something in sysfs )
eeprom_header_format - - List of header formats , by version
eeprom_header_keys - - List of keys for the entries in the EEPROM
expected_magic - - The magic value that is expected
max_size - - Max number of bytes to be read . If omitted , will read the full file .
"""
assert len ( eeprom_header_format ) == len ( eeprom_header_keys )
def _parse_eeprom_data (
data ,
version ,
) :
"""
Parses the raw ' data ' according to the version .
2017-07-24 21:19:32 +00:00
This also parses the CRC and assumes CRC is the last 4 bytes of each data .
2017-05-23 23:48:39 +00:00
Returns a dictionary .
"""
eeprom_parser = struct . Struct ( eeprom_header_format [ version ] )
eeprom_keys = eeprom_header_keys [ version ]
parsed_data = eeprom_parser . unpack_from ( data )
2017-07-24 21:19:32 +00:00
read_crc = parsed_data [ - 1 ]
2018-07-26 23:54:55 +00:00
rawdata_without_crc = data [ : eeprom_parser . size - 4 ]
2017-12-01 22:41:31 +00:00
expected_crc = zlib . crc32 ( rawdata_without_crc ) & 0xffffffff
2018-07-26 23:54:55 +00:00
if read_crc != expected_crc :
2017-07-24 21:19:32 +00:00
raise RuntimeError (
" Received incorrect CRC. " \
" Read: {:08X} Expected: {:08X} " . format (
read_crc , expected_crc ) )
2017-06-01 02:02:58 +00:00
return dict ( list ( zip ( eeprom_keys , parsed_data ) ) )
2017-05-23 23:48:39 +00:00
# Dawaj, dawaj
max_size = max_size or - 1
with open ( nvmem_path , " rb " ) as nvmem_file :
2018-03-20 22:13:14 +00:00
data = nvmem_file . read ( max_size ) [ offset : ]
2017-05-23 23:48:39 +00:00
eeprom_magic , eeprom_version = EEPROM_DEFAULT_HEADER . unpack_from ( data )
if eeprom_magic != expected_magic :
raise RuntimeError (
" Received incorrect EEPROM magic. " \
" Read: {:08X} Expected: {:08X} " . format (
2017-07-24 21:19:32 +00:00
eeprom_magic , expected_magic ) )
2017-05-23 23:48:39 +00:00
if eeprom_version > = len ( eeprom_header_format ) :
raise RuntimeError ( " Unexpected EEPROM version: ` {} ' " . format ( eeprom_version ) )
return ( _parse_eeprom_data ( data , eeprom_version ) , data )