#include <sys/types.h>
#include <sys/debug.h>
#include <sys/sunddi.h>
#define MIN_N_ITEMS 8
typedef struct i_ddi_soft_state {
void **array;
kmutex_t lock;
size_t size;
size_t n_items;
void *next;
} i_ddi_soft_state;
void *
ddi_get_soft_state(void *state, int item)
{
i_ddi_soft_state *ss = (i_ddi_soft_state *)state;
void *ret = NULL;
ASSERT((ss != NULL) && (item >= 0));
mutex_enter(&ss->lock);
if (item < ss->n_items && ss->array != NULL)
ret = ss->array[item];
mutex_exit(&ss->lock);
return (ret);
}
int
ddi_soft_state_init(void **state_p, size_t size, size_t n_items)
{
i_ddi_soft_state *ss;
if (state_p == NULL || size == 0)
return (EINVAL);
ss = kmem_zalloc(sizeof (*ss), KM_SLEEP);
mutex_init(&ss->lock, NULL, MUTEX_DRIVER, NULL);
ss->size = size;
if (n_items < MIN_N_ITEMS)
ss->n_items = MIN_N_ITEMS;
else {
ss->n_items = n_items;
}
ASSERT(ss->n_items >= n_items);
ss->array = kmem_zalloc(ss->n_items * sizeof (void *), KM_SLEEP);
*state_p = ss;
return (0);
}
int
ddi_soft_state_zalloc(void *state, int item)
{
i_ddi_soft_state *ss = (i_ddi_soft_state *)state;
void **array;
void *new_element;
if ((state == NULL) || (item < 0))
return (DDI_FAILURE);
mutex_enter(&ss->lock);
if (ss->size == 0) {
mutex_exit(&ss->lock);
cmn_err(CE_WARN, "ddi_soft_state_zalloc: bad handle: %s",
"fake");
return (DDI_FAILURE);
}
array = ss->array;
ASSERT(ss->n_items != 0 && array != NULL);
if (item < ss->n_items && array[item] != NULL) {
mutex_exit(&ss->lock);
return (DDI_FAILURE);
}
new_element = kmem_zalloc(ss->size, KM_SLEEP);
if (item >= ss->n_items) {
void **new_array;
size_t new_n_items;
new_n_items = ss->n_items;
while (new_n_items < (1 + item))
new_n_items <<= 1;
ASSERT(new_n_items >= (1 + item));
new_array = kmem_zalloc(new_n_items * sizeof (void *),
KM_SLEEP);
bcopy(array, new_array, ss->n_items * sizeof (void *));
kmem_free(ss->array, ss->n_items * sizeof (void *));
ss->array = (array = new_array);
ss->n_items = new_n_items;
}
ASSERT(array != NULL && item < ss->n_items && array[item] == NULL);
array[item] = new_element;
mutex_exit(&ss->lock);
return (DDI_SUCCESS);
}
void
ddi_soft_state_free(void *state, int item)
{
i_ddi_soft_state *ss = (i_ddi_soft_state *)state;
void **array;
void *element;
static char msg[] = "ddi_soft_state_free:";
if (ss == NULL) {
cmn_err(CE_WARN, "%s null handle: %s",
msg, "fake");
return;
}
element = NULL;
mutex_enter(&ss->lock);
if ((array = ss->array) == NULL || ss->size == 0) {
cmn_err(CE_WARN, "%s bad handle: %s",
msg, "fake");
} else if (item < 0 || item >= ss->n_items) {
cmn_err(CE_WARN, "%s item %d not in range [0..%lu]: %s",
msg, item, (ulong_t)ss->n_items - 1, "fake");
} else if (array[item] != NULL) {
element = array[item];
array[item] = NULL;
}
mutex_exit(&ss->lock);
if (element)
kmem_free(element, ss->size);
}
void
ddi_soft_state_fini(void **state_p)
{
i_ddi_soft_state *ss;
int item;
static char msg[] = "ddi_soft_state_fini:";
if (state_p == NULL ||
(ss = (i_ddi_soft_state *)(*state_p)) == NULL) {
cmn_err(CE_WARN, "%s null handle: %s",
msg, "fake");
return;
}
if (ss->size == 0) {
cmn_err(CE_WARN, "%s bad handle: %s",
msg, "fake");
return;
}
if (ss->n_items > 0) {
for (item = 0; item < ss->n_items; item++)
ddi_soft_state_free(ss, item);
kmem_free(ss->array, ss->n_items * sizeof (void *));
}
mutex_destroy(&ss->lock);
kmem_free(ss, sizeof (*ss));
*state_p = NULL;
}