#include <libipmi.h>
#include <string.h>
#include "ipmi_impl.h"
#define BITX(u, h, l) (((u) >> (l)) & ((1LU << ((h) - (l) + 1LU)) - 1LU))
#define DEF_CHUNK_SZ 128
#define MIN_CHUNK_SZ 16
typedef struct ipmi_fru_read
{
uint8_t ifr_devid;
uint8_t ifr_offset_lsb;
uint8_t ifr_offset_msb;
uint8_t ifr_count;
} ipmi_fru_read_t;
int
ipmi_fru_read(ipmi_handle_t *ihp, ipmi_sdr_fru_locator_t *fru_loc, char **buf)
{
ipmi_cmd_t cmd, *resp;
int ierrno;
uint8_t count, devid, chunksz;
uint16_t sz, offset = 0;
ipmi_fru_read_t cmd_data_in;
char *tmp;
devid = fru_loc->_devid_or_slaveaddr._logical._is_fl_devid;
cmd.ic_netfn = IPMI_NETFN_STORAGE;
cmd.ic_cmd = IPMI_CMD_GET_FRU_INV_AREA;
cmd.ic_data = &devid;
cmd.ic_dlen = sizeof (uint8_t);
cmd.ic_lun = 0;
if ((resp = ipmi_send(ihp, &cmd)) == NULL)
return (-1);
if (resp->ic_dlen != 3) {
(void) ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH, NULL);
return (-1);
}
(void) memcpy(&sz, resp->ic_data, sizeof (uint16_t));
if ((tmp = malloc(sz)) == NULL) {
(void) ipmi_set_error(ihp, EIPMI_NOMEM, NULL);
return (-1);
}
chunksz = DEF_CHUNK_SZ;
while (offset < sz) {
cmd_data_in.ifr_devid = devid;
cmd_data_in.ifr_offset_lsb = BITX(offset, 7, 0);
cmd_data_in.ifr_offset_msb = BITX(offset, 15, 8);
if ((sz - offset) < chunksz)
cmd_data_in.ifr_count = sz - offset;
else
cmd_data_in.ifr_count = chunksz;
cmd.ic_netfn = IPMI_NETFN_STORAGE;
cmd.ic_cmd = IPMI_CMD_READ_FRU_DATA;
cmd.ic_data = &cmd_data_in;
cmd.ic_dlen = sizeof (ipmi_fru_read_t);
cmd.ic_lun = 0;
if ((resp = ipmi_send(ihp, &cmd)) == NULL) {
ierrno = ipmi_errno(ihp);
if (chunksz > MIN_CHUNK_SZ &&
(ierrno == EIPMI_DATA_LENGTH_EXCEEDED ||
ierrno == EIPMI_INVALID_REQUEST)) {
chunksz = chunksz >> 1;
continue;
}
free(tmp);
return (-1);
}
(void) memcpy(&count, resp->ic_data, sizeof (uint8_t));
if (count != cmd_data_in.ifr_count) {
(void) ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH,
NULL);
free(tmp);
return (-1);
}
(void) memcpy(tmp+offset, (char *)(resp->ic_data)+1, count);
offset += count;
}
*buf = tmp;
return (sz);
}
int
ipmi_fru_parse_product(ipmi_handle_t *ihp, char *fru_area,
ipmi_fru_prod_info_t *buf)
{
ipmi_fru_hdr_t fru_hdr;
char *tmp;
uint8_t len, typelen;
(void) memcpy(&fru_hdr, fru_area, sizeof (ipmi_fru_hdr_t));
if (!fru_hdr.ifh_product_info_off) {
(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
return (-1);
}
tmp = fru_area + (fru_hdr.ifh_product_info_off * 8) + 3;
(void) memcpy(&typelen, tmp, sizeof (uint8_t));
len = BITX(typelen, 5, 0);
ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifpi_manuf_name);
tmp += len + 1;
(void) memcpy(&typelen, tmp, sizeof (uint8_t));
len = BITX(typelen, 5, 0);
ipmi_decode_string((typelen >> 6), len, tmp+1,
buf->ifpi_product_name);
tmp += len + 1;
(void) memcpy(&typelen, tmp, sizeof (uint8_t));
len = BITX(typelen, 5, 0);
ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifpi_part_number);
tmp += len + 1;
(void) memcpy(&typelen, tmp, sizeof (uint8_t));
len = BITX(typelen, 5, 0);
ipmi_decode_string((typelen >> 6), len, tmp+1,
buf->ifpi_product_version);
tmp += len + 1;
(void) memcpy(&typelen, tmp, sizeof (uint8_t));
len = BITX(typelen, 5, 0);
ipmi_decode_string((typelen >> 6), len, tmp+1,
buf->ifpi_product_serial);
tmp += len + 1;
(void) memcpy(&typelen, tmp, sizeof (uint8_t));
len = BITX(typelen, 5, 0);
ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifpi_asset_tag);
return (0);
}
int
ipmi_fru_parse_board(ipmi_handle_t *ihp, char *fru_area,
ipmi_fru_brd_info_t *buf)
{
ipmi_fru_hdr_t fru_hdr;
char *tmp;
uint8_t len, typelen;
(void) memcpy(&fru_hdr, fru_area, sizeof (ipmi_fru_hdr_t));
if (!fru_hdr.ifh_board_info_off) {
(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
return (-1);
}
tmp = fru_area + (fru_hdr.ifh_board_info_off * 8) + 3;
(void) memcpy(buf->ifbi_manuf_date, tmp, 3);
tmp += 3;
(void) memcpy(&typelen, tmp, sizeof (uint8_t));
len = BITX(typelen, 5, 0);
ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifbi_manuf_name);
tmp += len + 1;
(void) memcpy(&typelen, tmp, sizeof (uint8_t));
len = BITX(typelen, 5, 0);
ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifbi_board_name);
tmp += len + 1;
(void) memcpy(&typelen, tmp, sizeof (uint8_t));
len = BITX(typelen, 5, 0);
ipmi_decode_string((typelen >> 6), len, tmp+1,
buf->ifbi_product_serial);
tmp += len + 1;
(void) memcpy(&typelen, tmp, sizeof (uint8_t));
len = BITX(typelen, 5, 0);
ipmi_decode_string((typelen >> 6), len, tmp+1, buf->ifbi_part_number);
return (0);
}