meta-facebook/meta-elbert/recipes-elbert/platform-lib/files/pal/pal.c (812 lines of code) (raw):

/* * Copyright 2020-present Facebook. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * This file contains functions and logics that depends on Yamp specific * hardware and kernel drivers. Here, some of the empty "default" functions * are overridden with simple functions that returns non-zero error code. * This is for preventing any potential escape of failures through the * default functions that will return 0 no matter what. */ #include "pal.h" #include <ctype.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <sys/time.h> #include <syslog.h> #include <time.h> #include <unistd.h> #include <openbmc/obmc-i2c.h> const uint8_t pim_bus[] = { 16, 17, 18, 19, 20, 21, 22, 23 }; const uint8_t pim_bus_p1[] = { 16, 17, 18, 23, 20, 21, 22, 19 }; const char pal_fru_list[] = "all, scm, smb, pim2, pim3, pim4, pim5 \ pim6, pim7, pim8, pim9, psu1, psu2, psu3, psu4, fan"; char* key_list[] = { "pwr_server_last_state", "sysfw_ver_server", "scm_sensor_health", "smb_sensor_health", "pim2_sensor_health", "pim3_sensor_health", "pim4_sensor_health", "pim5_sensor_health", "pim6_sensor_health", "pim7_sensor_health", "pim8_sensor_health", "pim9_sensor_health", "psu1_sensor_health", "psu2_sensor_health", "psu3_sensor_health", "psu4_sensor_health", "fan_sensor_health", "server_boot_order", "server_restart_cause", "server_cpu_ppin", /* Add more Keys here */ LAST_KEY /* This is the last key of the list */ }; char * def_val_list[] = { "on", /* pwr_server_last_state */ "0", /* sysfw_ver_server */ "1", /* scm_sensor_health */ "1", /* smb_sensor_health */ "1", /* pim2_sensor_health */ "1", /* pim3_sensor_health */ "1", /* pim4_sensor_health */ "1", /* pim5_sensor_health */ "1", /* pim6_sensor_health */ "1", /* pim7_sensor_health */ "1", /* pim8_sensor_health */ "1", /* pim9_sensor_health */ "1", /* psu1_sensor_health */ "1", /* psu2_sensor_health */ "1", /* psu3_sensor_health */ "1", /* psu4_sensor_health */ "1", /* fan_sensor_health */ "0000000", /* server_boot_order */ "3", /* server_restart_cause */ "0", /* server_cpu_ppin */ /* Add more def values for the corresponding keys*/ LAST_KEY /* Same as last entry of the key_list */ }; // Elbert specific Platform Abstraction Layer (PAL) Functions int pal_get_platform_name(char* name) { // Return Elbert Specific value strcpy(name, ELBERT_PLATFORM_NAME); return 0; } int pal_get_num_slots(uint8_t* num) { // Return Elbert Specific Value *num = ELBERT_MAX_NUM_SLOTS; return 0; } static int pal_key_check(char* key) { int i; i = 0; while (strcmp(key_list[i], LAST_KEY)) { // If key is valid, return success if (!strcmp(key, key_list[i])) return 0; i++; } #ifdef DEBUG syslog(LOG_WARNING, "pal_key_check: invalid key - %s", key); #endif return -1; } int pal_get_key_value(char* key, char* value) { int ret; // Check if key is defined and valid if (pal_key_check(key)) return -1; ret = kv_get(key, value, NULL, KV_FPERSIST); return ret; } int pal_set_key_value(char* key, char* value) { // Check if key is defined and valid if (pal_key_check(key)) return -1; return kv_set(key, value, 0, KV_FPERSIST); } int pal_set_def_key_value(void) { int i; for (i = 0; strncmp(key_list[i], LAST_KEY, strlen(LAST_KEY)) != 0; i++) { if (kv_set(key_list[i], def_val_list[i], 0, KV_FPERSIST | KV_FCREATE)) { #ifdef DEBUG syslog(LOG_WARNING, "pal_set_def_key_value: kv_set failed."); #endif } } return 0; } int pal_set_sysfw_ver(uint8_t slot, uint8_t* ver) { int i; char key[MAX_KEY_LEN]; char str[MAX_VALUE_LEN] = {0}; char tstr[10]; sprintf(key, "%s", "sysfw_ver_server"); for (i = 0; i < SIZE_SYSFW_VER; i++) { sprintf(tstr, "%02x", ver[i]); strcat(str, tstr); } return pal_set_key_value(key, str); } int pal_get_sysfw_ver(uint8_t slot, uint8_t* ver) { int i; int j = 0; int ret; int msb, lsb; char key[MAX_KEY_LEN]; char str[MAX_VALUE_LEN] = {0}; char tstr[4]; sprintf(key, "%s", "sysfw_ver_server"); ret = pal_get_key_value(key, str); if (ret) { return ret; } for (i = 0; i < 2 * SIZE_SYSFW_VER; i += 2) { sprintf(tstr, "%c\n", str[i]); msb = strtol(tstr, NULL, 16); sprintf(tstr, "%c\n", str[i + 1]); lsb = strtol(tstr, NULL, 16); ver[j++] = (msb << 4) | lsb; } return 0; } int pal_get_boot_order( uint8_t slot, uint8_t* req_data, uint8_t* boot, uint8_t* res_len) { int ret, msb, lsb, i, j = 0; char str[MAX_VALUE_LEN] = {0}; char tstr[4]; ret = pal_get_key_value("server_boot_order", str); if (ret) { *res_len = 0; return ret; } for (i = 0; i < 2 * SIZE_BOOT_ORDER; i += 2) { snprintf(tstr, sizeof(tstr), "%c\n", str[i]); msb = strtol(tstr, NULL, 16); snprintf(tstr, sizeof(tstr), "%c\n", str[i + 1]); lsb = strtol(tstr, NULL, 16); boot[j++] = (msb << 4) | lsb; } *res_len = SIZE_BOOT_ORDER; return 0; } int pal_set_boot_order( uint8_t slot, uint8_t* boot, uint8_t* res_data, uint8_t* res_len) { int i, j, offset, network_dev = 0; char str[MAX_VALUE_LEN] = {0}; enum { BOOT_DEVICE_IPV4 = 0x1, BOOT_DEVICE_IPV6 = 0x9, }; *res_len = 0; for (i = offset = 0; i < SIZE_BOOT_ORDER && offset < sizeof(str); i++) { if (i > 0) { // byte[0] is boot mode, byte[1:5] are boot order for (j = i + 1; j < SIZE_BOOT_ORDER; j++) { if (boot[i] == boot[j]) return CC_INVALID_PARAM; } // If bit[2:0] is 001b (Network), bit[3] is IPv4/IPv6 order // bit[3]=0b: IPv4 first // bit[3]=1b: IPv6 first if ((boot[i] == BOOT_DEVICE_IPV4) || (boot[i] == BOOT_DEVICE_IPV6)) network_dev++; } offset += snprintf(str + offset, sizeof(str) - offset, "%02x", boot[i]); } // not allow having more than 1 network boot device in the boot order if (network_dev > 1) return CC_INVALID_PARAM; return pal_set_key_value("server_boot_order", str); } int pal_set_last_pwr_state(uint8_t fru, char* state) { int ret; char key[MAX_KEY_LEN]; sprintf(key, "%s", "pwr_server_last_state"); ret = pal_set_key_value(key, state); if (ret < 0) { #ifdef DEBUG syslog( LOG_WARNING, "pal_set_last_pwr_state: pal_set_key_value failed for " "fru %u", fru); #endif } return ret; } int pal_get_last_pwr_state(uint8_t fru, char* state) { int ret; char key[MAX_KEY_LEN]; sprintf(key, "%s", "pwr_server_last_state"); ret = pal_get_key_value(key, state); if (ret < 0) { #ifdef DEBUG syslog( LOG_WARNING, "pal_get_last_pwr_state: pal_get_key_value failed for " "fru %u", fru); #endif } return ret; } int pal_set_restart_cause(uint8_t slot, uint8_t restart_cause) { char value[MAX_VALUE_LEN] = {0}; sprintf(value, "%d", restart_cause); if (kv_set("server_restart_cause", value, 0, KV_FPERSIST)) { return -1; } return 0; } int pal_set_ppin_info( uint8_t slot, uint8_t* req_data, uint8_t req_len, uint8_t* res_data, uint8_t* res_len) { char key[] = "server_cpu_ppin"; char str[MAX_VALUE_LEN] = {0}; int i; int completion_code = CC_UNSPECIFIED_ERROR; *res_len = 0; if (req_len > SIZE_CPU_PPIN * 2) req_len = SIZE_CPU_PPIN * 2; for (i = 0; i < req_len; i++) { sprintf(str + (i * 2), "%02x", req_data[i]); } if (kv_set(key, str, 0, KV_FPERSIST) != 0) return completion_code; completion_code = CC_SUCCESS; return completion_code; } // Read output from sysfs into buffer int pal_read_sysfs(char* path, char* data, int data_size) { int fd; int ret; int bytes_read; fd = open(path, O_RDONLY); if (fd < 0) { ret = errno; close(fd); return ret; } bytes_read = read(fd, data, data_size); if (bytes_read == data_size) { ret = 0; } else { ret = -1; } close(fd); return ret; } // Power on/off/reset the server using wedge_power.sh static int run_wedge_power(const char* action) { int ret; char cmd[MAX_WEDGE_POWER_CMD_SIZE] = {0}; sprintf(cmd, "%s %s", WEDGE_POWER, action); ret = run_command(cmd); if (ret) { #ifdef DEBUG syslog(LOG_WARNING, "%s: Failed to power %s server", __func__, action); #endif return -1; } return 0; } int pal_get_server_power(uint8_t slot_id, uint8_t* status) { int ret; uint8_t retry = MAX_READ_RETRY; char value[MAX_VALUE_LEN]; char sysfs_value[4] = {0}; char path[LARGEST_DEVICE_NAME + 1]; snprintf(path, LARGEST_DEVICE_NAME, SCMCPLD_PATH_FMT, CPU_CTRL); /* Check if the SCM is turned on or not */ while (retry) { ret = pal_read_sysfs(path, sysfs_value, sizeof(sysfs_value) - 1); if (!ret) break; msleep(50); retry--; } if (ret) { // Check previous power state syslog( LOG_INFO, "pal_get_server_power: pal_read_sysfs returned error hence" " reading the kv_store for last power state for fru %d", slot_id); pal_get_last_pwr_state(slot_id, value); if (!(strncmp(value, "off", strlen("off")))) { *status = SERVER_POWER_OFF; } else if (!(strncmp(value, "on", strlen("on")))) { *status = SERVER_POWER_ON; } else { return ret; } return 0; } if (!strcmp(sysfs_value, ELBERT_SCM_PWR_ON)) { *status = SERVER_POWER_ON; } else { *status = SERVER_POWER_OFF; } return 0; } // Power Off, Power On, or Power Reset the server in given slot int pal_set_server_power(uint8_t slot_id, uint8_t cmd) { uint8_t status; if (pal_get_server_power(slot_id, &status) < 0) { return -1; } switch (cmd) { case SERVER_POWER_ON: if (status == SERVER_POWER_ON) return 1; else return run_wedge_power(WEDGE_POWER_ON); break; case SERVER_POWER_OFF: if (status == SERVER_POWER_OFF) return 1; else return run_wedge_power(WEDGE_POWER_OFF); break; case SERVER_POWER_CYCLE: if (status == SERVER_POWER_ON) { if (run_wedge_power(WEDGE_POWER_OFF)) return -1; sleep(DELAY_POWER_CYCLE); return run_wedge_power(WEDGE_POWER_ON); } else if (status == SERVER_POWER_OFF) { return run_wedge_power(WEDGE_POWER_ON); } break; case SERVER_POWER_RESET: if (status == SERVER_POWER_ON) { if (run_wedge_power(WEDGE_POWER_RESET)) return -1; } else if (status == SERVER_POWER_OFF) { syslog( LOG_CRIT, "Micro-server power status is off, " "ignore power reset action"); return -2; } break; default: return -1; } return 0; } bool pal_is_fw_update_ongoing_system(void) { bool ret = false; uint8_t retry = MAX_READ_RETRY; char buf[PS_BUF_SIZE]; FILE* fp; while (retry) { fp = popen(PS_CMD, "r"); if (fp) break; msleep(50); retry--; } if (fp) { while (fgets(buf, PS_BUF_SIZE, fp) != NULL) { if (strstr(buf, ELBERT_BIOS_UTIL) || strstr(buf, ELBERT_FPGA_UTIL) || strstr(buf, ELBERT_BMC_FLASH) || strstr(buf, ELBERT_PSU_UTIL)) { ret = true; break; } } pclose(fp); } else { // Assume we're updating if we can't be sure ret = true; #ifdef DEBUG syslog(LOG_WARNING, "%s: failed to get ps process output", __func__); #endif } return ret; } // Used as part of the function for OEM Command "CMD_OEM_GET_POSS_PCIE_CONFIG" // 0xF4 int pal_get_poss_pcie_config( uint8_t slot, uint8_t* req_data, uint8_t req_len, uint8_t* res_data, uint8_t* res_len) { uint8_t completion_code = CC_SUCCESS; uint8_t pcie_conf = 0x02; // Elbert uint8_t* data = res_data; *data++ = pcie_conf; *res_len = data - res_data; return completion_code; } // For OEM command "CMD_OEM_GET_PLAT_INFO" 0x7e int pal_get_plat_sku_id(void) { return 0x06; // Elbert } int pal_get_board_id( uint8_t slot, uint8_t* req_data, uint8_t req_len, uint8_t* res_data, uint8_t* res_len) { int board_sku_id = 0, board_rev = 0, ret = 0; unsigned char* data = res_data; struct wedge_eeprom_st eeprom; board_sku_id = pal_get_plat_sku_id(); ret = elbert_eeprom_parse(SCM_TARGET, &eeprom); if (ret) return CC_NODE_BUSY; board_rev = eeprom.fbw_product_version; *data++ = board_sku_id; *data++ = board_rev; *data++ = slot; *data++ = 0x00; // 1S Server. *res_len = data - res_data; return CC_SUCCESS; } void pal_get_eth_intf_name(char* intf_name) { snprintf(intf_name, MAX_INTF_SIZE, ELBERT_INTF); } // GUID for System and Device static int pal_set_guid(char* guid) { int ret; char key[MAX_KEY_LEN]; sprintf(key, "%s", "bmc_guid"); ret = kv_set(key, guid, GUID_SIZE, KV_FPERSIST); if (ret < 0) { #ifdef DEBUG syslog(LOG_WARNING, "pal_set_guid: pal_set_key_value failed"); #endif } return ret; } static int pal_get_guid(char* guid) { int ret; unsigned int size; char key[MAX_KEY_LEN]; struct wedge_eeprom_st eeprom; uint8_t retry = MAX_READ_RETRY; sprintf(key, "%s", "bmc_guid"); ret = kv_get(key, guid, &size, KV_FPERSIST); if (ret < 0 || size != GUID_SIZE) { // On Elbert, GUID is stored in kv rather than EEPROM, so GUID must be // generated from serial number when first accessed. while (retry) { ret = elbert_eeprom_parse(BMC_TARGET, &eeprom); if (!ret) break; msleep(50); retry--; } if (ret) return CC_NODE_BUSY; pal_populate_guid(guid, eeprom.fbw_product_serial); syslog( LOG_INFO, "pal_get_guid: generating GUID with UID %s", eeprom.fbw_product_serial); return pal_set_guid(guid); } return ret; } int pal_get_sys_guid(uint8_t fru, char* guid) { return pal_get_guid(guid); } int pal_set_sys_guid(uint8_t fru, char* str) { char guid[GUID_SIZE] = {0}; pal_populate_guid(guid, str); return pal_set_guid(guid); } int pal_get_dev_guid(uint8_t fru, char* guid) { return pal_get_guid(guid); } int pal_set_dev_guid(uint8_t fru, char* str) { char guid[GUID_SIZE] = {0}; pal_populate_guid(guid, str); return pal_set_guid(guid); } // Helper Functions static int read_device(const char *device, int *value) { FILE *fp; int rc; fp = fopen(device, "r"); if (!fp) { int err = errno; #ifdef DEBUG syslog(LOG_INFO, "failed to open device %s", device); #endif return err; } rc = fscanf(fp, "%i", value); fclose(fp); if (rc != 1) { #ifdef DEBUG syslog(LOG_INFO, "failed to read device %s", device); #endif return ENOENT; } else { return 0; } } static bool smb_is_p1(void) { if (access(ELBERT_SMB_P1_BOARD_PATH, F_OK) != -1) return true; else return false; } uint8_t get_pim_i2cbus(uint8_t fru) { uint8_t i2cbus; uint8_t pimid = fru - FRU_PIM2; // P1 SMB detected, use pim_bus_p1 smbus channel mapping. if (smb_is_p1() == true) i2cbus = pim_bus_p1[pimid]; else i2cbus = pim_bus[pimid]; return i2cbus; } static int pal_is_pim_prsnt(uint8_t fru, uint8_t *status) { int val; char tmp[LARGEST_DEVICE_NAME]; char prsnt_path[LARGEST_DEVICE_NAME + 1]; uint8_t i2cbus = get_pim_i2cbus(fru); // Check present bit and EEPROM. snprintf(tmp, LARGEST_DEVICE_NAME, SMBCPLD_PATH_FMT, PIM_PRSNT); snprintf(prsnt_path, LARGEST_DEVICE_NAME, tmp, fru - FRU_PIM2 + 2); if ((read_device(prsnt_path, &val) == 0 && val == 0x1) || i2c_detect_device(i2cbus, 0x50) == 0) { *status = 1; } else { *status = 0; } return 0; } int pal_is_fru_prsnt(uint8_t fru, uint8_t *status) { int val; char tmp[LARGEST_DEVICE_NAME]; char path[LARGEST_DEVICE_NAME + 1]; *status = 0; switch (fru) { case FRU_CHASSIS: case FRU_BMC: case FRU_SCM: case FRU_SMB: case FRU_SMB_EXTRA: case FRU_FAN: *status = 1; return 0; case FRU_PIM2: case FRU_PIM3: case FRU_PIM4: case FRU_PIM5: case FRU_PIM6: case FRU_PIM7: case FRU_PIM8: case FRU_PIM9: return pal_is_pim_prsnt(fru, status); case FRU_PSU1: case FRU_PSU2: case FRU_PSU3: case FRU_PSU4: snprintf(tmp, LARGEST_DEVICE_NAME, SMBCPLD_PATH_FMT, PSU_PRSNT); snprintf(path, LARGEST_DEVICE_NAME, tmp, fru - FRU_PSU1 + 1); break; default: return -1; } if (read_device(path, &val)) { return -1; } // Only consider present bit. if ((val & 0x1) == 0x0) { *status = 0; } else { *status = 1; } return 0; } int pal_is_psu_power_ok(uint8_t fru, uint8_t *status) { const char *targets[] = { PSU_INPUT_OK, PSU_OUTPUT_OK }; char tmp[LARGEST_DEVICE_NAME]; char path[LARGEST_DEVICE_NAME + 1]; int i, val; /* Check that both PSU input and output power are OK. */ for (i = 0; i < sizeof(targets) / sizeof(targets[0]); i++) { snprintf(tmp, LARGEST_DEVICE_NAME, SMBCPLD_PATH_FMT, targets[i]); snprintf(path, LARGEST_DEVICE_NAME, tmp, fru - FRU_PSU1 + 1); if (read_device(path, &val)) { syslog(LOG_ERR, "%s cannot get value from %s", __func__, path); return -1; } if (val == 0x0) { *status = 0; return 0; } else { *status = 1; } } return 0; } int pal_is_fru_ready(uint8_t fru, uint8_t *status) { int val; int expected_val = 0x0; uint8_t expected_status = 0; char tmp[LARGEST_DEVICE_NAME]; char path[LARGEST_DEVICE_NAME + 1]; *status = 0; switch (fru) { case FRU_CHASSIS: case FRU_BMC: case FRU_SCM: case FRU_SMB: case FRU_SMB_EXTRA: case FRU_FAN: *status = 1; return 0; case FRU_PIM2: case FRU_PIM3: case FRU_PIM4: case FRU_PIM5: case FRU_PIM6: case FRU_PIM7: case FRU_PIM8: case FRU_PIM9: return pal_is_pim_prsnt(fru, status); case FRU_PSU1: case FRU_PSU2: case FRU_PSU3: case FRU_PSU4: /* Use PSU presence to determine readiness. This is because we want to be able to view sensor data even when PSU is not supplying power. */ snprintf(tmp, LARGEST_DEVICE_NAME, SMBCPLD_PATH_FMT, PSU_PRSNT); snprintf(path, LARGEST_DEVICE_NAME, tmp, fru - FRU_PSU1 + 1); break; default: return -1; } if (read_device(path, &val)) { return -1; } if (val == expected_val) { *status = expected_status; } else { *status = !expected_status; } return 0; } int pal_get_fru_name(uint8_t fru, char *name) { switch(fru) { case FRU_SMB: strcpy(name, "smb"); break; case FRU_SCM: strcpy(name, "scm"); break; case FRU_PIM2: strcpy(name, "pim2"); break; case FRU_PIM3: strcpy(name, "pim3"); break; case FRU_PIM4: strcpy(name, "pim4"); break; case FRU_PIM5: strcpy(name, "pim5"); break; case FRU_PIM6: strcpy(name, "pim6"); break; case FRU_PIM7: strcpy(name, "pim7"); break; case FRU_PIM8: strcpy(name, "pim8"); break; case FRU_PIM9: strcpy(name, "pim9"); break; case FRU_PSU1: strcpy(name, "psu1"); break; case FRU_PSU2: strcpy(name, "psu2"); break; case FRU_PSU3: strcpy(name, "psu3"); break; case FRU_PSU4: strcpy(name, "psu4"); break; case FRU_FAN: strcpy(name, "fan"); break; default: if (fru > MAX_NUM_FRUS) return -1; sprintf(name, "fru%d", fru); break; } return 0; } int pal_get_fru_list(char *list) { strcpy(list, pal_fru_list); return 0; } int pal_get_fru_id(char *str, uint8_t *fru) { if (!strcmp(str, "all")) { *fru = FRU_ALL; } else if (!strcmp(str, "smb")) { *fru = FRU_SMB; } else if (!strcmp(str, "bmc")) { *fru = FRU_BMC; } else if (!strcmp(str, "scm")) { *fru = FRU_SCM; } else if (!strcmp(str, "pim2")) { *fru = FRU_PIM2; } else if (!strcmp(str, "pim3")) { *fru = FRU_PIM3; } else if (!strcmp(str, "pim4")) { *fru = FRU_PIM4; } else if (!strcmp(str, "pim5")) { *fru = FRU_PIM5; } else if (!strcmp(str, "pim6")) { *fru = FRU_PIM6; } else if (!strcmp(str, "pim7")) { *fru = FRU_PIM7; } else if (!strcmp(str, "pim8")) { *fru = FRU_PIM8; } else if (!strcmp(str, "pim9")) { *fru = FRU_PIM9; } else if (!strcmp(str, "psu1")) { *fru = FRU_PSU1; } else if (!strcmp(str, "psu2")) { *fru = FRU_PSU2; } else if (!strcmp(str, "psu3")) { *fru = FRU_PSU3; } else if (!strcmp(str, "psu4")) { *fru = FRU_PSU4; } else if (!strcmp(str, "fan")) { *fru = FRU_FAN; } else { syslog(LOG_WARNING, "pal_get_fru_id: Wrong fru#%s", str); return -1; } return 0; } int pal_get_pim_type(uint8_t fru, int retry) { int ret = -1; char fru_name[16]; struct wedge_eeprom_st eeprom; pal_get_fru_name(fru, fru_name); while ((ret = elbert_eeprom_parse(fru_name, &eeprom)) != 0 && retry--) { msleep(500); } if (ret) { return -1; } // Elbert uses product_asset instead of product_number for SKU field if (strstr(eeprom.fbw_product_asset, "88-16CD2")) { ret = PIM_TYPE_16Q2; } else if (strstr(eeprom.fbw_product_asset, "88-16CD")) { ret = PIM_TYPE_16Q; } else if (strstr(eeprom.fbw_product_asset, "88-8D")) { ret = PIM_TYPE_8DDM; } else { return -1; } return ret; } int pal_set_pim_type_to_file(uint8_t fru, char *type) { char fru_name[16]; char key[MAX_KEY_LEN]; pal_get_fru_name(fru, fru_name); sprintf(key, "%s_type", fru_name); return kv_set(key, type, 0, 0); } int pal_get_pim_type_from_file(uint8_t fru) { char fru_name[16]; char key[MAX_KEY_LEN]; char type[12] = {0}; pal_get_fru_name(fru, fru_name); sprintf(key, "%s_type", fru_name); if (kv_get(key, type, NULL, 0)) { return -1; } if (!strncmp(type, "16q2", sizeof("16q2"))) { return PIM_TYPE_16Q2; } else if (!strncmp(type, "16q", sizeof("16q"))) { return PIM_TYPE_16Q; } else if (!strncmp(type, "8ddm", sizeof("8ddm"))) { return PIM_TYPE_8DDM; } else if (!strncmp(type, "unplug", sizeof("unplug"))) { return PIM_TYPE_UNPLUG; } else { return PIM_TYPE_NONE; } }