int elbert_eeprom_parse()

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;
}