#ifndef	lint
static	char sccsid[] = "@(#)vuid_store.c 1.1 92/07/30 Copyr 1985 Sun Micro";
#endif

/*
 * Copyright (c) 1985 by Sun Microsystems, Inc.
 */

/*
 * Vuid_store.c - Implement the vuid_store.h event storage interface.
 */

#include <sys/types.h>
#include <sys/time.h>
#include <sundev/vuid_event.h>
#include <sundev/vuid_state.h>
#include <sundev/vuid_store.h>

static void vuid_destroy_seg();
static Vuid_seg * vuid_copy_seg();
static Vuid_seg * vuid_find_seg();
static Vuid_value * vuid_add_value();
static Vuid_value * vuid_find_value();

#ifdef	KERNEL
#include <sys/kmem_alloc.h>
extern int servicing_interrupt();
#define	vuid_alloc(bytes) \
	new_kmem_alloc((bytes), servicing_interrupt())
#define	vuid_free(ptr, bytes)	kmem_free((ptr), (bytes))
#else
#define	vuid_alloc(bytes)	malloc((bytes))
#define	vuid_free(ptr, bytes)	free((ptr))
#endif	KERNEL

void
vuid_set_value(client_state_ptr, event)
	Vuid_state *client_state_ptr;
	register Firm_event *event;
{
	Vuid_seg **state_ptr = (Vuid_seg **)client_state_ptr;
	Vuid_seg *state = *state_ptr;
	register Vuid_seg *seg;
	register Vuid_value *val_node;
	register Vuid_value *pair_val_node;
	register u_short offset = vuid_id_offset(event->id);
	register u_short pair = event->pair;
	int	int_bit, val_original;

	/* Get (search for) seg from state assoicated with event */
	if ((seg = vuid_find_seg(state, vuid_id_addr(event->id))) ==
	    VUID_SEG_NULL) {
		/* Allocate and initialize new seg for event */
		seg = (Vuid_seg *) vuid_alloc(sizeof (*seg));
		bzero((caddr_t)seg, sizeof (*seg));
		seg->addr = vuid_id_addr(event->id);
		/* Add the seg to state */
		*state_ptr = seg;
		seg->next = state;
	}
	int_bit = vuid_get_int_bit(seg, offset);
	/* See if no value node and event value is not boolean */
	if ((!int_bit) && vuid_int_value(event->value)) {
		(void) vuid_add_value(seg, offset);
		int_bit = 1;
	}
	/* If boolean event then set boolean bit */
	if (!int_bit) {
		if (event->value)
			vuid_set_boolean_bit(seg, offset);
		else
			vuid_clear_boolean_bit(seg, offset);
	} else {
		/* Get (search for) value node (should be there) */
		val_node = vuid_find_value(seg, offset);
		val_original = val_node->value;
		val_node->value = event->value;
		switch (event->pair_type) {

		case FE_PAIR_DELTA:
			/* See if value node for pair */
			if (!vuid_get_int_bit(seg, pair))
				(void) vuid_add_value(seg, pair);
			/* Get (search for) value node (should be there) */
			pair_val_node = vuid_find_value(seg, pair);
			/* Set pair value to difference */
			pair_val_node->value = event->value - val_original;
			break;

		case FE_PAIR_ABSOLUTE:
			/* See if value node for pair */
			if (!vuid_get_int_bit(seg, pair))
				(void) vuid_add_value(seg, pair);
			/* Get (search for) value node (should be there) */
			pair_val_node = vuid_find_value(seg, pair);
			/* Add event value to pair value */
			pair_val_node->value += event->value;
			break;

		default:
			{}
		}
	}
	/* Recursively call vuid_set_value if there is an associated pair */
	if (event->pair_type == FE_PAIR_SET) {
		Firm_event pair_event;

		pair_event = *event;
		pair_event.id = vuid_id_addr(event->id) | pair;
		pair_event.pair_type = FE_PAIR_NONE;
		pair_event.pair = 0;
		vuid_set_value(client_state_ptr, &pair_event);
	}
}

int
vuid_get_value(client_state, id)
	Vuid_state client_state;
	u_short id;
{
	Vuid_seg *state = vuid_cstate_to_state(client_state);
	register Vuid_seg *seg;
	Vuid_value *val_node;
	register u_short offset = vuid_id_offset(id);

	/* Get (search for) seg from state assoicated with id */
	if ((seg = vuid_find_seg(state, vuid_id_addr(id))) == VUID_SEG_NULL)
		return (0);
	/* If boolean event (i.e., no ints bit on) then return boolean value */
	if (!vuid_get_int_bit(seg, offset))
		return (vuid_get_boolean_bit(seg, offset) != 0);
	else {
		/* Get (search for) value node and return value */
		val_node = vuid_find_value(seg, offset);
		return (val_node->value);
	}
}

void
vuid_destroy_state(client_state)
	Vuid_state client_state;
{
	Vuid_seg *state = vuid_cstate_to_state(client_state);
	register Vuid_seg *seg;
	Vuid_seg *seg_next;

	for (seg = state; seg; seg = seg_next) {
		seg_next = seg->next;
		vuid_destroy_seg(seg);
	}
}

static void
vuid_destroy_seg(seg)
	Vuid_seg *seg;
{
	register Vuid_value *val_node;
	Vuid_value *val_node_next;

	for (val_node = seg->list; val_node; val_node = val_node_next) {
		val_node_next = val_node->next;
		vuid_free((caddr_t)val_node, sizeof (Vuid_value));
	}
	vuid_free((caddr_t)seg, sizeof (Vuid_seg));
}

Vuid_state
vuid_copy_state(client_state)
	Vuid_state client_state;
{
	Vuid_seg *state = vuid_cstate_to_state(client_state);
	register Vuid_seg *seg;
	Vuid_seg *new_first_seg = VUID_SEG_NULL;
	register Vuid_seg *new_previous_seg = VUID_SEG_NULL;
	register Vuid_seg *new_seg;

	for (seg = state; seg; seg = seg->next) {
		new_seg = vuid_copy_seg(seg);
		/* Remember first seg as state */
		if (new_first_seg == VUID_SEG_NULL)
			new_first_seg = new_seg;
		/* Link segs together */
		if (new_previous_seg != VUID_SEG_NULL)
			new_previous_seg->next = new_seg;
		/* Remember seg for linking later */
		new_previous_seg = new_seg;
	}
	return ((Vuid_state) new_first_seg);
}

static Vuid_seg *
vuid_copy_seg(seg)
	Vuid_seg *seg;
{
	register Vuid_value *val_node;
	Vuid_seg *new_seg;
	register Vuid_value *new_previous_val = VUID_VALUE_NULL;
	register Vuid_value *new_val;

	/* Allocate and initialize new seg for event */
	new_seg = (Vuid_seg *) vuid_alloc(sizeof (*seg));
	*new_seg = *seg;
	/* Terminate new pointer with null */
	new_seg->next = VUID_SEG_NULL;
	new_seg->list = VUID_VALUE_NULL;
	/* Copy list elements */
	for (val_node = seg->list; val_node; val_node = val_node->next) {
		new_val = (Vuid_value *) vuid_alloc(sizeof (*new_val));
		*new_val = *val_node;
		new_val->next = VUID_VALUE_NULL;
		/* Remember first value as head of list */
		if (new_seg->list == VUID_VALUE_NULL)
			new_seg->list = new_val;
		/* Link vals together */
		if (new_previous_val != VUID_VALUE_NULL)
			new_previous_val->next = new_val;
		/* Remember val for linking later */
		new_previous_val = new_val;
	}
	return (new_seg);
}

static Vuid_seg *
vuid_find_seg(state, addr)
	Vuid_seg *state;
	u_short addr;
{
	register Vuid_seg *seg;

	for (seg = state; seg; seg = seg->next) {
		if (seg->addr == addr)
			return (seg);
	}
	return (VUID_SEG_NULL);
}

static Vuid_value *
vuid_find_value(seg, offset)
	Vuid_seg *seg;
	u_short offset;
{
	register Vuid_value *val_node;

	for (val_node = seg->list; val_node; val_node = val_node->next) {
		if (vuid_id_offset(val_node->offset) == offset)
			return (val_node);
	}
	return (VUID_VALUE_NULL);
}

static Vuid_value *
vuid_add_value(seg, offset)
	Vuid_seg *seg;
	u_short offset;
{
	Vuid_value *list_tmp;
	Vuid_value *val_node;

	/* Allocate and initialize new value node for event */
	val_node = (Vuid_value *) vuid_alloc(sizeof (*val_node));
	bzero((caddr_t)val_node, sizeof (*val_node));
	val_node->offset = offset;
	/* Add the value node to list */
	list_tmp = seg->list;
	seg->list = val_node;
	val_node->next = list_tmp;
	vuid_set_int_bit(seg, offset);
	/* Clear boolean bit for event */
	vuid_clear_boolean_bit(seg, offset);
	return (val_node);
}
