/*
 * ipmi.c
 *
 * MontaVista IPMI generic code
 *
 * 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 <config.h>

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>

#include <OpenIPMI/os_handler.h>
#include <OpenIPMI/ipmi_conn.h>
#include <OpenIPMI/ipmi_err.h>
#include <OpenIPMI/ipmi_auth.h>
#include <OpenIPMI/ipmi_lan.h>
#include <OpenIPMI/ipmi_smi.h>
#include <OpenIPMI/ipmi_msgbits.h>

#include <OpenIPMI/internal/ipmi_domain.h>
#include <OpenIPMI/internal/ipmi_mc.h>
#include <OpenIPMI/internal/ipmi_int.h>
#include <OpenIPMI/internal/ipmi_oem.h>
#include <OpenIPMI/internal/locked_list.h>

#if defined(DEBUG_MSG) || defined(DEBUG_RAWMSG)
static void
dump_hex(const void *vdata, int len)
{
    const unsigned char *data = vdata;
    int i;
    for (i=0; i<len; i++) {
	if ((i != 0) && ((i % 16) == 0)) {
	    ipmi_log(IPMI_LOG_DEBUG_CONT, "\n  ");
	}
	ipmi_log(IPMI_LOG_DEBUG_CONT, " %2.2x", data[i]);
    }
}
#endif
static os_handler_t *ipmi_os_handler;

unsigned int i__ipmi_log_mask = 0;

os_handler_t *
ipmi_get_global_os_handler(void)
{
    return ipmi_os_handler;
}
    
int
ipmi_create_global_lock(ipmi_lock_t **new_lock)
{
    return ipmi_create_lock_os_hnd(ipmi_os_handler, new_lock);
}

int
ipmi_create_lock(ipmi_domain_t *domain, ipmi_lock_t **new_lock)
{
    return ipmi_create_lock_os_hnd(ipmi_domain_get_os_hnd(domain), new_lock);
}

void
ipmi_log(enum ipmi_log_type_e log_type, const char *format, ...)
{
    va_list ap;

    va_start(ap, format);
    if (ipmi_os_handler && ipmi_os_handler->vlog)
	ipmi_os_handler->vlog(ipmi_os_handler, log_type, format, ap);
    else {
	vfprintf(stderr, format, ap);
	switch (log_type) {
	case IPMI_LOG_DEBUG_START:
	case IPMI_LOG_DEBUG_CONT:
	    break;
	default:
	    fprintf(stderr, "\n");
	}
    }
    va_end(ap);
}

static long seq = 0;
static os_hnd_lock_t *seq_lock;
long
ipmi_get_seq(void)
{
    long rv;

    if (seq_lock)
	ipmi_os_handler->lock(ipmi_os_handler, seq_lock);
    rv = seq;
    seq++;
    if (seq_lock)
	ipmi_os_handler->unlock(ipmi_os_handler, seq_lock);

    return rv;
}

void
ipmi_event_state_init(ipmi_event_state_t *events)
{
    events->status = 0;
    events->__assertion_events = 0;
    events->__deassertion_events = 0;
}

void
ipmi_threshold_event_clear(ipmi_event_state_t          *events,
			   enum ipmi_thresh_e          type,
			   enum ipmi_event_value_dir_e value_dir,
			   enum ipmi_event_dir_e       dir)
{
    if (dir == IPMI_ASSERTION) {
	events->__assertion_events &= ~(1 << (type*2+value_dir));
    } else {
	events->__deassertion_events &= ~(1 << (type*2+value_dir));
    }
}

void
ipmi_threshold_event_set(ipmi_event_state_t          *events,
			 enum ipmi_thresh_e          type,
			 enum ipmi_event_value_dir_e value_dir,
			 enum ipmi_event_dir_e       dir)
{
    if (dir == IPMI_ASSERTION) {
	events->__assertion_events |= 1 << (type*2+value_dir);
    } else {
	events->__deassertion_events |= 1 << (type*2+value_dir);
    }
}

int
ipmi_is_threshold_event_set(ipmi_event_state_t          *events,
			    enum ipmi_thresh_e          type,
			    enum ipmi_event_value_dir_e value_dir,
			    enum ipmi_event_dir_e       dir)
{
    if (dir == IPMI_ASSERTION) {
	return (events->__assertion_events & (1 << (type*2+value_dir))) != 0;
    } else {
	return (events->__deassertion_events & (1 << (type*2+value_dir))) != 0;
    }
}

void
ipmi_discrete_event_clear(ipmi_event_state_t    *events,
			  int                   event_offset,
			  enum ipmi_event_dir_e dir)
{
    if (dir == IPMI_ASSERTION) {
	events->__assertion_events &= ~(1 << event_offset);
    } else {
	events->__deassertion_events &= ~(1 << event_offset);
    }
}

void
ipmi_discrete_event_set(ipmi_event_state_t    *events,
			int                   event_offset,
			enum ipmi_event_dir_e dir)
{
    if (dir == IPMI_ASSERTION) {
	events->__assertion_events |= 1 << event_offset;
    } else {
	events->__deassertion_events |= 1 << event_offset;
    }
}

int
ipmi_is_discrete_event_set(ipmi_event_state_t    *events,
			   int                   event_offset,
			   enum ipmi_event_dir_e dir)
{
    if (dir == IPMI_ASSERTION) {
	return (events->__assertion_events & (1 << event_offset)) != 0;
    } else {
	return (events->__deassertion_events & (1 << event_offset)) != 0;
    }
}

#define IPMI_SENSOR_EVENTS_ENABLED	0x80
#define IPMI_SENSOR_SCANNING_ENABLED	0x40
#define IPMI_SENSOR_BUSY		0x20

unsigned int ipmi_event_state_size(void)
{
    return sizeof(ipmi_event_state_t);
}

void
ipmi_copy_event_state(ipmi_event_state_t *dest, ipmi_event_state_t *src)
{
    *dest = *src;
}

void
ipmi_event_state_set_events_enabled(ipmi_event_state_t *events, int val)
{
    if (val)
	events->status |= IPMI_SENSOR_EVENTS_ENABLED;
    else
	events->status &= ~IPMI_SENSOR_EVENTS_ENABLED;
}

int
ipmi_event_state_get_events_enabled(ipmi_event_state_t *events)
{
    return (events->status >> 7) & 1;
}

void
ipmi_event_state_set_scanning_enabled(ipmi_event_state_t *events, int val)
{
    if (val)
	events->status |= IPMI_SENSOR_SCANNING_ENABLED;
    else
	events->status &= ~IPMI_SENSOR_SCANNING_ENABLED;
}

int
ipmi_event_state_get_scanning_enabled(ipmi_event_state_t *events)
{
    return (events->status >> 6) & 1;
}

void
ipmi_event_state_set_busy(ipmi_event_state_t *events, int val)
{
    if (val)
	events->status |= IPMI_SENSOR_BUSY;
    else
	events->status &= ~IPMI_SENSOR_BUSY;
}

int
ipmi_event_state_get_busy(ipmi_event_state_t *events)
{
    return (events->status >> 5) & 1;
}

unsigned int ipmi_thresholds_size(void)
{
    return sizeof(ipmi_thresholds_t);
}

void
ipmi_copy_thresholds(ipmi_thresholds_t *dest, ipmi_thresholds_t *src)
{
    *dest = *src;
}

int ipmi_thresholds_init(ipmi_thresholds_t *th)
{
    int i;
    for (i=0; i<6; i++)
	th->vals[i].status = 0;
    return 0;
}

int ipmi_threshold_set(ipmi_thresholds_t  *th,
		       ipmi_sensor_t      *sensor,
		       enum ipmi_thresh_e threshold,
		       double             value)
{
    int rv = 0;

    if (threshold > IPMI_UPPER_NON_RECOVERABLE)
	return EINVAL;

    if (sensor) {
	int val;
	rv = ipmi_sensor_threshold_settable(sensor, threshold, &val);
	if (rv)
	    return rv;
	if (!val)
	    return ENOSYS;
    }

    th->vals[threshold].status = 1;
    th->vals[threshold].val = value;
    return 0;
}

int ipmi_threshold_get(ipmi_thresholds_t  *th,
		       enum ipmi_thresh_e threshold,
		       double             *value)
{
    if (threshold > IPMI_UPPER_NON_RECOVERABLE)
	return EINVAL;

    if (th->vals[threshold].status) {
	*value = th->vals[threshold].val;
	return 0;
    } else {
	return ENOSYS;
    }
}

unsigned int ipmi_states_size(void)
{
    return sizeof(ipmi_states_t);
}

void
ipmi_copy_states(ipmi_states_t *dest, ipmi_states_t *src)
{
    *dest = *src;
}

void
ipmi_init_states(ipmi_states_t *states)
{
    states->__event_messages_enabled = 0;
    states->__sensor_scanning_enabled = 0;
    states->__initial_update_in_progress = 0;
    states->__states = 0;
}

int
ipmi_is_event_messages_enabled(ipmi_states_t *states)
{
    return states->__event_messages_enabled;
}

void
ipmi_set_event_messages_enabled(ipmi_states_t *states, int val)
{
    states->__event_messages_enabled = val;
}

int
ipmi_is_sensor_scanning_enabled(ipmi_states_t *states)
{
    return states->__sensor_scanning_enabled;
}

void
ipmi_set_sensor_scanning_enabled(ipmi_states_t *states, int val)
{
    states->__sensor_scanning_enabled = val;
}

int
ipmi_is_initial_update_in_progress(ipmi_states_t *states)
{
    return states->__initial_update_in_progress;
}

void
ipmi_set_initial_update_in_progress(ipmi_states_t *states, int val)
{
    states->__initial_update_in_progress = val;
}

int
ipmi_is_state_set(ipmi_states_t *states,
		  int           state_num)
{
    return (states->__states & (1 << state_num)) != 0;
}

void
ipmi_set_state(ipmi_states_t *states,
	       int           state_num,
	       int           val)
{
    if (val)
	states->__states |= 1 << state_num;
    else
	states->__states &= ~(1 << state_num);
}

int
ipmi_is_threshold_out_of_range(ipmi_states_t      *states,
			       enum ipmi_thresh_e thresh)
{
    return (states->__states & (1 << thresh)) != 0;
}

void
ipmi_set_threshold_out_of_range(ipmi_states_t      *states,
				enum ipmi_thresh_e thresh,
				int                val)
{
    if (val)
	states->__states |= 1 << thresh;
    else
	states->__states &= ~(1 << thresh);
}

void ipmi_oem_force_conn_init(void);
int ipmi_oem_motorola_mxp_init(void);
int ipmi_oem_intel_init(void);
int ipmi_oem_kontron_conn_init(void);
int ipmi_oem_atca_conn_init(void);
int ipmi_oem_atca_init(void);
int init_oem_test(void);
int i_ipmi_smi_init(os_handler_t *os_hnd);
int i_ipmi_lan_init(os_handler_t *os_hnd);
int ipmi_malloc_init(os_handler_t *os_hnd);
int i_ipmi_rakp_init(void);
int i_ipmi_aes_cbc_init(void);
int i_ipmi_hmac_init(void);
int i_ipmi_md5_init(void);
int i_ipmi_fru_init(void);
int i_ipmi_normal_fru_init(void);
int i_ipmi_fru_spd_decoder_init(void);
void i_ipmi_fru_shutdown(void);
void i_ipmi_normal_fru_shutdown(void);
void i_ipmi_fru_spd_decoder_shutdown(void);
int i_ipmi_sol_init(void);

void i_ipmi_rakp_shutdown(void);
void i_ipmi_aes_cbc_shutdown(void);
void i_ipmi_hmac_shutdown(void);
void i_ipmi_md5_shutdown(void);
void ipmi_oem_atca_conn_shutdown(void);
void ipmi_oem_intel_shutdown(void);
void ipmi_oem_kontron_conn_shutdown(void);
void ipmi_oem_atca_shutdown(void);
int i_ipmi_smi_shutdown(void);
int i_ipmi_lan_shutdown(void);
void i_ipmi_sol_shutdown(void);


static locked_list_t *con_type_list;
static int ipmi_initialized;

int
ipmi_init(os_handler_t *handler)
{
    int rv;

    if (ipmi_initialized)
	return 0;

    ipmi_os_handler = handler;

    /* Set up memory allocation first */
    ipmi_malloc_init(handler);

    /* Set up logging in malloc code. */
    ipmi_malloc_log = ipmi_log;

    con_type_list = locked_list_alloc(handler);

    rv = i_ipmi_conn_init(handler);
    if (rv)
	return rv;

    ipmi_initialized = 1;

    if (handler->create_lock) {
	rv = handler->create_lock(handler, &seq_lock);
	if (rv)
	    goto out_err;
    } else {
	seq_lock = NULL;
    }

#ifdef HAVE_OPENIPMI_SMI
    rv = i_ipmi_smi_init(handler);
    if (rv)
	goto out_err;
#endif

    rv = i_ipmi_lan_init(handler);
    if (rv)
	goto out_err;

    i_ipmi_domain_init();
    i_ipmi_mc_init();

    rv = i_ipmi_rakp_init();
    if (rv)
	goto out_err;
    rv = i_ipmi_aes_cbc_init();
    if (rv)
	goto out_err;
    rv = i_ipmi_hmac_init();
    if (rv)
	goto out_err;
    rv = i_ipmi_md5_init();
    if (rv)
	goto out_err;

    rv = i_ipmi_fru_init();
    if (rv)
	goto out_err;

    rv = i_ipmi_normal_fru_init();
    if (rv)
	goto out_err;

    rv = i_ipmi_fru_spd_decoder_init();
    if (rv)
	goto out_err;

    rv = i_ipmi_sol_init();
    if (rv)
	goto out_err;

    /* Call the OEM handlers. */
    ipmi_oem_force_conn_init();
    ipmi_oem_motorola_mxp_init();
    ipmi_oem_intel_init();
    ipmi_oem_kontron_conn_init();
    ipmi_oem_atca_conn_init();
    ipmi_oem_atca_init();
    init_oem_test();

    return 0;

 out_err:
    ipmi_shutdown();
    return rv;
}

void
ipmi_shutdown(void)
{
    if (! ipmi_initialized)
	return;

    i_ipmi_rakp_shutdown();
    i_ipmi_aes_cbc_shutdown();
    i_ipmi_hmac_shutdown();
    i_ipmi_md5_shutdown();
    i_ipmi_sol_shutdown();
    i_ipmi_lan_shutdown();
#ifdef HAVE_OPENIPMI_SMI
    i_ipmi_smi_shutdown();
#endif
    ipmi_oem_atca_shutdown();
    ipmi_oem_atca_conn_shutdown();
    ipmi_oem_intel_shutdown();
    ipmi_oem_kontron_conn_shutdown();
    i_ipmi_mc_shutdown();
    i_ipmi_domain_shutdown();
    i_ipmi_fru_spd_decoder_shutdown();
    i_ipmi_conn_shutdown();
    i_ipmi_normal_fru_shutdown();
    i_ipmi_fru_shutdown();
    if (seq_lock)
	ipmi_os_handler->destroy_lock(ipmi_os_handler, seq_lock);
    if (con_type_list)
	locked_list_destroy(con_type_list);

    ipmi_os_handler = NULL;

    ipmi_initialized = 0;
}

#define CHECK_ARG \
    do { \
        if (*curr_arg >= arg_count) { \
	    rv = EINVAL; \
	    goto out_err; \
        } \
    } while(0)

int
ipmi_parse_args(int         *curr_arg,
		int         arg_count,
		char        * const *args,
		ipmi_args_t **iargs)
{
    int rv;
    CHECK_ARG;

    if (strcmp(args[*curr_arg], "smi") == 0) {
	/* Format is unchanged. */
	return ipmi_parse_args2(curr_arg, arg_count, args, iargs);
    } else if (strcmp(args[*curr_arg], "lan") == 0) {
	/* Convert the format over to the new one and call the new
	   handler. */
	char *largs[16];
	int  c = 0;
	int  newcarg = 0;
	char *addr, *addr2 = NULL;
	char *port, *port2 = NULL;
	char *auth;
	char *priv;
	char *username;
	char *pw;

	largs[c] = args[*curr_arg];
	(*curr_arg)++; CHECK_ARG;
	c++;

	addr = args[*curr_arg];
	(*curr_arg)++; CHECK_ARG;

	port = args[*curr_arg];
	(*curr_arg)++; CHECK_ARG;

	if ((strcmp(args[*curr_arg], "none") != 0)
	    && (strcmp(args[*curr_arg], "md2") != 0)
	    && (strcmp(args[*curr_arg], "md5") != 0)
	    && (strcmp(args[*curr_arg], "straight") != 0)
	    && (strcmp(args[*curr_arg], "rmcp+") != 0))
	{
	    addr2 = args[*curr_arg];
	    (*curr_arg)++; CHECK_ARG;

	    port2 = args[*curr_arg];
	    (*curr_arg)++; CHECK_ARG;
	}

	auth = args[*curr_arg];
	(*curr_arg)++; CHECK_ARG;

	priv = args[*curr_arg];
	(*curr_arg)++; CHECK_ARG;

	username = args[*curr_arg];
	(*curr_arg)++; CHECK_ARG;

	pw = args[*curr_arg];
	(*curr_arg)++;

	largs[c] = "-U"; c++; largs[c] = username; c++;
	largs[c] = "-P"; c++; largs[c] = pw; c++;
	largs[c] = "-A"; c++; largs[c] = auth; c++;
	largs[c] = "-L"; c++; largs[c] = priv; c++;
	if (addr2) {
	    largs[c] = "-s"; c++;
	}
	largs[c] = "-p"; c++; largs[c] = port; c++;
	if (port2) {
	    largs[c] = "-p2"; c++; largs[c] = port2; c++;
	}
	largs[c] = addr; c++;
	if (addr2) {
	    largs[c] = addr2; c++;
	}

	return ipmi_parse_args2(&newcarg, c, largs, iargs);
    } else {
	rv = EINVAL;
	goto out_err;
    }
    return 0;

 out_err:
    return rv;
}

struct ipmi_args_s
{
    ipmi_args_free_cb     free;
    ipmi_args_connect_cb  connect;
    ipmi_args_get_val_cb  get_val;
    ipmi_args_set_val_cb  set_val;
    ipmi_args_copy_cb     copy;
    ipmi_args_validate_cb validate;
    ipmi_args_free_val_cb free_val;
    ipmi_args_get_type_cb get_type;
};

struct ipmi_con_setup_s
{
    ipmi_con_parse_args_cb parse;
    ipmi_con_get_help_cb   help;
    ipmi_con_alloc_args_cb alloc;
};
ipmi_con_setup_t *
i_ipmi_alloc_con_setup(ipmi_con_parse_args_cb parse,
		       ipmi_con_get_help_cb   help,
		       ipmi_con_alloc_args_cb alloc)
{
    ipmi_con_setup_t *rv;

    rv = ipmi_mem_alloc(sizeof(*rv));
    if (!rv)
	return NULL;
    memset(rv, 0, sizeof(*rv));
    rv->parse = parse;
    rv->help = help;
    rv->alloc = alloc;
    return rv;
}

void
i_ipmi_free_con_setup(ipmi_con_setup_t *v)
{
    ipmi_mem_free(v);
}

typedef struct con_type_alloc_data_s
{
    char        *con_type;
    ipmi_args_t *args;
    int         err;
} con_type_alloc_data_t;

static int
con_type_alloc_handler(void *cb_data, void *item1, void *item2)
{
    ipmi_con_setup_t      *setup = item2;
    con_type_alloc_data_t *data = cb_data;

    if (strcmp(data->con_type, item1) == 0) {
	data->args = setup->alloc();
	if (!data->args)
	    data->err = ENOMEM;
	else
	    data->err = 0;
	return LOCKED_LIST_ITER_STOP;
    }
    return LOCKED_LIST_ITER_CONTINUE;
}

int
ipmi_args_alloc(char *con_type, ipmi_args_t **args)
{
    con_type_alloc_data_t data;

    data.con_type = con_type;
    data.err = EINVAL;
    locked_list_iterate(con_type_list, con_type_alloc_handler, &data);
    if (!data.err)
	*args = data.args;
    return data.err;
}

typedef struct con_type_help_data_s
{
    ipmi_iter_help_cb help_cb;
    void              *cb_data;
} con_type_help_data_t;

static int
con_type_help_handler(void *cb_data, void *item1, void *item2)
{
    ipmi_con_setup_t     *setup = item2;
    con_type_help_data_t *data = cb_data;

    data->help_cb(item1, setup->help(), data->cb_data);
    return LOCKED_LIST_ITER_CONTINUE;
}

void
ipmi_parse_args_iter_help(ipmi_iter_help_cb help_cb, void *cb_data)
{
    con_type_help_data_t data;

    data.help_cb = help_cb;
    data.cb_data = cb_data;
    locked_list_iterate(con_type_list, con_type_help_handler, &data);
}

typedef struct con_setup_data_s
{
    const char  *name;
    int         err;

    int         *curr_arg;
    int         arg_count;
    char        * const *args;
    ipmi_args_t *iargs;
} con_setup_data_t;

static int
con_type_check_parse(void *cb_data, void *item1, void *item2)
{
    con_setup_data_t *data = cb_data;

    if (strcmp(item1, data->name) == 0) {
	ipmi_con_setup_t *setup = item2;
	data->err = setup->parse(data->curr_arg, data->arg_count, data->args,
				 &data->iargs);
	return LOCKED_LIST_ITER_STOP;
    }
    return LOCKED_LIST_ITER_CONTINUE;
}

int
ipmi_parse_args2(int         *curr_arg,
		 int         arg_count,
		 char        * const *args,
		 ipmi_args_t **iargs)
{
    con_setup_data_t data;
    int              rv;

    CHECK_ARG;
    data.err = EINVAL;
    data.name = args[*curr_arg];
    (*curr_arg)++;
    data.curr_arg = curr_arg;
    data.arg_count = arg_count;
    data.args = args;
    locked_list_iterate(con_type_list, con_type_check_parse, &data);
    if (data.err) {
	rv = data.err;
	goto out_err;
    }

    *iargs = data.iargs;
    return 0;

 out_err:
    return rv;
}

void
ipmi_free_args(ipmi_args_t *args)
{
    if (args->free)
	args->free(args);
    ipmi_mem_free(args);
}

int
ipmi_args_setup_con(ipmi_args_t  *args,
		    os_handler_t *handlers,
		    void         *user_data,
		    ipmi_con_t   **con)
{
    return args->connect(args, handlers, user_data, con);
}

const char *
ipmi_args_get_type(ipmi_args_t *args)
{
    return args->get_type(args);
}

int
ipmi_args_get_val(ipmi_args_t  *args,
		  unsigned int argnum,
		  const char   **name,
		  const char   **type,
		  const char   **help,
		  char         **value,
		  const char   ***range)
{
    return args->get_val(args, argnum, name, type, help, value, range);
}

int
ipmi_args_set_val(ipmi_args_t  *args,
		  unsigned int argnum,
		  const char   *name,
		  const char   *value)
{
    return args->set_val(args, argnum, name, value);
}

void
ipmi_args_free_str(ipmi_args_t *args, char *str)
{
    args->free_val(args, str);
}

ipmi_args_t *
ipmi_args_copy(ipmi_args_t *args)
{
    return args->copy(args);
}

int
ipmi_args_validate(ipmi_args_t *args, int *argnum)
{
    return args->validate(args, argnum);
}

ipmi_args_t *
i_ipmi_args_alloc(ipmi_args_free_cb     free,
		 ipmi_args_connect_cb  connect,
		 ipmi_args_get_val_cb  get_val,
		 ipmi_args_set_val_cb  set_val,
		 ipmi_args_copy_cb     copy,
		 ipmi_args_validate_cb validate,
		 ipmi_args_free_val_cb free_val,
		 ipmi_args_get_type_cb get_type,
		 unsigned int          extra_data_len)
{
    ipmi_args_t *val;

    val = ipmi_mem_alloc(sizeof(*val) + extra_data_len);
    if (!val)
	return NULL;
    memset(val, 0, sizeof(*val) + extra_data_len);
    val->free = free;
    val->connect = connect;
    val->get_val = get_val;
    val->set_val = set_val;
    val->copy = copy;
    val->validate = validate;
    val->free_val = free_val;
    val->get_type = get_type;
    return val;
}

void *
i_ipmi_args_get_extra_data(ipmi_args_t *args)
{
    return ((char *) args) + sizeof(*args);
}

int
i_ipmi_register_con_type(const char *name, ipmi_con_setup_t *setup)
{
    if (locked_list_add(con_type_list, (void *) name, setup))
	return 0;
    else
	return ENOMEM;
}

typedef struct con_type_check_remover_s
{
    const char *name;
    int        err;
} con_type_check_remover_t;

static int
con_type_check_remove(void *cb_data, void *item1, void *item2)
{
    con_type_check_remover_t *data = cb_data;

    if (strcmp(item1, data->name) == 0) {
	locked_list_remove(con_type_list, item1, item2);
	data->err = 0;
	return LOCKED_LIST_ITER_STOP;
    }
    return LOCKED_LIST_ITER_CONTINUE;
}

int
i_ipmi_unregister_con_type(const char *name, ipmi_con_setup_t *setup)
{
    con_type_check_remover_t data;

    data.err = EINVAL;
    data.name = name;
    locked_list_iterate(con_type_list, con_type_check_remove, &data);
    return data.err;
}

int
ipmi_parse_options(ipmi_open_option_t *option,
		   char               *arg)
{
    if (strcmp(arg, "-noall") == 0) {
	option->option = IPMI_OPEN_OPTION_ALL;
	option->ival = 0;
    } else if (strcmp(arg, "-all") == 0) {
	option->option = IPMI_OPEN_OPTION_ALL;
	option->ival = 1;
    } else if (strcmp(arg, "-nosdrs") == 0) {
	option->option = IPMI_OPEN_OPTION_SDRS;
	option->ival = 0;
    } else if (strcmp(arg, "-sdrs") == 0) {
	option->option = IPMI_OPEN_OPTION_SDRS;
	option->ival = 1;
    } else if (strcmp(arg, "-nofrus") == 0) {
	option->option = IPMI_OPEN_OPTION_FRUS;
	option->ival = 0;
    } else if (strcmp(arg, "-frus") == 0) {
	option->option = IPMI_OPEN_OPTION_FRUS;
	option->ival = 1;
    } else if (strcmp(arg, "-nosel") == 0) {
	option->option = IPMI_OPEN_OPTION_SEL;
	option->ival = 0;
    } else if (strcmp(arg, "-sel") == 0) {
	option->option = IPMI_OPEN_OPTION_SEL;
	option->ival = 1;
    } else if (strcmp(arg, "-noipmbscan") == 0) {
	option->option = IPMI_OPEN_OPTION_IPMB_SCAN;
	option->ival = 0;
    } else if (strcmp(arg, "-ipmbscan") == 0) {
	option->option = IPMI_OPEN_OPTION_IPMB_SCAN;
	option->ival = 1;
    } else if (strcmp(arg, "-nooeminit") == 0) {
	option->option = IPMI_OPEN_OPTION_OEM_INIT;
	option->ival = 0;
    } else if (strcmp(arg, "-oeminit") == 0) {
	option->option = IPMI_OPEN_OPTION_OEM_INIT;
	option->ival = 1;
    } else if (strcmp(arg, "-noseteventrcvr") == 0) {
	option->option = IPMI_OPEN_OPTION_SET_EVENT_RCVR;
	option->ival = 0;
    } else if (strcmp(arg, "-seteventrcvr") == 0) {
	option->option = IPMI_OPEN_OPTION_SET_EVENT_RCVR;
	option->ival = 1;
    } else if (strcmp(arg, "-noactivate") == 0) {
	option->option = IPMI_OPEN_OPTION_ACTIVATE_IF_POSSIBLE;
	option->ival = 0;
    } else if (strcmp(arg, "-activate") == 0) {
	option->option = IPMI_OPEN_OPTION_ACTIVATE_IF_POSSIBLE;
	option->ival = 1;
    } else if (strcmp(arg, "-nosetseltime") == 0) {
	option->option = IPMI_OPEN_OPTION_SET_SEL_TIME;
	option->ival = 0;
    } else if (strcmp(arg, "-setseltime") == 0) {
	option->option = IPMI_OPEN_OPTION_SET_SEL_TIME;
	option->ival = 1;
    } else if (strcmp(arg, "-nolocalonly") == 0) {
	option->option = IPMI_OPEN_OPTION_LOCAL_ONLY;
	option->ival = 0;
    } else if (strcmp(arg, "-localonly") == 0) {
	option->option = IPMI_OPEN_OPTION_LOCAL_ONLY;
	option->ival = 1;
    } else if (strcmp(arg, "-nocache") == 0) {
	option->option = IPMI_OPEN_OPTION_USE_CACHE;
	option->ival = 0;
    } else if (strcmp(arg, "-cache") == 0) {
	option->option = IPMI_OPEN_OPTION_USE_CACHE;
	option->ival = 1;
    } else
	return EINVAL;

    return 0;
}

const char *
ipmi_parse_options_help(void)
{
    return
	"-[no]all - all automatic handling\n"
	"-[no]sdrs - sdr fetching\n"
	"-[no]frus - FRU fetching\n"
	"-[no]sel - SEL fetching\n"
	"-[no]ipmbscan - IPMB bus scanning\n"
	"-[no]oeminit - special OEM processing (like ATCA)\n"
	"-[no]seteventrcvr - setting event receivers\n"
	"-[no]setseltime - setting the SEL clock\n"
	"-[no]activate - connection activation\n"
	"-[no]localonly - Just talk to the local BMC, (ATCA-only, for blades)\n"
        "-[no]cache - use the local cache for SDRs.  On by default."
	"-wait_til_up - wait until the domain is up before returning";
}

/* This is the number of seconds between 1/1/70 (IPMI event date) and
   1/1/98 (ipmi SNMP trap local timestamp). */
#define IPMI_SNMP_DATE_OFFSET 883612800

int
ipmi_handle_snmp_trap_data(const void          *src_addr,
			   unsigned int        src_addr_len,
			   int                 src_addr_type,
			   long                specific,
			   const unsigned char *data,
			   unsigned int        data_len)
{
    int           handled = 0;
    unsigned char pet_ack[12];
    ipmi_msg_t    *msg = NULL;

    if (DEBUG_RAWMSG) {
	ipmi_log(IPMI_LOG_DEBUG_START, "Got SNMP trap from:\n  ");
	dump_hex(src_addr, src_addr_len);
	ipmi_log(IPMI_LOG_DEBUG_CONT, "\n data is:\n  ");
	dump_hex(data, data_len);
	ipmi_log(IPMI_LOG_DEBUG_END, " ");
    }

    if (data_len < 46)
	return 0;

    /* I will take this opportunity to note that the SNMP trap format
       from IPMI is insufficient to actually perform the job.  It does
       not have:
       1 A guaranteed way to correlate the timestamp to the SEL.
       2 A guaranteed way to correlate the record id to the SEL.
       3 The channel or the LUN for the event generator.

       Because of these, there is no guaranteed way to correlate the
       data from the SNMP trap to an SEL event.  This can result in
       duplicate events, which is very bad.  So we currently do not
       deliver the events this way, we pass a NULL in the event
       message to tell the domain code to rescan the SEL for this MC.
       In addition, item 3 above means that you cannot determine which
       sensor issued the event, since the channel and the LUN are
       required to find the sensor.
    */

 /* Until we have some way to get full valid data from the trap, we
    just disable it. */
#if 0
    ipmi_msg_t     tmsg;
    unsigned char  edata[17];
    unsigned long  timestamp;
    int16_t        utc_off;
    unsigned short record_id;
    timestamp = ntohl(*((uint32_t *) (data+18)));
    if (data[27] == 0xff)
	/* Can't handle unspecific event generator */
	return 0;
    if ((data[28] == 0xff) || (data[28] == 0x00))
	/* Can't handle unspecific sensor */
	return 0;
    if (timestamp == 0)
	/* Can't handle unspecified timestamp. */
	return 0;
    utc_off = ntohs(*((uint16_t *) (data+22)));
    if (utc_off == -1)
	/* If unspecified (0xffff), we assume zero (UTC). */
	utc_off = 0;
    timestamp -= utc_off; /* Remove timezone offset */
    timestamp += IPMI_SNMP_DATE_OFFSET; /* Convert to 1/1/70 offset */

    /* We assume the record id is in the sequence # field, since that
       makes the most sense. */
    record_id = ntohs(*((uint16_t *) (data+16)));

    tmsg.netfn = IPMI_APP_NETFN;
    tmsg.cmd = IPMI_READ_EVENT_MSG_BUFFER_CMD;
    tmsg.data = edata;
    tmsg.data_len = 17;
    msg = &tmsg;
    edata[0] = 0;
    edata[1] = record_id & 0xff;
    edata[2] = (record_id >> 8) & 0xff;
    edata[3] = 2; /* record type - system event */
    edata[4] = timestamp & 0xff;
    edata[5] = (timestamp >> 8) & 0xff;
    edata[6] = (timestamp >> 16) & 0xff;
    edata[7] = (timestamp >> 24) & 0xff;
    edata[8] = data[27]; /* Event generator */
    /* FIXME - is there a way to get the LUN? */
    edata[9] = 0; /* Assume channel 0, lun 0 */
    edata[10] = 0x04; /* IPMI 1.5 revision */
    edata[11] = (specific >> 16) & 0xff; /* Sensor type */
    edata[12] = data[28]; /* Sensor number */
    edata[13] = (specific >> 8) & 0xff; /* Event dir/type */
    memcpy(edata+14, data+31, 3); /* Event data 1-3 */
#endif

    pet_ack[0] = data[17]; /* Record id */
    pet_ack[1] = data[16];
    pet_ack[2] = data[21]; /* Timestamp */
    pet_ack[3] = data[20];
    pet_ack[4] = data[19];
    pet_ack[5] = data[18];
    pet_ack[6] = data[25]; /* Event source type */
    pet_ack[7] = data[27]; /* Sensor device */
    pet_ack[8] = data[28]; /* Sensor number */
    memcpy(pet_ack+9, data+31, 3); /* Event data 1-3 */

    if (src_addr_type == IPMI_EXTERN_ADDR_IP)
	handled = ipmi_lan_handle_external_event(src_addr, msg, pet_ack);

    return handled;
}

char *
ipmi_openipmi_version(void)
{
    return OPENIPMI_VERSION;
}

ipmi_msgi_t *
ipmi_alloc_msg_item(void)
{
    ipmi_msgi_t *rv;

    rv = ipmi_mem_alloc(sizeof(ipmi_msgi_t));
    if (!rv)
	return NULL;
    memset(rv, 0, sizeof(*rv));
    rv->msg.data = rv->data;
    return rv;
}

void
ipmi_free_msg_item(ipmi_msgi_t *item)
{
    if (item->msg.data && (item->msg.data != item->data))
	ipmi_free_msg_item_data(item->msg.data);
    ipmi_mem_free(item);
}

void *
ipmi_alloc_msg_item_data(unsigned int size)
{
    return ipmi_mem_alloc(size);
}

void
ipmi_free_msg_item_data(void *data)
{
    ipmi_mem_free(data);
}

void
ipmi_move_msg_item(ipmi_msgi_t *new_item, ipmi_msgi_t *old_item)
{
    if (new_item->msg.data && (new_item->msg.data != new_item->data))
	ipmi_free_msg_item_data(new_item->msg.data);
    new_item->msg = old_item->msg;

    if (!old_item->msg.data) {
	/* Nothing to do */
    } else if (old_item->msg.data != old_item->data) {
	/* Copied the actual data pointer. */
	old_item->msg.data = NULL;
    } else {
	memcpy(new_item->data, old_item->data, old_item->msg.data_len);
	new_item->msg.data = new_item->data;
    }
}

void
ipmi_handle_rsp_item_copyall(ipmi_con_t            *ipmi,
			     ipmi_msgi_t           *rspi,
			     const ipmi_addr_t     *addr,
			     unsigned int          addr_len,
			     const ipmi_msg_t      *msg,
			     ipmi_ll_rsp_handler_t rsp_handler)
{
    int used = IPMI_MSG_ITEM_NOT_USED;

    memcpy(&rspi->addr, addr, addr_len);
    rspi->addr_len = addr_len;
    rspi->msg = *msg;
    memcpy(rspi->data, msg->data, msg->data_len);
    rspi->msg.data = rspi->data;

    /* call the user handler. */
    if (rsp_handler)
	used = rsp_handler(ipmi, rspi);

    if (!used)
	ipmi_free_msg_item(rspi);
}

void
ipmi_handle_rsp_item_copymsg(ipmi_con_t            *ipmi,
			     ipmi_msgi_t           *rspi,
			     const ipmi_msg_t      *msg,
			     ipmi_ll_rsp_handler_t rsp_handler)
{
    int used = IPMI_MSG_ITEM_NOT_USED;

    rspi->msg = *msg;
    memcpy(rspi->data, msg->data, msg->data_len);
    rspi->msg.data = rspi->data;

    /* call the user handler. */
    if (rsp_handler)
	used = rsp_handler(ipmi, rspi);

    if (!used)
	ipmi_free_msg_item(rspi);
}

void
ipmi_handle_rsp_item(ipmi_con_t            *ipmi,
		     ipmi_msgi_t           *rspi,
		     ipmi_ll_rsp_handler_t rsp_handler)
{
    int used = IPMI_MSG_ITEM_NOT_USED;

    /* call the user handler. */
    if (rsp_handler)
	used = rsp_handler(ipmi, rspi);

    if (!used)
	ipmi_free_msg_item(rspi);
}

os_handler_t *
ipmi_alloc_os_handler(void)
{
    os_handler_t *rv = ipmi_mem_alloc(sizeof(*rv));
    if (rv)
	memset(rv, 0, sizeof(*rv));
    return rv;
}

void
ipmi_free_os_handler(os_handler_t *handler)
{
    ipmi_mem_free(handler);
}

