/*
 * sensor.c
 *
 * MontaVista IPMI code for handling sensors
 *
 * Author: MontaVista Software, Inc.
 *         Corey Minyard <minyard@mvista.com>
 *         source@mvista.com
 *
 * Copyright 2002,2003 MontaVista Software Inc.
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public License
 *  as published by the Free Software Foundation; either version 2 of
 *  the License, or (at your option) any later version.
 *
 *
 *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this program; if not, write to the Free
 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <string.h>
#include <stdio.h>
#include <math.h>

#include <OpenIPMI/ipmiif.h>
#include <OpenIPMI/ipmi_sdr.h>
#include <OpenIPMI/ipmi_msgbits.h>
#include <OpenIPMI/ipmi_err.h>

#include <OpenIPMI/internal/locked_list.h>
#include <OpenIPMI/internal/opq.h>
#include <OpenIPMI/internal/ipmi_int.h>
#include <OpenIPMI/internal/ipmi_sensor.h>
#include <OpenIPMI/internal/ipmi_entity.h>
#include <OpenIPMI/internal/ipmi_domain.h>
#include <OpenIPMI/internal/ipmi_mc.h>
#include <OpenIPMI/internal/ipmi_event.h>

struct ipmi_sensor_info_s
{
    int                      destroyed;

    /* Indexed by LUN and sensor # */
    ipmi_sensor_t            **(sensors_by_idx[5]);
    /* Size of above sensor array, per LUN.  This will be 0 if the
       LUN has no sensors. */
    unsigned int             idx_size[5];
    /* In the above two, the 5th index is for non-standard sensors. */

    ipmi_lock_t              *idx_lock;

    /* Total number of sensors we have in this. */
    unsigned int             sensor_count;
};

#define SENSOR_ID_LEN 32 /* 16 bytes are allowed for a sensor. */
struct ipmi_sensor_s
{
    unsigned int  usecount;

    ipmi_domain_t *domain; /* Domain I am in. */

    ipmi_mc_t     *mc; /* My owner, NOT the SMI mc (unless that
                          happens to be my direct owner). */

    ipmi_mc_t     *source_mc; /* If the sensor came from the main SDR,
				 this will be NULL.  Otherwise, it
				 will be the MC that owned the device
				 SDR this came from. */
    int           source_idx; /* The index into the source array where
				 this is stored.  This will be -1 if
				 it does not have a source index (ie
				 it's a non-standard sensor) */
    int           source_recid; /* The SDR record ID the sensor came from. */
    ipmi_sensor_t **source_array; /* This is the source array where
                                     the sensor is stored. */

    int           destroyed;

    /* After the sensor is added, it will not be reported immediately.
       Instead, it will wait until the usecount goes to zero before
       being reported.  This marks that the add report is pending */
    int add_pending;

    unsigned char owner;
    unsigned char channel;
    unsigned char lun;
    unsigned char num;

    /* For OEM sensors, the sending LUN might be different that the
       LUN we use for storage. */
    unsigned char send_lun;

    ipmi_entity_t *entity;

    unsigned char entity_id;
    unsigned char entity_instance;

    /* Can the sensor be read?  Event-only sensors cannot be read.  */
    unsigned int  readable : 1;

    unsigned int  entity_instance_logical : 1;
    unsigned int  sensor_init_scanning : 1;
    unsigned int  sensor_init_events : 1;
    unsigned int  sensor_init_thresholds : 1;
    unsigned int  sensor_init_hysteresis : 1;
    unsigned int  sensor_init_type : 1;
    unsigned int  sensor_init_pu_events : 1;
    unsigned int  sensor_init_pu_scanning : 1;
    unsigned int  ignore_if_no_entity : 1;
    unsigned int  supports_auto_rearm : 1;
    unsigned int  hysteresis_support : 2;
    unsigned int  threshold_access : 2;
    unsigned int  event_support : 2;

    unsigned int sensor_direction : 2;

    unsigned int  ignore_for_presence : 1;

    int          hot_swap_requester;
    int          hot_swap_requester_val;

    unsigned char sensor_type;

    unsigned char event_reading_type;

#define IPMI_SENSOR_GET_MASK_BIT(mask, bit) (((mask) >> (bit)) & 1)
#define IPMI_SENSOR_SET_MASK_BIT(mask, bit, v) \
	(mask) = v ? (mask) | (1 << (bit)) : (mask) & ~(1 << (bit))
    uint16_t mask1;
    uint16_t mask2;
    uint16_t mask3;

    unsigned int  analog_data_format : 2;

    unsigned int  rate_unit : 3;

    unsigned int  modifier_unit_use : 2;

    unsigned int  percentage : 1;

    unsigned char base_unit;
    unsigned char modifier_unit;

    unsigned char linearization;

    struct {
	int m : 10;
	unsigned int tolerance : 6;
	int b : 10;
	int r_exp : 4;
	unsigned int accuracy_exp : 2;
	int accuracy : 10;
	int b_exp : 4;
    } conv[256];

    unsigned int  normal_min_specified : 1;
    unsigned int  normal_max_specified : 1;
    unsigned int  nominal_reading_specified : 1;

    unsigned char nominal_reading;
    unsigned char normal_max;
    unsigned char normal_min;
    unsigned char sensor_max;
    unsigned char sensor_min;
    unsigned char default_thresholds[6];
    unsigned char positive_going_threshold_hysteresis;
    unsigned char negative_going_threshold_hysteresis;


    unsigned char oem1;

    /* Note that the ID is *not* nil terminated. */
    enum ipmi_str_type_e id_type;
    unsigned int id_len;
    char id[SENSOR_ID_LEN]; /* The ID from the device SDR. */

    const char *sensor_type_string;
    const char *event_reading_type_string;
    const char *rate_unit_string;
    const char *base_unit_string;
    const char *modifier_unit_string;

    /* A list of handlers to call when an event for the sensor comes
       in. */
    locked_list_t *handler_list, *handler_list_cl;

    opq_t *waitq;
    ipmi_event_state_t event_state;

    /* Polymorphic functions. */
    ipmi_sensor_cbs_t cbs;

    /* OEM info */
    void                            *oem_info;
    ipmi_sensor_cleanup_oem_info_cb oem_info_cleanup_handler;

    ipmi_sensor_destroy_cb destroy_handler;
    void                   *destroy_handler_cb_data;

    /* Name we use for reporting.  We add a ' ' onto the end, thus
       the +1. */
    char name[IPMI_SENSOR_NAME_LEN+1];

    /* Used for temporary linking. */
    ipmi_sensor_t *tlink;

    /* Cruft. */
    ipmi_sensor_threshold_event_handler_nd_cb threshold_event_handler;
    ipmi_sensor_discrete_event_handler_nd_cb  discrete_event_handler;
    void                         *cb_data;
};

static void sensor_final_destroy(ipmi_sensor_t *sensor);

/***********************************************************************
 *
 * Sensor ID handling.
 *
 **********************************************************************/

/* Must be called with the domain entity lock held. */
int
i_ipmi_sensor_get(ipmi_sensor_t *sensor)
{
    if (sensor->destroyed)
	return EINVAL;
    sensor->usecount++;
    return 0;
}

void
i_ipmi_sensor_put(ipmi_sensor_t *sensor)
{
    ipmi_domain_t *domain = sensor->domain;
    i_ipmi_domain_entity_lock(domain);
    if (sensor->usecount == 1) {
	if (sensor->add_pending) {
	    sensor->add_pending = 0;
	    i_ipmi_domain_entity_unlock(sensor->domain);
	    i_ipmi_entity_call_sensor_handlers(sensor->entity,
					      sensor, IPMI_ADDED);
	    i_ipmi_domain_entity_lock(sensor->domain);
	}
	if (sensor->destroyed
	    && (!sensor->waitq
		|| (!opq_stuff_in_progress(sensor->waitq))))
	{
	    i_ipmi_domain_entity_unlock(domain);
	    sensor_final_destroy(sensor);
	    return;
	}
    }
    sensor->usecount--;
    i_ipmi_domain_entity_unlock(domain);
}

ipmi_sensor_id_t
ipmi_sensor_convert_to_id(ipmi_sensor_t *sensor)
{
    ipmi_sensor_id_t val;

    CHECK_SENSOR_LOCK(sensor);

    val.mcid = ipmi_mc_convert_to_id(sensor->mc);
    val.lun = sensor->lun;
    val.sensor_num = sensor->num;

    return val;
}

int
ipmi_cmp_sensor_id(ipmi_sensor_id_t id1, ipmi_sensor_id_t id2)
{
    int rv;

    rv = ipmi_cmp_mc_id(id1.mcid, id2.mcid);
    if (rv)
	return rv;
    if (id1.lun > id2.lun)
	return 1;
    if (id1.lun < id2.lun)
	return -1;
    if (id1.sensor_num > id2.sensor_num)
	return 1;
    if (id1.sensor_num < id2.sensor_num)
	return -1;
    return 0;
}

void
ipmi_sensor_id_set_invalid(ipmi_sensor_id_t *id)
{
    memset(id, 0, sizeof(*id));
}

int
ipmi_sensor_id_is_invalid(const ipmi_sensor_id_t *id)
{
    return (id->mcid.domain_id.domain == NULL);
}

typedef struct mc_cb_info_s
{
    ipmi_sensor_ptr_cb handler;
    void               *cb_data;
    ipmi_sensor_id_t   id;
    int                err;
} mc_cb_info_t;

static void
mc_cb(ipmi_mc_t *mc, void *cb_data)
{
    mc_cb_info_t       *info = cb_data;
    ipmi_sensor_info_t *sensors;
    ipmi_domain_t      *domain = ipmi_mc_get_domain(mc);
    ipmi_sensor_t      *sensor;
    ipmi_entity_t      *entity = NULL;
    
    sensors = i_ipmi_mc_get_sensors(mc);
    i_ipmi_domain_entity_lock(domain);
    if (info->id.lun > 4) {
	info->err = EINVAL;
	goto out_unlock;
    }

    if (info->id.sensor_num >= sensors->idx_size[info->id.lun]) {
	info->err = EINVAL;
	goto out_unlock;
    }

    sensor = sensors->sensors_by_idx[info->id.lun][info->id.sensor_num];
    if (!sensor) {
	info->err = EINVAL;
	goto out_unlock;
    }

    info->err = i_ipmi_entity_get(sensor->entity);
    if (info->err)
	goto out_unlock;
    entity = sensor->entity;

    info->err = i_ipmi_sensor_get(sensor);
    if (info->err)
	goto out_unlock;

    i_ipmi_domain_entity_unlock(domain);

    info->handler(sensor, info->cb_data);

    i_ipmi_sensor_put(sensor);
    i_ipmi_entity_put(entity);
    return;

 out_unlock:
    i_ipmi_domain_entity_unlock(domain);
    if (entity)
	i_ipmi_entity_put(entity);
}

int
ipmi_sensor_pointer_cb(ipmi_sensor_id_t   id,
		       ipmi_sensor_ptr_cb handler,
		       void               *cb_data)
{
    int          rv;
    mc_cb_info_t info;

    if (id.lun >= 5)
	return EINVAL;

    info.handler = handler;
    info.cb_data = cb_data;
    info.id = id;
    info.err = 0;

    rv = ipmi_mc_pointer_cb(id.mcid, mc_cb, &info);
    if (!rv)
	rv = info.err;

    return rv;
}

int
ipmi_sensor_pointer_noseq_cb(ipmi_sensor_id_t   id,
			     ipmi_sensor_ptr_cb handler,
			     void               *cb_data)
{
    int          rv;
    mc_cb_info_t info;

    if (id.lun >= 5)
	return EINVAL;

    info.handler = handler;
    info.cb_data = cb_data;
    info.id = id;
    info.err = 0;

    rv = ipmi_mc_pointer_noseq_cb(id.mcid, mc_cb, &info);
    if (!rv)
	rv = info.err;

    return rv;
}

typedef struct sensor_find_info_s
{
    ipmi_sensor_id_t id;
    char             *id_name;
    int              rv;
} sensor_find_info_t;

static void
sensor_search_cmp(ipmi_entity_t *entity, ipmi_sensor_t *sensor, void *cb_data)
{
    sensor_find_info_t *info = cb_data;
    char               id[33];
    int                rv;

    rv = ipmi_sensor_get_id(sensor, id, 33);
    if (rv) 
	return;
    if (strcmp(info->id_name, id) == 0) {
	info->id = ipmi_sensor_convert_to_id(sensor);
	info->rv = 0;
    }
}

static void
sensor_search(ipmi_entity_t *entity, void *cb_data)
{
    sensor_find_info_t *info = cb_data;

    ipmi_entity_iterate_sensors(entity, sensor_search_cmp, info);
}

int
ipmi_sensor_find_id(ipmi_domain_id_t domain_id,
		    int entity_id, int entity_instance,
		    int channel, int slave_address,
		    char *id_name,
		    ipmi_sensor_id_t *id)
{
    int                rv;
    ipmi_entity_id_t   entity;
    sensor_find_info_t info;

    rv = ipmi_entity_find_id(domain_id, entity_id, entity_instance,
			     channel, slave_address, &entity);
    if (rv)
	return rv;

    info.id_name = id_name;
    info.rv = EINVAL;

    rv = ipmi_entity_pointer_cb(entity, sensor_search, &info);
    if (!rv)
	rv = info.rv;
    if (!rv)
	*id = info.id;

    return rv;
}

/***********************************************************************
 *
 * Various sensor allocation/deallocation/opq/etc.
 *
 **********************************************************************/

static int
sensor_ok_to_use(ipmi_sensor_t *sensor)
{
    return (   !sensor->destroyed
	    && !i_ipmi_domain_in_shutdown(sensor->domain));
}

static void sensor_set_name(ipmi_sensor_t *sensor);

static void
sensor_opq_ready2(ipmi_sensor_t *sensor, void *cb_data)
{
    ipmi_sensor_op_info_t *info = cb_data;
    if (info->__handler)
	info->__handler(sensor, 0, info->__cb_data);
}

static int
sensor_opq_ready(void *cb_data, int shutdown)
{
    ipmi_sensor_op_info_t *info = cb_data;
    int                   rv;

    if (shutdown) {
	if (info->__handler)
	    info->__handler(info->__sensor, ECANCELED, info->__cb_data);
	return OPQ_HANDLER_STARTED;
    }

    rv = ipmi_sensor_pointer_cb(info->__sensor_id, sensor_opq_ready2, info);
    if (rv)
	if (info->__handler)
	    info->__handler(info->__sensor, rv, info->__cb_data);
    return OPQ_HANDLER_STARTED;
}

int
ipmi_sensor_add_opq(ipmi_sensor_t         *sensor,
		    ipmi_sensor_op_cb     handler,
		    ipmi_sensor_op_info_t *info,
		    void                  *cb_data)
{
    if (sensor->destroyed)
	return EINVAL;

    info->__sensor = sensor;
    info->__sensor_id = ipmi_sensor_convert_to_id(sensor);
    info->__cb_data = cb_data;
    info->__handler = handler;
    if (!opq_new_op(sensor->waitq, sensor_opq_ready, info, 0))
	return ENOMEM;
    return 0;
}

static void
sensor_id_add_opq_cb(ipmi_sensor_t *sensor, void *cb_data)
{
    ipmi_sensor_op_info_t *info = cb_data;

    info->__sensor = sensor;
    if (!opq_new_op(sensor->waitq, sensor_opq_ready, info, 0))
	info->__err = ENOMEM;
}

int
ipmi_sensor_id_add_opq(ipmi_sensor_id_t      sensor_id,
		       ipmi_sensor_op_cb     handler,
		       ipmi_sensor_op_info_t *info,
		       void                  *cb_data)
{
    int rv;

    info->__sensor_id = sensor_id;
    info->__cb_data = cb_data;
    info->__handler = handler;
    info->__err = 0;
    rv = ipmi_sensor_pointer_cb(sensor_id, sensor_id_add_opq_cb, info);
    if (!rv)
	rv = info->__err;
    return rv;
}

void
ipmi_sensor_opq_done(ipmi_sensor_t *sensor)
{
    /* Protect myself from NULL sensors.  This way, it doesn't have to
       be done in each call. */
    if (!sensor)
	return;

    /* This gets called on ECANCELLED error cases, if the sensor is
       already destroyed we need to clear out the opq. */
    if (sensor->destroyed) {
	if (sensor->waitq) {
	    opq_destroy(sensor->waitq);
	    sensor->waitq = NULL;
	}
	return;
    }

    /* No check for the sensor lock.  It will sometimes fail at
       destruction time. */

    opq_op_done(sensor->waitq);
}

static void
sensor_rsp_handler2(ipmi_sensor_t *sensor, void *cb_data)
{
    ipmi_sensor_op_info_t *info = cb_data;

    if (info->__rsp_handler)
	info->__rsp_handler(sensor, 0, info->__rsp, info->__cb_data);
}

static void
sensor_rsp_handler(ipmi_mc_t  *mc,
		   ipmi_msg_t *rsp,
		   void       *rsp_data)
{
    ipmi_sensor_op_info_t *info = rsp_data;
    int                   rv;
    ipmi_sensor_t         *sensor = info->__sensor;
    ipmi_entity_t         *entity = NULL;

    if (sensor->destroyed) {
	i_ipmi_domain_entity_lock(sensor->domain);
	sensor->usecount++;
	rv = i_ipmi_entity_get(sensor->entity);
	if (! rv)
	    entity = sensor->entity;
	i_ipmi_domain_entity_unlock(sensor->domain);

	if (info->__rsp_handler)
	    info->__rsp_handler(sensor, ECANCELED, NULL, info->__cb_data);

	i_ipmi_sensor_put(sensor);
	if (entity)
	    i_ipmi_entity_put(entity);
	return;
    }

    if (!mc) {
	i_ipmi_domain_entity_lock(sensor->domain);
	sensor->usecount++;
	rv = i_ipmi_entity_get(sensor->entity);
	if (! rv)
	    entity = sensor->entity;
	i_ipmi_domain_entity_unlock(sensor->domain);

	if (info->__rsp_handler)
	    info->__rsp_handler(sensor, ECANCELED, rsp, info->__cb_data);

	i_ipmi_sensor_put(sensor);
	if (entity)
	    i_ipmi_entity_put(entity);
	return;
    }

    /* Call the next stage with the lock held. */
    info->__rsp = rsp;
    rv = ipmi_sensor_pointer_cb(info->__sensor_id,
				sensor_rsp_handler2,
				info);
    if (rv) {
	int nrv;

	ipmi_log(IPMI_LOG_ERR_INFO,
		 "%ssensor.c(sensor_rsp_handler):"
		 " Could not convert sensor id to a pointer",
		 MC_NAME(mc));

	i_ipmi_domain_entity_lock(sensor->domain);
	sensor->usecount++;
	nrv = i_ipmi_entity_get(sensor->entity);
	if (! nrv)
	    entity = sensor->entity;
	i_ipmi_domain_entity_unlock(sensor->domain);

	if (info->__rsp_handler)
	    info->__rsp_handler(sensor, rv, NULL, info->__cb_data);

	i_ipmi_sensor_put(sensor);
	if (entity)
	    i_ipmi_entity_put(entity);
    }
}
			 
int
ipmi_sensor_send_command(ipmi_sensor_t         *sensor,
			 ipmi_mc_t             *mc,
			 unsigned int          lun,
			 ipmi_msg_t            *msg,
			 ipmi_sensor_rsp_cb    handler,
			 ipmi_sensor_op_info_t *info,
			 void                  *cb_data)
{
    int rv;

    CHECK_MC_LOCK(mc);
    CHECK_SENSOR_LOCK(sensor);

    if (sensor->destroyed)
	return EINVAL;

    info->__sensor = sensor;
    info->__sensor_id = ipmi_sensor_convert_to_id(sensor);
    info->__cb_data = cb_data;
    info->__rsp_handler = handler;
    rv = ipmi_mc_send_command(mc, lun, msg, sensor_rsp_handler, info);
    return rv;
}

static int
sensor_addr_response_handler(ipmi_domain_t *domain, ipmi_msgi_t *rspi)
{
    ipmi_msg_t            *msg = &rspi->msg;
    ipmi_sensor_op_info_t *info = rspi->data1;
    int                   rv;
    ipmi_sensor_t         *sensor = info->__sensor;

    if (sensor->destroyed) {
	if (info->__rsp_handler) {
	    i_ipmi_domain_mc_lock(sensor->domain);
	    i_ipmi_mc_get(sensor->mc);
	    i_ipmi_domain_mc_unlock(sensor->domain);
	    i_ipmi_domain_entity_lock(sensor->domain);
	    i_ipmi_entity_get(sensor->entity);
	    sensor->usecount++;
	    i_ipmi_domain_entity_unlock(sensor->domain);
	    info->__rsp_handler(NULL, ECANCELED, NULL, info->__cb_data);
	    i_ipmi_sensor_put(sensor);
	    i_ipmi_mc_put(sensor->mc);
	    i_ipmi_entity_put(sensor->entity);
	}
	return IPMI_MSG_ITEM_NOT_USED;
    }

    /* Call the next stage with the lock held. */
    info->__rsp = msg;
    rv = ipmi_sensor_pointer_cb(info->__sensor_id,
				sensor_rsp_handler2,
				info);
    if (rv) {
	ipmi_log(IPMI_LOG_ERR_INFO,
		 "%ssensor.c(sensor_addr_rsp_handler):"
		 " Could not convert sensor id to a pointer",
		 DOMAIN_NAME(domain));
	if (info->__rsp_handler) {
	    i_ipmi_domain_mc_lock(sensor->domain);
	    i_ipmi_mc_get(sensor->mc);
	    i_ipmi_domain_mc_unlock(sensor->domain);
	    i_ipmi_domain_entity_lock(sensor->domain);
	    i_ipmi_entity_get(sensor->entity);
	    sensor->usecount++;
	    i_ipmi_domain_entity_unlock(sensor->domain);
	    info->__rsp_handler(sensor, rv, NULL, info->__cb_data);
	    i_ipmi_sensor_put(sensor);
	    i_ipmi_mc_put(sensor->mc);
	    i_ipmi_entity_put(sensor->entity);
	}
    }
    return IPMI_MSG_ITEM_NOT_USED;
}

int
ipmi_sensor_send_command_addr(ipmi_domain_t         *domain,
			      ipmi_sensor_t         *sensor,
			      ipmi_addr_t           *addr,
			      unsigned int          addr_len,
			      ipmi_msg_t            *msg,
			      ipmi_sensor_rsp_cb    handler,
			      ipmi_sensor_op_info_t *info,
			      void                  *cb_data)
{
    int rv;

    CHECK_SENSOR_LOCK(sensor);
    CHECK_MC_LOCK(sensor->mc);

    info->__sensor = sensor;
    info->__sensor_id = ipmi_sensor_convert_to_id(sensor);
    info->__cb_data = cb_data;
    info->__rsp_handler = handler;
    rv = ipmi_send_command_addr(domain, addr, addr_len,
				msg, sensor_addr_response_handler, info, NULL);
    return rv;
}

int
ipmi_sensors_alloc(ipmi_mc_t *mc, ipmi_sensor_info_t **new_sensors)
{
    ipmi_sensor_info_t *sensors;
    ipmi_domain_t      *domain;
    os_handler_t       *os_hnd;
    int                i;
    int                rv;

    CHECK_MC_LOCK(mc);

    domain = ipmi_mc_get_domain(mc);
    os_hnd = ipmi_domain_get_os_hnd(domain);

    sensors = ipmi_mem_alloc(sizeof(*sensors));
    if (!sensors)
	return ENOMEM;

    rv = ipmi_create_lock_os_hnd(os_hnd, &sensors->idx_lock);
    if (rv) {
	ipmi_mem_free(sensors);
	return rv;
    }

    sensors->destroyed = 0;
    sensors->sensor_count = 0;
    for (i=0; i<5; i++) {
	sensors->sensors_by_idx[i] = NULL;
	sensors->idx_size[i] = 0;
    }

    *new_sensors = sensors;
    return 0;
}

unsigned int
ipmi_sensors_get_count(ipmi_sensor_info_t *sensors)
{
    return sensors->sensor_count;
}

int
ipmi_sensor_alloc_nonstandard(ipmi_sensor_t **new_sensor)
{
    ipmi_sensor_t *sensor;

    sensor = ipmi_mem_alloc(sizeof(*sensor));
    if (!sensor)
	return ENOMEM;

    memset(sensor, 0, sizeof(*sensor));

    sensor->hot_swap_requester = -1;
    sensor->usecount = 1;
    sensor->readable = 1;

    *new_sensor = sensor;
    return 0;
}

int
ipmi_sensor_add_nonstandard(ipmi_mc_t              *mc,
			    ipmi_mc_t              *source_mc,
			    ipmi_sensor_t          *sensor,
			    unsigned int           num,
			    unsigned int           send_lun,
			    ipmi_entity_t          *ent,
			    ipmi_sensor_destroy_cb destroy_handler,
			    void                   *destroy_handler_cb_data)
{
    ipmi_sensor_info_t *sensors = i_ipmi_mc_get_sensors(mc);
    ipmi_domain_t      *domain;
    os_handler_t       *os_hnd;
    void               *link;
    int                err;
    unsigned int       i;

    CHECK_MC_LOCK(mc);
    CHECK_ENTITY_LOCK(ent);

    domain = ipmi_mc_get_domain(mc);
    os_hnd = ipmi_domain_get_os_hnd(domain);

    if ((num >= 256) && (num != UINT_MAX))
	return EINVAL;

    i_ipmi_domain_entity_lock(domain);
    ipmi_lock(sensors->idx_lock);

    if (num == UINT_MAX){
	for (i=0; i<sensors->idx_size[4]; i++) {
	    if (! sensors->sensors_by_idx[4][i])
		break;
	}
	num = i;
	if (num >= 256) {
	    err = EAGAIN;
	    goto out_err;
	}
    }

    if (num >= sensors->idx_size[4]) {
	ipmi_sensor_t **new_array;
	unsigned int  new_size;
	unsigned int  i;

	/* Allocate the array in multiples of 16 (to avoid thrashing malloc
	   too much). */
	new_size = ((num / 16) * 16) + 16;
	new_array = ipmi_mem_alloc(sizeof(*new_array) * new_size);
	if (!new_array) {
	    err = ENOMEM;
	    goto out_err;
	}
	if (sensors->sensors_by_idx[4]) {
	    memcpy(new_array, sensors->sensors_by_idx[4],
		   sizeof(*new_array) * (sensors->idx_size[4]));
	    ipmi_mem_free(sensors->sensors_by_idx[4]);
	}
	for (i=sensors->idx_size[4]; i<new_size; i++)
	    new_array[i] = NULL;
	sensors->sensors_by_idx[4] = new_array;
	sensors->idx_size[4] = new_size;
    }

    sensor->waitq = opq_alloc(os_hnd);
    if (! sensor->waitq) {
	err = ENOMEM;
	goto out_err;
    }

    sensor->handler_list = locked_list_alloc(os_hnd);
    if (! sensor->handler_list) {
	opq_destroy(sensor->waitq);
	err = ENOMEM;
	goto out_err;
    }

    sensor->handler_list_cl = locked_list_alloc(os_hnd);
    if (! sensor->handler_list_cl) {
	locked_list_destroy(sensor->handler_list);
	opq_destroy(sensor->waitq);
	err = ENOMEM;
	goto out_err;
    }

    link = locked_list_alloc_entry();
    if (!link) {
	opq_destroy(sensor->waitq);
	sensor->waitq = NULL;
	locked_list_destroy(sensor->handler_list);
	locked_list_destroy(sensor->handler_list_cl);
	sensor->handler_list = NULL;
	err = ENOMEM;
	goto out_err;
    }

    sensor->domain = domain;
    sensor->mc = mc;
    sensor->source_mc = source_mc;
    sensor->lun = 4;
    sensor->send_lun = send_lun;
    sensor->num = num;
    sensor->source_idx = -1;
    sensor->source_array = NULL;
    if (!sensors->sensors_by_idx[4][num])
	sensors->sensor_count++;
    sensors->sensors_by_idx[4][num] = sensor;
    sensor->entity = ent;
    sensor->entity_id = ipmi_entity_get_entity_id(ent);
    sensor->entity_instance = ipmi_entity_get_entity_instance(ent);
    sensor->destroy_handler = destroy_handler;
    sensor->destroy_handler_cb_data = destroy_handler_cb_data;
    sensor_set_name(sensor);

    ipmi_unlock(sensors->idx_lock);

    i_ipmi_domain_entity_unlock(domain);

    ipmi_entity_add_sensor(ent, sensor, link);

    sensor->add_pending = 1;

    return 0;

 out_err:
    ipmi_unlock(sensors->idx_lock);
    i_ipmi_domain_entity_unlock(domain);
    return err;
}

typedef struct threshold_handler_cl_info_s
{
    ipmi_sensor_threshold_event_cb handler;
    void                           *handler_data;
} threshold_handler_cl_info_t;

static int
iterate_threshold_handler_cl(void *cb_data, void *item1, void *item2)
{
    threshold_handler_cl_info_t       *info = cb_data;
    ipmi_sensor_threshold_event_cl_cb handler = item1;

    handler(info->handler, info->handler_data, item2);
    return LOCKED_LIST_ITER_CONTINUE;
}

typedef struct discrete_handler_cl_info_s
{
    ipmi_sensor_discrete_event_cb handler;
    void                          *handler_data;
} discrete_handler_cl_info_t;

static int
iterate_discrete_handler_cl(void *cb_data, void *item1, void *item2)
{
    discrete_handler_cl_info_t       *info = cb_data;
    ipmi_sensor_discrete_event_cl_cb handler = item1;

    handler(info->handler, info->handler_data, item2);
    return LOCKED_LIST_ITER_CONTINUE;
}

static int
handler_list_cleanup(void *cb_data, void *item1, void *item2)
{
    ipmi_sensor_t *sensor = cb_data;

    if (sensor->event_reading_type == IPMI_EVENT_READING_TYPE_THRESHOLD) {
	threshold_handler_cl_info_t info;
	info.handler = item1;
	info.handler_data = item2;
	locked_list_iterate(sensor->handler_list_cl,
			    iterate_threshold_handler_cl,
			    &info);
    } else {
	discrete_handler_cl_info_t info;
	info.handler = item1;
	info.handler_data = item2;
	locked_list_iterate(sensor->handler_list_cl,
			    iterate_discrete_handler_cl,
			    &info);
    }
    return LOCKED_LIST_ITER_CONTINUE;
}

static void
sensor_final_destroy(ipmi_sensor_t *sensor)
{
    i_ipmi_entity_get(sensor->entity);
    i_ipmi_entity_call_sensor_handlers(sensor->entity, sensor, IPMI_DELETED);

    sensor->mc = NULL;

    if (sensor->destroy_handler)
	sensor->destroy_handler(sensor, sensor->destroy_handler_cb_data);

    if (sensor->waitq)
	opq_destroy(sensor->waitq);

    if (sensor->handler_list) {
	locked_list_iterate(sensor->handler_list, handler_list_cleanup,
			    sensor);
	locked_list_destroy(sensor->handler_list);
    }

    if (sensor->handler_list_cl)
	locked_list_destroy(sensor->handler_list_cl);

    if (sensor->entity)
	ipmi_entity_remove_sensor(sensor->entity, sensor);

    if (sensor->oem_info_cleanup_handler)
	sensor->oem_info_cleanup_handler(sensor, sensor->oem_info);

    i_ipmi_entity_put(sensor->entity);
    ipmi_mem_free(sensor);
}

int
ipmi_sensor_destroy(ipmi_sensor_t *sensor)
{
    ipmi_sensor_info_t *sensors;
    ipmi_mc_t          *mc = sensor->mc;

    i_ipmi_domain_mc_lock(sensor->domain);
    i_ipmi_mc_get(mc);
    i_ipmi_domain_mc_unlock(sensor->domain);
    sensors = i_ipmi_mc_get_sensors(sensor->mc);

    ipmi_lock(sensors->idx_lock);
    if (sensor == sensors->sensors_by_idx[sensor->lun][sensor->num]) {
	sensors->sensor_count--;
	sensors->sensors_by_idx[sensor->lun][sensor->num] = NULL;
    }

    i_ipmi_sensor_get(sensor);

    if (sensor->source_array)
	sensor->source_array[sensor->source_idx] = NULL;

    ipmi_unlock(sensors->idx_lock);

    sensor->destroyed = 1;
    i_ipmi_sensor_put(sensor);
    i_ipmi_mc_put(mc);
    return 0;
}

int
ipmi_sensors_destroy(ipmi_sensor_info_t *sensors)
{
    unsigned int i, j;

    if (sensors->destroyed)
	return EINVAL;

    sensors->destroyed = 1;
    for (i=0; i<=4; i++) {
	for (j=0; j<sensors->idx_size[i]; j++) {
	    if (sensors->sensors_by_idx[i][j]) {
		ipmi_sensor_destroy(sensors->sensors_by_idx[i][j]);
	    }
	}
	if (sensors->sensors_by_idx[i])
	    ipmi_mem_free(sensors->sensors_by_idx[i]);
    }
    if (sensors->idx_lock)
	ipmi_destroy_lock(sensors->idx_lock);
    ipmi_mem_free(sensors);
    return 0;
}

static void
sensor_set_name(ipmi_sensor_t *sensor)
{
    int length;

    length = ipmi_entity_get_name(sensor->entity, sensor->name,
				  sizeof(sensor->name)-2);
    sensor->name[length] = '.';
    length++;
    length += snprintf(sensor->name+length, IPMI_SENSOR_NAME_LEN-length-2,
		       "%s", sensor->id);
    sensor->name[length] = ' ';
    length++;
    sensor->name[length] = '\0';
    length++;
}

const char *
i_ipmi_sensor_name(const ipmi_sensor_t *sensor)
{
    return sensor->name;
}

int
ipmi_sensor_get_name(ipmi_sensor_t *sensor, char *name, int length)
{
    int  slen;

    if (length <= 0)
	return 0;

    /* Never changes, no lock needed. */
    slen = strlen(sensor->name);
    if (slen == 0) {
	if (name)
	    *name = '\0';
	goto out;
    }

    slen -= 1; /* Remove the trailing ' ' */
    if (slen >= length)
	slen = length - 1;

    if (name) {
	memcpy(name, sensor->name, slen);
	name[slen] = '\0';
    }
 out:
    return slen;
}

/***********************************************************************
 *
 * Sensor SDR handling
 *
 **********************************************************************/

static int
get_sensors_from_sdrs(ipmi_domain_t      *domain,
		      ipmi_mc_t          *source_mc,
		      ipmi_sdr_info_t    *sdrs,
		      ipmi_sensor_t      ***sensors,
		      unsigned int       *sensor_count)
{
    ipmi_sdr_t    sdr;
    unsigned int  count;
    ipmi_sensor_t **s = NULL;
    unsigned int  p, s_size = 0;
    int           val;
    int           rv;
    unsigned int  i;
    int           j;
    int           share_count;
    int           id_string_mod_type;
    int           entity_instance_incr;
    int           id_string_modifier_offset;
    unsigned char *str;
    unsigned int  str_len;
    

    rv = ipmi_get_sdr_count(sdrs, &count);
    if (rv) {
	ipmi_log(IPMI_LOG_WARNING,
		 "%ssensor.c(get_sensors_from_sdrs):"
		 " Could not fetch SDR count fron the SDR record.",
		 MC_NAME(source_mc));
	goto out_err;
    }
    
    /* Get a real count on the number of sensors, since a single SDR can
       contain multiple sensors. */
    p = 0;
    for (i=0; i<count; i++) {
	int incr;

	rv = ipmi_get_sdr_by_index(sdrs, i, &sdr);
	if (rv) {
	    ipmi_log(IPMI_LOG_WARNING,
		     "%ssensor.c(get_sensors_from_sdrs):"
		     " SDR record %d could not be fetched from the SDR"
		     " record: %d",
		     MC_NAME(source_mc), i, rv);
	    goto out_err;
	}

	if (sdr.type == 1) {
	    incr = 1;
	} else if (sdr.type == 2) {
	    if (sdr.data[18] & 0x0f)
		incr = sdr.data[18] & 0x0f;
	    else
		incr = 1;
	} else if (sdr.type == 3) {
	    if (sdr.data[7] & 0x0f)
		incr = sdr.data[7] & 0x0f;
	    else
		incr = 1;
	} else
	    continue;

	p += incr;
    }
    if (!p)
	return 0;

    /* Setup memory to hold the sensors. */
    s = ipmi_mem_alloc(sizeof(*s) * p);
    if (!s)
	goto out_err_enomem;
    s_size = p;
    memset(s, 0, sizeof(*s) * p);

    p = 0;
    for (i=0; i<count; i++) {
	rv = ipmi_get_sdr_by_index(sdrs, i, &sdr);
	if (rv) {
	    ipmi_log(IPMI_LOG_WARNING,
		     "%ssensor.c(get_sensors_from_sdrs):"
		     " SDR record %d could not be fetched from the SDR"
		     " record index: %d (2)",
		     MC_NAME(source_mc), i, rv);
	    goto out_err;
	}

	if ((sdr.type != 1) && (sdr.type != 2) && (sdr.type != 3))
	    continue;

	s[p] = ipmi_mem_alloc(sizeof(*s[p]));
	if (!s[p])
	    goto out_err_enomem;
	memset(s[p], 0, sizeof(*s[p]));

	s[p]->source_recid = sdr.record_id;
	s[p]->hot_swap_requester = -1;

	s[p]->waitq = opq_alloc(ipmi_domain_get_os_hnd(domain));
	if (!s[p]->waitq)
	    goto out_err_enomem;

	s[p]->handler_list_cl
	    = locked_list_alloc(ipmi_domain_get_os_hnd(domain));
	if (! s[p]->handler_list_cl) {
	    opq_destroy(s[p]->waitq);
	    goto out_err_enomem;
	}

	s[p]->handler_list = locked_list_alloc(ipmi_domain_get_os_hnd(domain));
	if (! s[p]->handler_list) {
	    locked_list_destroy(s[i]->handler_list_cl);
	    opq_destroy(s[p]->waitq);
	    goto out_err_enomem;
	}

	s[p]->destroyed = 0;
	s[p]->destroy_handler = NULL;

	rv = i_ipmi_find_or_create_mc_by_slave_addr(domain,
						    sdr.data[1] >> 4,
						    sdr.data[0],
						    &(s[p]->mc));
	if (rv) {
	    ipmi_log(IPMI_LOG_WARNING,
		     "%ssensor.c(get_sensors_from_sdrs):"
		     " Could not create MC for SDR record %d, channel %d"
		     " owner 0x%x: %d",
		     MC_NAME(source_mc), i, sdr.data[1] >> 4, sdr.data[0], rv);
	    goto out_err;
	}

	share_count = 0;
	id_string_mod_type = 0;
	entity_instance_incr = 0;
	id_string_modifier_offset = 0;

	s[p]->usecount = 1;
	s[p]->domain = domain;
	s[p]->source_mc = source_mc;
	s[p]->source_idx = p;
	s[p]->source_array = s;
	s[p]->owner = sdr.data[0];
	s[p]->channel = sdr.data[1] >> 4;
	s[p]->lun = sdr.data[1] & 0x03;
	s[p]->send_lun = s[p]->lun;
	s[p]->num = sdr.data[2];
	s[p]->entity_id = sdr.data[3];
	s[p]->entity_instance_logical = sdr.data[4] >> 7;
	s[p]->entity_instance = sdr.data[4] & 0x7f;
	if ((sdr.type == 1) || (sdr.type == 2)) {
	    s[p]->readable = 1;

	    s[p]->sensor_init_scanning = (sdr.data[5] >> 6) & 1;
	    s[p]->sensor_init_events = (sdr.data[5] >> 5) & 1;
	    s[p]->sensor_init_thresholds = (sdr.data[5] >> 4) & 1;
	    s[p]->sensor_init_hysteresis = (sdr.data[5] >> 3) & 1;
	    s[p]->sensor_init_type = (sdr.data[5] >> 2) & 1;
	    s[p]->sensor_init_pu_events = (sdr.data[5] >> 1) & 1;
	    s[p]->sensor_init_pu_scanning = (sdr.data[5] >> 0) & 1;
	    s[p]->ignore_if_no_entity = (sdr.data[6] >> 7) & 1;
	    s[p]->supports_auto_rearm = (sdr.data[6] >> 6) & 1 ;
	    s[p]->hysteresis_support = (sdr.data[6] >> 4) & 3;
	    s[p]->threshold_access = (sdr.data[6] >> 2) & 3;
	    s[p]->event_support = sdr.data[6] & 3;
	    s[p]->sensor_type = sdr.data[7];
	    s[p]->event_reading_type = sdr.data[8];

	    s[p]->mask1 = ipmi_get_uint16(sdr.data+9);
	    s[p]->mask2 = ipmi_get_uint16(sdr.data+11);
	    s[p]->mask3 = ipmi_get_uint16(sdr.data+13);

	    s[p]->analog_data_format = (sdr.data[15] >> 6) & 3;
	    s[p]->rate_unit = (sdr.data[15] >> 3) & 7;
	    s[p]->modifier_unit_use = (sdr.data[15] >> 1) & 3;
	    s[p]->percentage = sdr.data[15] & 1;
	    s[p]->base_unit = sdr.data[16];
	    s[p]->modifier_unit = sdr.data[17];
	}

	if (sdr.type == 1) {
	    /* A full sensor record. */
	    s[p]->linearization = sdr.data[18] & 0x7f;

	    if (s[p]->linearization <= 11) {
		for (j=0; j<256; j++) {
		    s[p]->conv[j].m = sdr.data[19] | ((sdr.data[20] & 0xc0) << 2);
		    s[p]->conv[j].tolerance = sdr.data[20] & 0x3f;
		    s[p]->conv[j].b = sdr.data[21] | ((sdr.data[22] & 0xc0) << 2);
		    s[p]->conv[j].accuracy = ((sdr.data[22] & 0x3f)
					     | ((sdr.data[23] & 0xf0) << 2));
		    s[p]->conv[j].accuracy_exp = (sdr.data[23] >> 2) & 0x3;
		    s[p]->conv[j].r_exp = (sdr.data[24] >> 4) & 0xf;
		    s[p]->conv[j].b_exp = sdr.data[24] & 0xf;
		}
	    }

	    s[p]->sensor_direction = sdr.data[23] & 0x3;
	    s[p]->normal_min_specified = (sdr.data[25] >> 2) & 1;
	    s[p]->normal_max_specified = (sdr.data[25] >> 1) & 1;
	    s[p]->nominal_reading_specified = sdr.data[25] & 1;
	    s[p]->nominal_reading = sdr.data[26];
	    s[p]->normal_max = sdr.data[27];
	    s[p]->normal_min = sdr.data[28];
	    s[p]->sensor_max = sdr.data[29];
	    s[p]->sensor_min = sdr.data[30];
	    s[p]->default_thresholds[IPMI_UPPER_NON_RECOVERABLE]= sdr.data[31];
	    s[p]->default_thresholds[IPMI_UPPER_CRITICAL] = sdr.data[32];
	    s[p]->default_thresholds[IPMI_UPPER_NON_CRITICAL] = sdr.data[33];
	    s[p]->default_thresholds[IPMI_LOWER_NON_RECOVERABLE] = sdr.data[34];
	    s[p]->default_thresholds[IPMI_LOWER_CRITICAL] = sdr.data[35];
	    s[p]->default_thresholds[IPMI_LOWER_NON_CRITICAL] = sdr.data[36];
	    s[p]->positive_going_threshold_hysteresis = sdr.data[37];
	    s[p]->negative_going_threshold_hysteresis = sdr.data[38];
	    s[p]->oem1 = sdr.data[41];

	    str = sdr.data + 42;
	    str_len = sdr.length - 42;

	    if (s[p]->entity)
		sensor_set_name(s[p]);
	} else if (sdr.type == 2) {
	    /* FIXME - make sure this is not a threshold sensor.  The
               question is, what do I do if it is? */
	    /* A short sensor record. */

	    s[p]->sensor_direction = (sdr.data[18] >> 6) & 0x3;

	    s[p]->positive_going_threshold_hysteresis = sdr.data[20];
	    s[p]->negative_going_threshold_hysteresis = sdr.data[21];
	    s[p]->oem1 = sdr.data[25];

	    str = sdr.data + 26;
	    str_len = sdr.length - 26;

	    share_count = sdr.data[18] & 0x0f;
	    if (share_count == 0)
		share_count = 1;
	    id_string_mod_type = (sdr.data[18] >> 4) & 0x3;
	    entity_instance_incr = (sdr.data[19] >> 7) & 0x01;
	    id_string_modifier_offset = sdr.data[19] & 0x7f;
	} else {
	    /* Event-only sensor.  It is not readable. */

	    s[p]->sensor_type = sdr.data[5];
	    s[p]->event_reading_type = sdr.data[6];
	    s[p]->oem1 = sdr.data[9];

	    str = sdr.data + 10;
	    str_len = sdr.length - 10;

	    share_count = sdr.data[7] & 0x0f;
	    if (share_count == 0)
		share_count = 1;
	    id_string_mod_type = (sdr.data[7] >> 4) & 0x3;
	    entity_instance_incr = (sdr.data[8] >> 7) & 0x01;
	    id_string_modifier_offset = sdr.data[8] & 0x7f;
	}

	rv = ipmi_get_device_string(&str, str_len,
				    s[p]->id, IPMI_STR_SDR_SEMANTICS, 0,
				    &s[p]->id_type, SENSOR_ID_LEN,
				    &s[p]->id_len);
	if (rv) {
	    ipmi_log(IPMI_LOG_WARNING,
		     "%ssensor.c(get_sensors_from_sdrs):"
		     " Error getting device ID string from SDR record %d: %d,"
		     " this sensor will be named **INVALID**",
		     MC_NAME(source_mc), sdr.record_id, rv);
	    strncpy(s[p]->id, "**INVALID**", sizeof(s[p]->id));
	    s[p]->id_len = strlen(s[p]->id);
	    s[p]->id_type = IPMI_ASCII_STR;
	}

	if (share_count > 1) {
	    /* Duplicate the sensor records for each instance.  Go
	       backwards to avoid destroying the first one until we
	       finish the others. */
	    for (j=share_count-1; j>=0; j--) {
		int len;

		if (j != 0) {
		    /* The first one is already allocated, we are
                       using it to copy to the other ones, so this is
                       not necessary.  We still have to iterate the
                       first one to set its string name, though. */
		    s[p+j] = ipmi_mem_alloc(sizeof(ipmi_sensor_t));
		    if (!s[p+j])
			goto out_err_enomem;
		    memcpy(s[p+j], s[p], sizeof(ipmi_sensor_t));
		    
		    /* In case of error */
		    s[p+j]->handler_list = NULL;

		    /* For every sensor except the first, increment the usage
		       count for the MC so that it will decrement properly.
		       This cannot fail because we have already gotten it
		       before. */
		    i_ipmi_find_or_create_mc_by_slave_addr(domain,
							   s[p+j]->channel,
							   s[p+j]->owner,
							   &(s[p+j]->mc));

		    s[p+j]->waitq = opq_alloc(ipmi_domain_get_os_hnd(domain));
		    if (!s[p+j]->waitq)
			goto out_err_enomem;

		    s[p+j]->handler_list_cl
			= locked_list_alloc(ipmi_domain_get_os_hnd(domain));
		    if (! s[p+j]->handler_list_cl)
			goto out_err_enomem;

		    s[p+j]->handler_list
			= locked_list_alloc(ipmi_domain_get_os_hnd(domain));
		    if (! s[p+j]->handler_list)
			goto out_err_enomem;

		    s[p+j]->num += j;

		    if (entity_instance_incr & 0x80) {
			s[p+j]->entity_instance += j;
		    }

		    s[p+j]->source_idx += j;
		}

		val = id_string_modifier_offset + j;
		len = s[p+j]->id_len;
		switch (id_string_mod_type) {
		    case 0: /* Numeric */
			if ((val / 10) > 0) {
			    if (len < SENSOR_ID_LEN) {
				s[p+j]->id[len] = (val/10) + '0';
				len++;
			    }
			}
			if (len < SENSOR_ID_LEN) {
			    s[p+j]->id[len] = (val%10) + '0';
			    len++;
			}
			break;
		    case 1: /* Alpha */
			if ((val / 26) > 0) {
			    if (len < SENSOR_ID_LEN) {
				s[p+j]->id[len] = (val/26) + 'A';
				len++;
			    }
			}
			if (len < SENSOR_ID_LEN) {
			    s[p+j]->id[len] = (val%26) + 'A';
			    len++;
			}
			break;
		    /* FIXME - unicode handling? */
		}
		s[p+j]->id_len = len;
		if (s[p+j]->entity)
		    sensor_set_name(s[p+j]);
	    }

	    p += share_count;
	} else
	    p++;
    }

    *sensors = s;
    *sensor_count = s_size;
    return 0;

 out_err_enomem:
    rv = ENOMEM;
    ipmi_log(IPMI_LOG_WARNING,
	     "%ssensor.c(get_sensors_from_sdrs):"
	     " Out of memory while processing the SDRS.",
	     MC_NAME(source_mc));
 out_err:
    if (s) {
	for (i=0; i<s_size; i++)
	    if (s[i]) {
		if (s[i]->mc)
		    i_ipmi_mc_put(s[i]->mc);
		if (s[i]->waitq)
		    opq_destroy(s[i]->waitq);
		if (s[i]->handler_list)
		    locked_list_destroy(s[i]->handler_list);
		if (s[i]->handler_list_cl)
		    locked_list_destroy(s[i]->handler_list_cl);
		ipmi_mem_free(s[i]);
	    }
	ipmi_mem_free(s);
    }
    return rv;
}

static void
handle_new_sensor(ipmi_domain_t *domain,
		  ipmi_sensor_t *sensor,
		  void          *link)
{
    /* Call this before the OEM call so the OEM call can replace it. */
    sensor->cbs = ipmi_standard_sensor_cb;
    sensor->sensor_type_string
	= ipmi_get_sensor_type_string(sensor->sensor_type);
    sensor->event_reading_type_string
	= ipmi_get_event_reading_type_string(sensor->event_reading_type);
    sensor->rate_unit_string
	= ipmi_get_rate_unit_string(sensor->rate_unit);
    sensor->base_unit_string
	= ipmi_get_unit_type_string(sensor->base_unit);
    sensor->modifier_unit_string
	= ipmi_get_unit_type_string(sensor->modifier_unit);

    sensor_set_name(sensor);

    if ((sensor->source_mc)
	&& (i_ipmi_mc_new_sensor(sensor->source_mc, sensor->entity,
				 sensor, link)))
    {
        /* Nothing to do, OEM code handled the sensor. */
    } else {
	ipmi_entity_add_sensor(sensor->entity, sensor, link);
    }

    i_call_new_sensor_handlers(domain, sensor);
}

static int cmp_sensor(ipmi_sensor_t *s1,
		      ipmi_sensor_t *s2)
{
    int i;

    if (s1->entity_instance_logical != s2->entity_instance_logical) return 0;
    if (s1->sensor_init_scanning != s2->sensor_init_scanning) return 0;
    if (s1->sensor_init_events != s2->sensor_init_events) return 0;
    if (s1->sensor_init_thresholds != s2->sensor_init_thresholds) return 0;
    if (s1->sensor_init_hysteresis != s2->sensor_init_hysteresis) return 0;
    if (s1->sensor_init_type != s2->sensor_init_type) return 0;
    if (s1->sensor_init_pu_events != s2->sensor_init_pu_events) return 0;
    if (s1->sensor_init_pu_scanning != s2->sensor_init_pu_scanning) return 0;
    if (s1->ignore_if_no_entity != s2->ignore_if_no_entity) return 0;
    if (s1->supports_auto_rearm != s2->supports_auto_rearm) return 0;
    if (s1->hysteresis_support != s2->hysteresis_support) return 0;
    if (s1->threshold_access != s2->threshold_access) return 0;
    if (s1->event_support != s2->event_support) return 0;
    if (s1->sensor_type != s2->sensor_type) return 0;
    if (s1->event_reading_type != s2->event_reading_type) return 0;

    if (s1->mask1 != s2->mask1) return 0;
    if (s1->mask2 != s2->mask2) return 0;
    if (s1->mask3 != s2->mask3) return 0;
    
    if (s1->analog_data_format != s2->analog_data_format) return 0;
    if (s1->rate_unit != s2->rate_unit) return 0;
    if (s1->modifier_unit_use != s2->modifier_unit_use) return 0;
    if (s1->percentage != s2->percentage) return 0;
    if (s1->base_unit != s2->base_unit) return 0;
    if (s1->modifier_unit != s2->modifier_unit) return 0;
    if (s1->linearization != s2->linearization) return 0;
    if (s1->linearization <= 11) {
	if (s1->conv[0].m != s2->conv[0].m) return 0;
	if (s1->conv[0].tolerance != s2->conv[0].tolerance) return 0;
	if (s1->conv[0].b != s2->conv[0].b) return 0;
	if (s1->conv[0].accuracy != s2->conv[0].accuracy) return 0;
	if (s1->conv[0].accuracy_exp != s2->conv[0].accuracy_exp) return 0;
	if (s1->conv[0].r_exp != s2->conv[0].r_exp) return 0;
	if (s1->conv[0].b_exp != s2->conv[0].b_exp) return 0;
    }
    if (s1->normal_min_specified != s2->normal_min_specified) return 0;
    if (s1->normal_max_specified != s2->normal_max_specified) return 0;
    if (s1->nominal_reading_specified != s2->nominal_reading_specified) return 0;
    if (s1->nominal_reading != s2->nominal_reading) return 0;
    if (s1->normal_max != s2->normal_max) return 0;
    if (s1->normal_min != s2->normal_min) return 0;
    if (s1->sensor_max != s2->sensor_max) return 0;
    if (s1->sensor_min != s2->sensor_min) return 0;
    for (i=0; i<6; i++) {
	if (s1->default_thresholds[i] != s2->default_thresholds[i])
	    return 0;
    }
    if (s1->positive_going_threshold_hysteresis
	!= s2->positive_going_threshold_hysteresis)
	return 0;
    if (s1->negative_going_threshold_hysteresis
	!= s2->negative_going_threshold_hysteresis)
	return 0;
    if (s1->oem1 != s2->oem1) return 0;

    if (s1->id_type != s2->id_type) return 0;
    if (s1->id_len != s2->id_len) return 0;
    if (memcmp(s1->id, s2->id, s1->id_len) != 0) return 0;
    
    return 1;
}

enum entity_list_op { ENT_LIST_OLD, ENT_LIST_NEW, ENT_LIST_DUP };
typedef struct entity_list_s
{
    ipmi_entity_t        *ent;
    ipmi_sensor_t        *sensor;
    ipmi_sensor_t        *osensor;
    ipmi_mc_t            *mc;
    enum entity_list_op  op;
    struct entity_list_s *next;
} entity_list_t;

/* Assume it has enough space for one pointer. */
struct locked_list_entry_s
{
    locked_list_entry_t *next;
};

int
ipmi_sensor_handle_sdrs(ipmi_domain_t   *domain,
			ipmi_mc_t       *source_mc,
			ipmi_sdr_info_t *sdrs)
{
    int                 rv;
    unsigned int        i, j;
    ipmi_sensor_t       **sdr_sensors = NULL;
    ipmi_sensor_t       **old_sdr_sensors;
    unsigned int        old_count;
    unsigned int        count = 0;
    ipmi_entity_info_t  *ents;
    ipmi_entity_t       *ent;
    entity_list_t       *new_sensors = NULL;
    entity_list_t       *del_sensors = NULL;
    entity_list_t       *ent_item;
    entity_list_t       *new_ent_item;
    locked_list_entry_t *link, *links = NULL;
    ipmi_sensor_t       **sens_tmp;
    

    CHECK_DOMAIN_LOCK(domain);
    if (source_mc)
	CHECK_MC_LOCK(source_mc);

    rv = get_sensors_from_sdrs(domain, source_mc, sdrs, &sdr_sensors, &count);
    if (rv)
	goto out_err;

    ents = ipmi_domain_get_entities(domain);

    /* Pre-allocate all the links we will need for registering sensors
       with the entities, and we make sure all the entities exist. */
    for (i=0; i<count; i++) {
	ipmi_sensor_t      *nsensor = sdr_sensors[i];

	ent = NULL;

	if (nsensor != NULL) {
	    ipmi_sensor_info_t *sensors;

	    /* Make sure the entity exists for ALL sensors in the
	       new list.  This way, if a sensor has changed
	       entities, the new entity will exist. */
	    rv = ipmi_entity_add(ents,
				 domain,
				 ipmi_mc_get_channel(nsensor->mc),
				 ipmi_mc_get_address(nsensor->mc),
				 i,
				 nsensor->entity_id,
				 nsensor->entity_instance,
				 "",
				 IPMI_ASCII_STR,
				 0,
				 NULL,
				 NULL,
				 &ent);
	    if (rv)
		goto out_err_free;

	    nsensor->entity = ent;

	    sensors = i_ipmi_mc_get_sensors(nsensor->mc);

	    ipmi_lock(sensors->idx_lock);
	    if (nsensor->num >= sensors->idx_size[nsensor->lun]) {
		/* There's not enough room in the sensor repository
		   for the new item, so expand the array. */
		ipmi_sensor_t **new_by_idx;
		unsigned int  new_size = nsensor->num+10;
		new_by_idx = ipmi_mem_alloc(sizeof(ipmi_sensor_t *) * new_size);
		if (!new_by_idx) {
		    ipmi_unlock(sensors->idx_lock);
		    rv = ENOMEM;
		    i_ipmi_entity_put(ent);
		    goto out_err_free;
		}
		if (sensors->sensors_by_idx[nsensor->lun]) {
		    memcpy(new_by_idx,
			   sensors->sensors_by_idx[nsensor->lun],
			   (sensors->idx_size[nsensor->lun]
			    * sizeof(ipmi_sensor_t *)));
		    ipmi_mem_free(sensors->sensors_by_idx[nsensor->lun]);
		}
		for (j=sensors->idx_size[nsensor->lun]; j<new_size; j++)
		    new_by_idx[j] = NULL;
		sensors->sensors_by_idx[nsensor->lun] = new_by_idx;
		sensors->idx_size[nsensor->lun] = new_size;
	    }
	    ipmi_unlock(sensors->idx_lock);

	    /* Keep track of each entity/sensor pair. */
	    new_ent_item = ipmi_mem_alloc(sizeof(*new_ent_item));
	    if (!new_ent_item) {
		i_ipmi_entity_put(ent);
		goto out_err_free;
	    }
	    new_ent_item->ent = ent;
	    new_ent_item->sensor = nsensor;
	    new_ent_item->osensor = NULL;
	    new_ent_item->mc = nsensor->mc;
	    new_ent_item->op = ENT_LIST_OLD;
	    new_ent_item->next = new_sensors;
	    new_sensors = new_ent_item;

	    /* Pre-allocate link entries for putting the sensor into
	       the entity. */
	    link = locked_list_alloc_entry();
	    if (!link)
		goto out_err_free;
	    link->next = links;
	    links = link;
	}
    }

    /* Check for duplicate sensor numbers in the new sensor set. */
    sens_tmp = ipmi_mem_alloc(256 * sizeof(ipmi_sensor_t **));
    if (!sens_tmp) {
	rv = ENOMEM;
	goto out_err_free;
    }
    memset(sens_tmp, 0, 256 * sizeof(ipmi_sensor_t **));
    ent_item = new_sensors;
    while (ent_item) {
	ipmi_sensor_t *nsensor = ent_item->sensor;
	ipmi_sensor_t *csensor;

	if ((!ent_item->ent) || (!nsensor)) {
	    ent_item = ent_item->next;
	    continue;
	}

	csensor = sens_tmp[nsensor->num];
	while (csensor) {
	    if ((csensor->lun == nsensor->lun)
		&& (csensor->num == nsensor->num)
		&& (csensor->mc == nsensor->mc))
	    {
		break;
	    }
	    csensor = csensor->tlink;
	}
	if (csensor) {
	    ipmi_log(IPMI_LOG_WARNING,
		     "%ssensor.c(ipmi_sensor_handle_sdrs):"
		     " SDR record %d has the same sensor number as record"
		     " %d in the repository.  Ignoring second sensor."
		     " Fix your SDRs!",
		     SENSOR_NAME(nsensor),
		     csensor->source_recid,
		     nsensor->source_recid);
	    ent_item->op = ENT_LIST_DUP;
	    ent_item->osensor = NULL;
	} else {
	    nsensor->tlink = sens_tmp[nsensor->num];
	    sens_tmp[nsensor->num] = nsensor;
	}
	ent_item = ent_item->next;
    }
    ipmi_mem_free(sens_tmp);

    i_ipmi_domain_entity_lock(domain);

    i_ipmi_get_sdr_sensors(domain, source_mc,
			   &old_sdr_sensors, &old_count);

    ent_item = new_sensors;
    while (ent_item) {
	ipmi_sensor_t      *nsensor = ent_item->sensor;
	ipmi_sensor_info_t *sensors;

	if ((!ent_item->ent) || (!nsensor) || (ent_item->op == ENT_LIST_DUP)) {
	    ent_item = ent_item->next;
	    continue;
	}

	sensors = i_ipmi_mc_get_sensors(nsensor->mc);
	ipmi_lock(sensors->idx_lock);
	if (sensors->sensors_by_idx[nsensor->lun]
	    && (nsensor->num < sensors->idx_size[nsensor->lun])
	    && sensors->sensors_by_idx[nsensor->lun][nsensor->num])
	{
	    /* A sensor is already there. */
	    ipmi_sensor_t *osensor
		= sensors->sensors_by_idx[nsensor->lun][nsensor->num];

	    if (cmp_sensor(nsensor, osensor)) {
		/* Since the data is the same, there is no need to get
		   the old sensor entity or mc, since they are already
		   gotten by the new sensor. */
		ent_item->op = ENT_LIST_DUP;
		ent_item->osensor = osensor;
	    } else {
		/* We have to delete the old sensor. */
		new_ent_item = ipmi_mem_alloc(sizeof(*new_ent_item));
		if (!new_ent_item) {
		    ipmi_unlock(sensors->idx_lock);
		    rv = ENOMEM;
		    goto out_err_free_unlock;
		}
		/* It's possible this can fail, but that means that
		   the MC is currently being destroyed.  No big deal,
		   just ignore it. */
		new_ent_item->mc = NULL;
		i_ipmi_find_or_create_mc_by_slave_addr(domain,
						       osensor->channel,
						       osensor->owner,
						       &new_ent_item->mc);
		i_ipmi_entity_get(osensor->entity);
		i_ipmi_sensor_get(osensor);
		new_ent_item->ent = osensor->entity;
		new_ent_item->sensor = osensor;
		new_ent_item->next = del_sensors;
		del_sensors = new_ent_item;
		ent_item->op = ENT_LIST_NEW;
	    }
	} else {
	    ent_item->op = ENT_LIST_NEW;
	}
	ipmi_unlock(sensors->idx_lock);

	ent_item = ent_item->next;
    }

    i_ipmi_domain_entity_unlock(domain);

    /* After this point, the operation cannot fail.  Nothing above
       this actually changes anything, it just gets it ready.  Now we
       put into place all the changes. */

    /* First delete the sensors that we are replacing. */
    ent_item = del_sensors;
    while (ent_item) {
	ipmi_sensor_t *osensor = ent_item->sensor;

	if (osensor->source_array) {
	    osensor->source_array[osensor->source_idx] = NULL;
	    osensor->source_array = NULL;
	}
	/* Note that the actual destroy is deferred until we put the
	   sensor. */
	ipmi_sensor_destroy(osensor);

	ent_item = ent_item->next;
    }

    ent_item = new_sensors;
    while (ent_item) {
	ipmi_sensor_t      *nsensor = ent_item->sensor;
	ipmi_sensor_t      *osensor = ent_item->osensor;
	ipmi_sensor_info_t *sensors;

	if ((!ent_item->ent) || (!nsensor)) {
	    ent_item = ent_item->next;
	    continue;
	}

	sensors = i_ipmi_mc_get_sensors(nsensor->mc);
	ipmi_lock(sensors->idx_lock);
	switch (ent_item->op) {
	case ENT_LIST_NEW:
	    sensors->sensors_by_idx[nsensor->lun][nsensor->num] = nsensor;
	    sensors->sensor_count++;
	    link = links;
	    links = link->next;
	    handle_new_sensor(domain, nsensor, link);
	    break;

	case ENT_LIST_OLD:
	    break;

	case ENT_LIST_DUP:
	    /* They compare, prefer to keep the old data. */
	    i = nsensor->source_idx;
	    opq_destroy(nsensor->waitq);
	    locked_list_destroy(nsensor->handler_list);
	    locked_list_destroy(nsensor->handler_list_cl);
	    ipmi_mem_free(nsensor);
	    ent_item->sensor = NULL;
	    sdr_sensors[i] = osensor;
	    if (osensor) {
		if (osensor->source_array)
		    osensor->source_array[osensor->source_idx] = NULL;
		osensor->source_idx = i;
		osensor->source_array = sdr_sensors;
	    }
	    break;
	}
	ipmi_unlock(sensors->idx_lock);
	ent_item = ent_item->next;
    }

    i_ipmi_set_sdr_sensors(domain, source_mc, sdr_sensors, count);

    if (old_sdr_sensors) {
	for (i=0; i<old_count; i++) {
	    ipmi_sensor_t *osensor = old_sdr_sensors[i];
	    if (osensor != NULL) {
		/* This sensor was not in the new repository, so it must
		   have been deleted. */
		i_ipmi_domain_entity_lock(domain);
		i_ipmi_sensor_get(osensor);
		i_ipmi_domain_entity_unlock(domain);
		ipmi_sensor_destroy(osensor);
	    }
	}
    }

    if (old_sdr_sensors) {
	for (i=0; i<old_count; i++) {
	    ipmi_sensor_t *osensor = old_sdr_sensors[i];
	    if (osensor != NULL) {
		i_ipmi_sensor_put(osensor);
	    }
	}
	ipmi_mem_free(old_sdr_sensors);
    }

    /* Free up all the deleted sensors. */
    while (del_sensors) {
	ent_item = del_sensors;
	del_sensors = del_sensors->next;
	if (ent_item->sensor)
	    i_ipmi_sensor_put(ent_item->sensor);
	if (ent_item->ent)
	    i_ipmi_entity_put(ent_item->ent);
	if (ent_item->mc)
	    i_ipmi_mc_put(ent_item->mc);
	ipmi_mem_free(ent_item);
    }

    /* Report then free up all the new sensors. */
    while (new_sensors) {
	ent_item = new_sensors;
	new_sensors = new_sensors->next;

	if (ent_item->ent && ent_item->sensor)
	    ent_item->sensor->add_pending = 1;
	if (ent_item->sensor)
	    i_ipmi_sensor_put(ent_item->sensor);
	if (ent_item->ent)
	    i_ipmi_entity_put(ent_item->ent);
	if (ent_item->mc)
	    i_ipmi_mc_put(ent_item->mc);
	ipmi_mem_free(ent_item);
    }

 out:
    /* Cleanup unused links. */
    while (links) {
	link = links;
	links = link->next;
	locked_list_free_entry(link);
    }

    return rv;

 out_err:
    /* Release all the entities, sensors, etc. */
    while (del_sensors) {
	ent_item = del_sensors;
	del_sensors = del_sensors->next;
	if (ent_item->sensor)
	    i_ipmi_sensor_put(ent_item->sensor);
	if (ent_item->ent)
	    i_ipmi_entity_put(ent_item->ent);
	if (ent_item->mc)
	    i_ipmi_mc_put(ent_item->mc);
	ipmi_mem_free(ent_item);
    }
    while (new_sensors) {
	ent_item = new_sensors;
	new_sensors = new_sensors->next;
	if (ent_item->sensor)
	    i_ipmi_sensor_put(ent_item->sensor);
	if (ent_item->ent)
	    i_ipmi_entity_put(ent_item->ent);
	if (ent_item->mc)
	    i_ipmi_mc_put(ent_item->mc);
	ipmi_mem_free(ent_item);
    }
    goto out;

 out_err_free_unlock:
    i_ipmi_domain_entity_unlock(domain);
 out_err_free:
    /* Free up the usecounts on all the MCs we got. */
    for (i=0; i<count; i++) {
	ipmi_sensor_t *nsensor = sdr_sensors[i];

	if ((nsensor) && (nsensor->mc))
	    i_ipmi_mc_put(nsensor->mc);
    }
    goto out_err;
}
			
/***********************************************************************
 *
 * Get/set various local information about a sensor.
 *
 **********************************************************************/

int
ipmi_sensor_get_nominal_reading(ipmi_sensor_t *sensor,
				double *nominal_reading)
{
    CHECK_SENSOR_LOCK(sensor);

    if (!sensor->nominal_reading_specified)
	return ENOSYS;

    return (ipmi_sensor_convert_from_raw(sensor,
					 sensor->nominal_reading,
					 nominal_reading));
}

int
ipmi_sensor_get_normal_max(ipmi_sensor_t *sensor, double *normal_max)
{
    CHECK_SENSOR_LOCK(sensor);

    if (!sensor->normal_max_specified)
	return ENOSYS;

    return (ipmi_sensor_convert_from_raw(sensor,
					 sensor->normal_max,
					 normal_max));
}

int
ipmi_sensor_get_normal_min(ipmi_sensor_t *sensor, double *normal_min)
{
    CHECK_SENSOR_LOCK(sensor);

    if (!sensor->normal_min_specified)
	return ENOSYS;

    return (ipmi_sensor_convert_from_raw(sensor,
					 sensor->normal_min,
					 normal_min));
}

int
ipmi_sensor_get_sensor_max(ipmi_sensor_t *sensor, double *sensor_max)
{
    CHECK_SENSOR_LOCK(sensor);

    return (ipmi_sensor_convert_from_raw(sensor,
					 sensor->sensor_max,
					 sensor_max));
}

int
ipmi_sensor_get_sensor_min(ipmi_sensor_t *sensor, double *sensor_min)
{
    CHECK_SENSOR_LOCK(sensor);

    return (ipmi_sensor_convert_from_raw(sensor,
					 sensor->sensor_min,
					 sensor_min));
}

int ipmi_sensor_set_raw_default_threshold(ipmi_sensor_t *sensor,
					  int           threshold,
					  int           val)
{
    if ((threshold < 0) || (threshold > 5))
	return EINVAL;

    sensor->default_thresholds[threshold] = val;
    return 0;
}

int ipmi_sensor_get_default_threshold_raw(ipmi_sensor_t *sensor,
					  int           threshold,
					  int           *raw)
{
    int rv;
    int val;

    CHECK_SENSOR_LOCK(sensor);

    if ((threshold < 0) || (threshold > 5))
	return EINVAL;

    rv = ipmi_sensor_threshold_settable(sensor, threshold, &val);
    if (rv)
	return rv;

    if (!val)
	return ENOSYS;

    if (!ipmi_sensor_get_sensor_init_thresholds(sensor))
	return ENOSYS;

    *raw = sensor->default_thresholds[threshold];
    return 0;
}

int ipmi_sensor_get_default_threshold_cooked(ipmi_sensor_t *sensor,
					     int           threshold,
					     double        *cooked)
{
    int rv;
    int val;

    CHECK_SENSOR_LOCK(sensor);

    if ((threshold < 0) || (threshold > 5))
	return EINVAL;

    rv = ipmi_sensor_threshold_settable(sensor, threshold, &val);
    if (rv)
	return rv;

    if (!val)
	return ENOSYS;

    if (!ipmi_sensor_get_sensor_init_thresholds(sensor))
	return ENOSYS;

    return (ipmi_sensor_convert_from_raw(sensor,
					 sensor->default_thresholds[threshold],
					 cooked));
}

ipmi_mc_t *
ipmi_sensor_get_mc(ipmi_sensor_t *sensor)
{
    return sensor->mc;
}

ipmi_mc_t *
ipmi_sensor_get_source_mc(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->source_mc;
}

int
ipmi_sensor_get_num(ipmi_sensor_t *sensor,
		    int           *lun,
		    int           *num)
{
    CHECK_SENSOR_LOCK(sensor);

    if (lun)
	*lun = sensor->lun;
    if (num)
	*num = sensor->num;
    return 0;
}

static void
ipmi_sensor_get_event_masks(ipmi_sensor_t *sensor,
			    uint16_t      *mask1,
			    uint16_t      *mask2)
{
    if (sensor->event_reading_type == IPMI_EVENT_READING_TYPE_THRESHOLD) {
	/* Remove the reading mask, as that is not part of the event
	   values allowed. */
	*mask1 = sensor->mask1 & 0x0fff;
	*mask2 = sensor->mask2 & 0x0fff;
    } else {
	/* Cannot set bit 15 */
	*mask1 = sensor->mask1 & 0x7fff;
	*mask2 = sensor->mask2 & 0x7fff;
    }
}

int
ipmi_sensor_threshold_event_supported(ipmi_sensor_t               *sensor,
				      enum ipmi_thresh_e          event,
				      enum ipmi_event_value_dir_e value_dir,
				      enum ipmi_event_dir_e       dir,
				      int                         *val)
{
    int      idx;
    uint16_t mask;

    CHECK_SENSOR_LOCK(sensor);

    if (sensor->event_reading_type != IPMI_EVENT_READING_TYPE_THRESHOLD)
	/* Not a threshold sensor, it doesn't have readings. */
	return ENOSYS;
    if (sensor->threshold_access == IPMI_THRESHOLD_ACCESS_SUPPORT_NONE) {
	/* No thresholds supported. */
	*val = 0;
	return 0;
    }

    if (dir == IPMI_ASSERTION)
	mask = sensor->mask1;
    else if (dir == IPMI_DEASSERTION)
	mask = sensor->mask2;
    else
	return EINVAL;

    idx = (event * 2) + value_dir;
    if (idx > 11)
	return EINVAL;

    *val = IPMI_SENSOR_GET_MASK_BIT(mask, idx);
    return 0;
}

void
ipmi_sensor_set_threshold_assertion_event_supported(
    ipmi_sensor_t               *sensor,
    enum ipmi_thresh_e          event,
    enum ipmi_event_value_dir_e dir,
    int                         val)
{
    int idx;

    idx = (event * 2) + dir;
    if (idx > 11)
	return;

    IPMI_SENSOR_SET_MASK_BIT(sensor->mask1, idx, val);
}

void
ipmi_sensor_set_threshold_deassertion_event_supported(
    ipmi_sensor_t               *sensor,
    enum ipmi_thresh_e          event,
    enum ipmi_event_value_dir_e dir,
    int                         val)
{
    int idx;

    idx = (event * 2) + dir;
    if (idx > 11)
	return;

    IPMI_SENSOR_SET_MASK_BIT(sensor->mask2, idx, val);
}

int
ipmi_sensor_threshold_reading_supported(ipmi_sensor_t      *sensor,
					enum ipmi_thresh_e thresh,
					int                *val)
{
    CHECK_SENSOR_LOCK(sensor);

    if (sensor->event_reading_type != IPMI_EVENT_READING_TYPE_THRESHOLD)
	/* Not a threshold sensor, it doesn't have readings. */
	return ENOSYS;

    switch(thresh) {
    case IPMI_LOWER_NON_CRITICAL:
	*val = IPMI_SENSOR_GET_MASK_BIT(sensor->mask1, 12);
	break;
    case IPMI_LOWER_CRITICAL:
	*val = IPMI_SENSOR_GET_MASK_BIT(sensor->mask1, 13);
	break;
    case IPMI_LOWER_NON_RECOVERABLE:
	*val = IPMI_SENSOR_GET_MASK_BIT(sensor->mask1, 14);
	break;
    case IPMI_UPPER_NON_CRITICAL:
	*val = IPMI_SENSOR_GET_MASK_BIT(sensor->mask2, 12);
	break;
    case IPMI_UPPER_CRITICAL:
	*val = IPMI_SENSOR_GET_MASK_BIT(sensor->mask2, 13);
	break;
    case IPMI_UPPER_NON_RECOVERABLE:
	*val = IPMI_SENSOR_GET_MASK_BIT(sensor->mask2, 14);
	break;
    default:
	return EINVAL;
    }
    return 0;
}

int
ipmi_sensor_threshold_settable(ipmi_sensor_t      *sensor,
			       enum ipmi_thresh_e event,
			       int                *val)
{
    CHECK_SENSOR_LOCK(sensor);

    if (sensor->event_reading_type != IPMI_EVENT_READING_TYPE_THRESHOLD)
	/* Not a threshold sensor, it doesn't have readings. */
	return ENOSYS;
    if (sensor->threshold_access != IPMI_THRESHOLD_ACCESS_SUPPORT_SETTABLE) {
	/* Threshold setting not supported for any thresholds. */
	*val = 0;
	return 0;
    }

    if (event > IPMI_UPPER_NON_RECOVERABLE)
	return EINVAL;

    *val = IPMI_SENSOR_GET_MASK_BIT(sensor->mask3, event + 8);
    return 0;
}

void
ipmi_sensor_threshold_set_settable(ipmi_sensor_t      *sensor,
				   enum ipmi_thresh_e event,
				   int                val)
{
    if (sensor->event_reading_type != IPMI_EVENT_READING_TYPE_THRESHOLD)
	/* Not a threshold sensor, it doesn't have readings. */
	return;

    if (event > IPMI_UPPER_NON_RECOVERABLE)
	return;

    IPMI_SENSOR_SET_MASK_BIT(sensor->mask3, event + 8, val);
}

int
ipmi_sensor_threshold_readable(ipmi_sensor_t      *sensor,
			       enum ipmi_thresh_e event,
			       int                *val)
{
    CHECK_SENSOR_LOCK(sensor);

    if (sensor->event_reading_type != IPMI_EVENT_READING_TYPE_THRESHOLD)
	/* Not a threshold sensor, it doesn't have readings. */
	return ENOSYS;
    if ((sensor->threshold_access == IPMI_THRESHOLD_ACCESS_SUPPORT_NONE)
	|| (sensor->threshold_access == IPMI_THRESHOLD_ACCESS_SUPPORT_FIXED))
    {
	/* Threshold reading not supported for any thresholds. */
	*val = 0;
	return 0;
    }

    if (event > IPMI_UPPER_NON_RECOVERABLE)
	return EINVAL;

    *val = IPMI_SENSOR_GET_MASK_BIT(sensor->mask3, event);
    return 0;
}

void
ipmi_sensor_threshold_set_readable(ipmi_sensor_t      *sensor,
				   enum ipmi_thresh_e event,
				   int                val)
{
    if (sensor->event_reading_type != IPMI_EVENT_READING_TYPE_THRESHOLD)
	/* Not a threshold sensor, it doesn't have readings. */
	return;

    if (event > IPMI_UPPER_NON_RECOVERABLE)
	return;

    IPMI_SENSOR_SET_MASK_BIT(sensor->mask3, event, val);
}

int
ipmi_sensor_discrete_event_supported(ipmi_sensor_t         *sensor,
				     int                   event,
				     enum ipmi_event_dir_e dir,
				     int                   *val)
{
    uint16_t mask;

    CHECK_SENSOR_LOCK(sensor);

    if (sensor->event_reading_type == IPMI_EVENT_READING_TYPE_THRESHOLD)
	/* A threshold sensor, it doesn't have events. */
	return ENOSYS;

    if (dir == IPMI_ASSERTION)
	mask = sensor->mask1;
    else if (dir == IPMI_DEASSERTION)
	mask = sensor->mask2;
    else
	return EINVAL;

    if (event > 14)
	return EINVAL;

    *val = IPMI_SENSOR_GET_MASK_BIT(mask, event);
    return 0;
}

void
ipmi_sensor_set_discrete_assertion_event_supported(ipmi_sensor_t *sensor,
						   int           event,
						   int           val)
{
    if (event > 14)
	return;

    IPMI_SENSOR_SET_MASK_BIT(sensor->mask1, event, val);
}

void
ipmi_sensor_set_discrete_deassertion_event_supported(ipmi_sensor_t *sensor,
						     int           event,
						     int           val)
{
    if (event > 14)
	return;

    IPMI_SENSOR_SET_MASK_BIT(sensor->mask2, event, val);
}

int
ipmi_sensor_discrete_event_readable(ipmi_sensor_t *sensor,
				    int           event,
				    int           *val)
{
    CHECK_SENSOR_LOCK(sensor);

    if (sensor->event_reading_type == IPMI_EVENT_READING_TYPE_THRESHOLD)
	/* A threshold sensor, it doesn't have events. */
	return ENOSYS;

    if (event > 14)
	return EINVAL;

    *val = IPMI_SENSOR_GET_MASK_BIT(sensor->mask3, event);
    return 0;
}

void
ipmi_sensor_discrete_set_event_readable(ipmi_sensor_t *sensor,
					int           event,
					int           val)
{
    if (sensor->event_reading_type == IPMI_EVENT_READING_TYPE_THRESHOLD)
	/* A threshold sensor, it doesn't have events. */
	return;

    if (event > 14)
	return;

    IPMI_SENSOR_SET_MASK_BIT(sensor->mask3, event, val);
}

int
ipmi_sensor_get_owner(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->owner;
}

int
ipmi_sensor_get_channel(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->channel;
}

int
ipmi_sensor_get_entity_id(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->entity_id;
}

int
ipmi_sensor_get_entity_instance(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->entity_instance;
}

int
ipmi_sensor_get_entity_instance_logical(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->entity_instance_logical;
}

int
ipmi_sensor_get_sensor_init_scanning(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->sensor_init_scanning;
}

int
ipmi_sensor_get_sensor_init_events(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->sensor_init_events;
}

int
ipmi_sensor_get_sensor_init_thresholds(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->sensor_init_thresholds;
}

int
ipmi_sensor_get_sensor_init_hysteresis(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->sensor_init_hysteresis;
}

int
ipmi_sensor_get_sensor_init_type(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->sensor_init_type;
}

int
ipmi_sensor_get_sensor_init_pu_events(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->sensor_init_pu_events;
}

int
ipmi_sensor_get_sensor_init_pu_scanning(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->sensor_init_pu_scanning;
}

int
ipmi_sensor_get_ignore_if_no_entity(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->ignore_if_no_entity;
}

int
ipmi_sensor_get_ignore_for_presence(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->ignore_for_presence;
}

int
ipmi_sensor_get_supports_auto_rearm(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->supports_auto_rearm;
}

int
ipmi_sensor_get_hysteresis_support(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->hysteresis_support;
}

int
ipmi_sensor_get_threshold_access(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->threshold_access;
}

int
ipmi_sensor_get_event_support(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->event_support;
}

int
ipmi_sensor_get_sensor_type(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->sensor_type;
}

int
ipmi_sensor_get_event_reading_type(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->event_reading_type;
}

int
ipmi_sensor_get_sensor_direction(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->sensor_direction;
}

int
ipmi_sensor_get_is_readable(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->readable;
}

int
ipmi_sensor_get_analog_data_format(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->analog_data_format;
}

enum ipmi_rate_unit_e
ipmi_sensor_get_rate_unit(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->rate_unit;
}

enum ipmi_modifier_unit_use_e
ipmi_sensor_get_modifier_unit_use(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->modifier_unit_use;
}

int
ipmi_sensor_get_percentage(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->percentage;
}

enum ipmi_unit_type_e
ipmi_sensor_get_base_unit(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->base_unit;
}

enum ipmi_unit_type_e
ipmi_sensor_get_modifier_unit(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->modifier_unit;
}

int
ipmi_sensor_get_linearization(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->linearization;
}

int
ipmi_sensor_get_raw_m(ipmi_sensor_t *sensor, int val)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->conv[val].m;
}

int
ipmi_sensor_get_raw_tolerance(ipmi_sensor_t *sensor, int val)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->conv[val].tolerance;
}

int
ipmi_sensor_get_raw_b(ipmi_sensor_t *sensor, int val)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->conv[val].b;
}

int
ipmi_sensor_get_raw_accuracy(ipmi_sensor_t *sensor, int val)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->conv[val].accuracy;
}

int
ipmi_sensor_get_raw_accuracy_exp(ipmi_sensor_t *sensor, int val)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->conv[val].accuracy_exp;
}

int
ipmi_sensor_get_raw_r_exp(ipmi_sensor_t *sensor, int val)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->conv[val].r_exp;
}

int
ipmi_sensor_get_raw_b_exp(ipmi_sensor_t *sensor, int val)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->conv[val].b_exp;
}

int
ipmi_sensor_get_normal_min_specified(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->normal_min_specified;
}

int
ipmi_sensor_get_normal_max_specified(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->normal_max_specified;
}

int
ipmi_sensor_get_nominal_reading_specified(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->nominal_reading_specified;
}

int
ipmi_sensor_get_raw_nominal_reading(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->nominal_reading;
}

int
ipmi_sensor_get_raw_normal_max(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->normal_max;
}

int
ipmi_sensor_get_raw_normal_min(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->normal_min;
}

int
ipmi_sensor_get_raw_sensor_max(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->sensor_max;
}

int
ipmi_sensor_get_raw_sensor_min(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->sensor_min;
}

int
ipmi_sensor_get_positive_going_threshold_hysteresis(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->positive_going_threshold_hysteresis;
}

int
ipmi_sensor_get_negative_going_threshold_hysteresis(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->negative_going_threshold_hysteresis;
}

int
ipmi_sensor_get_oem1(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->oem1;
}

int
ipmi_sensor_get_id_length(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    if (sensor->id_type == IPMI_ASCII_STR)
	return sensor->id_len+1;
    else
	return sensor->id_len;
}

enum ipmi_str_type_e
ipmi_sensor_get_id_type(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->id_type;
}

int
ipmi_sensor_get_id(ipmi_sensor_t *sensor, char *id, int length)
{
    int clen;

    CHECK_SENSOR_LOCK(sensor);

    if ((int) sensor->id_len > length)
	clen = length;
    else
	clen = sensor->id_len;
    memcpy(id, sensor->id, clen);

    if (sensor->id_type == IPMI_ASCII_STR) {
	/* NIL terminate the ASCII string. */
	if (clen == length)
	    clen--;

	id[clen] = '\0';
    }

    return clen;
}

void
ipmi_sensor_set_owner(ipmi_sensor_t *sensor, int owner)
{
    sensor->owner = owner;
}

void
ipmi_sensor_set_channel(ipmi_sensor_t *sensor, int channel)
{
    sensor->channel = channel;
}

void
ipmi_sensor_set_entity_id(ipmi_sensor_t *sensor, int entity_id)
{
    sensor->entity_id = entity_id;
}

void
ipmi_sensor_set_entity_instance(ipmi_sensor_t *sensor, int entity_instance)
{
    sensor->entity_instance = entity_instance;
}

void
ipmi_sensor_set_entity_instance_logical(ipmi_sensor_t *sensor,
					int           entity_instance_logical)
{
    sensor->entity_instance_logical = entity_instance_logical;
}

void
ipmi_sensor_set_sensor_init_scanning(ipmi_sensor_t *sensor,
				     int           sensor_init_scanning)
{
    sensor->sensor_init_scanning = sensor_init_scanning;
}

void
ipmi_sensor_set_sensor_init_events(ipmi_sensor_t *sensor,
				   int           sensor_init_events)
{
    sensor->sensor_init_events = sensor_init_events;
}

void
ipmi_sensor_set_sensor_init_thresholds(ipmi_sensor_t *sensor,
				       int           sensor_init_thresholds)
{
    sensor->sensor_init_thresholds = sensor_init_thresholds;
}

void
ipmi_sensor_set_sensor_init_hysteresis(ipmi_sensor_t *sensor,
				       int           sensor_init_hysteresis)
{
    sensor->sensor_init_hysteresis = sensor_init_hysteresis;
}

void
ipmi_sensor_set_sensor_init_type(ipmi_sensor_t *sensor, int sensor_init_type)
{
    sensor->sensor_init_type = sensor_init_type;
}

void
ipmi_sensor_set_sensor_init_pu_events(ipmi_sensor_t *sensor,
				      int           sensor_init_pu_events)
{
    sensor->sensor_init_pu_events = sensor_init_pu_events;
}

void
ipmi_sensor_set_sensor_init_pu_scanning(ipmi_sensor_t *sensor,
					int           sensor_init_pu_scanning)
{
    sensor->sensor_init_pu_scanning = sensor_init_pu_scanning;
}

void
ipmi_sensor_set_ignore_if_no_entity(ipmi_sensor_t *sensor,
				    int           ignore_if_no_entity)
{
    sensor->ignore_if_no_entity = ignore_if_no_entity;
}

void
ipmi_sensor_set_ignore_for_presence(ipmi_sensor_t *sensor, int ignore)
{
    sensor->ignore_for_presence = ignore;
}

void
ipmi_sensor_set_supports_auto_rearm(ipmi_sensor_t *sensor, int val)
{
    sensor->supports_auto_rearm = val;
}

void
ipmi_sensor_set_hysteresis_support(ipmi_sensor_t *sensor,
				   int           hysteresis_support)
{
    sensor->hysteresis_support = hysteresis_support;
}

void
ipmi_sensor_set_threshold_access(ipmi_sensor_t *sensor, int threshold_access)
{
    sensor->threshold_access = threshold_access;
}

void
ipmi_sensor_set_event_support(ipmi_sensor_t *sensor, int event_support)
{
    sensor->event_support = event_support;
}

void
ipmi_sensor_set_sensor_type(ipmi_sensor_t *sensor, int sensor_type)
{
    sensor->sensor_type = sensor_type;
}

void
ipmi_sensor_set_event_reading_type(ipmi_sensor_t *sensor,
				   int           event_reading_type)
{
    sensor->event_reading_type = event_reading_type;
}

void
ipmi_sensor_set_direction(ipmi_sensor_t *sensor,
			  int           direction)
{
    sensor->sensor_direction = direction;
}

void
ipmi_sensor_set_is_readable(ipmi_sensor_t *sensor,
			    int           readable)
{
    sensor->readable = readable != 0;
}

void
ipmi_sensor_set_analog_data_format(ipmi_sensor_t *sensor,
				   int           analog_data_format)
{
    sensor->analog_data_format = analog_data_format;
}

void
ipmi_sensor_set_rate_unit(ipmi_sensor_t *sensor, int rate_unit)
{
    sensor->rate_unit = rate_unit;
}

void
ipmi_sensor_set_modifier_unit_use(ipmi_sensor_t *sensor, int modifier_unit_use)
{
    sensor->modifier_unit_use = modifier_unit_use;
}

void
ipmi_sensor_set_percentage(ipmi_sensor_t *sensor, int percentage)
{
    sensor->percentage = percentage;
}

void
ipmi_sensor_set_base_unit(ipmi_sensor_t *sensor, int base_unit)
{
    sensor->base_unit = base_unit;
}

void
ipmi_sensor_set_modifier_unit(ipmi_sensor_t *sensor, int modifier_unit)
{
    sensor->modifier_unit = modifier_unit;
}

void
ipmi_sensor_set_linearization(ipmi_sensor_t *sensor, int linearization)
{
    sensor->linearization = linearization;
}

void
ipmi_sensor_set_raw_m(ipmi_sensor_t *sensor, int idx, int val)
{
    sensor->conv[idx].m = val;
}

void
ipmi_sensor_set_raw_tolerance(ipmi_sensor_t *sensor, int idx, int val)
{
    sensor->conv[idx].tolerance = val;
}

void
ipmi_sensor_set_raw_b(ipmi_sensor_t *sensor, int idx, int val)
{
    sensor->conv[idx].b = val;
}

void
ipmi_sensor_set_raw_accuracy(ipmi_sensor_t *sensor, int idx, int val)
{
    sensor->conv[idx].accuracy = val;
}

void
ipmi_sensor_set_raw_accuracy_exp(ipmi_sensor_t *sensor, int idx, int val)
{
    sensor->conv[idx].accuracy_exp = val;
}

void
ipmi_sensor_set_raw_r_exp(ipmi_sensor_t *sensor, int idx, int val)
{
    sensor->conv[idx].r_exp = val;
}

void
ipmi_sensor_set_raw_b_exp(ipmi_sensor_t *sensor, int idx, int val)
{
    sensor->conv[idx].b_exp = val;
}

void
ipmi_sensor_set_normal_min_specified(ipmi_sensor_t *sensor,
				     int           normal_min_specified)
{
    sensor->normal_min_specified = normal_min_specified;
}

void
ipmi_sensor_set_normal_max_specified(ipmi_sensor_t *sensor,
				     int           normal_max_specified)
{
    sensor->normal_max_specified = normal_max_specified;
}

void
ipmi_sensor_set_nominal_reading_specified(
    ipmi_sensor_t *sensor,
    int            nominal_reading_specified)
{
    sensor->nominal_reading_specified = nominal_reading_specified;
}

void
ipmi_sensor_set_raw_nominal_reading(ipmi_sensor_t *sensor,
				    int           raw_nominal_reading)
{
    sensor->nominal_reading = raw_nominal_reading;
}

void
ipmi_sensor_set_raw_normal_max(ipmi_sensor_t *sensor, int raw_normal_max)
{
    sensor->normal_max = raw_normal_max;
}

void
ipmi_sensor_set_raw_normal_min(ipmi_sensor_t *sensor, int raw_normal_min)
{
    sensor->normal_min = raw_normal_min;
}

void
ipmi_sensor_set_raw_sensor_max(ipmi_sensor_t *sensor, int raw_sensor_max)
{
    sensor->sensor_max = raw_sensor_max;
}

void
ipmi_sensor_set_raw_sensor_min(ipmi_sensor_t *sensor, int raw_sensor_min)
{
    sensor->sensor_min = raw_sensor_min;
}

void
ipmi_sensor_set_positive_going_threshold_hysteresis(
    ipmi_sensor_t *sensor,
    int           positive_going_threshold_hysteresis)
{
    sensor->positive_going_threshold_hysteresis
	= positive_going_threshold_hysteresis;
}

void
ipmi_sensor_set_negative_going_threshold_hysteresis(
    ipmi_sensor_t *sensor,
    int           negative_going_threshold_hysteresis)
{
    sensor->negative_going_threshold_hysteresis
	= negative_going_threshold_hysteresis;
}

void
ipmi_sensor_set_oem1(ipmi_sensor_t *sensor, int oem1)
{
    sensor->oem1 = oem1;
}

void
ipmi_sensor_set_id(ipmi_sensor_t *sensor, char *id,
		   enum ipmi_str_type_e type, int length)
{
    if (length > SENSOR_ID_LEN)
	length = SENSOR_ID_LEN;
    
    memcpy(sensor->id, id, length);
    sensor->id_type = type;
    sensor->id_len = length;
    if (sensor->entity)
	sensor_set_name(sensor);
}

void
ipmi_sensor_set_oem_info(ipmi_sensor_t *sensor, void *oem_info,
			 ipmi_sensor_cleanup_oem_info_cb cleanup_handler)
{
    sensor->oem_info = oem_info;
    sensor->oem_info_cleanup_handler = cleanup_handler;
}

void *
ipmi_sensor_get_oem_info(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->oem_info;
}

const char *
ipmi_sensor_get_sensor_type_string(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->sensor_type_string;
}

void
ipmi_sensor_set_sensor_type_string(ipmi_sensor_t *sensor, const char *str)
{
    sensor->sensor_type_string = str;
}

const char *
ipmi_sensor_get_event_reading_type_string(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->event_reading_type_string;
}

void
ipmi_sensor_set_event_reading_type_string(ipmi_sensor_t *sensor,
					  const char    *str)
{
    sensor->event_reading_type_string = str;
}

const char *
ipmi_sensor_get_rate_unit_string(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->rate_unit_string;
}

void
ipmi_sensor_set_rate_unit_string(ipmi_sensor_t *sensor,
				 const char    *str)
{
    sensor->rate_unit_string = str;
}

const char *
ipmi_sensor_get_base_unit_string(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->base_unit_string;
}

void
ipmi_sensor_set_base_unit_string(ipmi_sensor_t *sensor, const char *str)
{
    sensor->base_unit_string = str;
}

const char *
ipmi_sensor_get_modifier_unit_string(ipmi_sensor_t *sensor)
{
    CHECK_SENSOR_LOCK(sensor);

    return sensor->modifier_unit_string;
}

void
ipmi_sensor_set_modifier_unit_string(ipmi_sensor_t *sensor, const char *str)
{
    sensor->modifier_unit_string = str;
}

void
ipmi_sensor_set_hot_swap_requester(ipmi_sensor_t *sensor,
				   int           offset,
				   int           val_when_requesting)
{
    sensor->hot_swap_requester = offset;
    sensor->hot_swap_requester_val = val_when_requesting;
}

int
ipmi_sensor_is_hot_swap_requester(ipmi_sensor_t *sensor,
				  int           *offset,
				  int           *val_when_requesting)
{
    CHECK_SENSOR_LOCK(sensor);

    if (sensor->hot_swap_requester != -1) {
	if (offset)
	    *offset = sensor->hot_swap_requester;
	if (val_when_requesting)
	    *val_when_requesting = sensor->hot_swap_requester_val;
	return 1;
    }
    return 0;
}

ipmi_domain_t *
ipmi_sensor_get_domain(ipmi_sensor_t *sensor)
{
    return sensor->domain;
}

ipmi_entity_t *
ipmi_sensor_get_entity(ipmi_sensor_t *sensor)
{
    return sensor->entity;
}


/***********************************************************************
 *
 * Incoming event handling for sensors.
 *
 **********************************************************************/

int
ipmi_sensor_threshold_set_event_handler(
    ipmi_sensor_t                             *sensor,
    ipmi_sensor_threshold_event_handler_nd_cb handler,
    void                                      *cb_data)
{
    CHECK_SENSOR_LOCK(sensor);

    sensor->threshold_event_handler = handler;
    sensor->cb_data = cb_data;
    return 0;
}

int
ipmi_sensor_add_threshold_event_handler(
    ipmi_sensor_t                  *sensor,
    ipmi_sensor_threshold_event_cb handler,
    void                           *cb_data)
{
    CHECK_SENSOR_LOCK(sensor);

    if (! locked_list_add(sensor->handler_list, handler, cb_data))
	return ENOMEM;

    return 0;
}

int
ipmi_sensor_remove_threshold_event_handler(
    ipmi_sensor_t                  *sensor,
    ipmi_sensor_threshold_event_cb handler,
    void                           *cb_data)
{
    CHECK_SENSOR_LOCK(sensor);

    if (! locked_list_remove(sensor->handler_list, handler, cb_data))
	return ENOENT;

    return 0;
}

int
ipmi_sensor_add_threshold_event_handler_cl(
    ipmi_sensor_t                     *sensor,
    ipmi_sensor_threshold_event_cl_cb handler,
    void                              *cb_data)
{
    CHECK_SENSOR_LOCK(sensor);

    if (! locked_list_add(sensor->handler_list_cl, handler, cb_data))
	return ENOMEM;

    return 0;
}

int
ipmi_sensor_remove_threshold_event_handler_cl(
    ipmi_sensor_t                     *sensor,
    ipmi_sensor_threshold_event_cl_cb handler,
    void                              *cb_data)
{
    CHECK_SENSOR_LOCK(sensor);

    if (! locked_list_remove(sensor->handler_list_cl, handler, cb_data))
	return ENOENT;

    return 0;
}


int
ipmi_sensor_discrete_set_event_handler(
    ipmi_sensor_t                            *sensor,
    ipmi_sensor_discrete_event_handler_nd_cb handler,
    void                                     *cb_data)
{
    CHECK_SENSOR_LOCK(sensor);

    sensor->discrete_event_handler = handler;
    sensor->cb_data = cb_data;
    return 0;
}

int
ipmi_sensor_add_discrete_event_handler(
    ipmi_sensor_t                 *sensor,
    ipmi_sensor_discrete_event_cb handler,
    void                          *cb_data)
{
    CHECK_SENSOR_LOCK(sensor);

    if (! locked_list_add(sensor->handler_list, handler, cb_data))
	return ENOMEM;

    return 0;
}

int
ipmi_sensor_remove_discrete_event_handler(
    ipmi_sensor_t                 *sensor,
    ipmi_sensor_discrete_event_cb handler,
    void                          *cb_data)
{
    CHECK_SENSOR_LOCK(sensor);

    if (! locked_list_remove(sensor->handler_list, handler, cb_data))
	return ENOENT;

    return 0;
}

int
ipmi_sensor_add_discrete_event_handler_cl(
    ipmi_sensor_t                    *sensor,
    ipmi_sensor_discrete_event_cl_cb handler,
    void                             *cb_data)
{
    CHECK_SENSOR_LOCK(sensor);

    if (! locked_list_add(sensor->handler_list_cl, handler, cb_data))
	return ENOMEM;

    return 0;
}

int
ipmi_sensor_remove_discrete_event_handler_cl(
    ipmi_sensor_t                    *sensor,
    ipmi_sensor_discrete_event_cl_cb handler,
    void                             *cb_data)
{
    CHECK_SENSOR_LOCK(sensor);

    if (! locked_list_remove(sensor->handler_list_cl, handler, cb_data))
	return ENOENT;

    return 0;
}

typedef struct sensor_event_info_s
{
    ipmi_sensor_t               *sensor;
    int                         handled;

    enum ipmi_event_dir_e       dir;
    enum ipmi_thresh_e          threshold;
    enum ipmi_event_value_dir_e high_low;
    enum ipmi_value_present_e   value_present;
    unsigned int                raw_value;
    double                      value;

    int                         offset;
    int                         severity;
    int                         prev_severity;

    ipmi_event_t                *event;
} sensor_event_info_t;

static int
threshold_sensor_event_call_handler(void *cb_data, void *item1, void *item2)
{
    ipmi_sensor_threshold_event_cb handler = item1;
    sensor_event_info_t            *info = cb_data;
    int                            handled;

    handled = handler(info->sensor, info->dir,
		      info->threshold,
		      info->high_low,
		      info->value_present,
		      info->raw_value, info->value,
		      item2, info->event);
    if (handled != IPMI_EVENT_NOT_HANDLED) {
	if (info->handled != IPMI_EVENT_HANDLED)
	    /* Allow handled to override handled_pass, but not the
	       other way. */
	    info->handled = handled;
	if (handled == IPMI_EVENT_HANDLED)
	    info->event = NULL;
    }
    return LOCKED_LIST_ITER_CONTINUE;
}

void
ipmi_sensor_call_threshold_event_handlers
(ipmi_sensor_t               *sensor,
 enum ipmi_event_dir_e       dir,
 enum ipmi_thresh_e          threshold,
 enum ipmi_event_value_dir_e high_low,
 enum ipmi_value_present_e   value_present,
 unsigned int                raw_value,
 double                      value,
 ipmi_event_t                **event,
 int                         *handled)
{
    sensor_event_info_t info;

    info.sensor = sensor;
    info.dir = dir;
    info.threshold = threshold;
    info.high_low = high_low;
    info.value_present = value_present;
    info.raw_value = raw_value;
    info.value = value;
    info.event = *event;
    if (handled)
	info.handled = *handled;
    else
	info.handled = IPMI_EVENT_NOT_HANDLED;

    if (sensor->threshold_event_handler) {
	sensor->threshold_event_handler(sensor, info.dir,
					info.threshold,
					info.high_low,
					info.value_present,
					info.raw_value, info.value,
					sensor->cb_data, info.event);
	if (info.event)
	    info.handled = IPMI_EVENT_HANDLED;
	info.event = NULL;
    }
    locked_list_iterate(sensor->handler_list,
			threshold_sensor_event_call_handler, &info);

    if (handled)
	*handled = info.handled;
    *event = info.event;
}

static int
discrete_sensor_event_call_handler(void *cb_data, void *item1, void *item2)
{
    ipmi_sensor_discrete_event_cb handler = item1;
    sensor_event_info_t           *info = cb_data;
    int                           handled;

    handled = handler(info->sensor, info->dir, info->offset,
		      info->severity,
		      info->prev_severity,
		      item2, info->event);
    if (handled != IPMI_EVENT_NOT_HANDLED) {
	if (info->handled != IPMI_EVENT_HANDLED)
	    /* Allow handled to override handled_pass, but not the
	       other way. */
	    info->handled = handled;
	if (handled == IPMI_EVENT_HANDLED)
	    info->event = NULL;
    }
    return LOCKED_LIST_ITER_CONTINUE;
}

void
ipmi_sensor_call_discrete_event_handlers(ipmi_sensor_t         *sensor,
					 enum ipmi_event_dir_e dir,
					 int                   offset,
					 int                   severity,
					 int                   prev_severity,
					 ipmi_event_t          **event,
					 int                   *handled)
{
    sensor_event_info_t info;

    info.sensor = sensor;
    info.dir = dir;
    info.offset = offset;
    info.severity = severity;
    info.prev_severity = prev_severity;
    info.event = *event;
    if (handled)
	info.handled = *handled;
    else
	info.handled = IPMI_EVENT_NOT_HANDLED;

    if (sensor->discrete_event_handler) {
	sensor->discrete_event_handler(sensor, info.dir, info.offset,
				       info.severity,
				       info.prev_severity,
				       sensor->cb_data, info.event);
	if (info.event)
	    info.handled = IPMI_EVENT_HANDLED;
	info.event = NULL;
    }
    locked_list_iterate(sensor->handler_list,
			discrete_sensor_event_call_handler, &info);

    if (handled)
	*handled = info.handled;
    *event = info.event;
}

int
ipmi_sensor_event(ipmi_sensor_t *sensor, ipmi_event_t *event)
{
    int rv;
    int handled;

    CHECK_SENSOR_LOCK(sensor);

    handled = IPMI_EVENT_NOT_HANDLED;

    if (sensor->event_reading_type == IPMI_EVENT_READING_TYPE_THRESHOLD) {
	enum ipmi_event_dir_e       dir;
	enum ipmi_thresh_e          threshold;
	enum ipmi_event_value_dir_e high_low;
	enum ipmi_value_present_e   value_present;
	unsigned int                raw_value;
	double                      value;
	const unsigned char         *data;

	data = ipmi_event_get_data_ptr(event);
	dir = data[9] >> 7;
	threshold = (data[10] >> 1) & 0x07;
	high_low = data[10] & 1;
	raw_value = data[11];
	value = 0.0;

	if ((data[10] >> 6) == 1) {
	    rv = ipmi_sensor_convert_from_raw(sensor, raw_value, &value);
	    if (rv)
		value_present = IPMI_RAW_VALUE_PRESENT;
	    else
		value_present = IPMI_BOTH_VALUES_PRESENT;
	} else {
	    value_present = IPMI_NO_VALUES_PRESENT;
	}
	ipmi_sensor_call_threshold_event_handlers(sensor, dir,
						  threshold,
						  high_low,
						  value_present,
						  raw_value, value,
						  &event,
						  &handled);
    } else {
	enum ipmi_event_dir_e dir;
	int                   offset;
	int                   severity = 0;
	int                   prev_severity = 0;
	const unsigned char   *data;

	data = ipmi_event_get_data_ptr(event);
	dir = data[9] >> 7;
	offset = data[10] & 0x0f;
	if ((data[10] >> 6) == 2) {
	    severity = data[11] >> 4;
	    prev_severity = data[11] & 0xf;
	    if (severity == 0xf)
		severity = -1;
	    if (prev_severity == 0xf)
		prev_severity = -1;
	}

	ipmi_sensor_call_discrete_event_handlers(sensor, dir, offset,
						 severity,
						 prev_severity,
						 &event,
						 &handled);
    }

    /* Make sure the caller knows if we didn't deliver the event. */
    if (handled == IPMI_EVENT_NOT_HANDLED)
	return EINVAL;
    return 0;
}

/***********************************************************************
 *
 * Standard sensor messaging.
 *
 **********************************************************************/

typedef void (*sensor_done_handler_cb)(ipmi_sensor_t *sensor,
				       int           err,
				       void          *sinfo);

static int
sensor_done_check_rsp(ipmi_sensor_t          *sensor,
		      int                    err,
		      ipmi_msg_t             *rsp,
		      unsigned int           min_length,
		      char                   *name,
		      sensor_done_handler_cb done,
		      void                   *sinfo)
{
    if (err) {
	ipmi_log(IPMI_LOG_ERR_INFO,
		 "%ssensor.c(%s): Got error: %x",
		 SENSOR_NAME(sensor), name, err);
	done(sensor, err, sinfo);
	return err;
    }

    if (!sensor) {
	/* This *should* never happen, but we check it to shake out
	   bugs. */
	ipmi_log(IPMI_LOG_ERR_INFO,
		 "%ssensor.c(%s): Sensor when away during operation",
		 SENSOR_NAME(sensor), name);
	done(sensor, ECANCELED, sinfo);
	return ECANCELED;
    }

    if (rsp && rsp->data[0]) {
#if 0
	/* This is sometimes expected. */
	ipmi_log(IPMI_LOG_ERR_INFO,
		 "%ssensor.c(%s): Got IPMI error in response: %x",
		 SENSOR_NAME(sensor), name, rsp->data[0]);
#endif
	done(sensor, IPMI_IPMI_ERR_VAL(rsp->data[0]), sinfo);
	return IPMI_IPMI_ERR_VAL(rsp->data[0]);
    }

    if (rsp && (rsp->data_len < min_length)) {
	ipmi_log(IPMI_LOG_ERR_INFO,
		 "%ssensor.c(%s): Response was too short, got %d, expected %d",
		 SENSOR_NAME(sensor), name, rsp->data_len, min_length);
	done(sensor, EINVAL, sinfo);
	return EINVAL;
    }

    return 0;
}

typedef struct event_enable_info_s
{
    ipmi_sensor_op_info_t sdata;
    ipmi_event_state_t    state;
    ipmi_sensor_done_cb   done;
    void                  *cb_data;
    int                   do_enable;
    int                   do_disable;
} event_enable_info_t;

static void enables_done_handler(ipmi_sensor_t *sensor,
				 int           err,
				 void          *sinfo)
{
    event_enable_info_t *info = sinfo;

    if (info->done)
	info->done(sensor, err, info->cb_data);
    ipmi_sensor_opq_done(sensor);
    ipmi_mem_free(info);
}

static void
disables_set(ipmi_sensor_t *sensor,
	     int           err,
	     ipmi_msg_t    *rsp,
	     void          *cb_data)
{
    event_enable_info_t *info = cb_data;

    if (sensor_done_check_rsp(sensor, err, rsp, 1, "disables_set",
			      enables_done_handler, info))
	return;

    enables_done_handler(sensor, 0, info);
}

static void
enables_set(ipmi_sensor_t *sensor,
	    int           err,
	    ipmi_msg_t    *rsp,
	    void          *cb_data)
{
    event_enable_info_t *info = cb_data;
    unsigned char       cmd_data[MAX_IPMI_DATA_SIZE];
    ipmi_msg_t          cmd_msg;
    int                 rv;

    if (sensor_done_check_rsp(sensor, err, rsp, 1, "enables_set",
			      enables_done_handler, info))
	return;

    if (info->do_disable) {
	/* Enables were set, now disable all the other ones.  Make
	   sure we only set event bits that we support. */
	uint16_t val1, val2;

	cmd_msg.netfn = IPMI_SENSOR_EVENT_NETFN;
	cmd_msg.cmd = IPMI_SET_SENSOR_EVENT_ENABLE_CMD;
	cmd_msg.data_len = 6;
	cmd_msg.data = cmd_data;
	cmd_data[0] = sensor->num;
	cmd_data[1] = (info->state.status & 0xc0) | (0x02 << 4);
	ipmi_sensor_get_event_masks(sensor, &val1, &val2);
	val1 &= ~info->state.__assertion_events;
	val2 &= ~info->state.__deassertion_events;
	ipmi_set_uint16(cmd_data+2, val1);
	ipmi_set_uint16(cmd_data+4, val2);
	rv = ipmi_sensor_send_command(sensor, sensor->mc, sensor->send_lun,
				      &cmd_msg, disables_set,
				      &(info->sdata), info);
	if (rv) {
	    ipmi_log(IPMI_LOG_ERR_INFO,
		     "%ssensors.c(enables_set):"
		     " Error sending event enable command to clear events: %x",
		     SENSOR_NAME(sensor), rv);
	    enables_done_handler(sensor, rv, info);
	}
    } else {
	/* Just doing enables, we are done. */
	enables_done_handler(sensor, 0, info);
    }
}

static void
event_enable_set_start(ipmi_sensor_t *sensor, int err, void *cb_data)
{
    event_enable_info_t *info = cb_data;
    unsigned char       cmd_data[MAX_IPMI_DATA_SIZE];
    ipmi_msg_t          cmd_msg;
    int                 event_support;
    int                 rv;

    if (sensor_done_check_rsp(sensor, err, NULL, 0, "event_enable_set_start",
			      enables_done_handler, info))
	return;

    event_support = ipmi_sensor_get_event_support(sensor);

    cmd_msg.data = cmd_data;
    cmd_msg.netfn = IPMI_SENSOR_EVENT_NETFN;
    cmd_msg.cmd = IPMI_SET_SENSOR_EVENT_ENABLE_CMD;
    cmd_msg.data = cmd_data;
    cmd_data[0] = sensor->num;
    if (event_support == IPMI_EVENT_SUPPORT_ENTIRE_SENSOR) {
	/* We can only turn on/off the entire sensor, just pass the
           status to the sensor. */
	cmd_data[1] = info->state.status & 0xc0;
	cmd_msg.data_len = 2;
	rv = ipmi_sensor_send_command(sensor, sensor->mc, sensor->send_lun,
				      &cmd_msg, disables_set, &(info->sdata),
				      info);
    } else if (info->do_enable) {
	/* Start by first setting the enables, then set the disables
           in a second operation.  We do this because enables and
           disables cannot both be set at the same time, and it's
           safer to first enable the new events then to disable the
           events we want disabled.  It would be *really* nice if IPMI
           had a way to do this in one operation, such as using 11b in
           the request byte 2 bits 5:4 to say "set the events to
           exactly this state". */
	cmd_data[1] = (info->state.status & 0xc0) | (0x01 << 4);
	cmd_data[2] = info->state.__assertion_events & 0xff;
	cmd_data[3] = info->state.__assertion_events >> 8;
	cmd_data[4] = info->state.__deassertion_events & 0xff;
	cmd_data[5] = info->state.__deassertion_events >> 8;
	cmd_msg.data_len = 6;
	rv = ipmi_sensor_send_command(sensor, sensor->mc, sensor->send_lun,
				      &cmd_msg, enables_set, &(info->sdata),
				      info);
    } else {
	/* We are only doing disables. */
	cmd_data[1] = (info->state.status & 0xc0) | (0x02 << 4);
	cmd_data[2] = info->state.__assertion_events & 0xff;
	cmd_data[3] = info->state.__assertion_events >> 8;
	cmd_data[4] = info->state.__deassertion_events & 0xff;
	cmd_data[5] = info->state.__deassertion_events >> 8;
	cmd_msg.data_len = 6;
	rv = ipmi_sensor_send_command(sensor, sensor->mc, sensor->send_lun,
				      &cmd_msg, disables_set,
				      &(info->sdata), info);
    }
    if (rv) {
	ipmi_log(IPMI_LOG_ERR_INFO,
		 "%ssensor.c(event_enable_set_start):"
		 " Error sending event enable command: %x",
		 SENSOR_NAME(sensor), rv);
	enables_done_handler(sensor, rv, info);
    }
}

static int
check_events_capability(ipmi_sensor_t      *sensor,
			ipmi_event_state_t *states)
{
    int event_support;

    event_support = ipmi_sensor_get_event_support(sensor);
    if ((event_support == IPMI_EVENT_SUPPORT_NONE)
	|| (event_support == IPMI_EVENT_SUPPORT_GLOBAL_ENABLE))
    {
	/* We don't support setting events for this sensor. */
	return EINVAL;
    }

    if ((event_support == IPMI_EVENT_SUPPORT_ENTIRE_SENSOR)
	&& ((states->__assertion_events != 0)
	    || (states->__deassertion_events != 0)))
    {
	/* This sensor does not support individual event states, but
           the user is trying to set them. */
	return EINVAL;
    }

    if (event_support == IPMI_EVENT_SUPPORT_PER_STATE) {
	uint16_t mask1, mask2;

	ipmi_sensor_get_event_masks(sensor, &mask1, &mask2);
	if (((~mask1) & states->__assertion_events)
	    || ((~mask2) & states->__deassertion_events))
	{
	    /* The user is attempting to set a state that the
	       sensor does not support. */
	    return EINVAL;
	}
    }

    return 0;
}

static int
stand_ipmi_sensor_set_event_enables(ipmi_sensor_t         *sensor,
				    ipmi_event_state_t    *states,
				    ipmi_sensor_done_cb   done,
				    void                  *cb_data)
{
    event_enable_info_t *info;
    int                 rv;

    rv = check_events_capability(sensor, states);
    if (rv)
	return rv;

    info = ipmi_mem_alloc(sizeof(*info));
    if (!info)
	return ENOMEM;
    info->state = *states;
    info->done = done;
    info->cb_data = cb_data;
    info->do_enable = 1;
    info->do_disable = 1;
    rv = ipmi_sensor_add_opq(sensor, event_enable_set_start,
			     &(info->sdata), info);
    if (rv)
	ipmi_mem_free(info);
    return rv;
}

static int
stand_ipmi_sensor_enable_events(ipmi_sensor_t         *sensor,
				ipmi_event_state_t    *states,
				ipmi_sensor_done_cb   done,
				void                  *cb_data)
{
    event_enable_info_t *info;
    int                 rv;

    rv = check_events_capability(sensor, states);
    if (rv)
	return rv;

    info = ipmi_mem_alloc(sizeof(*info));
    if (!info)
	return ENOMEM;
    info->state = *states;
    info->done = done;
    info->cb_data = cb_data;
    info->do_enable = 1;
    info->do_disable = 0;
    rv = ipmi_sensor_add_opq(sensor, event_enable_set_start,
			     &(info->sdata), info);
    if (rv)
	ipmi_mem_free(info);
    return rv;
}

static int
stand_ipmi_sensor_disable_events(ipmi_sensor_t         *sensor,
				 ipmi_event_state_t    *states,
				 ipmi_sensor_done_cb   done,
				 void                  *cb_data)
{
    event_enable_info_t *info;
    int                 rv;

    rv = check_events_capability(sensor, states);
    if (rv)
	return rv;

    info = ipmi_mem_alloc(sizeof(*info));
    if (!info)
	return ENOMEM;
    info->state = *states;
    info->done = done;
    info->cb_data = cb_data;
    info->do_enable = 0;
    info->do_disable = 1;
    rv = ipmi_sensor_add_opq(sensor, event_enable_set_start,
			     &(info->sdata), info);
    if (rv)
	ipmi_mem_free(info);
    return rv;
}

typedef struct event_enable_get_info_s
{
    ipmi_sensor_op_info_t        sdata;
    ipmi_event_state_t           state;
    ipmi_sensor_event_enables_cb done;
    void                         *cb_data;
} event_enable_get_info_t;

static void enables_get_done_handler(ipmi_sensor_t *sensor,
				     int           err,
				     void          *sinfo)
{
    event_enable_get_info_t *info = sinfo;

    if (info->done)
	info->done(sensor, err, &info->state, info->cb_data);
    ipmi_sensor_opq_done(sensor);
    ipmi_mem_free(info);
}

static void
enables_get(ipmi_sensor_t *sensor,
	    int           err,
	    ipmi_msg_t    *rsp,
	    void          *cb_data)
{
    event_enable_get_info_t *info = cb_data;

    if (sensor_done_check_rsp(sensor, err, rsp, 2, "enables_get",
			      enables_get_done_handler, info))
	return;

    info->state.status = rsp->data[1] & 0xc0;
    if (rsp->data_len >= 3)
	info->state.__assertion_events = rsp->data[2];
    if (rsp->data_len >= 4)
        info->state.__assertion_events |= rsp->data[3] << 8;
    if (rsp->data_len >= 5)
        info->state.__deassertion_events = rsp->data[4];
    if (rsp->data_len >= 6)
        info->state.__deassertion_events |= rsp->data[5] << 8;

    /* Mask off reserved bits */
    if (sensor->event_reading_type == IPMI_EVENT_READING_TYPE_THRESHOLD) {
	info->state.__assertion_events &= 0x0fff;
        info->state.__deassertion_events &= 0x0fff;
    } else {
	info->state.__assertion_events &= 0x7fff;
        info->state.__deassertion_events &= 0x7fff;
    }

    /* It is possible that there are events set here that are not in
       sensor->mask1 (assertion events) or sensor->mask2 (deassertion
       events).  That's a bug in the sensor; it shouldn't be setting
       those bits.  If it ever comes to the point where we need to
       handle that here, a simple mask operation would do it. */

    enables_get_done_handler(sensor, 0, info);
}

static void
event_enable_get_start(ipmi_sensor_t *sensor, int err, void *cb_data)
{
    event_enable_get_info_t *info = cb_data;
    unsigned char           cmd_data[MAX_IPMI_DATA_SIZE];
    ipmi_msg_t              cmd_msg;
    int                     rv;

    if (sensor_done_check_rsp(sensor, err, NULL, 0, "event_enable_get_start",
			      enables_get_done_handler, info))
	return;

    cmd_msg.data = cmd_data;
    cmd_msg.netfn = IPMI_SENSOR_EVENT_NETFN;
    cmd_msg.cmd = IPMI_GET_SENSOR_EVENT_ENABLE_CMD;
    cmd_msg.data_len = 1;
    cmd_msg.data = cmd_data;
    cmd_data[0] = sensor->num;
    rv = ipmi_sensor_send_command(sensor, sensor->mc, sensor->send_lun,
				  &cmd_msg, enables_get, &(info->sdata), info);
    if (rv) {
	ipmi_log(IPMI_LOG_ERR_INFO,
		 "%ssensor.c(event_enable_get_start):"
		 " Error sending get event enables command: %x",
		 SENSOR_NAME(sensor), rv);
	enables_get_done_handler(sensor, rv, info);
    }
}

static int
stand_ipmi_sensor_get_event_enables(ipmi_sensor_t                *sensor,
				    ipmi_sensor_event_enables_cb done,
				    void                         *cb_data)
{
    event_enable_get_info_t *info;
    int                     rv;

    info = ipmi_mem_alloc(sizeof(*info));
    if (!info)
	return ENOMEM;
    memset(info, 0, sizeof(*info));
    info->done = done;
    info->cb_data = cb_data;
    rv = ipmi_sensor_add_opq(sensor, event_enable_get_start,
			     &(info->sdata), info);
    if (rv)
	ipmi_mem_free(info);
    return rv;
}

typedef struct sensor_rearm_info_s
{
    ipmi_sensor_op_info_t sdata;
    ipmi_event_state_t    state;
    int                   global_enable;
    ipmi_sensor_done_cb   done;
    void                  *cb_data;
} sensor_rearm_info_t;

static void sensor_rearm_done_handler(ipmi_sensor_t *sensor,
				      int           err,
				      void          *sinfo)
{
    sensor_rearm_info_t *info = sinfo;

    if (info->done)
	info->done(sensor, err, info->cb_data);
    ipmi_sensor_opq_done(sensor);
    ipmi_mem_free(info);
}

static void
sensor_rearm(ipmi_sensor_t *sensor,
	     int           err,
	     ipmi_msg_t    *rsp,
	     void          *cb_data)
{
    sensor_rearm_info_t *info = cb_data;

    if (sensor_done_check_rsp(sensor, err, rsp, 1, "sensor_rearm",
			      sensor_rearm_done_handler, info))
	return;

    sensor_rearm_done_handler(sensor, 0, info);
}

static void
sensor_rearm_start(ipmi_sensor_t *sensor, int err, void *cb_data)
{
    sensor_rearm_info_t *info = cb_data;
    unsigned char   cmd_data[MAX_IPMI_DATA_SIZE];
    ipmi_msg_t      cmd_msg;
    int             rv;

    if (sensor_done_check_rsp(sensor, err, NULL, 0, "sensor_rearm_start",
			      sensor_rearm_done_handler, info))
	return;

    cmd_msg.data = cmd_data;
    cmd_msg.netfn = IPMI_SENSOR_EVENT_NETFN;
    cmd_msg.cmd = IPMI_REARM_SENSOR_EVENTS_CMD;
    if (info->global_enable) {
	cmd_msg.data_len = 2;
	cmd_msg.data = cmd_data;
	cmd_data[0] = sensor->num;
	cmd_data[1] = 0; /* Rearm all events. */
    } else {
	cmd_msg.data_len = 6;
	cmd_msg.data = cmd_data;
	cmd_data[0] = sensor->num;
	cmd_data[1] = 0x80; /* Rearm only specific sensors. */
	cmd_data[2] = info->state.__assertion_events & 0xff;
	cmd_data[3] = info->state.__assertion_events >> 8;
	cmd_data[4] = info->state.__deassertion_events & 0xff;
	cmd_data[5] = info->state.__deassertion_events >> 8;
    }
    rv = ipmi_sensor_send_command(sensor, sensor->mc, sensor->send_lun,
				  &cmd_msg, sensor_rearm,
				  &(info->sdata), info);
    if (rv) {
	ipmi_log(IPMI_LOG_ERR_INFO,
		 "%ssensor.c(sensor_rearm_start):"
		 " Error sending rearm command: %x",
		 SENSOR_NAME(sensor), rv);
	sensor_rearm_done_handler(sensor, rv, info);
    }
}

static int
stand_ipmi_sensor_rearm(ipmi_sensor_t       *sensor,
			int                 global_enable,
			ipmi_event_state_t  *state,
			ipmi_sensor_done_cb done,
			void                *cb_data)
{
    sensor_rearm_info_t *info;
    int                 rv;
    
    if (!global_enable && !state)
	return EINVAL;

    info = ipmi_mem_alloc(sizeof(*info));
    if (!info)
	return ENOMEM;
    info->done = done;
    info->cb_data = cb_data;
    info->global_enable = global_enable;
    if (state)
	memcpy(&info->state, state, sizeof(info->state));
    rv = ipmi_sensor_add_opq(sensor, sensor_rearm_start, &(info->sdata), info);
    if (rv)
	ipmi_mem_free(info);
    return rv;
}

typedef struct hyst_get_info_s
{
    ipmi_sensor_op_info_t     sdata;
    ipmi_sensor_hysteresis_cb done;
    void                      *cb_data;
    unsigned int              positive;
    unsigned int              negative;
} hyst_get_info_t;

static void hyst_get_done_handler(ipmi_sensor_t *sensor,
				  int           err,
				  void          *sinfo)
{
    hyst_get_info_t *info = sinfo;

    if (info->done)
	info->done(sensor, err, info->positive, info->negative, info->cb_data);
    ipmi_sensor_opq_done(sensor);
    ipmi_mem_free(info);
}

static void
hyst_get(ipmi_sensor_t *sensor,
	 int           err,
	 ipmi_msg_t    *rsp,
	 void          *cb_data)
{
    hyst_get_info_t *info = cb_data;

    if (sensor_done_check_rsp(sensor, err, rsp, 3, "hyst_get",
			      hyst_get_done_handler, info))
	return;

    info->positive = rsp->data[1];
    info->negative = rsp->data[2];
    hyst_get_done_handler(sensor, 0, info);
}

static void
hyst_get_start(ipmi_sensor_t *sensor, int err, void *cb_data)
{
    hyst_get_info_t *info = cb_data;
    unsigned char       cmd_data[MAX_IPMI_DATA_SIZE];
    ipmi_msg_t          cmd_msg;
    int                 rv;

    if (sensor_done_check_rsp(sensor, err, NULL, 0, "hyst_get_start",
			      hyst_get_done_handler, info))
	return;

    cmd_msg.data = cmd_data;
    cmd_msg.netfn = IPMI_SENSOR_EVENT_NETFN;
    cmd_msg.cmd = IPMI_GET_SENSOR_HYSTERESIS_CMD;
    cmd_msg.data_len = 2;
    cmd_msg.data = cmd_data;
    cmd_data[0] = sensor->num;
    cmd_data[1] = 0xff;
    rv = ipmi_sensor_send_command(sensor, sensor->mc, sensor->send_lun,
				  &cmd_msg, hyst_get, &(info->sdata), info);
    if (rv) {
	ipmi_log(IPMI_LOG_ERR_INFO,
		 "%ssensor.c(hyst_get_start):"
		 " Error sending hysteresis get command: %x",
		 SENSOR_NAME(sensor), rv);
	hyst_get_done_handler(sensor, rv, info);
    }
}

static int
stand_ipmi_sensor_get_hysteresis(ipmi_sensor_t             *sensor,
				 ipmi_sensor_hysteresis_cb done,
				 void                      *cb_data)
{
    hyst_get_info_t *info;
    int             rv;
    
    if (sensor->event_reading_type != IPMI_EVENT_READING_TYPE_THRESHOLD)
	/* Not a threshold sensor, it doesn't have readings. */
	return ENOSYS;

    if ((sensor->hysteresis_support != IPMI_HYSTERESIS_SUPPORT_READABLE)
        && (sensor->hysteresis_support != IPMI_HYSTERESIS_SUPPORT_SETTABLE))
	return ENOSYS;
    
    info = ipmi_mem_alloc(sizeof(*info));
    if (!info)
	return ENOMEM;
    memset(info, 0, sizeof(*info));
    info->done = done;
    info->cb_data = cb_data;
    rv = ipmi_sensor_add_opq(sensor, hyst_get_start, &(info->sdata), info);
    if (rv)
	ipmi_mem_free(info);
    return rv;
}

typedef struct hyst_set_info_s
{
    ipmi_sensor_op_info_t sdata;
    unsigned int          positive, negative;
    ipmi_sensor_done_cb   done;
    void                  *cb_data;
} hyst_set_info_t;

static void hyst_set_done_handler(ipmi_sensor_t *sensor,
				  int           err,
				  void          *sinfo)
{
    hyst_set_info_t *info = sinfo;

    if (info->done)
	info->done(sensor, err, info->cb_data);
    ipmi_sensor_opq_done(sensor);
    ipmi_mem_free(info);
}

static void
hyst_set(ipmi_sensor_t *sensor,
	 int           err,
	 ipmi_msg_t    *rsp,
	 void          *cb_data)
{
    hyst_set_info_t *info = cb_data;

    if (sensor_done_check_rsp(sensor, err, rsp, 1, "hyst_set",
			      hyst_set_done_handler, info))
	return;

    hyst_set_done_handler(sensor, 0, info);
}

static void
hyst_set_start(ipmi_sensor_t *sensor, int err, void *cb_data)
{
    hyst_set_info_t *info = cb_data;
    unsigned char   cmd_data[MAX_IPMI_DATA_SIZE];
    ipmi_msg_t      cmd_msg;
    int             rv;

    if (sensor_done_check_rsp(sensor, err, NULL, 0, "hyst_set_start",
			      hyst_set_done_handler, info))
	return;

    cmd_msg.data = cmd_data;
    cmd_msg.netfn = IPMI_SENSOR_EVENT_NETFN;
    cmd_msg.cmd = IPMI_SET_SENSOR_HYSTERESIS_CMD;
    cmd_msg.data_len = 4;
    cmd_msg.data = cmd_data;
    cmd_data[0] = sensor->num;
    cmd_data[1] = 0xff;
    cmd_data[2] = info->positive;
    cmd_data[3] = info->negative;
    rv = ipmi_sensor_send_command(sensor, sensor->mc, sensor->send_lun,
				  &cmd_msg, hyst_set, &(info->sdata), info);
    if (rv) {
	ipmi_log(IPMI_LOG_ERR_INFO,
		 "%ssensor.c(hyst_set_start):"
		 " Error sending hysteresis set command: %x",
		 SENSOR_NAME(sensor), rv);
	hyst_set_done_handler(sensor, rv, info);
    }
}

static int
stand_ipmi_sensor_set_hysteresis(ipmi_sensor_t       *sensor,
				 unsigned int        positive_hysteresis,
				 unsigned int        negative_hysteresis,
				 ipmi_sensor_done_cb done,
				 void                *cb_data)
{
    hyst_set_info_t *info;
    int             rv;
    
    if (sensor->event_reading_type != IPMI_EVENT_READING_TYPE_THRESHOLD)
	/* Not a threshold sensor, it doesn't have readings. */
	return ENOSYS;

    if (sensor->hysteresis_support != IPMI_HYSTERESIS_SUPPORT_SETTABLE)
	return ENOSYS;
    
    info = ipmi_mem_alloc(sizeof(*info));
    if (!info)
	return ENOMEM;
    info->positive = positive_hysteresis;
    info->negative = negative_hysteresis;
    info->done = done;
    info->cb_data = cb_data;
    rv = ipmi_sensor_add_opq(sensor, hyst_set_start, &(info->sdata), info);
    if (rv)
	ipmi_mem_free(info);
    return rv;
}

typedef struct thresh_get_info_s
{
    ipmi_sensor_op_info_t     sdata;
    ipmi_thresholds_t         th;
    ipmi_sensor_thresholds_cb done;
    void                      *cb_data;
} thresh_get_info_t;

static void thresh_get_done_handler(ipmi_sensor_t *sensor,
				    int           err,
				    void          *sinfo)
{
    thresh_get_info_t *info = sinfo;

    if (info->done)
	info->done(sensor, err, &info->th, info->cb_data);
    ipmi_sensor_opq_done(sensor);
    ipmi_mem_free(info);
}

static void
thresh_get(ipmi_sensor_t *sensor,
	   int           err,
	   ipmi_msg_t    *rsp,
	   void          *cb_data)
{
    thresh_get_info_t  *info = cb_data;
    enum ipmi_thresh_e th;

    if (sensor_done_check_rsp(sensor, err, rsp, 8, "thresh_get",
			      thresh_get_done_handler, info))
	return;
    
    for (th=IPMI_LOWER_NON_CRITICAL; th<=IPMI_UPPER_NON_RECOVERABLE; th++) {
	int rv;
	if (rsp->data[1] & (1 << th)) {
	    info->th.vals[th].status = 1;
	    rv = ipmi_sensor_convert_from_raw(sensor,
					      rsp->data[th+2],
					      &(info->th.vals[th].val));
	    if (rv) {
		ipmi_log(IPMI_LOG_ERR_INFO,
			 "%ssensor.c(thresh_get):"
			 " Could not convert raw threshold value: %x",
			 SENSOR_NAME(sensor), rv);
		thresh_get_done_handler(sensor, rv, info);
		return;
	    }
	} else {
	    info->th.vals[th].status = 0;
	}
    }

    thresh_get_done_handler(sensor, 0, info);
}

int
ipmi_get_default_sensor_thresholds(ipmi_sensor_t     *sensor,
				   ipmi_thresholds_t *th)
{
    enum ipmi_thresh_e thnum;
    int                rv = 0;

    CHECK_SENSOR_LOCK(sensor);

    for (thnum = IPMI_LOWER_NON_CRITICAL;
	 thnum <= IPMI_UPPER_NON_RECOVERABLE;
	 thnum++)
    {
	th->vals[thnum].status = 1;
	rv = ipmi_sensor_convert_from_raw(sensor,
					  sensor->default_thresholds[thnum],
					  &(th->vals[thnum].val));
	if (rv)
	    goto out;
    }
 out:
    return rv;
}

static void
thresh_get_start(ipmi_sensor_t *sensor, int err, void *cb_data)
{
    thresh_get_info_t *info = cb_data;
    unsigned char     cmd_data[MAX_IPMI_DATA_SIZE];
    ipmi_msg_t        cmd_msg;
    int               rv;

    if (sensor_done_check_rsp(sensor, err, NULL, 0, "thresh_get_start",
			      thresh_get_done_handler, info))
	return;
    
    if (sensor->threshold_access == IPMI_THRESHOLD_ACCESS_SUPPORT_FIXED) {
	int thnum;
	/* Thresholds are fixed, they cannot be read. */
	for (thnum = IPMI_LOWER_NON_CRITICAL;
	     thnum <= IPMI_UPPER_NON_RECOVERABLE;
	     thnum++)
	{
	    info->th.vals[thnum].status = 0;
	}
	thresh_get_done_handler(sensor, 0, info);
	return;
    }

    cmd_msg.data = cmd_data;
    cmd_msg.netfn = IPMI_SENSOR_EVENT_NETFN;
    cmd_msg.cmd = IPMI_GET_SENSOR_THRESHOLD_CMD;
    cmd_msg.data_len = 1;
    cmd_msg.data = cmd_data;
    cmd_data[0] = sensor->num;
    rv = ipmi_sensor_send_command(sensor, sensor->mc, sensor->send_lun,
				  &cmd_msg, thresh_get, &(info->sdata), info);
    if (rv) {
	ipmi_log(IPMI_LOG_ERR_INFO,
		 "%ssensor.c(thresh_get_start):"
		 " Error sending threshold get command: %x",
		 SENSOR_NAME(sensor), rv);
	thresh_get_done_handler(sensor, rv, info);
    }
}

static int
stand_ipmi_sensor_get_thresholds(ipmi_sensor_t             *sensor,
				 ipmi_sensor_thresholds_cb done,
				 void                      *cb_data)
{
    thresh_get_info_t *info;
    int               rv;
    
    if (sensor->event_reading_type != IPMI_EVENT_READING_TYPE_THRESHOLD)
	/* Not a threshold sensor, it doesn't have readings. */
	return ENOSYS;

    if (sensor->threshold_access == IPMI_THRESHOLD_ACCESS_SUPPORT_NONE)
	return ENOSYS;
    
    info = ipmi_mem_alloc(sizeof(*info));
    if (!info)
	return ENOMEM;
    info->done = done;
    info->cb_data = cb_data;
    rv = ipmi_sensor_add_opq(sensor, thresh_get_start, &(info->sdata), info);
    if (rv)
	ipmi_mem_free(info);
    return rv;
}

typedef struct thresh_set_info_s
{
    ipmi_sensor_op_info_t sdata;
    ipmi_thresholds_t     th;
    ipmi_sensor_done_cb   done;
    void                  *cb_data;
} thresh_set_info_t;

static void thresh_set_done_handler(ipmi_sensor_t *sensor,
				    int           err,
				    void          *sinfo)
{
    thresh_set_info_t *info = sinfo;

    if (info->done)
	info->done(sensor, err, info->cb_data);
    ipmi_sensor_opq_done(sensor);
    ipmi_mem_free(info);
}

static void
thresh_set(ipmi_sensor_t *sensor,
	   int           err,
	   ipmi_msg_t    *rsp,
	   void          *cb_data)
{
    thresh_set_info_t *info = cb_data;

    if (sensor_done_check_rsp(sensor, err, rsp, 1, "thresh_set",
			      thresh_set_done_handler, info))
	return;

    thresh_set_done_handler(sensor, 0, info);
}

static void
thresh_set_start(ipmi_sensor_t *sensor, int err, void *cb_data)
{
    thresh_set_info_t  *info = cb_data;
    unsigned char      cmd_data[MAX_IPMI_DATA_SIZE];
    ipmi_msg_t         cmd_msg;
    int                rv;
    enum ipmi_thresh_e th;

    if (sensor_done_check_rsp(sensor, err, NULL, 0, "thresh_set_start",
			      thresh_set_done_handler, info))
	return;

    cmd_msg.data = cmd_data;
    cmd_msg.netfn = IPMI_SENSOR_EVENT_NETFN;
    cmd_msg.cmd = IPMI_SET_SENSOR_THRESHOLD_CMD;
    cmd_msg.data_len = 8;
    cmd_msg.data = cmd_data;
    cmd_data[0] = sensor->num;
    cmd_data[1] = 0;
    for (th=IPMI_LOWER_NON_CRITICAL; th<=IPMI_UPPER_NON_RECOVERABLE; th++) {
	int val;
	if (info->th.vals[th].status) {
	    cmd_data[1] |= (1 << th);
	    rv = ipmi_sensor_convert_to_raw(sensor,
					    ROUND_NORMAL,
					    info->th.vals[th].val,
					    &val);
	    if (rv) {
		ipmi_log(IPMI_LOG_ERR_INFO,
			 "%ssensor.c(thresh_set_start):"
			 "Error converting threshold to raw: %x",
			 SENSOR_NAME(sensor), rv);
		thresh_set_done_handler(sensor, rv, info);
		return;
	    }
	} else {
	    val = 0;
	}
	cmd_data[th+2] = val;
    }

    rv = ipmi_sensor_send_command(sensor, sensor->mc, sensor->send_lun,
				  &cmd_msg, thresh_set, &(info->sdata), info);
    if (rv) {
	ipmi_log(IPMI_LOG_ERR_INFO,
		 "%ssensor.c(thresh_set_start):"
		 "Error sending thresholds set command: %x",
		 SENSOR_NAME(sensor), rv);
	thresh_set_done_handler(sensor, rv, info);
    }
}

static int
stand_ipmi_sensor_set_thresholds(ipmi_sensor_t       *sensor,
				 ipmi_thresholds_t   *thresholds,
				 ipmi_sensor_done_cb done,
				 void                *cb_data)
{
    thresh_set_info_t *info;
    int               rv;
    
    if (sensor->event_reading_type != IPMI_EVENT_READING_TYPE_THRESHOLD)
	/* Not a threshold sensor, it doesn't have readings. */
	return ENOSYS;

    if (sensor->threshold_access != IPMI_THRESHOLD_ACCESS_SUPPORT_SETTABLE)
	return ENOSYS;
    
    info = ipmi_mem_alloc(sizeof(*info));
    if (!info)
	return ENOMEM;
    info->th = *thresholds;
    info->done = done;
    info->cb_data = cb_data;
    rv = ipmi_sensor_add_opq(sensor, thresh_set_start, &(info->sdata), info);
    if (rv)
	ipmi_mem_free(info);
    return rv;
}

typedef struct reading_get_info_s
{
    ipmi_sensor_op_info_t      sdata;
    ipmi_sensor_reading_cb     done;
    void                       *cb_data;
    ipmi_states_t              states;
    enum ipmi_value_present_e  value_present;
    unsigned int               raw_val;
    double                     cooked_val;
} reading_get_info_t;

static void reading_get_done_handler(ipmi_sensor_t *sensor,
				     int           err,
				     void          *sinfo)
{
    reading_get_info_t *info = sinfo;

    if (info->done)
	info->done(sensor, err, info->value_present,
		   info->raw_val, info->cooked_val, &info->states,
		   info->cb_data);
    ipmi_sensor_opq_done(sensor);
    ipmi_mem_free(info);
}

static void
reading_get(ipmi_sensor_t *sensor,
	    int           err,
	    ipmi_msg_t    *rsp,
	    void          *rsp_data)
{
    reading_get_info_t        *info = rsp_data;
    int                       rv;

    if (sensor_done_check_rsp(sensor, err, rsp, 3, "reading_get",
			      reading_get_done_handler, info))
	return;

    info->raw_val = rsp->data[1];
    if (sensor->analog_data_format != IPMI_ANALOG_DATA_FORMAT_NOT_ANALOG) {
	rv = ipmi_sensor_convert_from_raw(sensor,
					  info->raw_val,
					  &info->cooked_val);
	if (rv)
	    info->value_present = IPMI_RAW_VALUE_PRESENT;
	else
	    info->value_present = IPMI_BOTH_VALUES_PRESENT;
    } else {
	info->value_present = IPMI_NO_VALUES_PRESENT;
    }

    info->states.__event_messages_enabled = (rsp->data[2] >> 7) & 1;
    info->states.__sensor_scanning_enabled = (rsp->data[2] >> 6) & 1;
    info->states.__initial_update_in_progress = (rsp->data[2] >> 5) & 1;
    if (rsp->data_len >= 4)
	info->states.__states = rsp->data[3];

    reading_get_done_handler(sensor, 0, info);
}

static void
reading_get_start(ipmi_sensor_t *sensor, int err, void *cb_data)
{
    reading_get_info_t *info = cb_data;
    unsigned char      cmd_data[MAX_IPMI_DATA_SIZE];
    ipmi_msg_t         cmd_msg;
    int                rv;

    if (sensor_done_check_rsp(sensor, err, NULL, 0, "reading_get_start",
			      reading_get_done_handler, info))
	return;

    cmd_msg.data = cmd_data;
    cmd_msg.netfn = IPMI_SENSOR_EVENT_NETFN;
    cmd_msg.cmd = IPMI_GET_SENSOR_READING_CMD;
    cmd_msg.data_len = 1;
    cmd_msg.data = cmd_data;
    cmd_data[0] = sensor->num;
    rv = ipmi_sensor_send_command(sensor, sensor->mc, sensor->send_lun,
				  &cmd_msg, reading_get,
				  &(info->sdata), info);
    if (rv) {
	ipmi_log(IPMI_LOG_ERR_INFO,
		 "%ssensor.c(reading_get_start):"
		 "Error sending reading get command: %x",
		 SENSOR_NAME(sensor), rv);
	reading_get_done_handler(sensor, rv, info);
    }
}

static int
stand_ipmi_sensor_get_reading(ipmi_sensor_t          *sensor,
			      ipmi_sensor_reading_cb done,
			      void                   *cb_data)
{
    reading_get_info_t *info;
    int                rv;
    
    if (sensor->event_reading_type != IPMI_EVENT_READING_TYPE_THRESHOLD)
	/* Not a threshold sensor, it doesn't have readings. */
	return ENOSYS;
    if (!sensor->readable)
	return ENOSYS;

    info = ipmi_mem_alloc(sizeof(*info));
    if (!info)
	return ENOMEM;
    info->done = done;
    info->cb_data = cb_data;
    info->value_present = IPMI_NO_VALUES_PRESENT;
    info->raw_val = 0;
    info->cooked_val = 0.0;
    ipmi_init_states(&info->states);
    rv = ipmi_sensor_add_opq(sensor, reading_get_start, &(info->sdata), info);
    if (rv)
	ipmi_mem_free(info);
    return rv;
}


typedef struct states_get_info_s
{
    ipmi_sensor_op_info_t sdata;
    ipmi_sensor_states_cb done;
    void                  *cb_data;
    ipmi_states_t         states;
} states_get_info_t;

static void states_get_done_handler(ipmi_sensor_t *sensor,
				    int           err,
				    void          *sinfo)
{
    states_get_info_t *info = sinfo;

    if (info->done)
	info->done(sensor, err, &info->states, info->cb_data);
    ipmi_sensor_opq_done(sensor);
    ipmi_mem_free(info);
}

static void
states_get(ipmi_sensor_t *sensor,
	   int           err,
	   ipmi_msg_t    *rsp,
	   void          *cb_data)
{
    states_get_info_t *info = cb_data;

    if (sensor_done_check_rsp(sensor, err, rsp, 3, "states_get",
			      states_get_done_handler, info))
	return;

    info->states.__event_messages_enabled = (rsp->data[2] >> 7) & 1;
    info->states.__sensor_scanning_enabled = (rsp->data[2] >> 6) & 1;
    info->states.__initial_update_in_progress = (rsp->data[2] >> 5) & 1;

    if (rsp->data_len >= 4)
	info->states.__states |= rsp->data[3];
    if (rsp->data_len >= 5)
	info->states.__states |= rsp->data[4] << 8;

    states_get_done_handler(sensor, 0, info);
}

static void
states_get_start(ipmi_sensor_t *sensor, int err, void *cb_data)
{
    states_get_info_t *info = cb_data;
    unsigned char     cmd_data[MAX_IPMI_DATA_SIZE];
    ipmi_msg_t        cmd_msg;
    int               rv;

    if (sensor_done_check_rsp(sensor, err, NULL, 0, "states_get_start",
			      states_get_done_handler, info))
	return;

    cmd_msg.data = cmd_data;
    cmd_msg.netfn = IPMI_SENSOR_EVENT_NETFN;
    cmd_msg.cmd = IPMI_GET_SENSOR_READING_CMD;
    cmd_msg.data_len = 1;
    cmd_msg.data = cmd_data;
    cmd_data[0] = sensor->num;
    rv = ipmi_sensor_send_command(sensor, sensor->mc, sensor->send_lun,
				  &cmd_msg, states_get,
				  &(info->sdata), info);
    if (rv) {
	ipmi_log(IPMI_LOG_ERR_INFO,
		 "%sstates.c(states_get_start):"
		 " Error sending states get command: %x",
		 SENSOR_NAME(sensor), rv);
	states_get_done_handler(sensor, rv, info);
    }
}

static int
stand_ipmi_sensor_get_states(ipmi_sensor_t         *sensor,
			     ipmi_sensor_states_cb done,
			     void                  *cb_data)
{
    states_get_info_t *info;
    int               rv;
    
    if (sensor->event_reading_type == IPMI_EVENT_READING_TYPE_THRESHOLD)
	/* A threshold sensor, it doesn't have states. */
	return ENOSYS;
    if (!sensor->readable)
	return ENOSYS;

    info = ipmi_mem_alloc(sizeof(*info));
    if (!info)
	return ENOMEM;
    info->done = done;
    info->cb_data = cb_data;
    ipmi_init_states(&info->states);
    rv = ipmi_sensor_add_opq(sensor, states_get_start, &(info->sdata), info);
    if (rv)
	ipmi_mem_free(info);
    return rv;
}

/***********************************************************************
 *
 * Various data conversion stuff.
 *
 **********************************************************************/

static double c_linear(double val)
{
    return val;
}

static double c_log2(double val)
{
    return log(val) / 0.69314718 /* log(2) */;
}

static double c_exp10(double val)
{
    return pow(10.0, val);
}

static double c_exp2(double val)
{
    return pow(2.0, val);
}

static double c_1_over_x(double val)
{
    return 1.0 / val;
}

static double c_sqr(double val)
{
    return pow(val, 2.0);
}

static double c_cube(double val)
{
    return pow(val, 3.0);
}

static double c_1_over_cube(double val)
{
    return 1.0 / pow(val, 3.0);
}

typedef double (*linearizer)(double val);
static linearizer linearize[12] =
{
    c_linear,
    log,
    log10,
    c_log2,
    exp,
    c_exp10,
    c_exp2,
    c_1_over_x,
    c_sqr,
    c_cube,
    sqrt,
    c_1_over_cube
};

static int
sign_extend(int m, int bits)
{
    if (m & (1 << (bits-1)))
	return m | (-1 << bits);
    else
	return m & (~(-1 << bits));
}

static int
stand_ipmi_sensor_convert_from_raw(ipmi_sensor_t *sensor,
				   int           val,
				   double        *result)
{
    double m, b, b_exp, r_exp, fval;
    linearizer c_func;

    if (sensor->event_reading_type != IPMI_EVENT_READING_TYPE_THRESHOLD)
	/* Not a threshold sensor, it doesn't have readings. */
	return ENOSYS;

    if (sensor->linearization == IPMI_LINEARIZATION_NONLINEAR)
	c_func = c_linear;
    else if (sensor->linearization <= 11)
	c_func = linearize[sensor->linearization];
    else
	return EINVAL;

    val &= 0xff;

    m = sensor->conv[val].m;
    b = sensor->conv[val].b;
    r_exp = sensor->conv[val].r_exp;
    b_exp = sensor->conv[val].b_exp;

    switch(sensor->analog_data_format) {
	case IPMI_ANALOG_DATA_FORMAT_UNSIGNED:
	    fval = val;
	    break;
	case IPMI_ANALOG_DATA_FORMAT_1_COMPL:
	    val = sign_extend(val, 8);
	    if (val < 0)
		val += 1;
	    fval = val;
	    break;
	case IPMI_ANALOG_DATA_FORMAT_2_COMPL:
	    fval = sign_extend(val, 8);
	    break;
	default:
	    return EINVAL;
    }

    *result = c_func(((m * fval) + (b * pow(10, b_exp))) * pow(10, r_exp));
    return 0;
}

static int
stand_ipmi_sensor_convert_to_raw(ipmi_sensor_t     *sensor,
				 enum ipmi_round_e rounding,
				 double            val,
				 int               *result)
{
    double cval;
    int    lowraw, highraw, raw, maxraw, minraw, next_raw;
    int    rv;

    if (sensor->event_reading_type != IPMI_EVENT_READING_TYPE_THRESHOLD)
	/* Not a threshold sensor, it doesn't have readings. */
	return ENOSYS;

    switch(sensor->analog_data_format) {
	case IPMI_ANALOG_DATA_FORMAT_UNSIGNED:
	    lowraw = 0;
	    highraw = 255;
	    minraw = 0;
	    maxraw = 255;
	    next_raw = 128;
	    break;
	case IPMI_ANALOG_DATA_FORMAT_1_COMPL:
	    lowraw = -127;
	    highraw = 127;
	    minraw = -127;
	    maxraw = 127;
	    next_raw = 0;
	    break;
	case IPMI_ANALOG_DATA_FORMAT_2_COMPL:
	    lowraw = -128;
	    highraw = 127;
	    minraw = -128;
	    maxraw = 127;
	    next_raw = 0;
	    break;
	default:
	    return EINVAL;
    }

    /* We do a binary search for the right value.  Yuck, but I don't
       have a better plan that will work with non-linear sensors. */
    do {
	raw = next_raw;
	rv = ipmi_sensor_convert_from_raw(sensor, raw, &cval);
	if (rv)
	    return rv;

	if (cval < val) {
	    next_raw = ((highraw - raw) / 2) + raw;
	    lowraw = raw;
	} else {
	    next_raw = ((raw - lowraw) / 2) + lowraw;
	    highraw = raw;
	}
    } while (raw != next_raw);

    /* The above loop gets us to within 1 of what it should be, we
       have to look at rounding to make the final decision. */
    switch (rounding)
    {
	case ROUND_NORMAL:
	    if (val > cval) {
		if (raw < maxraw) {
		    double nval;
		    rv = ipmi_sensor_convert_from_raw(sensor, raw+1, &nval);
		    if (rv)
			return rv;
		    nval = cval + ((nval - cval) / 2.0);
		    if (val >= nval)
			raw++;
		}
	    } else {
		if (raw > minraw) {
		    double pval;
		    rv = ipmi_sensor_convert_from_raw(sensor, raw-1, &pval);
		    if (rv)
			return rv;
		    pval = pval + ((cval - pval) / 2.0);
		    if (val < pval)
			raw--;
		}
	    }
	    break;
	case ROUND_UP:
	    if ((val > cval) && (raw < maxraw)) {
		raw++;
	    }
	    break;
	case ROUND_DOWN:
	    if ((val < cval) && (raw > minraw)) {
		raw--;
	    }
	    break;
    }

    if (sensor->analog_data_format == IPMI_ANALOG_DATA_FORMAT_1_COMPL) {
	if (raw < 0)
	    raw -= 1;
    }

    *result = raw & 0xff;
    return 0;
}

static int
stand_ipmi_sensor_get_tolerance(ipmi_sensor_t *sensor,
				int           val,
				double        *tolerance)
{
    double m, r_exp, fval;
    linearizer c_func;

    if (sensor->event_reading_type != IPMI_EVENT_READING_TYPE_THRESHOLD)
	/* Not a threshold sensor, it doesn't have readings. */
	return ENOSYS;

    if (sensor->linearization == IPMI_LINEARIZATION_NONLINEAR)
	c_func = c_linear;
    else if (sensor->linearization <= 11)
	c_func = linearize[sensor->linearization];
    else
	return EINVAL;

    val &= 0xff;

    m = sensor->conv[val].m;
    r_exp = sensor->conv[val].r_exp;

    fval = sign_extend(val, 8);

    *tolerance = c_func(((m * fval) / 2.0) * pow(10, r_exp));
    return 0;
}

/* Returns accuracy as a percentage value. */
static int
stand_ipmi_sensor_get_accuracy(ipmi_sensor_t *sensor, int val, double *accuracy)
{
    double a, a_exp;

    if (sensor->event_reading_type != IPMI_EVENT_READING_TYPE_THRESHOLD)
	/* Not a threshold sensor, it doesn't have readings. */
	return ENOSYS;

    val &= 0xff;

    a = sensor->conv[val].accuracy;
    a_exp = sensor->conv[val].r_exp;

    *accuracy = (a * pow(10, a_exp)) / 100.0;
    return 0;
}

static const char *
stand_ipmi_sensor_reading_name_string(ipmi_sensor_t *sensor, int offset)
{
    return ipmi_get_reading_name(sensor->event_reading_type,
				 sensor->sensor_type,
				 offset);
}

/***********************************************************************
 *
 * The standard callback structure
 *
 **********************************************************************/

const ipmi_sensor_cbs_t ipmi_standard_sensor_cb =
{
    .ipmi_sensor_set_event_enables = stand_ipmi_sensor_set_event_enables,
    .ipmi_sensor_get_event_enables = stand_ipmi_sensor_get_event_enables,
    .ipmi_sensor_enable_events     = stand_ipmi_sensor_enable_events,
    .ipmi_sensor_disable_events    = stand_ipmi_sensor_disable_events,
    .ipmi_sensor_rearm             = stand_ipmi_sensor_rearm,

    .ipmi_sensor_convert_from_raw  = stand_ipmi_sensor_convert_from_raw,
    .ipmi_sensor_convert_to_raw    = stand_ipmi_sensor_convert_to_raw,
    .ipmi_sensor_get_accuracy      = stand_ipmi_sensor_get_accuracy,
    .ipmi_sensor_get_tolerance     = stand_ipmi_sensor_get_tolerance,
    .ipmi_sensor_get_hysteresis    = stand_ipmi_sensor_get_hysteresis,
    .ipmi_sensor_set_hysteresis    = stand_ipmi_sensor_set_hysteresis,
    .ipmi_sensor_set_thresholds    = stand_ipmi_sensor_set_thresholds,
    .ipmi_sensor_get_thresholds    = stand_ipmi_sensor_get_thresholds,
    .ipmi_sensor_get_reading       = stand_ipmi_sensor_get_reading,

    .ipmi_sensor_get_states        = stand_ipmi_sensor_get_states,
    .ipmi_sensor_reading_name_string = stand_ipmi_sensor_reading_name_string,
};

void
ipmi_sensor_get_callbacks(ipmi_sensor_t *sensor, ipmi_sensor_cbs_t *cbs)
{
    *cbs = sensor->cbs;
}

void
ipmi_sensor_set_callbacks(ipmi_sensor_t *sensor, ipmi_sensor_cbs_t *cbs)
{
    sensor->cbs = *cbs;
}

/***********************************************************************
 *
 * Polymorphic calls to the callback handlers.
 *
 **********************************************************************/

int
ipmi_sensor_set_event_enables(ipmi_sensor_t         *sensor,
			      ipmi_event_state_t    *states,
			      ipmi_sensor_done_cb   done,
			      void                  *cb_data)
{
    if (!sensor_ok_to_use(sensor))
	return ECANCELED;
      
    CHECK_SENSOR_LOCK(sensor);

    if (!sensor->cbs.ipmi_sensor_set_event_enables)
	return ENOSYS;
    return sensor->cbs.ipmi_sensor_set_event_enables(sensor,
						     states,
						     done,
						     cb_data);
}

int
ipmi_sensor_enable_events(ipmi_sensor_t         *sensor,
			  ipmi_event_state_t    *states,
			  ipmi_sensor_done_cb   done,
			  void                  *cb_data)
{
    if (!sensor_ok_to_use(sensor))
	return ECANCELED;
      
    CHECK_SENSOR_LOCK(sensor);

    if (!sensor->cbs.ipmi_sensor_enable_events)
	return ENOSYS;
    return sensor->cbs.ipmi_sensor_enable_events(sensor,
						 states,
						 done,
						 cb_data);
}

int
ipmi_sensor_disable_events(ipmi_sensor_t         *sensor,
			   ipmi_event_state_t    *states,
			   ipmi_sensor_done_cb   done,
			   void                  *cb_data)
{
    if (!sensor_ok_to_use(sensor))
	return ECANCELED;
      
    CHECK_SENSOR_LOCK(sensor);

    if (!sensor->cbs.ipmi_sensor_disable_events)
	return ENOSYS;
    return sensor->cbs.ipmi_sensor_disable_events(sensor,
						  states,
						  done,
						  cb_data);
}

int
ipmi_sensor_rearm(ipmi_sensor_t       *sensor,
		  int                 global_enable,
		  ipmi_event_state_t  *state,
		  ipmi_sensor_done_cb done,
		  void                *cb_data)
{
    if (!sensor_ok_to_use(sensor))
	return ECANCELED;
      
    CHECK_SENSOR_LOCK(sensor);

    if (!sensor->cbs.ipmi_sensor_rearm)
	return ENOSYS;
    return sensor->cbs.ipmi_sensor_rearm(sensor,
					 global_enable,
					 state,
					 done,
					 cb_data);
}

int
ipmi_sensor_get_event_enables(ipmi_sensor_t                *sensor,
			      ipmi_sensor_event_enables_cb done,
			      void                         *cb_data)
{
    if (!sensor_ok_to_use(sensor))
	return ECANCELED;
      
    CHECK_SENSOR_LOCK(sensor);

    if (!sensor->cbs.ipmi_sensor_get_event_enables)
	return ENOSYS;
    return sensor->cbs.ipmi_sensor_get_event_enables(sensor,
						     done,
						     cb_data);
}

int
ipmi_sensor_get_hysteresis(ipmi_sensor_t             *sensor,
			   ipmi_sensor_hysteresis_cb done,
			   void                      *cb_data)
{
    if (!sensor_ok_to_use(sensor))
	return ECANCELED;
      
    CHECK_SENSOR_LOCK(sensor);

    if (!sensor->cbs.ipmi_sensor_get_hysteresis)
	return ENOSYS;
    return sensor->cbs.ipmi_sensor_get_hysteresis(sensor,
						  done,
						  cb_data);
}

int
ipmi_sensor_set_hysteresis(ipmi_sensor_t       *sensor,
			   unsigned int        positive_hysteresis,
			   unsigned int        negative_hysteresis,
			   ipmi_sensor_done_cb done,
			   void                *cb_data)
{
    if (!sensor_ok_to_use(sensor))
	return ECANCELED;
      
    CHECK_SENSOR_LOCK(sensor);

    if (!sensor->cbs.ipmi_sensor_set_hysteresis)
	return ENOSYS;
    return sensor->cbs.ipmi_sensor_set_hysteresis(sensor,
						  positive_hysteresis,
						  negative_hysteresis,
						  done,
						  cb_data);
}

int
ipmi_sensor_get_thresholds(ipmi_sensor_t             *sensor,
			   ipmi_sensor_thresholds_cb done,
			   void                      *cb_data)
{
    if (!sensor_ok_to_use(sensor))
	return ECANCELED;
      
    CHECK_SENSOR_LOCK(sensor);

    if (!sensor->cbs.ipmi_sensor_get_thresholds)
	return ENOSYS;
    return sensor->cbs.ipmi_sensor_get_thresholds(sensor, done, cb_data);
}

int
ipmi_sensor_set_thresholds(ipmi_sensor_t       *sensor,
			   ipmi_thresholds_t   *thresholds,
			   ipmi_sensor_done_cb done,
			   void                *cb_data)
{
    if (!sensor_ok_to_use(sensor))
	return ECANCELED;
      
    CHECK_SENSOR_LOCK(sensor);

    if (!sensor->cbs.ipmi_sensor_set_thresholds)
	return ENOSYS;
    return sensor->cbs.ipmi_sensor_set_thresholds(sensor, thresholds,
						  done, cb_data);
}

int
ipmi_sensor_get_reading(ipmi_sensor_t          *sensor,
			ipmi_sensor_reading_cb done,
			void                   *cb_data)
{
    if (!sensor_ok_to_use(sensor))
	return ECANCELED;
      
    CHECK_SENSOR_LOCK(sensor);

    if (!sensor->cbs.ipmi_sensor_get_reading)
	return ENOSYS;
    return sensor->cbs.ipmi_sensor_get_reading(sensor, done, cb_data);
}

int
ipmi_sensor_get_states(ipmi_sensor_t         *sensor,
		       ipmi_sensor_states_cb done,
		       void                  *cb_data)
{
    if (!sensor_ok_to_use(sensor))
	return ECANCELED;
      
    CHECK_SENSOR_LOCK(sensor);

    if (!sensor->cbs.ipmi_sensor_get_states)
	return ENOSYS;
    return sensor->cbs.ipmi_sensor_get_states(sensor, done, cb_data);
}

const char *
ipmi_sensor_reading_name_string(ipmi_sensor_t *sensor, int offset)
{
    CHECK_SENSOR_LOCK(sensor);

    if (!sensor->cbs.ipmi_sensor_reading_name_string)
	return NULL;
    return sensor->cbs.ipmi_sensor_reading_name_string(sensor, offset);
}

int
ipmi_sensor_convert_from_raw(ipmi_sensor_t *sensor,
			     int           val,
			     double        *result)
{
    CHECK_SENSOR_LOCK(sensor);

    if (!sensor->cbs.ipmi_sensor_convert_from_raw)
	return ENOSYS;
    return sensor->cbs.ipmi_sensor_convert_from_raw(sensor, val, result);
}

int
ipmi_sensor_convert_to_raw(ipmi_sensor_t     *sensor,
			   enum ipmi_round_e rounding,
			   double            val,
			   int               *result)
{
    CHECK_SENSOR_LOCK(sensor);

    if (!sensor->cbs.ipmi_sensor_convert_to_raw)
	return ENOSYS;
    return sensor->cbs.ipmi_sensor_convert_to_raw(sensor,
						  rounding,
						  val,
						  result);
}

int
ipmi_sensor_get_tolerance(ipmi_sensor_t *sensor, int val, double *tolerance)
{
    CHECK_SENSOR_LOCK(sensor);

    if (!sensor->cbs.ipmi_sensor_get_tolerance)
	return ENOSYS;
    return sensor->cbs.ipmi_sensor_get_tolerance(sensor, val, tolerance);
}

/* Returns accuracy as a percentage value. */
int
ipmi_sensor_get_accuracy(ipmi_sensor_t *sensor, int val, double *accuracy)
{
    CHECK_SENSOR_LOCK(sensor);

    if (!sensor->cbs.ipmi_sensor_get_accuracy)
	return ENOSYS;
    return sensor->cbs.ipmi_sensor_get_accuracy(sensor, val, accuracy);
}


/***********************************************************************
 *
 * Convenience functions that take ids.
 *
 **********************************************************************/

typedef struct sensor_id_events_enable_set_s
{
    ipmi_event_state_t    *states;
    ipmi_sensor_done_cb   done;
    void                  *cb_data;
    int                   rv;
} sensor_id_events_enable_set_t;

static void
sensor_id_set_event_enables_cb(ipmi_sensor_t *sensor, void *cb_data)
{
    sensor_id_events_enable_set_t *info = cb_data;

    info->rv = ipmi_sensor_set_event_enables(sensor, info->states,
					     info->done, info->cb_data);
}

int
ipmi_sensor_id_set_event_enables(ipmi_sensor_id_t      sensor_id,
				 ipmi_event_state_t    *states,
				 ipmi_sensor_done_cb   done,
				 void                  *cb_data)
{
    sensor_id_events_enable_set_t info;
    int                           rv;

    info.states = states;
    info.done = done;
    info.cb_data = cb_data;
    rv = ipmi_sensor_pointer_cb(sensor_id, sensor_id_set_event_enables_cb,
				&info);
    if (!rv)
	rv = info.rv;
    return rv;
}

typedef struct sensor_id_events_enable_s
{
    ipmi_event_state_t    *states;
    ipmi_sensor_done_cb   done;
    void                  *cb_data;
    int                   rv;
} sensor_id_events_enable_t;

static void
sensor_id_enable_events_cb(ipmi_sensor_t *sensor, void *cb_data)
{
    sensor_id_events_enable_t *info = cb_data;

    info->rv = ipmi_sensor_enable_events(sensor,
					 info->states,
					 info->done,
					 info->cb_data);
}

int
ipmi_sensor_id_enable_events(ipmi_sensor_id_t      sensor_id,
			     ipmi_event_state_t    *states,
			     ipmi_sensor_done_cb   done,
			     void                  *cb_data)
{
    sensor_id_events_enable_t info;
    int                       rv;

    info.states = states;
    info.done = done;
    info.cb_data = cb_data;
    rv = ipmi_sensor_pointer_cb(sensor_id, sensor_id_enable_events_cb, &info);
    if (!rv)
	rv = info.rv;
    return rv;
}

typedef struct sensor_id_events_disable_s
{
    ipmi_event_state_t    *states;
    ipmi_sensor_done_cb   done;
    void                  *cb_data;
    int                   rv;
} sensor_id_events_disable_t;

static void
sensor_id_disable_events_cb(ipmi_sensor_t *sensor, void *cb_data)
{
    sensor_id_events_disable_t *info = cb_data;

    info->rv = ipmi_sensor_disable_events(sensor,
					  info->states,
					  info->done,
					  info->cb_data);
}

int
ipmi_sensor_id_disable_events(ipmi_sensor_id_t      sensor_id,
			      ipmi_event_state_t    *states,
			      ipmi_sensor_done_cb   done,
			      void                  *cb_data)
{
    sensor_id_events_enable_t info;
    int                       rv;

    info.states = states;
    info.done = done;
    info.cb_data = cb_data;
    rv = ipmi_sensor_pointer_cb(sensor_id, sensor_id_disable_events_cb, &info);
    if (!rv)
	rv = info.rv;
    return rv;
}

typedef struct sensor_id_events_enable_get_s
{
    ipmi_sensor_event_enables_cb done;
    void                         *cb_data;
    int                          rv;
} sensor_id_events_enable_get_t;

static void
sensor_id_get_event_enables_cb(ipmi_sensor_t *sensor, void *cb_data)
{
    sensor_id_events_enable_get_t *info = cb_data;

    info->rv = ipmi_sensor_get_event_enables(sensor,
					     info->done,
					     info->cb_data);
}

int
ipmi_sensor_id_get_event_enables(ipmi_sensor_id_t             sensor_id,
				 ipmi_sensor_event_enables_cb done,
				 void                         *cb_data)
{
    sensor_id_events_enable_get_t info;
    int                           rv;

    info.done = done;
    info.cb_data = cb_data;
    rv = ipmi_sensor_pointer_cb(sensor_id, sensor_id_get_event_enables_cb,
				&info);
    if (!rv)
	rv = info.rv;
    return rv;
}

typedef struct sensor_id_rearm_s
{
    int                 global_enable;
    ipmi_event_state_t  *state;
    ipmi_sensor_done_cb done;
    void                *cb_data;
    int                 rv;
} sensor_id_rearm_t;

static void
sensor_id_rearm_cb(ipmi_sensor_t *sensor, void *cb_data)
{
    sensor_id_rearm_t *info = cb_data;

    info->rv = ipmi_sensor_rearm(sensor,
				 info->global_enable,
				 info->state,
				 info->done,
				 info->cb_data);
}

int
ipmi_sensor_id_rearm(ipmi_sensor_id_t    sensor_id,
		     int                 global_enable,
		     ipmi_event_state_t  *state,
		     ipmi_sensor_done_cb done,
		     void                *cb_data)
{
    sensor_id_rearm_t info;
    int               rv;

    info.global_enable = global_enable;
    info.state = state;
    info.done = done;
    info.cb_data = cb_data;
    rv = ipmi_sensor_pointer_cb(sensor_id, sensor_id_rearm_cb, &info);
    if (!rv)
	rv = info.rv;
    return rv;
}

typedef struct sensor_id_get_hysteresis_s
{
    ipmi_sensor_hysteresis_cb done;
    void                      *cb_data;
    int                       rv;
} sensor_id_get_hysteresis_t;

static void
sensor_id_get_hysteresis_cb(ipmi_sensor_t *sensor, void *cb_data)
{
    sensor_id_get_hysteresis_t *info = cb_data;

    info->rv = ipmi_sensor_get_hysteresis(sensor,
					  info->done,
					  info->cb_data);
}

int
ipmi_sensor_id_get_hysteresis(ipmi_sensor_id_t          sensor_id,
			      ipmi_sensor_hysteresis_cb done,
			      void                      *cb_data)
{
    sensor_id_get_hysteresis_t info;
    int                        rv;

    info.done = done;
    info.cb_data = cb_data;
    rv = ipmi_sensor_pointer_cb(sensor_id, sensor_id_get_hysteresis_cb, &info);
    if (!rv)
	rv = info.rv;
    return rv;
}

typedef struct sensor_id_set_hysteresis_s
{
    unsigned int        positive_hysteresis;
    unsigned int        negative_hysteresis;
    ipmi_sensor_done_cb done;
    void                *cb_data;
    int                 rv;
} sensor_id_set_hysteresis_t;

static void
sensor_id_set_hysteresis_cb(ipmi_sensor_t *sensor, void *cb_data)
{
    sensor_id_set_hysteresis_t *info = cb_data;

    info->rv = ipmi_sensor_set_hysteresis(sensor,
					  info->positive_hysteresis,
					  info->negative_hysteresis,
					  info->done,
					  info->cb_data);
}

int
ipmi_sensor_id_set_hysteresis(ipmi_sensor_id_t    sensor_id,
			      unsigned int        positive_hysteresis,
			      unsigned int        negative_hysteresis,
			      ipmi_sensor_done_cb done,
			      void                *cb_data)
{
    sensor_id_set_hysteresis_t info;
    int                        rv;

    info.positive_hysteresis = positive_hysteresis;
    info.negative_hysteresis = negative_hysteresis;
    info.done = done;
    info.cb_data = cb_data;
    rv = ipmi_sensor_pointer_cb(sensor_id, sensor_id_set_hysteresis_cb, &info);
    if (!rv)
	rv = info.rv;
    return rv;
}

typedef struct sensor_id_thresholds_set_s
{
    ipmi_thresholds_t   *thresholds;
    ipmi_sensor_done_cb done;
    void                *cb_data;
    int                 rv;
} sensor_id_thresholds_set_t;

static void
sensor_id_set_thresholds_cb(ipmi_sensor_t *sensor, void *cb_data)
{
    sensor_id_thresholds_set_t *info = cb_data;

    info->rv = ipmi_sensor_set_thresholds(sensor,
					  info->thresholds,
					  info->done,
					  info->cb_data);
}

int
ipmi_sensor_id_set_thresholds(ipmi_sensor_id_t    sensor_id,
			      ipmi_thresholds_t   *thresholds,
			      ipmi_sensor_done_cb done,
			      void                *cb_data)
{
    sensor_id_thresholds_set_t info;
    int               rv;

    info.thresholds = thresholds;
    info.done = done;
    info.cb_data = cb_data;
    rv = ipmi_sensor_pointer_cb(sensor_id, sensor_id_set_thresholds_cb, &info);
    if (!rv)
	rv = info.rv;
    return rv;
}

typedef struct sensor_id_thresholds_get_s
{
    ipmi_sensor_thresholds_cb done;
    void                      *cb_data;
    int                       rv;
} sensor_id_thresholds_get_t;

static void
sensor_id_get_thresholds_cb(ipmi_sensor_t *sensor, void *cb_data)
{
    sensor_id_thresholds_get_t *info = cb_data;

    info->rv = ipmi_sensor_get_thresholds(sensor,
					  info->done,
					  info->cb_data);
}

int
ipmi_sensor_id_get_thresholds(ipmi_sensor_id_t          sensor_id,
			      ipmi_sensor_thresholds_cb done,
			      void                      *cb_data)
{
    sensor_id_thresholds_get_t info;
    int                        rv;

    info.done = done;
    info.cb_data = cb_data;
    rv = ipmi_sensor_pointer_cb(sensor_id, sensor_id_get_thresholds_cb, &info);
    if (!rv)
	rv = info.rv;
    return rv;
}

typedef struct sensor_id_reading_get_s
{
    ipmi_sensor_reading_cb done;
    void                   *cb_data;
    int                    rv;
} sensor_id_reading_get_t;

static void
sensor_id_get_reading_cb(ipmi_sensor_t *sensor, void *cb_data)
{
    sensor_id_reading_get_t *info = cb_data;
    
    info->rv = ipmi_sensor_get_reading(sensor,
				       info->done,
				       info->cb_data);
}

int
ipmi_sensor_id_get_reading(ipmi_sensor_id_t       sensor_id,
			   ipmi_sensor_reading_cb done,
			   void                   *cb_data)
{
    sensor_id_reading_get_t info;
    int                     rv;

    info.done = done;
    info.cb_data = cb_data;
    rv = ipmi_sensor_pointer_cb(sensor_id, sensor_id_get_reading_cb, &info);
    if (!rv)
	rv = info.rv;
    return rv;
}

typedef struct sensor_id_states_get_s
{
    ipmi_sensor_states_cb done;
    void                  *cb_data;
    int                   rv;
} sensor_id_states_get_t;

static void
sensor_id_get_states_cb(ipmi_sensor_t *sensor, void *cb_data)
{
    sensor_id_states_get_t *info = cb_data;

    info->rv = ipmi_sensor_get_states(sensor,
				      info->done,
				      info->cb_data);
}

int
ipmi_sensor_id_get_states(ipmi_sensor_id_t      sensor_id,
			  ipmi_sensor_states_cb done,
			  void                  *cb_data)
{
    sensor_id_states_get_t info;
    int                    rv;

    info.done = done;
    info.cb_data = cb_data;
    rv = ipmi_sensor_pointer_cb(sensor_id, sensor_id_get_states_cb, &info);
    if (!rv)
	rv = info.rv;
    return rv;
}


#ifdef IPMI_CHECK_LOCKS
void
i__ipmi_check_sensor_lock(const ipmi_sensor_t *sensor)
{
    if (!sensor)
	return;

    if (!DEBUG_LOCKS)
	return;

    CHECK_ENTITY_LOCK(sensor->entity);
    CHECK_MC_LOCK(sensor->mc);

    if (sensor->usecount == 0)
	ipmi_report_lock_error(ipmi_domain_get_os_hnd(sensor->domain),
			       "sensor not locked when it should have been");
}
#endif

/***********************************************************************
 *
 * Cruft
 *
 **********************************************************************/

int
ipmi_sensor_threshold_assertion_event_supported(
    ipmi_sensor_t               *sensor,
    enum ipmi_thresh_e          event,
    enum ipmi_event_value_dir_e dir,
    int                         *val)
{
    int idx;

    CHECK_SENSOR_LOCK(sensor);

    if (sensor->event_reading_type != IPMI_EVENT_READING_TYPE_THRESHOLD)
	/* Not a threshold sensor, it doesn't have readings. */
	return ENOSYS;

    idx = (event * 2) + dir;
    if (idx > 11)
	return EINVAL;

    *val = IPMI_SENSOR_GET_MASK_BIT(sensor->mask1, idx);
    return 0;
}

int
ipmi_sensor_threshold_deassertion_event_supported(
    ipmi_sensor_t               *sensor,
    enum ipmi_thresh_e          event,
    enum ipmi_event_value_dir_e dir,
    int                         *val)
{
    int idx;

    CHECK_SENSOR_LOCK(sensor);

    if (sensor->event_reading_type != IPMI_EVENT_READING_TYPE_THRESHOLD)
	/* Not a threshold sensor, it doesn't have readings. */
	return ENOSYS;

    idx = (event * 2) + dir;
    if (idx > 11)
	return 0;

    *val = IPMI_SENSOR_GET_MASK_BIT(sensor->mask2, idx);
    return 0;
}

int
ipmi_sensor_discrete_assertion_event_supported(ipmi_sensor_t *sensor,
					       int           event,
					       int           *val)
{
    CHECK_SENSOR_LOCK(sensor);

    if (sensor->event_reading_type == IPMI_EVENT_READING_TYPE_THRESHOLD)
	/* A threshold sensor, it doesn't have events. */
	return ENOSYS;

    if (event > 14)
	return EINVAL;

    *val = IPMI_SENSOR_GET_MASK_BIT(sensor->mask1, event);
    return 0;
}

int
ipmi_sensor_discrete_deassertion_event_supported(ipmi_sensor_t *sensor,
						 int           event,
						 int           *val)
{
    CHECK_SENSOR_LOCK(sensor);

    if (sensor->event_reading_type == IPMI_EVENT_READING_TYPE_THRESHOLD)
	/* A threshold sensor, it doesn't have events. */
	return ENOSYS;

    if (event > 14)
	return EINVAL;

    *val = IPMI_SENSOR_GET_MASK_BIT(sensor->mask2, event);
    return 0;
}

int
ipmi_sensor_events_enable_get(ipmi_sensor_t                *sensor,
			      ipmi_sensor_event_enables_cb done,
			      void                         *cb_data)
{
    return ipmi_sensor_get_event_enables(sensor, done, cb_data);
}

int
ipmi_sensor_events_disable(ipmi_sensor_t         *sensor,
			   ipmi_event_state_t    *states,
			   ipmi_sensor_done_cb   done,
			   void                  *cb_data)
{
    return ipmi_sensor_disable_events(sensor, states, done, cb_data);
}

int
ipmi_sensor_events_enable(ipmi_sensor_t         *sensor,
			  ipmi_event_state_t    *states,
			  ipmi_sensor_done_cb   done,
			  void                  *cb_data)
{
    return ipmi_sensor_enable_events(sensor, states, done, cb_data);
}

int ipmi_sensor_events_enable_set(ipmi_sensor_t         *sensor,
				  ipmi_event_state_t    *states,
				  ipmi_sensor_done_cb   done,
				  void                  *cb_data)
{
    return ipmi_sensor_set_event_enables(sensor, states, done, cb_data);
}

int
ipmi_sensor_id_events_enable_set(ipmi_sensor_id_t      sensor_id,
				 ipmi_event_state_t    *states,
				 ipmi_sensor_done_cb   done,
				 void                  *cb_data)
{
    return ipmi_sensor_id_set_event_enables(sensor_id, states, done, cb_data);
}

int
ipmi_sensor_id_events_enable(ipmi_sensor_id_t      sensor_id,
			     ipmi_event_state_t    *states,
			     ipmi_sensor_done_cb   done,
			     void                  *cb_data)
{
    return ipmi_sensor_id_enable_events(sensor_id, states, done, cb_data);
}

int
ipmi_sensor_id_events_disable(ipmi_sensor_id_t      sensor_id,
			      ipmi_event_state_t    *states,
			      ipmi_sensor_done_cb   done,
			      void                  *cb_data)
{
    return ipmi_sensor_id_disable_events(sensor_id, states, done, cb_data);
}

int
ipmi_sensor_id_events_enable_get(ipmi_sensor_id_t             sensor_id,
				 ipmi_sensor_event_enables_cb done,
				 void                         *cb_data)
{
    return ipmi_sensor_id_get_event_enables(sensor_id, done, cb_data);
}

int
ipmi_states_get(ipmi_sensor_t         *sensor,
		ipmi_sensor_states_cb done,
		void                  *cb_data)
{
    return ipmi_sensor_get_states(sensor, done, cb_data);
}

int
ipmi_reading_get(ipmi_sensor_t          *sensor,
		 ipmi_sensor_reading_cb done,
		 void                   *cb_data)
{
    return ipmi_sensor_get_reading(sensor, done, cb_data);
}

int
ipmi_thresholds_set(ipmi_sensor_t       *sensor,
		    ipmi_thresholds_t   *thresholds,
		    ipmi_sensor_done_cb done,
		    void                *cb_data)
{
    return ipmi_sensor_set_thresholds(sensor, thresholds, done, cb_data);
}

int
ipmi_thresholds_get(ipmi_sensor_t             *sensor,
		    ipmi_sensor_thresholds_cb done,
		    void                      *cb_data)
{
    return ipmi_sensor_get_thresholds(sensor, done, cb_data);
}

int
ipmi_sensor_id_thresholds_set(ipmi_sensor_id_t    sensor_id,
			      ipmi_thresholds_t   *thresholds,
			      ipmi_sensor_done_cb done,
			      void                *cb_data)
{
    return ipmi_sensor_id_set_thresholds(sensor_id, thresholds, done, cb_data);
}

int
ipmi_sensor_id_thresholds_get(ipmi_sensor_id_t          sensor_id,
			      ipmi_sensor_thresholds_cb done,
			      void                      *cb_data)
{
    return ipmi_sensor_id_get_thresholds(sensor_id, done, cb_data);
}

int
ipmi_sensor_id_reading_get(ipmi_sensor_id_t       sensor_id,
			   ipmi_sensor_reading_cb done,
			   void                   *cb_data)
{
    return ipmi_sensor_id_get_reading(sensor_id, done, cb_data);
}

int
ipmi_sensor_id_states_get(ipmi_sensor_id_t      sensor_id,
			  ipmi_sensor_states_cb done,
			  void                  *cb_data)
{
    return ipmi_sensor_id_get_states(sensor_id, done, cb_data);
}

int
ipmi_discrete_event_readable(ipmi_sensor_t *sensor,
			     int           event,
			     int           *val)
{
    CHECK_SENSOR_LOCK(sensor);

    if (sensor->event_reading_type == IPMI_EVENT_READING_TYPE_THRESHOLD)
	/* A threshold sensor, it doesn't have events. */
	return ENOSYS;

    if (event > 14)
	return EINVAL;

    *val = IPMI_SENSOR_GET_MASK_BIT(sensor->mask3, event);
    return 0;
}
