in meta-facebook/meta-elbert/recipes-utils/wedge-eeprom/files/lib/elbert_eeprom.c [217:467]
int elbert_eeprom_parse(const char *target, struct wedge_eeprom_st *eeprom)
{
int rc = 0;
int read_pointer = 0;
uint32_t len;
FILE *fin;
int field_type;
int field_len;
int ver_major = 0;
int ver_minor = 0;
int dot_location = 0;
int sku_length = ELBERT_MAX_CHAR_LENGTH;
char local_target[256];
char field_value[ELBERT_EEPROM_SIZE]; // Will never overflow
char fn[64];
char buf[ELBERT_EEPROM_SIZE];
char bus_name[32];
bool mfgtime2 = false; // set to True if MFGTIME2 field is detected
if (!eeprom) {
return -EINVAL;
}
if (!target)
return -EINVAL;
snprintf(local_target, sizeof(local_target), "%s", target);
elbert_str_toupper((char *)local_target);
if (!strcmp(local_target, ELBERT_EEPROM_SCM_OBJ)) {
sprintf(fn, "%s%s/eeprom", ELBERT_EEPROM_PATH_BASE,
ELBERT_EEPROM_SCM);
} else if (!strcmp(local_target, ELBERT_EEPROM_BMC_OBJ)) {
sprintf(fn, "%s%s/eeprom", ELBERT_EEPROM_PATH_BASE,
ELBERT_EEPROM_BMC);
sku_length = 8; // BMC field is only 8 chars long
} else if (!strcmp(local_target, ELBERT_EEPROM_CHS_OBJ)) {
sprintf(fn, "%s%s/eeprom", ELBERT_EEPROM_PATH_BASE,
ELBERT_EEPROM_CHASSIS);
sku_length = 4; // Chassis field is only 4 chars long
} else if (!strcmp(local_target, ELBERT_EEPROM_SMB_OBJ)) {
sprintf(fn, "%s%s/eeprom", ELBERT_EEPROM_PATH_BASE,
ELBERT_EEPROM_SMB);
} else if (!strcmp(local_target, ELBERT_EEPROM_SMB_EXTRA_OBJ)) {
sprintf(fn, "%s%s/eeprom", ELBERT_EEPROM_PATH_BASE,
ELBERT_EEPROM_SMB_EXTRA);
} else if (elbert_get_pim_bus_name((const char *)local_target, bus_name) == 0) {
sprintf(fn, "%s%s/eeprom", ELBERT_EEPROM_PATH_BASE, bus_name);
} else {
return -EINVAL;
}
fin = fopen(fn, "r");
if (fin == NULL) {
rc = errno;
OBMC_ERROR(rc, "Failed to open %s", fn);
goto out;
}
/* Read the file into buffer */
rc = fread(buf, 1, sizeof(buf), fin);
if (rc <= 0) {
OBMC_ERROR(ENOSPC, "Failed to complete the read. Error code %d", rc);
rc = ENOSPC;
goto out;
}
/*
* There are many fields in FB standard eeprom, that doesn't exist in
* ELBERT eeprom. Start with all fields filled up as NULL or 0x0,
* then overwrite the values as we get the information from eeprom
*/
memset(eeprom, 0, sizeof(struct wedge_eeprom_st));
/* Check that the eeprom field format header is as expected */
if ((buf[read_pointer] != ELBERT_EEPROM_HEADER_B1) ||
(buf[read_pointer + 1] != ELBERT_EEPROM_HEADER_B2) ||
(buf[read_pointer + 2] != ELBERT_EEPROM_HEADER_B3) ||
(buf[read_pointer + 3] != ELBERT_EEPROM_HEADER_B4)) {
OBMC_ERROR(EINVAL, "Wrong EEPROM header! Expected %02x %02x %02x %02x, \
got %02x %02x %02x %02x",
ELBERT_EEPROM_HEADER_B1, ELBERT_EEPROM_HEADER_B2,
ELBERT_EEPROM_HEADER_B3, ELBERT_EEPROM_HEADER_B4,
buf[read_pointer], buf[read_pointer +1],
buf[read_pointer + 2], buf[read_pointer + 3]);
rc = -EINVAL;
goto out;
}
/* As we read the first four bytes, advance the read pointer */
read_pointer += ELBERT_EEPROM_TYPE_SIZE;
/* Bypass the eeprom size field */
read_pointer += ELBERT_EEPROM_TYPE_SIZE;
/* Check eeprom format */
if ((buf[read_pointer] != ELBERT_EEPROM_FORMAT_V2[0]) ||
(buf[read_pointer + 1] != ELBERT_EEPROM_FORMAT_V2[1]) ||
(buf[read_pointer + 2] != ELBERT_EEPROM_FORMAT_V2[2]) ||
(buf[read_pointer + 3] != ELBERT_EEPROM_FORMAT_V2[3])) {
OBMC_ERROR(EINVAL, "Unsupported eeprom format! Expected %02x %02x %02x %02x, \
got %02x %02x %02x %02x",
ELBERT_EEPROM_FORMAT_V2[0], ELBERT_EEPROM_FORMAT_V2[1],
ELBERT_EEPROM_FORMAT_V2[2], ELBERT_EEPROM_FORMAT_V2[3],
buf[read_pointer], buf[read_pointer + 1],
buf[read_pointer + 2], buf[read_pointer + 3]);
rc = -EINVAL;
goto out;
}
read_pointer += ELBERT_EEPROM_TYPE_SIZE;
/* Parse PCA and serial. If there is additional serial TLV parsed,,
* this value will be overwritten */
elbert_parse_string(&read_pointer, (char *)&eeprom->fbw_odm_pcba_number,
buf, ELBERT_EEPROM_PCA_SIZE);
elbert_parse_string(&read_pointer, (char *)&eeprom->fbw_product_serial,
buf, ELBERT_EEPROM_SERIAL_SIZE);
read_pointer += ELBERT_EEPROM_KVN_SIZE;
/* Product type is hardcoded to ELBERT */
sprintf(eeprom->fbw_product_name, "ELBERT");
rc = 0;
/* Now go though EEPROM contents to fill out the fields */
while ((rc == 0) && (read_pointer < ELBERT_EEPROM_SIZE)) {
rc = elbert_parse_hexadecimal(&read_pointer, &field_type, buf, 2);
if (rc < 0)
goto out;
rc = elbert_parse_hexadecimal(&read_pointer, &field_len, buf, 4);
if (rc < 0)
goto out;
/*
* If this is the end of EEPROM (specified as "END" TLV),
* do not read any value, but just bail out
*/
if (field_type == ELBERT_EEPROM_FIELD_END)
break;
/* Otherwise, read the field */
memcpy(field_value, &buf[read_pointer], field_len);
read_pointer += field_len;
/* Now, map the value into similar field in FB EEPROM */
switch(field_type)
{
case ELBERT_EEPROM_FIELD_MFGTIME:
if (!mfgtime2)
memcpy(eeprom->fbw_system_manufacturing_date, field_value, 10);
break;
case ELBERT_EEPROM_FIELD_MFGTIME2:
mfgtime2 = true; // Do not allow MFGTIME to override MFGTIME2
memcpy(eeprom->fbw_system_manufacturing_date, field_value, 10);
break;
case ELBERT_EEPROM_FIELD_ASY:
memcpy(eeprom->fbw_assembly_number, field_value,
FBW_EEPROM_F_ASSEMBLY_NUMBER);
// We allocate FBW_EEPROM_F_ASSEMBLY_NUMBER + 3
eeprom->fbw_assembly_number[FBW_EEPROM_F_ASSEMBLY_NUMBER + 2] = '\0';
// Set production state based on ASY:
// Production = 1, Prototype = 0
if (isalpha(eeprom->fbw_assembly_number[FBW_EEPROM_F_ASSEMBLY_NUMBER - 2]) ||
isalpha(eeprom->fbw_assembly_number[FBW_EEPROM_F_ASSEMBLY_NUMBER - 1]))
eeprom->fbw_production_state = 1;
else
eeprom->fbw_production_state = 0;
break;
case ELBERT_EEPROM_FIELD_SKU:
memcpy(eeprom->fbw_product_asset, field_value, sku_length);
// Elbert SKU names can be from 8 to 10 char long
// Use product_asset field instead of product_name as it allocates
// more char in the eeprom struct.
/* Remove garbage characters from the end of 7388-16CD2 */
if(!strncmp(
eeprom->fbw_product_asset,
ELBERT_PIM16CD2,
strlen(ELBERT_PIM16CD2))) {
eeprom->fbw_product_asset[strlen(ELBERT_PIM16CD2)] = '\0';
}
/* Remove garbage characters from the end of 7388-16CD */
else if(!strncmp(
eeprom->fbw_product_asset,
ELBERT_PIM16CD,
strlen(ELBERT_PIM16CD))) {
eeprom->fbw_product_asset[strlen(ELBERT_PIM16CD)] = '\0';
}
/* Remove garbage characters from the end of 7388-8D */
else if(!strncmp(
eeprom->fbw_product_asset,
ELBERT_PIM8DDM,
strlen(ELBERT_PIM8DDM))) {
eeprom->fbw_product_asset[strlen(ELBERT_PIM8DDM)] = '\0';
}
break;
case ELBERT_EEPROM_FIELD_MACBASE:
for (int i = 0; i < 6; i++)
eeprom->fbw_mac_base[i] = elbert_htoi(field_value[2*i]) * 16 +
elbert_htoi(field_value[2*i + 1]);
/* Also hardcade mac size as the same value as Minipack */
eeprom->fbw_mac_size = 139;
break;
case ELBERT_EEPROM_FIELD_HWREV:
/* Scan for period character */
dot_location = field_len;
for (int i = 0; i < field_len; i++)
if (field_value[i] == '.')
dot_location = i;
/* Parse the number before dot, and fit it into uint8_t */
for (int i = 0; i < dot_location; i++) {
ver_major *= 10;
ver_major += elbert_htoi(field_value[i]);
}
/* Parse the number after dot, and fit it into uint8_t */
ver_major = ver_major % 256;
for (int i = dot_location + 1; i < field_len; i++) {
ver_minor *= 10;
ver_minor += elbert_htoi(field_value[i]);
}
/* Fit the value into uint8_t */
ver_minor = ver_minor % 256;
eeprom->fbw_product_version = ver_major;
eeprom->fbw_product_subversion = ver_minor;
break;
case ELBERT_EEPROM_FIELD_SERIAL:
memcpy(&eeprom->fbw_product_serial, field_value,
ELBERT_EEPROM_SERIAL_SIZE);
eeprom->fbw_product_serial[ELBERT_EEPROM_SERIAL_SIZE] = '\0';
break;
default:
/* Unknown field */
break;
}
}
out:
if (fin) {
fclose(fin);
}
// rc > 0 means success here.
// (it will usually have the number of bytes read)
return (rc >= 0) ? 0 : -rc;
}