ipmi_sdr.c revision 9113a79cf228b8f7bd509b1328adf88659dfe218
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <libipmi.h>
#include <stddef.h>
#include <string.h>
#include "ipmi_impl.h"
typedef struct ipmi_cmd_get_sdr {
uint16_t ic_gs_resid;
uint16_t ic_gs_recid;
uint8_t ic_gs_offset;
uint8_t ic_gs_len;
} ipmi_cmd_get_sdr_t;
typedef struct ipmi_rsp_get_sdr {
uint16_t ir_gs_next;
uint8_t ir_gs_record[1];
} ipmi_rsp_get_sdr_t;
/*
* Issue the "Reserve SDR Repository" command.
*/
static int
ipmi_sdr_reserve_repository(ipmi_handle_t *ihp)
{
ipmi_cmd_t cmd, *rsp;
cmd.ic_netfn = IPMI_NETFN_STORAGE;
cmd.ic_lun = 0;
cmd.ic_cmd = IPMI_CMD_RESERVE_SDR_REPOSITORY;
cmd.ic_dlen = 0;
cmd.ic_data = NULL;
if ((rsp = ipmi_send(ihp, &cmd)) == NULL)
return (-1);
ihp->ih_reservation = *((uint16_t *)rsp->ic_data);
return (0);
}
/*
* Refresh the cache of sensor data records.
*/
static int
ipmi_sdr_refresh(ipmi_handle_t *ihp)
{
size_t len;
uint16_t id;
ipmi_sdr_t *sdr;
ipmi_sdr_cache_ent_t *ent;
ipmi_sdr_generic_locator_t *gen_src, *gen_dst;
ipmi_sdr_fru_locator_t *fru_src, *fru_dst;
/*
* Free any existing SDRs.
*/
while (ihp->ih_sdr_cache != NULL) {
ent = ihp->ih_sdr_cache->isc_next;
ipmi_free(ihp, ent->isc_generic);
ipmi_free(ihp, ent->isc_fru);
ipmi_free(ihp, ent);
ihp->ih_sdr_cache = ent;
}
/*
* Iterate over all existing SDRs and add them to the cache.
*/
id = IPMI_SDR_FIRST;
while (id != IPMI_SDR_LAST) {
if ((sdr = ipmi_sdr_get(ihp, id, &id)) == NULL)
return (-1);
/*
* We currently only understand FRU and generic device records.
*/
if (sdr->is_type != IPMI_SDR_TYPE_GENERIC_LOCATOR &&
sdr->is_type != IPMI_SDR_TYPE_FRU_LOCATOR)
continue;
/*
* Create a copy of the SDR-specific data.
*/
gen_dst = NULL;
fru_dst = NULL;
switch (sdr->is_type) {
case IPMI_SDR_TYPE_GENERIC_LOCATOR:
gen_src = (ipmi_sdr_generic_locator_t *)sdr->is_record;
len = offsetof(ipmi_sdr_generic_locator_t,
is_gl_idstring) + gen_src->is_gl_idlen + 1;
if ((gen_dst = ipmi_alloc(ihp, len)) == NULL)
return (-1);
(void) memcpy(gen_dst, gen_src, len - 1);
((char *)gen_dst)[len - 1] = '\0';
break;
case IPMI_SDR_TYPE_FRU_LOCATOR:
fru_src = (ipmi_sdr_fru_locator_t *)sdr->is_record;
len = offsetof(ipmi_sdr_fru_locator_t,
is_fl_idstring) + fru_src->is_fl_idlen + 1;
if ((fru_dst = ipmi_alloc(ihp, len)) == NULL)
return (-1);
(void) memcpy(fru_dst, fru_src, len - 1);
((char *)fru_dst)[len - 1] = '\0';
break;
}
if ((ent = ipmi_alloc(ihp,
sizeof (ipmi_sdr_cache_ent_t))) == NULL) {
ipmi_free(ihp, gen_dst);
ipmi_free(ihp, fru_dst);
}
ent->isc_generic = gen_dst;
ent->isc_fru = fru_dst;
ent->isc_next = ihp->ih_sdr_cache;
ent->isc_type = sdr->is_type;
ihp->ih_sdr_cache = ent;
}
return (0);
}
ipmi_sdr_t *
ipmi_sdr_get(ipmi_handle_t *ihp, uint16_t id, uint16_t *next)
{
ipmi_cmd_t cmd, *rsp;
ipmi_cmd_get_sdr_t req;
ipmi_rsp_get_sdr_t *sdr;
int i;
req.ic_gs_resid = ihp->ih_reservation;
req.ic_gs_recid = id;
req.ic_gs_offset = 0;
req.ic_gs_len = 0xFF;
cmd.ic_netfn = IPMI_NETFN_STORAGE;
cmd.ic_lun = 0;
cmd.ic_cmd = IPMI_CMD_GET_SDR;
cmd.ic_dlen = sizeof (req);
cmd.ic_data = &req;
for (i = 0; i < ihp->ih_retries; i++) {
if ((rsp = ipmi_send(ihp, &cmd)) == NULL) {
if (ipmi_errno(ihp) != EIPMI_INVALID_RESERVATION)
return (NULL);
if (ipmi_sdr_reserve_repository(ihp) != 0)
return (NULL);
req.ic_gs_resid = ihp->ih_reservation;
}
}
if (rsp == NULL)
return (NULL);
if (rsp->ic_dlen < sizeof (uint16_t) + sizeof (ipmi_sdr_t)) {
(void) ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH, NULL);
return (NULL);
}
sdr = rsp->ic_data;
*next = sdr->ir_gs_next;
return ((ipmi_sdr_t *)sdr->ir_gs_record);
}
ipmi_sdr_fru_locator_t *
ipmi_sdr_lookup_fru(ipmi_handle_t *ihp, const char *idstr)
{
ipmi_sdr_cache_ent_t *ent;
if (ihp->ih_sdr_cache == NULL &&
ipmi_sdr_refresh(ihp) != 0)
return (NULL);
for (ent = ihp->ih_sdr_cache; ent != NULL; ent = ent->isc_next) {
if (ent->isc_type != IPMI_SDR_TYPE_FRU_LOCATOR)
continue;
if (strcmp(ent->isc_fru->is_fl_idstring, idstr) == 0)
return (ent->isc_fru);
}
(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
return (NULL);
}
ipmi_sdr_generic_locator_t *
ipmi_sdr_lookup_generic(ipmi_handle_t *ihp, const char *idstr)
{
ipmi_sdr_cache_ent_t *ent;
if (ihp->ih_sdr_cache == NULL &&
ipmi_sdr_refresh(ihp) != 0)
return (NULL);
for (ent = ihp->ih_sdr_cache; ent != NULL; ent = ent->isc_next) {
if (ent->isc_type != IPMI_SDR_TYPE_GENERIC_LOCATOR)
continue;
if (strcmp(ent->isc_generic->is_gl_idstring, idstr) == 0)
return (ent->isc_generic);
}
(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
return (NULL);
}