/*
 * gh.c
 *
 * DSP-BIOS Bridge driver support functions for TI OMAP processors.
 *
 * Copyright (C) 2005-2006 Texas Instruments, Inc.
 *
 * This package is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#include <linux/types.h>

#include <dspbridge/host_os.h>
#include <dspbridge/gh.h>

struct element {
	struct element *next;
	u8 data[1];
};

struct gh_t_hash_tab {
	u16 max_bucket;
	u16 val_size;
	struct element **buckets;
	 u16(*hash) (void *, u16);
	 bool(*match) (void *, void *);
	void (*delete) (void *);
};

static void noop(void *p);

/*
 *  ======== gh_create ========
 */

struct gh_t_hash_tab *gh_create(u16 max_bucket, u16 val_size,
				u16(*hash) (void *, u16), bool(*match) (void *,
									void *),
				void (*delete) (void *))
{
	struct gh_t_hash_tab *hash_tab;
	u16 i;
	hash_tab = kzalloc(sizeof(struct gh_t_hash_tab), GFP_KERNEL);
	if (hash_tab == NULL)
		return NULL;
	hash_tab->max_bucket = max_bucket;
	hash_tab->val_size = val_size;
	hash_tab->hash = hash;
	hash_tab->match = match;
	hash_tab->delete = delete == NULL ? noop : delete;

	hash_tab->buckets =
	    kzalloc(sizeof(struct element *) * max_bucket, GFP_KERNEL);
	if (hash_tab->buckets == NULL) {
		gh_delete(hash_tab);
		return NULL;
	}

	for (i = 0; i < max_bucket; i++)
		hash_tab->buckets[i] = NULL;

	return hash_tab;
}

/*
 *  ======== gh_delete ========
 */
void gh_delete(struct gh_t_hash_tab *hash_tab)
{
	struct element *elem, *next;
	u16 i;

	if (hash_tab != NULL) {
		if (hash_tab->buckets != NULL) {
			for (i = 0; i < hash_tab->max_bucket; i++) {
				for (elem = hash_tab->buckets[i]; elem != NULL;
				     elem = next) {
					next = elem->next;
					(*hash_tab->delete) (elem->data);
					kfree(elem);
				}
			}

			kfree(hash_tab->buckets);
		}

		kfree(hash_tab);
	}
}

/*
 *  ======== gh_find ========
 */

void *gh_find(struct gh_t_hash_tab *hash_tab, void *key)
{
	struct element *elem;

	elem = hash_tab->buckets[(*hash_tab->hash) (key, hash_tab->max_bucket)];

	for (; elem; elem = elem->next) {
		if ((*hash_tab->match) (key, elem->data))
			return elem->data;
	}

	return NULL;
}

/*
 *  ======== gh_insert ========
 */

void *gh_insert(struct gh_t_hash_tab *hash_tab, void *key, void *value)
{
	struct element *elem;
	u16 i;
	char *src, *dst;

	elem = kzalloc(sizeof(struct element) - 1 + hash_tab->val_size,
			GFP_KERNEL);
	if (elem != NULL) {

		dst = (char *)elem->data;
		src = (char *)value;
		for (i = 0; i < hash_tab->val_size; i++)
			*dst++ = *src++;

		i = (*hash_tab->hash) (key, hash_tab->max_bucket);
		elem->next = hash_tab->buckets[i];
		hash_tab->buckets[i] = elem;

		return elem->data;
	}

	return NULL;
}

/*
 *  ======== noop ========
 */
/* ARGSUSED */
static void noop(void *p)
{
	p = p;			/* stifle compiler warning */
}

#ifdef CONFIG_TIDSPBRIDGE_BACKTRACE
/**
 * gh_iterate() - This function goes through all the elements in the hash table
 *		looking for the dsp symbols.
 * @hash_tab:	Hash table
 * @callback:	pointer to callback function
 * @user_data:	User data, contains the find_symbol_context pointer
 *
 */
void gh_iterate(struct gh_t_hash_tab *hash_tab,
		void (*callback)(void *, void *), void *user_data)
{
	struct element *elem;
	u32 i;

	if (hash_tab && hash_tab->buckets)
		for (i = 0; i < hash_tab->max_bucket; i++) {
			elem = hash_tab->buckets[i];
			while (elem) {
				callback(&elem->data, user_data);
				elem = elem->next;
			}
		}
}
#endif