/*
 * Copyright (c) 2002-2005, Network Appliance, Inc. All rights reserved.
 *
 * This Software is licensed under one of the following licenses:
 *
 * 1) under the terms of the "Common Public License 1.0" a copy of which is
 *    in the file LICENSE.txt in the root directory. The license is also
 *    available from the Open Source Initiative, see
 *    http://www.opensource.org/licenses/cpl.php.
 *
 * 2) under the terms of the "The BSD License" a copy of which is in the file
 *    LICENSE2.txt in the root directory. The license is also available from
 *    the Open Source Initiative, see
 *    http://www.opensource.org/licenses/bsd-license.php.
 *
 * 3) under the terms of the "GNU General Public License (GPL) Version 2" a 
 *    copy of which is in the file LICENSE3.txt in the root directory. The 
 *    license is also available from the Open Source Initiative, see
 *    http://www.opensource.org/licenses/gpl-license.php.
 *
 * Licensee has the right to choose one of the above licenses.
 *
 * Redistributions of source code must retain the above copyright
 * notice and one of the license notices.
 *
 * Redistributions in binary form must reproduce both the above copyright
 * notice, one of the license notices in the documentation
 * and/or other materials provided with the distribution.
 */

/**********************************************************************
 *
 * MODULE: udat.c
 *
 * PURPOSE: DAT Provider and Consumer registry functions.
 *
 * $Id: udat.c,v 1.22 2005/03/24 05:58:35 jlentini Exp $
 **********************************************************************/

#include <dat2/udat.h>
#include <dat2/dat_registry.h>	/* Provider API function prototypes */

#include "dat_dr.h"
#include "dat_init.h"
#include "dat_osd.h"
#ifndef DAT_NO_STATIC_REGISTRY
#include "dat_sr.h"
#endif

#define UDAT_IS_BAD_POINTER(p) ( NULL == (p) )

/*********************************************************************
 *                                                                   *
 * Internal Function Declarations                                    *
 *                                                                   *
 *********************************************************************/

DAT_BOOLEAN udat_check_state(void);

/*********************************************************************
 *                                                                   *
 * External Function Definitions                                     *
 *                                                                   *
 *********************************************************************/

/*
 *  Use a global to get an unresolved when run with pre-extension library
 */
int g_dat_extensions = 0;

/*
 *
 * Provider API
 *
 */

/***********************************************************************
 * Function: dat_registry_add_provider
 ***********************************************************************/

DAT_RETURN DAT_API
dat_registry_add_provider(IN const DAT_PROVIDER * provider,
			  IN const DAT_PROVIDER_INFO * provider_info)
{
	DAT_DR_ENTRY entry;

	dat_os_dbg_print(DAT_OS_DBG_TYPE_PROVIDER_API,
			 "DAT Registry: %s (%s,%x:%x,%x)\n", __FUNCTION__,
			 provider_info->ia_name,
			 provider_info->dapl_version_major,
			 provider_info->dapl_version_minor,
			 provider_info->is_thread_safe);

	if (UDAT_IS_BAD_POINTER(provider)) {
		return DAT_ERROR(DAT_INVALID_PARAMETER, DAT_INVALID_ARG1);
	}

	if (UDAT_IS_BAD_POINTER(provider_info)) {
		return DAT_ERROR(DAT_INVALID_PARAMETER, DAT_INVALID_ARG2);
	}

	if (DAT_FALSE == udat_check_state()) {
		return DAT_ERROR(DAT_INVALID_STATE, 0);
	}

	entry.ref_count = 0;
	entry.ia_open_func = provider->ia_open_func;
	entry.info = *provider_info;
#ifdef DAT_EXTENSIONS
	entry.ia_ext_func = provider->handle_extendedop_func;
#endif
	return dat_dr_insert(provider_info, &entry);
}

//***********************************************************************
// Function: dat_registry_remove_provider
//***********************************************************************

DAT_RETURN DAT_API
dat_registry_remove_provider(IN const DAT_PROVIDER * provider,
			     IN const DAT_PROVIDER_INFO * provider_info)
{
	dat_os_dbg_print(DAT_OS_DBG_TYPE_PROVIDER_API,
			 "DAT Registry: dat_registry_remove_provider () called\n");

	if (UDAT_IS_BAD_POINTER(provider)) {
		return DAT_ERROR(DAT_INVALID_PARAMETER, DAT_INVALID_ARG1);
	}

	if (DAT_FALSE == udat_check_state()) {
		return DAT_ERROR(DAT_INVALID_STATE, 0);
	}

	return dat_dr_remove(provider_info);
}

/*
 *
 * Consumer API
 *
 */

/***********************************************************************
 * Function: dat_ia_open
 ***********************************************************************/

DAT_RETURN DAT_API
dat_ia_openv(IN const DAT_NAME_PTR name,
	     IN DAT_COUNT async_event_qlen,
	     INOUT DAT_EVD_HANDLE * async_event_handle,
	     OUT DAT_IA_HANDLE * ia_handle,
	     IN DAT_UINT32 dapl_major,
	     IN DAT_UINT32 dapl_minor, IN DAT_BOOLEAN thread_safety)
{
	DAT_IA_OPEN_FUNC ia_open_func;
	DAT_PROVIDER_INFO info;
	DAT_OS_SIZE len;
	DAT_RETURN dat_status;

	dat_os_dbg_print(DAT_OS_DBG_TYPE_CONSUMER_API,
			 "DAT Registry: dat_ia_openv (%s,%x:%x,%x) called\n",
			 name, dapl_major, dapl_minor, thread_safety);

	if (UDAT_IS_BAD_POINTER(name)) {
		return DAT_ERROR(DAT_INVALID_PARAMETER, DAT_INVALID_ARG1);
	}

	len = dat_os_strlen(name);

	if (DAT_NAME_MAX_LENGTH <= len) {
		return DAT_ERROR(DAT_INVALID_PARAMETER, DAT_INVALID_ARG1);
	}

	if (UDAT_IS_BAD_POINTER(ia_handle)) {
		return DAT_ERROR(DAT_INVALID_HANDLE, DAT_INVALID_HANDLE_IA);
	}

	if (DAT_FALSE == udat_check_state()) {
		return DAT_ERROR(DAT_INVALID_STATE, 0);
	}

	dat_os_strncpy(info.ia_name, name, len + 1);

	info.dapl_version_major = dapl_major;
	info.dapl_version_minor = dapl_minor;
	info.is_thread_safe = thread_safety;

	/*
	 * Since DAT allows providers to be loaded by either the static
	 * registry or explicitly through OS dependent methods, do not
	 * return an error if no providers are loaded via the static registry.
	 * Don't even bother calling the static registry if DAT is compiled
	 * with no static registry support.
	 */

#ifndef DAT_NO_STATIC_REGISTRY
	(void)dat_sr_provider_open(&info);
#endif

	dat_status = dat_dr_provider_open(&info, &ia_open_func);
	if (dat_status != DAT_SUCCESS) {
		dat_os_dbg_print(DAT_OS_DBG_TYPE_CONSUMER_API,
				 "DAT Registry: dat_ia_open () provider information "
				 "for IA name %s not found in dynamic registry\n",
				 name);

		return dat_status;
	}

	dat_status = (*ia_open_func) (name,
				      async_event_qlen,
				      async_event_handle, ia_handle);
	if (dat_status == DAT_SUCCESS) {
		*ia_handle = (DAT_IA_HANDLE) dats_set_ia_handle(*ia_handle);
	} else {
		(void)dat_dr_provider_close(&info);
#ifndef DAT_NO_STATIC_REGISTRY
		(void)dat_sr_provider_close(&info);
#endif
		return dat_status;
	}

	/*
	 *  See if provider supports extensions
	 */
	if (dat_status == DAT_SUCCESS) {
		DAT_IA_ATTR ia_attr;

		if (dat_ia_query(*ia_handle,
				 NULL,
				 DAT_IA_ALL,
				 &ia_attr, 0, NULL) == DAT_SUCCESS) {
			if (ia_attr.extension_supported) {
				dat_os_dbg_print(DAT_OS_DBG_TYPE_CONSUMER_API,
						 "DAT Registry: dat_ia_open () "
						 "Extensions (Type=%d, ver=%d) supported!\n",
						 ia_attr.extension_supported,
						 ia_attr.extension_version);
				g_dat_extensions = 1;
			}
		}
	}
	return dat_status;
}

/************************************************************************
 * Function: dat_ia_close
 ***********************************************************************/

DAT_RETURN DAT_API
dat_ia_close(IN DAT_IA_HANDLE ia_handle, IN DAT_CLOSE_FLAGS ia_flags)
{
	DAT_PROVIDER *provider;
	DAT_PROVIDER_ATTR provider_attr;
	DAT_IA_HANDLE dapl_ia_handle;
	DAT_RETURN dat_status;
	const char *ia_name;

	dat_os_dbg_print(DAT_OS_DBG_TYPE_CONSUMER_API,
			 "DAT Registry: dat_ia_close () called\n");

	dat_status = dats_get_ia_handle(ia_handle, &dapl_ia_handle);
	if (dat_status != DAT_SUCCESS) {
		return DAT_ERROR(DAT_INVALID_HANDLE, DAT_INVALID_HANDLE_IA);
	}

	if (DAT_FALSE == udat_check_state()) {
		return DAT_ERROR(DAT_INVALID_STATE, 0);
	}

	provider = DAT_HANDLE_TO_PROVIDER(dapl_ia_handle);
	ia_name = provider->device_name;

	if (DAT_SUCCESS != (dat_status = dat_ia_query(ia_handle,
						      NULL,
						      0,
						      NULL,
						      DAT_PROVIDER_FIELD_ALL,
						      &provider_attr))) {
		dat_os_dbg_print(DAT_OS_DBG_TYPE_CONSUMER_API,
				 "DAT Registry: query function for %s provider failed\n",
				 ia_name);
	} else if (DAT_SUCCESS != (dat_status =
				   (*provider->ia_close_func) (dapl_ia_handle,
							       ia_flags))) {
		dat_os_dbg_print(DAT_OS_DBG_TYPE_CONSUMER_API,
				 "DAT Registry: close function for %s provider failed\n",
				 ia_name);
	} else {
		DAT_PROVIDER_INFO info;
		DAT_OS_SIZE len;

		len = dat_os_strlen(ia_name);

		dat_os_assert(len < DAT_NAME_MAX_LENGTH);

		dat_os_strncpy(info.ia_name, ia_name, len + 1);

		info.dapl_version_major = provider_attr.dapl_version_major;
		info.dapl_version_minor = provider_attr.dapl_version_minor;
		info.is_thread_safe = provider_attr.is_thread_safe;

		dat_status = dat_dr_provider_close(&info);
		if (DAT_SUCCESS != dat_status) {
			dat_os_dbg_print(DAT_OS_DBG_TYPE_CONSUMER_API,
					 "DAT Registry: dynamic registry unable to close "
					 "provider for IA name %s\n", ia_name);
		}

		/* Remove the handle from the handle table */
		(void)dats_free_ia_handle(ia_handle);

#ifndef DAT_NO_STATIC_REGISTRY
		dat_status = dat_sr_provider_close(&info);
		if (DAT_SUCCESS != dat_status) {
			dat_os_dbg_print(DAT_OS_DBG_TYPE_CONSUMER_API,
					 "DAT Registry: static registry unable to close "
					 "provider for IA name %s\n", ia_name);
		}
#endif
	}

	return dat_status;
}

//***********************************************************************
// Function: dat_registry_list_providers
//***********************************************************************

DAT_RETURN DAT_API
dat_registry_list_providers(IN DAT_COUNT max_to_return,
			    OUT DAT_COUNT * entries_returned,
			    OUT DAT_PROVIDER_INFO * (dat_provider_list[]))
{
	DAT_RETURN dat_status;

	dat_status = DAT_SUCCESS;
	dat_os_dbg_print(DAT_OS_DBG_TYPE_CONSUMER_API,
			 "DAT Registry: dat_registry_list_providers () called\n");

	if (DAT_FALSE == udat_check_state()) {
		return DAT_ERROR(DAT_INVALID_STATE, 0);
	}

	if ((UDAT_IS_BAD_POINTER(entries_returned))) {
		return DAT_ERROR(DAT_INVALID_PARAMETER, DAT_INVALID_ARG2);
	}

	if (0 != max_to_return && (UDAT_IS_BAD_POINTER(dat_provider_list))) {
		return DAT_ERROR(DAT_INVALID_PARAMETER, DAT_INVALID_ARG3);
	}

	if (0 == max_to_return) {
		/* the user is allowed to call with max_to_return set to zero.
		 * in which case we simply return (in *entries_returned) the
		 * number of providers currently installed.  We must also
		 * (per spec) return an error
		 */
#ifndef DAT_NO_STATIC_REGISTRY
		(void)dat_sr_size(entries_returned);
#else
		(void)dat_dr_size(entries_returned);
#endif
		return DAT_ERROR(DAT_INVALID_PARAMETER, DAT_INVALID_ARG1);
	} else {
#ifndef DAT_NO_STATIC_REGISTRY
		dat_status = dat_sr_list(max_to_return,
					 entries_returned, dat_provider_list);
#else
		dat_status = dat_dr_list(max_to_return,
					 entries_returned, dat_provider_list);
#endif
	}
	return dat_status;
}

/*********************************************************************
 *                                                                   *
 * Internal Function Definitions                                     *
 *                                                                   *
 *********************************************************************/

//***********************************************************************
// Function: udat_check_state
//***********************************************************************

/*
 * This function returns TRUE if the DAT registry is in a state capable
 * of handling DAT API calls and false otherwise.
 */

DAT_BOOLEAN udat_check_state(void)
{
	DAT_MODULE_STATE state;
	DAT_BOOLEAN status;

	state = dat_module_get_state();

	if (DAT_MODULE_STATE_UNINITIALIZED == state) {
		dat_init();
		status = DAT_TRUE;
	} else if (DAT_MODULE_STATE_DEINITIALIZED == state) {
		status = DAT_FALSE;
	} else {
		status = DAT_TRUE;
	}

	return status;
}

#ifdef DAT_EXTENSIONS

/***********************************************************************
 * Function: udat_extension_open - provider name supplied
 ***********************************************************************/
DAT_RETURN DAT_API
udat_extension_open(IN const DAT_NAME_PTR name,
		    IN DAT_EXTENDED_OP ext_op,
		    IN va_list args)
{
	DAT_HANDLE_EXTENDEDOP_FUNC prov_ext_func;
	DAT_PROVIDER_INFO info;
	DAT_OS_SIZE len;
	DAT_RETURN dat_status;

	dat_os_dbg_print(DAT_OS_DBG_TYPE_CONSUMER_API,
			 " udat_extenstion_op(%s,%x:%x,%x,%d) called\n",
			 name, ext_op, DAT_VERSION_MAJOR, DAT_VERSION_MINOR, DAT_THREADSAFE);

	if (UDAT_IS_BAD_POINTER(name)) {
		return DAT_ERROR(DAT_INVALID_PARAMETER, DAT_INVALID_ARG1);
	}

	len = dat_os_strlen(name);

	if (DAT_NAME_MAX_LENGTH <= len) {
		return DAT_ERROR(DAT_INVALID_PARAMETER, DAT_INVALID_ARG1);
	}

	if (DAT_FALSE == udat_check_state()) {
		return DAT_ERROR(DAT_INVALID_STATE, 0);
	}

	dat_os_strncpy(info.ia_name, name, len + 1);

	info.dapl_version_major = DAT_VERSION_MAJOR;
	info.dapl_version_minor = DAT_VERSION_MINOR;
	info.is_thread_safe = DAT_THREADSAFE;

#ifndef DAT_NO_STATIC_REGISTRY
	(void)dat_sr_provider_open(&info);
#endif
	dat_status = dat_dr_provider_open_ext(&info, &prov_ext_func);
	if (dat_status != DAT_SUCCESS) {
		dat_os_dbg_print(DAT_OS_DBG_TYPE_CONSUMER_API,
				 " DAT Registry: udat_ext_op () provider information "
				 "for IA name %s not found in dynamic registry\n",
				 name);
		return dat_status;
	}
	g_dat_extensions = 1;
	dat_status = (*prov_ext_func) (name, ext_op, args);

	dat_os_dbg_print(DAT_OS_DBG_TYPE_CONSUMER_API,
			" udat_extension_op () return = 0x%x for IA name %s\n",
			 dat_status, name);

	if (dat_status != DAT_SUCCESS) {
		(void)dat_dr_provider_close(&info);
#ifndef DAT_NO_STATIC_REGISTRY
		(void)dat_sr_provider_close(&info);
#endif
	}

	return dat_status;
}
/***********************************************************************
 * Function: udat_extension_close - provider handle supplied
 ***********************************************************************/
DAT_RETURN DAT_API
udat_extension_close(IN DAT_IA_HANDLE ia_handle,
		     IN DAT_EXTENDED_OP ext_op,
		     IN va_list args)
{
	DAT_PROVIDER *provider;
	DAT_PROVIDER_INFO info;
	DAT_OS_SIZE len;
	DAT_RETURN dat_status;
	const char* ia_name;

	dat_os_dbg_print(DAT_OS_DBG_TYPE_CONSUMER_API,
			 " udat_ext_close_op(ia=%p,op=%x) called\n",
			 ia_handle, ext_op);

	if (ia_handle == NULL)
		return DAT_ERROR(DAT_INVALID_HANDLE, DAT_INVALID_HANDLE_IA);

	if (DAT_FALSE == udat_check_state())
		return DAT_ERROR(DAT_INVALID_STATE, 0);

	provider = DAT_HANDLE_TO_PROVIDER(ia_handle);
	ia_name = provider->device_name;

	dat_os_dbg_print(DAT_OS_DBG_TYPE_CONSUMER_API,
			 " udat_ext_close_op(ia=%p,op=%x,name=%s) called\n",
			 ia_handle, ext_op, ia_name);

	dat_status = (*provider->handle_extendedop_func) (ia_handle, ext_op, args);

	dat_os_dbg_print(DAT_OS_DBG_TYPE_CONSUMER_API,
			" udat_extension_op () return = 0x%x for IA name %s\n",
			 dat_status, ia_name);

	if (dat_status != DAT_SUCCESS)
		return dat_status;

	len = dat_os_strlen(ia_name);
	dat_os_assert(len < DAT_NAME_MAX_LENGTH);
	dat_os_strncpy(info.ia_name, ia_name, len + 1);
	info.dapl_version_major = DAT_VERSION_MAJOR;
	info.dapl_version_minor = DAT_VERSION_MINOR;
	info.is_thread_safe = DAT_THREADSAFE;

	dat_status = dat_dr_provider_close(&info);
	if (DAT_SUCCESS != dat_status) {
		dat_os_dbg_print(DAT_OS_DBG_TYPE_CONSUMER_API,
				 "udat_ext_close_op: dynamic registry unable to close "
				 "provider for IA name %s\n", ia_name);
	}

#ifndef DAT_NO_STATIC_REGISTRY
	dat_status = dat_sr_provider_close(&info);
	if (DAT_SUCCESS != dat_status) {
		dat_os_dbg_print(DAT_OS_DBG_TYPE_CONSUMER_API,
				 "udat_ext_close_op: static registry unable to close "
				 "provider for IA name %s\n", ia_name);
	}
#endif
	return dat_status;
}
#endif

/*
 * Local variables:
 *  c-indent-level: 4
 *  c-basic-offset: 4
 *  tab-width: 8
 * End:
 */
