/*
 HPI Functional API

Copyright (C) 1997-2017 AudioScience, Inc. All rights reserved.

This software is provided 'as-is', without any express or implied warranty.
In no event will AudioScience Inc. be held liable for any damages arising
from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not
   claim that you wrote the original software. If you use this software
   in a product, an acknowledgment in the product documentation would be
   appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
   misrepresented as being the original software.
3. This copyright notice and list of conditions may not be altered or removed
   from any source distribution.

AudioScience, Inc. <support@audioscience.com>

( This license is GPL compatible see http://www.gnu.org/licenses/license-list.html#GPLCompatibleLicenses )

*/
/** \file hpifunc.c
 Hardware Programming Interface (HPI) functions
*/

#include "hpi_internal.h"
#ifndef HPI_BUILD_SANITISE
#ifndef HPI_BUILD_ALSA
#include "hpicheck.h"
#endif
#endif
#include "hpimsginit.h"

#ifndef HPI_BUILD_KERNEL_MODE
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <math.h>		// for log10 in meter get level
#include <assert.h>
#endif

#include "hpidebug.h"

#ifdef HPI_OS_DELETE
static hpi_err_t mixer_match_control(
	hpi_handle_t hMixer,
	uint16_t i,
	struct hpi_control_t c_spec,
	hpi_handle_t* control,
	int *match
);
#endif

#define HPI_UNUSED(param) (void)param

/*=========================================================================*/
// local functions

/** handle struct for internal hpifunc use.  Total bit count must be <= 32 */
/* Driver 4.15.14 was last build with objIndex:12 */
struct hpi_handle {
	unsigned int objIndex:13;	///< Up to 8192 objects
	unsigned int objType:4;	///< HPI_OBJ_*
	unsigned int adapterIndex:14;	///< up to 16K
	unsigned int readOnly:1;	///< future readonly flag
};

/** allow conversion from handle to uint32_t */
union handle_word {
	struct hpi_handle h;
	uint32_t w;
};

/** Encode 3 indices and an object type into a 32 bit handle.
	\internal
	\return Object handle
*/
hpi_handle_t HPI_IndexesToHandle(
	const char cObject,	///< HPI_OBJ_* - the type code of object
	const uint16_t wAdapterIndex,	///< The Adapter index
	const uint16_t wObjectIndex	///< The stream or control index, if used
)
{
	union handle_word handle;

	handle.h.adapterIndex = wAdapterIndex;
	handle.h.readOnly = 0;
	handle.h.objType = cObject;
	handle.h.objIndex = wObjectIndex;
	return handle.w;
}

/** Extract one or two indices from an HPI handle,

	/sa HPI_IndexesToHandle()
*/
static hpi_err_t hpi_handle_indexes(
	const hpi_handle_t h, ///< The handle to decode
	uint16_t *p1, ///< return the Adapter index
	uint16_t *p2 ///< return the object index, may be NULL
)
{
	union handle_word uhandle;
	if (!h)
		return HPI_ERROR_INVALID_HANDLE;

	uhandle.w = h;

	*p1 = (uint16_t)uhandle.h.adapterIndex;
	if (p2)
		*p2 = (uint16_t)uhandle.h.objIndex;

	return 0;
}

void HPI_HandleToIndexes(
	const hpi_handle_t dwHandle,	///< The handle to decode
	uint16_t *pwAdapterIndex,	///< return the Adapter index
	uint16_t *pwObjectIndex	///< return the stream or control index
)
{
	hpi_handle_indexes(dwHandle, pwAdapterIndex, pwObjectIndex);
}

char HPI_HandleObject(
	const hpi_handle_t dwHandle
)
{
	union handle_word uhandle;
	uhandle.w = dwHandle;
	return (char)uhandle.h.objType;
}

#ifdef HPI_OS_LINUX_KERNEL
static
#endif
void HPI_FormatToMsg(
	struct hpi_msg_format *pMF,
	const struct hpi_format *pF
)
{
/* Note: Can't use memcpy or typecast because
	some fields have different offsets */
	pMF->dwSampleRate = pF->dwSampleRate;
	pMF->dwBitRate = pF->dwBitRate;
	pMF->dwAttributes = pF->dwAttributes;
	pMF->wChannels = pF->wChannels;
	pMF->wFormat = pF->wFormat;
}

static void HPI_MsgToFormat(
	struct hpi_format *pF,
	struct hpi_msg_format *pMF
)
{
	pF->dwSampleRate = pMF->dwSampleRate;
	pF->dwBitRate = pMF->dwBitRate;
	pF->dwAttributes = pMF->dwAttributes;
	pF->wChannels = pMF->wChannels;
	pF->wFormat = pMF->wFormat;
	pF->wModeLegacy = 0;
	pF->wUnused = 0;
}

#ifndef HPI_OS_LINUX_KERNEL
/* Note: These assignments must be in this order to avoid corrupting fields */
void HPI_StreamResponseToLegacy(
	struct hpi_stream_res *pSR
)
{
	pSR->u.legacy_stream_info.dwAuxiliaryDataAvailable =
		pSR->u.stream_info.dwAuxiliaryDataAvailable;
	pSR->u.legacy_stream_info.wState = pSR->u.stream_info.wState;
}
#endif

/* Cast msg/resp header structs into V0 msg/resp */
static inline void HPI_MessageV1(struct hpi_message_header *m, struct hpi_response_header *r)
{
	HPI_Message((struct hpi_message *)m, (struct hpi_response *)r);
}

#ifndef HPI_OS_LINUX_KERNEL
/*=========================================================================*/
/** \defgroup entity Entity
\{
*/

// Private Entity API

//Internal helper variables and functions

/* Initial size allocated for V1 responses, will be increased if target adapter
   requires it. Minimum value here is MIN_STRV_PACKET_SIZE
*/
static size_t strv_packet_size = sizeof(struct hpi_response) + 256;

#ifndef C99
#define C99(x)
#endif

static size_t entity_type_to_size[LAST_ENTITY_TYPE] = { \
	C99([entity_type_null]=) sizeof(uint8_t), \
	C99([entity_type_sequence]=) sizeof(struct hpi_entity), \
	C99([entity_type_reference]=) sizeof(uint32_t), \
	C99([entity_type_int]=) sizeof(int), \
	C99([entity_type_float]=) sizeof(float), \
	C99([entity_type_double]=) sizeof(double), \
	C99([entity_type_cstring]=) sizeof(char), \
	C99([entity_type_octet]=) sizeof(uint8_t), \
	C99([entity_type_ip4_address]=) 4 * sizeof(uint8_t), \
	C99([entity_type_ip6_address]=) 16 * sizeof(uint8_t), \
	C99([entity_type_mac_address]=) 6 * sizeof(uint8_t), \
	C99([entity_type_boolean]=) sizeof(uint8_t) \
};

static const char * const entity_type_names[LAST_ENTITY_TYPE]= { \
	C99([entity_type_null]=) "null", \
	C99([entity_type_sequence]=) "sequence", \
	C99([entity_type_reference]=) "reference", \
	C99([entity_type_int]=) "integer", \
	C99([entity_type_float]=) "float", \
	C99([entity_type_double]=) "double", \
	C99([entity_type_cstring]=) "C_string", \
	C99([entity_type_octet]=) "octet", \
	C99([entity_type_ip4_address]=) "IPv4_address", \
	C99([entity_type_ip6_address]=) "IPv6_address", \
	C99([entity_type_mac_address]=) "MAC_address", \
	C99([entity_type_boolean]=) "boolean" \
};

static const char * const entity_role_names[LAST_ENTITY_ROLE]= {
	C99([entity_role_null]=) "null",
	C99([entity_role_value]=) "value",
	C99([entity_role_classname]=) "classname",
	C99([entity_role_units]=) "units",
	C99([entity_role_flags]=) "flags",
	C99([entity_role_range]=) "range",
	C99([entity_role_mapping]=) "mapping",
	C99([entity_role_enum]=) "enum",
	C99([entity_role_instance_of]=) "instance_of",
	C99([entity_role_depends_on]=) "depends_on",
	C99([entity_role_member_of_group]=) "member_of_group",
	C99([entity_role_value_constraint]=) "value_constraint",
	C99([entity_role_parameter_port]=) "parameter_port",
	C99([entity_role_block]=) "block" ,
	C99([entity_role_label]=) "label" ,
	C99([entity_role_key]=)"key",
	C99([entity_role_value_label]=)"value_label"
};
	/* C99([entity_role_node_group] =)"node_group", */
	/* C99([entity_role_audio_port] =) "audio_port", */
	/* C99([entity_role_clock_port] =) "clock_port" */


static inline size_t hpi_entity_item_count(struct hpi_entity *entity_ptr)
{
	return hpi_entity_value_size(entity_ptr) / entity_type_to_size[entity_ptr->type];
}

/** Check if the type is a valid value for struct hpi_entity.type

We need to restrict the type to known types, because we use it to look up the
associated size of the type.

\return 0 on success or HPI_ERROR_ENTITY_TYPE_INVALID
*/
static inline hpi_err_t hpi_entity_check_type(const enum e_entity_type t)
{
	if ((int)t >= entity_type_null && t < LAST_ENTITY_TYPE)
		return 0;
	return HPI_ERROR_ENTITY_TYPE_INVALID;
}

/** Check if the role is a valid value for struct hpi_entity.role

It is not possible to restrict the check to the values defined by the
enum e_entity_role because it would prevent the functions from accepting
unknown roles defined in subsequent versions

\return 0 on success or HPI_ERROR_ENTITY_ROLE_INVALID
*/

static inline hpi_err_t hpi_entity_check_role(const enum e_entity_role r) {
	if (r >= entity_role_null && r < STR_ROLE_FIELD_MAX)
		return 0;
	return HPI_ERROR_ENTITY_ROLE_INVALID;
}

#if 0
static void hpi_dump_bytes(char *label, void *pv, int count)
{
	int i;
	uint8_t *p = pv;

	HPIOS_DEBUG_PRINTF("%s ", label);
	for (i = 0; i < count; i++) {
		HPIOS_DEBUG_PRINTF(" %d:%d", i, p[i]);
	}
	HPIOS_DEBUG_PRINTF("\n");
}
#else
#define hpi_dump_bytes(l, p, c)
#endif

/** Return the role of the entity */
enum e_entity_role HPI_Entity_GetRole(
	struct hpi_entity *entity ///< Pointer to the entity
)
{
	if (entity)
		return (enum e_entity_role)entity->role;
	else
		return entity_role_null;
}

/** Return the typename of the entity */
const char *HPI_Entity_GetTypeName(
	struct hpi_entity *entity ///< Pointer to the entity
)
{
	if (entity->type < ARRAY_SIZE(entity_type_names))
		return entity_type_names[entity->type];
	else
		return "unknown";
}

const char *HPI_Entity_GetRoleName(
	struct hpi_entity *entity ///< Pointer to the entity
)
{
	if (entity->role < ARRAY_SIZE(entity_role_names))
		return entity_role_names[entity->role];
	else
		return "unknown";
}


/** Get the next Entity in a sequence of entities.

The variable pointed by next will be updated with the address of the next Entity or set to NULL if no more entities are found.

If the current entity is a sequence and the recursive_flag is non-zero then the first item in the sequence is returned.
If the current entity is a sequence and the recursive_flag is zero then the first entity after the sequence is returned.
In all other cases the next entity is returned.

\param entity Pointer to the current entity.
\param next Pointer to the variable where the pointer to the next entity will be copied. If no next entity is found *next is set to NULL.
\param recursive_flag Indicate if the next entity should be obtained using a depth-first search
\param guard_p Pointer to the last valid memory location of the entities buffer. No Entity spanning beyond this location will be returned.
This typically means the the guard_p is computed from a container entity that has many entities in it and
is therefore set to span all the entities that are being stepped through with each call.
\return_hpierr
*/
static hpi_err_t HPI_Entity_GetNext(
	struct hpi_entity *entity,
	int recursive_flag,
	void *guard_p,
	struct hpi_entity **next
)
{

	//?HPI_DEBUG_ASSERT(entity);
	//?HPI_DEBUG_ASSERT(next);
	//?HPI_DEBUG_ASSERT(hpi_entity_size(entity) != 0);

	if (!next)
		return HPI_ERROR_MEMORY_ALLOC;

	*next = NULL;

	if (!entity)
		return HPI_ERROR_MEMORY_ALLOC;
	if (!hpi_entity_size(entity))
		return HPI_ERROR_ENTITY_ITEM_COUNT;

	if ((void*)entity >= guard_p)
		return 0;

	if (recursive_flag && entity->type == entity_type_sequence)
		*next = hpi_entity_value_ptr(entity);
	else
		*next = hpi_entity_ptr_to_next(entity);

	if ((void*)*next >= guard_p) {
		*next = NULL;
		return 0;
	}

	//?HPI_DEBUG_ASSERT(guard_p >= (void*)hpi_entity_ptr_to_next(*next));
	if ((void*)hpi_entity_ptr_to_next(*next) > guard_p)
		return HPI_ERROR_ENTITY_ITEM_COUNT;

	return 0;
}

/** Search for an entity with the given _type_ and _role_ in a container
entity.

Search for an entity with the given type and role in container_entity.
The search starts with the first entity inside the container_entity.
If container_entity is not a sequence then the function will return immediately.
The variable pointed by current_match will be updated with the address of
the next Entity or set to NULL if no more entities are found.
If current_match is not NULL upon call then the search resumes from
the entity following current_match. The recursive_flag is honored so if
current match is a sequence and recursive_flag is non-zero then searching
will start with the first item in the sequence.

\param container_entity Pointer to the container entity to be searched.
\param type Entity type selector value or entity_type_null. If a non-null
type is specified then the returned match will have this type. If a null
type is specified then the type of the entity is ignored when searching.
\param role Entity role selector value or entity_role_null. If a non-null
role is specified then the returned match will have this type. If a null
role is specified then the role of the entity is ignored when searching.
\param current_match Pointer to the variable where the pointer to the next
matching entity will be copied. If no matching entity is found *current_match is set to NULL.
\param recursive_flag Indicates the next entity should be obtained using
a depth-first search. When the recursive_flag is set, the search starts at the
current position in the entity tree and proceeds through the list of entity
objects. Every time a sequence is encountered the new sequence is searched until
the bottom of the tree is reached at which point the search backtracks.

For example, the tree below would be searched in order A,B,C,D,E,F,G,H if the
recursive_flag is set. If the recursive_flag is not set the search order becomes
A,B,C,H.
\code
|------[A - entity of type sequence]
|------[B]
|------[C- entity of type sequence]
|      |---[D]
|      |---[E- entity of type sequence]
|            |---[F]
|            |---[G]
|
|------[H]
\endcode

\return_hpierr
*/
hpi_err_t HPI_Entity_FindNext(
	struct hpi_entity *container_entity,
	enum e_entity_type type,
	enum e_entity_role role,
	int recursive_flag,
	struct hpi_entity **current_match
)
{
	struct hpi_entity *tmp = NULL;
	void *guard_p = NULL;

	//? HPI_DEBUG_ASSERT(container_entity != NULL);
	if (!container_entity)
		return HPI_ERROR_MEMORY_ALLOC;

	guard_p = hpi_entity_ptr_to_next(container_entity);

	// Search starting from root by default
	// If current_match is non NULL then start searching from the entity following current_match
	// otherwise iterate on the members of the container
	if (*current_match != NULL)
		HPI_Entity_GetNext(*current_match, recursive_flag, guard_p, &tmp);
	else
		HPI_Entity_GetNext(container_entity, 1, guard_p, &tmp);

	while (tmp) {
		hpi_err_t err;

		// current entity must be inside the root entity
		//? HPI_DEBUG_ASSERT((void*)tmp >= (void*)container_entity);
		if ((void*)tmp < (void*)container_entity)
			return HPI_ERROR_ENTITY_ITEM_COUNT;

		if ((!type || tmp->type == type) && (!role || tmp->role == role)) {
			*current_match = tmp;
			return 0;
		}

		err = HPI_Entity_GetNext(tmp, recursive_flag, guard_p, current_match);
		if (err) return err;

		tmp = *current_match;
	}

	*current_match = NULL;
	return 0;
}

/** Free an hpi_entity allocated by HPI_Universal_* and HPI_Entity_* functions

\param entity Pointer to the entity to be freed
*/
void HPI_Entity_Free(struct hpi_entity *entity)
{
	HpiOs_MemFree(entity);
}

/** Allocates an appropriately sized buffer and copies the src entity into it.
The newly allocated buffer is returned in dst.

\param src Pointer to the original entity to be copied.
\param dst Pointer to the newly allocated buffer where the copy is returned.
\return_hpierr
*/
static hpi_err_t HPI_Entity_AllocAndCopy(
	struct hpi_entity *src,
	struct hpi_entity **dst
)
{
	size_t buf_size;

	//? HPI_DEBUG_ASSERT(dst != NULL);
	//? HPI_DEBUG_ASSERT(src != NULL);
	if (!dst || !src)
		return HPI_ERROR_MEMORY_ALLOC;

	buf_size = hpi_entity_size(src);
	*dst = HpiOs_MemAlloc(buf_size);
	if (!*dst)
		return HPI_ERROR_MEMORY_ALLOC;
	memcpy(*dst, src, buf_size);
	return 0;
}

/** Allocate and initialize an entity with the provided parameters

\param type Entity type.
\param item_count Number of instances of _type_ contained in the _value_ array.
\param role Entity role.
\param value_size Size in bytes of variable pointed to by value.
\param value Pointer to an array of _type_ with at least _item_count_ elements.
\param pentity Location where a pointer to the newly allocated buffer containing the entity is saved.
The buffer must be freed by the caller using HPI_Entity_Free() even when the call returns an error.

\return_hpierr

\code
int integer = 0;
uint8_t ip4[4] = {192, 168, 0, 123};
struct hpi_entity *address, *enableflag;

HPI_Entity_AllocAndPack(entity_type_int, 1, entity_role_value, integer, &enableflag);
hpi_err_t HPI_SubSys_OptionSet(ss, HPI_SUBSYS_OPT_NET_BROADCAST, enableflag);
HPI_Entity_Free(enableflag);


HPI_Entity_AllocAndPack(entity_type_ip4_address, 1, entity_role_value, ip4, &address);
hpi_err_t HPI_SubSys_OptionSet(ss, HPI_SUBSYS_OPT_NET_ADDR, address);
HPI_Entity_Free(address);
\endcode

*/
hpi_err_t HPI_Entity_AllocAndPack(
	const enum e_entity_type type,
	const size_t item_count,
	const enum e_entity_role role,
	const void *value,
	const size_t value_size,
	struct hpi_entity **pentity
)
{
	size_t bytes_to_copy, total_size;
	hpi_err_t err = 0;
	struct hpi_entity *entity;

	*pentity = NULL;

	err = hpi_entity_check_type(type);
	if (err)
		return err;

	bytes_to_copy = entity_type_to_size[type] * item_count;

	if (bytes_to_copy > value_size)
		return HPI_ERROR_ENTITY_ITEM_COUNT;

	total_size = hpi_entity_header_size() + bytes_to_copy;

	//? HPI_DEBUG_ASSERT(total_size >= hpi_entity_header_size() && total_size <= STR_SIZE_FIELD_MAX);
	if (total_size < hpi_entity_header_size() || total_size > STR_SIZE_FIELD_MAX)
		return HPI_ERROR_ENTITY_ITEM_COUNT;

	entity = HpiOs_MemAlloc(total_size);
	if (!entity)
		return HPI_ERROR_MEMORY_ALLOC;
	memcpy(hpi_entity_value_ptr(entity), value, bytes_to_copy);
	entity->size = (uint16_t)total_size;
	entity->type = type;
	entity->role = role;
	*pentity = entity;
	return 0;
}

/** Copies item_count items from the Entity's value array into *value_dst_p

If type is different from the type of the entity
no value is copied and #HPI_ERROR_ENTITY_TYPE_MISMATCH is returned.

If the item_count is larger than the size of the Entity's value array then
no value is copied and #HPI_ERROR_ENTITY_ITEM_COUNT is returned.

When type is entity_type_cstring, a zero terminator is appended to the
copied bytes.

\param entity Pointer to the entity to copy values from.
\param type Type of the variable pointed by value_dst_p.
\param item_count Number of items to copy. This should match the storage associated with value_dst_p.
\param value_dst_p Pointer to output buffer
\param value_size Size in bytes of variable pointed to by value.

\return_hpierr

\code
int integer;
struct hpi_entity *enableflag;

HPI_SubSys_OptionGet(ss, HPI_SUBSYS_OPT_NET_BROADCAST, &enableflag);
HPI_Entity_CopyValueFrom(enableflag, entity_type_int, 1, &integer);
HPI_Entity_Free(enableflag);
\endcode

*/
hpi_err_t HPI_Entity_CopyValueFrom(
	struct hpi_entity *entity,
	enum e_entity_type type,
	size_t item_count,
	void *value_dst_p,
	size_t value_size
)
{
	size_t bytes_to_copy;

	if (entity->type != type)
		return HPI_ERROR_ENTITY_TYPE_MISMATCH;

	if (hpi_entity_item_count(entity) != item_count)
		return HPI_ERROR_ENTITY_ITEM_COUNT;

	bytes_to_copy = entity_type_to_size[type] * item_count;
	/* Because entity strings have count and no nul terminator, a nul
	   terminator is added. First, allow space for terminator.
	*/
	if (type == entity_type_cstring)
		value_size = value_size - 1;

	if (bytes_to_copy > value_size)
		return HPI_ERROR_ENTITY_SIZE_MISMATCH;

	memcpy(value_dst_p, hpi_entity_value_ptr(entity), bytes_to_copy);

	if (type == entity_type_cstring)
		((char *)value_dst_p)[bytes_to_copy] = 0;

	return 0;
}

/** Extracts information from an entity into the specified locations.

\param entity The entity being unpacked.
\param type Pointer to the variable where the type of the entity is copied to.
\param item_count Pointer to the variable where the number of instances of
       _type_ contained in the entity is copied to.
\param role Pointer to the variable where the role of the entity is copied to.
\param value Pointer to the location where a pointer to the value array of the
       entity is updated.
Note: the value array of the entity is not copied,
a pointer to its location is copied into the variable pointed by value.
\return_hpierr
*/
hpi_err_t HPI_Entity_Unpack(
	struct hpi_entity *entity,
	enum e_entity_type *type,
	size_t *item_count,
	enum e_entity_role *role,
	void **value
)
{
	hpi_err_t err = 0;

	//? HPI_DEBUG_ASSERT(entity != NULL);
	if (!entity)
		return HPI_ERROR_MEMORY_ALLOC;

	hpi_dump_bytes("Unpack", entity, entity->size);

	*type = (enum e_entity_type)entity->type;
	*role = (enum e_entity_role)entity->role;
	*value = hpi_entity_value_ptr(entity);
	if (entity->type == entity_type_sequence) {
		void *guard_p = hpi_entity_ptr_to_next(entity);
		struct hpi_entity *next = NULL;
		void *contents = hpi_entity_value_ptr(entity);
		// loop over the items of the sequence and count them
		*item_count = 0;
		while (contents < guard_p) {
			(*item_count)++;
			err = HPI_Entity_GetNext(contents, 0, guard_p, &next);
			if (next == NULL || err)
				break;
			contents = next;
		}
	} else {
		*item_count = hpi_entity_item_count(entity);
	}
	return err;
}

/*
hpi_err_t HPI_Entity_PutValue(
	struct hpi_entity *entity,
	enum e_entity_type type,
	void *value_p,
    size_t item_count
)
{
	size_t bytes_to_copy;

	if (entity->type != type)
		return HPI_ERROR_ENTITY_TYPE_MISMATCH;

	bytes_to_copy = entity_type_to_size[type] * item_count;

	if (entity->size - sizeof(*entity) < bytes_to_copy)
		return HPI_ERROR_ENTITY_ITEM_COUNT;

	memcpy(entity->value, value_p, bytes_to_copy);
	return 0;
}

// User friendly (?) helpers
enum e_entity_type HPI_Entity_GetType(
	const struct hpi_entity *entity
)
{
	if (entity)
		return entity->type;
	else
		return entity_type_null;
}

hpi_err_t HPI_Entity_SetFloat(
	struct hpi_entity *entity,
	float *value_p
)
{
	return HPI_Entity_PutValue(entity, entity_type_float, value_p, 1);
}

hpi_err_t HPI_Entity_SetInteger(
	struct hpi_entity *entity,
	int *value_p
)
{
	return HPI_Entity_PutValue(entity, entity_type_int, value_p, 1);
}

hpi_err_t HPI_Entity_SetString(
	struct hpi_entity *entity,
	char *value_p,
    size_t max_str_len
)
{
	return HPI_Entity_PutValue(entity, entity_type_cstring, value_p, max_str_len);
}
*/

/** Get an Entity value from an HPI object.

HPI_Entity_Free() must always be called on value, even if error is returned,
to ensure an allocated memory is freed.
*/
static hpi_err_t HPI_Entity_Get(
	const hpi_hsubsys_t *phSubSys, ///< Vestigial subsys handle (unused), may be set to NULL
	uint16_t adapter_index, ///< Adapter index (if not subsys)
	uint16_t obj_index, ///< Obj index, eg control index, subsys option
	uint16_t func, ///< HPI function ID
	struct hpi_entity **value ///< Will be set to pointer to allocated buffer
)
{
	struct hpi_msg_strv hm;
	struct hpi_res_strv *phr;
	hpi_err_t hpi_err = 0;
	int remaining_attempts = 2;
	HPI_UNUSED(phSubSys);

	*value = NULL;

	while(remaining_attempts--) {
		phr = HpiOs_MemAlloc(strv_packet_size);
		if (!phr)
			return HPI_ERROR_MEMORY_ALLOC;

		HPI_InitMessageResponseV1(
			&hm.h, (uint16_t)sizeof(hm),
			&phr->h, (uint16_t)strv_packet_size,
			(func >> 8), func);

		hm.h.wAdapterIndex = adapter_index;
		hm.h.wObjIndex = obj_index;

		hm.strv.size = sizeof(hm.strv);
		//phr->strv.size = (uint16_t)(strv_packet_size - sizeof(phr->h));

		HPI_MessageV1(&hm.h, &phr->h);
		if (phr->h.wError == HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL) {
			/* Adaptive allocation learns the actual max entity size */
			/*? HPI_DEBUG_ASSERT(
				phr->h.wSpecificError > MIN_STRV_PACKET_SIZE &&
				phr->h.wSpecificError < 1500); */
			if (phr->h.wSpecificError < MIN_STRV_PACKET_SIZE ||
			    phr->h.wSpecificError > 1500) {
				hpi_err = HPI_ERROR_MEMORY_ALLOC;
				HpiOs_MemFree(phr);
				break;
			}

			HPI_DEBUG_LOG2(DEBUG, "Increasing entity response buffer from %ld to %d bytes\n",
				(long int)strv_packet_size, phr->h.wSpecificError);
			strv_packet_size = phr->h.wSpecificError;
		} else {
			remaining_attempts = 0;
			if (!phr->h.wError) {
				HPI_Entity_AllocAndCopy(&phr->strv, value);
				hpi_dump_bytes("EntityGet", &phr->strv, strv_packet_size);
			}
		}

		hpi_err = phr->h.wError;
		HpiOs_MemFree(phr);
	}

	return hpi_err;
}
/** Send an entity value to an HPI object.

*/
static hpi_err_t HPI_Entity_Set(
	const hpi_hsubsys_t *phSubSys, ///< Vestigial subsys handle (unused), may be set to NULL
	uint16_t adapter_index, ///< Adapter index (if not subsys)
	uint16_t obj_index, ///< Obj index, eg control index, subsys option
	uint16_t func, ///< HPI function ID
	const struct hpi_entity *value ///< entity value to send
)
{
	struct hpi_msg_strv *phm;
	struct hpi_response_header hr;
	HPI_UNUSED(phSubSys);

	phm = HpiOs_MemAlloc(sizeof(phm->h) + value->size);
	//? HPI_DEBUG_ASSERT(phm);
	if (!phm)
		return HPI_ERROR_MEMORY_ALLOC;

	HPI_InitMessageResponseV1(
		&phm->h, sizeof(phm->h) + value->size,
		&hr, sizeof(hr),
		(func >> 8), func);

	phm->h.wAdapterIndex = adapter_index;
	phm->h.wObjIndex = obj_index;

	memcpy(&phm->strv, value, value->size);

	HPI_MessageV1(&phm->h, &hr);

	HpiOs_MemFree(phm);
	return hr.wError;
}

/** @} */ /* defgroup entity */
#endif

#ifndef HPI_OS_LINUX_KERNEL
/*=========================================================================*/
/** \defgroup object Object

Starting with driver 4.08, AudioScience has begun representing certain new
controls as abstract objects. Each equivalent of a "knob" is an object of
type parameter. Parameters that logically belong together are grouped into
an object of type block.

Blocks are represented as HPI controls of type HPI_CONTROL_UNIVERSAL. An
application can look for a specific block by using
HPI_Object_BlockHandle(). Parameters are also represented as HPI controls
of type HPI_CONTROL_UNIVERSAL and HPI_Object_ParameterHandle() should be be
used to locate a specific parameter within a block.

See \ref universal_delay for an example of block and parameter usage.

\{
*/

static const char protocol[] = "hpi://";
static const char prefix[] = "subsystem/network/";
static struct {
	const char * uri;
	uint16_t obj;
	uint16_t idx;
} obj_uri_to_idx[] =
{
	{"enabled",  HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_OPT_NET_ENABLE},
	{"ip/address", HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_OPT_NET_ADDR},
	{"ip/mask", HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_OPT_NET_MASK},
	{"ip/adapter_address_add", HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_OPT_NET_ADAPTER_ADDRESS_ADD},
	{"ip/udp/broadcast_enabled", HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_OPT_NET_BROADCAST},
	{"ip/udp/unicast_enabled", HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_OPT_NET_UNICAST}
};

/** Get an object handle given the object's URI

For a complete list of valid URIs, see \ref uri_doc.

Usage:
\code
hpi_handle_t h;
int enable;
err = HPI_Object_UriToHandle("hpi:///subsystem/network/enable",  &h);
err = HPI_Object_GetValue(h, entity_type_int, 1, &enable, sizeof(enable) );
enable = 1;
err = HPI_Object_SetValue(h, entity_type_int, 1,  &enable, sizeof(enable));
\endcode


\retval 0 OK
\retval HPI_ERROR_INVALID_OBJ_INDEX The URI doesn't correspond to any known object
\retval HPI_ERROR_INVALID_RESOURCE The URI must start with "hpi://"
*/
hpi_err_t HPI_Object_UriToHandle(
	const char *uri, ///< URI of HPI object, \ref uri_doc
	hpi_handle_t *h) ///< Object handle
{
	uint16_t adapter_idx = HPI_ADAPTER_INDEX_INVALID;
	uint16_t obj_idx = 0;
	uint16_t obj_type = 0;
	int ofs = 0;
	int i;

	if (strncmp(uri, protocol, sizeof(protocol) - 1) != 0)
		return HPI_ERROR_INVALID_RESOURCE;

	ofs +=  sizeof(protocol) - 1;

	if (uri[ofs] == '/') // allow hpi:///subsystem/...
		ofs += 1;

	if (strncmp(&uri[ofs],  prefix, sizeof(prefix) - 1) != 0)
		return HPI_ERROR_INVALID_OBJ_INDEX;

	ofs +=  sizeof(prefix) - 1;

	for (i = 0; i < ARRAY_SIZE(obj_uri_to_idx); i++) {
		if (strcmp(&uri[ofs], obj_uri_to_idx[i].uri) == 0) {
			obj_idx = obj_uri_to_idx[i].idx;
			obj_type = obj_uri_to_idx[i].obj;
			break;
		}
	}

	if (obj_type) {
		*h = HPI_IndexesToHandle((char)obj_type, adapter_idx, obj_idx);
		return 0;
	} else {
		*h = 0;
		return HPI_ERROR_INVALID_OBJ_INDEX;
	}
}

static struct {
	uint16_t info;
	uint16_t get;
	uint16_t set;
} ent_func_ids[] = {
	{0, 0, 0},
	{HPI_SUBSYS_OPTION_INFO, HPI_SUBSYS_OPTION_GET, HPI_SUBSYS_OPTION_SET},
	{0, 0, 0}, // HPI_OBJ_ADAPTER		= 2,
	{0, 0, 0}, // HPI_OBJ_OSTREAM		= 3,
	{0, 0, 0}, // HPI_OBJ_ISTREAM		= 4,
	{0, 0, 0}, // HPI_OBJ_MIXER		= 5,
	{0, 0, 0}, // HPI_OBJ_NODE		= 6,
	{HPI_CONTROL_GET_INFO, HPI_CONTROL_GET_STATE, HPI_CONTROL_SET_STATE}
};

/** Get the complete information entity for an object addressed by handle

\retval HPI_ERROR_INVALID_OBJ_INDEX the handle doesnt correspond to an object type
that might support this function.

\note HPI_Entity_Free() must always be called on info, even if error is returned,
to ensure an allocated memory is freed.
*/
hpi_err_t HPI_Object_GetInfoEntity(
		hpi_handle_t h, ///< object handle from HPI_Object_UriToHandle()
		struct hpi_entity **info ///< returns address of allocated info entity
)
{
	hpi_err_t err;
	union handle_word uh;
	uint16_t obj_type;
	uint16_t adap_idx;
	uint16_t obj_func;

	*info = NULL;

	uh.w = h;
	obj_type = (uint16_t)uh.h.objType;
	adap_idx = (uint16_t)uh.h.adapterIndex;

	if (obj_type == HPI_OBJ_SUBSYSTEM)
		adap_idx = HPI_ADAPTER_INDEX_INVALID;

	if (obj_type >= ARRAY_SIZE(ent_func_ids))
		return HPI_ERROR_INVALID_OBJ_INDEX;

	obj_func = ent_func_ids[obj_type].info;
	if (!obj_func)
		return HPI_ERROR_INVALID_OBJ_INDEX;

	err = HPI_Entity_Get(NULL, adap_idx, (uint16_t)uh.h.objIndex,
		obj_func, info);

	return err;
}
/** Get information about an object.

\param hObject 	Object handle.
\param type type of entity to unpack.
\param role role of the entity to unpack.
\param value passed in pointer to data buffer where result is copied. May be
	set to NULL to retreive size and item count information.
\param value_size size in bytes of the passed in buffer. This is also updated
	and returned.
\param value_count number of items. This is always updated and returned.
\return_hpierr
*/
hpi_err_t HPI_Object_GetInfo(
	hpi_handle_t hObject,
	enum e_entity_type type,
	enum e_entity_role role,
	void *value,
	size_t *value_size,
	size_t *value_count
)
{
	hpi_err_t hpiErr;
	struct hpi_entity *c;
	enum e_entity_type t;
	enum e_entity_role r;
	void * pv;
	size_t n_bytes;
	int terminator_byte = 0;
	struct hpi_entity *info;

	hpiErr = HPI_Universal_Info(NULL, hObject, &info);
	if (hpiErr)
		goto free;

	c = NULL;
	hpiErr = HPI_Entity_FindNext(info, type, role, 0, &c);
	if (hpiErr)
		goto free;

	if (!c) {
		hpiErr = HPI_ERROR_ENTITY_TYPE_MISMATCH;
		goto free;
	}

	hpiErr = HPI_Entity_Unpack(c, &t, value_count, &r, &pv);
	if (hpiErr)
		goto free;

	if (type == entity_type_cstring)
		terminator_byte = 1;
	n_bytes = *value_count * entity_type_to_size[type];
	if (value && (n_bytes + terminator_byte > *value_size)) {
		hpiErr = HPI_ERROR_ENTITY_ITEM_COUNT;
		goto free;
	}

	*value_size = n_bytes + terminator_byte;

	if (value) {
		memcpy( value, pv, n_bytes );
		if (type == entity_type_cstring)
			((char *)value)[n_bytes] = 0;
	}

free:
	HPI_Entity_Free(info);
	return hpiErr;
}

/** Get the object role.
\return_hpierr
*/
HPI_API (hpi_err_t) HPI_Object_GetRole(
	hpi_handle_t hObject,	///< Object handle.
	enum e_entity_role *r	///< Returned role.
)
{
	hpi_err_t hpiErr;
	struct hpi_entity *info;

	hpiErr = HPI_Universal_Info(NULL, hObject, &info);
	if (!hpiErr) {
		*r = HPI_Entity_GetRole(info);
	}
	HPI_Entity_Free(info);
	return hpiErr;
}

/** Given a block handle, return a list of parameter object handles.
\return_hpierr
*/
hpi_err_t HPI_Object_BlockParameters(
	hpi_handle_t hMixer ,	///< Mixer handle.
	hpi_handle_t block,	///< Block handle.
	hpi_handle_t *params,	///< Array of parameter handles (may be set to NULL).
	size_t *param_count	///< Parameter count. This is always updated on return to reflect the parameter count.
)
{
	hpi_err_t err;
	struct hpi_entity *e;
	size_t count;
	void *pv;
	int *index;
	enum e_entity_type t;
	enum e_entity_role r;
	size_t i;

	struct hpi_entity *info;

	err = HPI_Universal_Info(NULL, block, &info);
	if (err)
		goto exit_error;

	if (HPI_Entity_GetRole(info) != entity_role_block)
		err = HPI_ERROR_ENTITY_TYPE_MISMATCH;
	if (err)
		goto exit_error;

	// fetch parameter blocks by index
	e = NULL;
	err = HPI_Entity_FindNext(info, entity_type_reference, entity_role_value, 0, &e);
	if (err)
		goto exit_error;

	err = HPI_Entity_Unpack(e, &t, &count, &r, &pv);
	if (err)
		goto exit_error;

	if (  params && (count > *param_count) )
		err = HPI_ERROR_ENTITY_ITEM_COUNT;

	*param_count = count;
	if (err)
		goto exit_error;

	index = (int *)pv;
	for( i=0; i<count; i++) {
		struct hpi_control_t c_read;
		err = HPI_MixerGetControlByIndex(NULL, hMixer,
			(uint16_t)hpi_entity_read_int_value(index+i),
			&c_read.wSrcNodeType, &c_read.wSrcNodeIndex,
			&c_read.wDstNodeType, &c_read.wDstNodeIndex,
			&c_read.wControlType,
			&params[i]);
		if (err)
			break;
	}

exit_error:
	HPI_Entity_Free(info);
	if (err)
		return err;

	return err;
}


/** Given block location information and name, fetch an object handle for the block.
\return_hpierr
*/
hpi_err_t HPI_Object_BlockHandle(
	hpi_handle_t hMixer,	///< Mixer handle.
	uint16_t wSrcNodeType,	///< Source node type i.e. #HPI_SOURCENODE_OSTREAM.
	uint16_t wSrcNodeTypeIndex,	///< Index of particular source node type i.e. the 2nd \ref HPI_SOURCENODE_OSTREAM would be specified as index=1.
	uint16_t wDstNodeType,	///< Destination node type i.e. \ref HPI_DESTNODE_LINEOUT.
	uint16_t wDstNodeTypeIndex,	///< Index of particular source node type i.e. the 3rd \ref HPI_DESTNODE_LINEOUT would be specified as index=2
	const char *block_name,  	///< Name of the block to find.
	hpi_handle_t * phBlock	///< Returned object handle.
)
{
	struct hpi_msg_block_handle hm;
	HPI_RESPONSE hr;
	uint16_t func = HPI_MIXER_GET_BLOCK_HANDLE;
	size_t name_len = strlen(block_name) + 1;

	if ( name_len > (sizeof(hm.name)) )
		return HPI_ERROR_INVALID_CONTROL_VALUE;

	HPI_InitMessageResponseV1(
		&hm.h,  sizeof(struct hpi_message_header) +
			sizeof(struct hpi_mixer_msg) +
			name_len,
		(void *)&hr, sizeof(hr),
		(func >> 8), func);

	if (hpi_handle_indexes(hMixer, &hm.h.wAdapterIndex, NULL))
		return HPI_ERROR_INVALID_HANDLE;

	hm.m.wNodeType1 = wSrcNodeType;
	hm.m.wNodeIndex1 = wSrcNodeTypeIndex;
	hm.m.wNodeType2 = wDstNodeType;
	hm.m.wNodeIndex2 = wDstNodeTypeIndex;
	hm.m.wControlType = HPI_CONTROL_UNIVERSAL;
	strcpy(hm.name, block_name);

	HPI_MessageV1(&hm.h, (void *)&hr);

	if (hr.wError == 0)
		*phBlock =
			HPI_IndexesToHandle(HPI_OBJ_CONTROL, hm.h.wAdapterIndex,
				hr.u.m.wControlIndex);
	else
		*phBlock = 0;
	return hr.wError;
}


/** Given a block handle and parameter name, fetch an object handle for the specified parameter.
\return_hpierr
*/
hpi_err_t HPI_Object_ParameterHandle(
	hpi_handle_t hMixer,	///< Mixer handle.
	hpi_handle_t hBlock,	///< Block handle.
	const char *parameter_name,  	///< Name of the parameter to return.
	hpi_handle_t * phParameter	///< Returned object handle.
)
{
	struct hpi_msg_parameter_handle hm;
	HPI_RESPONSE hr;
	uint16_t func = HPI_MIXER_GET_PARAMETER_HANDLE;
	uint16_t adap_index, obj_index;
	size_t name_len = strlen(parameter_name) + 1;

	if ( name_len > (sizeof(hm.name)) )
		return HPI_ERROR_INVALID_CONTROL_VALUE;

	HPI_InitMessageResponseV1(
		&hm.h,  sizeof(struct hpi_message_header) +
			sizeof(uint32_t) +
			name_len,
		(void *)&hr, sizeof(hr),
		(func >> 8), func);

	if (hpi_handle_indexes(hMixer, &hm.h.wAdapterIndex, NULL))
		return HPI_ERROR_INVALID_HANDLE;

	if (hpi_handle_indexes(hBlock, &adap_index, &obj_index))
		return HPI_ERROR_INVALID_HANDLE;

	hm.block_control_index = obj_index;
	if ( strlen(parameter_name) > (sizeof(hm.name)-1) )
		return HPI_ERROR_INVALID_CONTROL_VALUE;
	strcpy(hm.name, parameter_name);

	HPI_MessageV1(&hm.h, (void *)&hr);

	if (hr.wError == 0)
		*phParameter =
			HPI_IndexesToHandle(HPI_OBJ_CONTROL, hm.h.wAdapterIndex,
				hr.u.m.wControlIndex);
	else
		*phParameter = 0;
	return hr.wError;
}

/** Get the value entity of an object addressed by its handle.

\retval HPI_ERROR_INVALID_OBJ_INDEX the handle doesnt correspond to an object type
that might support this function.

\note HPI_Entity_Free() must always be called on value, even if error is returned,
to ensure an allocated memory is freed.

*/
hpi_err_t HPI_Object_GetValueEntity(
		hpi_handle_t h, ///< object handle from HPI_Object_UriToHandle()
		struct hpi_entity **value ///< returns address of allocated value entity
)
{
	hpi_err_t err;
	union handle_word uh;
	uint16_t obj_type;
	uint16_t adap_idx;
	uint16_t obj_func;

	*value = NULL;

	uh.w = h;
	obj_type = (uint16_t)uh.h.objType;
	adap_idx = (uint16_t)uh.h.adapterIndex;

	if (obj_type == HPI_OBJ_SUBSYSTEM)
		adap_idx = HPI_ADAPTER_INDEX_INVALID;

	if (obj_type >= ARRAY_SIZE(ent_func_ids))
		return HPI_ERROR_INVALID_OBJ_INDEX;

	obj_func = ent_func_ids[obj_type].get;
	if (!obj_func)
		return HPI_ERROR_INVALID_OBJ_INDEX;

	err = HPI_Entity_Get(NULL, adap_idx, (uint16_t)uh.h.objIndex,
		obj_func, value);

	return err;
}

/** Set the value entity of an object addressed by its handle.

*/
hpi_err_t HPI_Object_SetValueEntity(
		hpi_handle_t h, ///< object handle from HPI_Object_UriToHandle()
		const struct hpi_entity *value ///< entity, with role=entity_role_value
)
{
	hpi_err_t err;
	uint16_t obj_type;
	uint16_t adap_idx;
	uint16_t obj_func;
	union handle_word uh;

	uh.w = h;
	obj_type = (uint16_t)uh.h.objType;
	adap_idx = (uint16_t)uh.h.adapterIndex;

	if (obj_type == HPI_OBJ_SUBSYSTEM)
		adap_idx = HPI_ADAPTER_INDEX_INVALID;

	if (obj_type >= ARRAY_SIZE(ent_func_ids))
		return HPI_ERROR_INVALID_OBJ_INDEX;

	obj_func = ent_func_ids[obj_type].set;
	if (!obj_func)
		return HPI_ERROR_INVALID_OBJ_INDEX;

	err = HPI_Entity_Set(NULL, adap_idx, (uint16_t)uh.h.objIndex,
		obj_func, value);

	return err;
}

/** Retrieve an object's value
*/
hpi_err_t HPI_Object_GetValue(
		hpi_handle_t h, ///< object handle from HPI_Object_UriToHandle()
		enum e_entity_type type, ///< base type of *value
		size_t count, ///< number of items of the base type
		void *value, ///< value retrieved from object
		size_t value_size ///< size in bytes of *value
)
{
	struct hpi_entity *e;
	hpi_err_t err;

	err = HPI_Object_GetValueEntity(h, &e);
	if (!err)
		err = HPI_Entity_CopyValueFrom(e, type, count, value, value_size);

	HPI_Entity_Free(e);
	return err;
}

/** Set an object's value
*/
hpi_err_t HPI_Object_SetValue(
		hpi_handle_t h, ///< object handle from HPI_Object_UriToHandle()
		enum e_entity_type type, ///< base type of *value
		size_t count, ///< number of items of the base type
		const void *value, ///< value to be set in object
		size_t value_size ///< size in bytes of *value
)
{
	struct hpi_entity *e;
	hpi_err_t err;

	// create entity data "blob" containing a single instance of value
	err = HPI_Entity_AllocAndPack(type, count, entity_role_value, value, value_size, &e);
	if (!err)
		err = HPI_Object_SetValueEntity(h, e);
	HPI_Entity_Free(e);
	return err;
}
/** Free memory allocated by a previous HPI call.

\param mem Pointer to the memory to be freed.
*/
void HPI_MemFree(void *mem)
{
	HpiOs_MemFree(mem);
}

/** @} */ /* defgroup object */
#endif

/*=========================================================================*/
/** \defgroup subsys Subsystem
\{
*/
#ifndef HPI_BUILD_KERNEL_MODE
struct hpi_hsubsys {
	int not_really_used;
};

static struct hpi_hsubsys  ghSubSys = {0};	// not really used

/** HPI Subsystem create.
  * Creates, opens and initializes the audio subsystem. Must be called before
  * other HPI functions are called.
  * \return Non-zero pointer on success, NULL on error.
  */
hpi_hsubsys_t *HPI_SubSysCreate(void)
{
	memset(&ghSubSys, 0, sizeof(hpi_hsubsys_t));
	if (HPI_DriverOpen())
		return &ghSubSys;
	else
		return NULL;
}

/** HPI Subsystem free.
  * Closes the HPI subsystem, freeing any resources allocated.
  * Must be the last HPI function called.
  */
void HPI_SubSysFree(
	const hpi_hsubsys_t *phSubSys ///< Vestigial subsys handle (unused), may be set to NULL
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	// tell HPI to shutdown
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_CLOSE);
	HPI_Message(&hm, &hr);

	HPI_DriverClose();
}
#endif

/** Extended HPI_SubSysGetVersion() that returns Major, Minor and Build versions
   * Returns extended HPI subsystem version that was embedded into the
  * HPI module at compile time. On a
   * Windows machine this version is embedded in the kernel driver .sys file.
   *
   * \param phSubSys Vestigial subsys handle (unused), may be set to NULL
   * \param pdwVersionEx 32 bit word containing version of HPI.\n
   *	B23..16 = Major version\n
   *	B15..8 = Minor version\n
   *	B7..0 = Build version\n
   *	i.e. 0x00030402 is version 3.04.02
   *
   * \return_hpierr
   */
hpi_err_t HPI_SubSysGetVersionEx(
	const hpi_hsubsys_t *phSubSys,
	uint32_t *pdwVersionEx
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;

	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_GET_VERSION);
	HPI_Message(&hm, &hr);
	*pdwVersionEx = hr.u.s.dwData;
	return hr.wError;
}

#ifndef HPI_OS_LINUX_KERNEL
/** Return the total number of adapters including networked adapters.
  * If multiple adapters have the same adapter index (jumper setting) they will be included in the total.
  * \return_hpierr
  */
hpi_err_t HPI_SubSysGetNumAdapters(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	/** total number of adapters including local and networked. */
	int *pnNumAdapters
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_GET_NUM_ADAPTERS);
	HPI_Message(&hm, &hr);
	*pnNumAdapters = (int)hr.u.s.wNumAdapters;
	return hr.wError;
}

/** Extended version of HPI_SubSysFindAdapters() that iterates through all
  * adapters present, returning adapter index and type for each one.
  * If an adapter's index is duplicated then only one will return success; any others will set the
  * adapter type and index (to report the error to the user) and return HPI_ERROR_DUPLICATE_ADAPTER_NUMBER.
  * \return_hpierr
  */
hpi_err_t HPI_SubSysGetAdapter(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
/// Adapter iterator {0 .. total number of adapters - 1}.
	int nIterator,
	uint32_t *pdwAdapterIndex,	///< Index of adapter.
	uint16_t *pwAdapterType	///< Type of adapter.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_GET_ADAPTER);
	hm.wObjIndex = (uint16_t)nIterator;
	HPI_Message(&hm, &hr);
	*pdwAdapterIndex = (int)hr.u.s.wAdapterIndex;
	*pwAdapterType = hr.u.s.wAdapterType;

	return hr.wError;
}

/** Sets the HPI networking subsystem to use the network interface specified.
  * This is a required call before UDP
  * messaging will work to interface with an ASI2416.
  * \return_hpierr
  */
hpi_err_t HPI_SubSysSetHostNetworkInterface(
	const hpi_hsubsys_t *phSubSys, ///< Vestigial subsys handle (unused), may be set to NULL
	/** String containing network interface information.
	On Windows this is the IP address of the network adapter,
	ie "192.168.1.11". */
	const char *szInterface
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_SUBSYSTEM,
		HPI_SUBSYS_SET_NETWORK_INTERFACE);
	if (szInterface == NULL)
		return HPI_ERROR_INVALID_RESOURCE;
	hm.u.s.resource.r.net_if = szInterface;
	HPI_Message(&hm, &hr);
	return hr.wError;
}

#endif

#ifdef HPI_BUILD_INCLUDE_DEPRECATED
/** HPI subsystem get version.
  * Returns the HPI subsystem major and minor versions that were embedded into
  * the HPI module at compile time. On a Windows machine this version is
  * embedded in the kernel driver .sys file.
  *
  * \deprecated Use HPI_SubSysGetVersionEx() instead.
  * \param phSubSys Vestigial subsys handle (unused), may be set to NULL
  * \param pdwVersion 32 bit word containing version of HPI.
  * Upper 24bits is major version number and lower 8 bits is minor
  * version number, i.e., 0x00000304 is version 3.04.
  * \return_hpierr
  */
hpi_err_t HPI_SubSysGetVersion(
	const hpi_hsubsys_t *phSubSys,
	uint32_t *pdwVersion
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;

	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_GET_VERSION);
	HPI_Message(&hm, &hr);
	*pdwVersion = hr.u.s.dwVersion;
	return hr.wError;
}

static hpi_err_t SubSysFindAdapters(
	const hpi_hsubsys_t *phSubSys, ///< Vestigial subsys handle (unused), may be set to NULL
	uint16_t *pwNumAdapters,
	uint16_t awAdapterList[],
	uint16_t wListLength
)
{
	hpi_err_t e;
	int numAdapters;
	int i;

	e = HPI_SubSysGetNumAdapters(phSubSys, &numAdapters);
	if (e) {
		*pwNumAdapters = 0;
		return e;
	}

	*pwNumAdapters = (uint16_t)numAdapters;

	memset(awAdapterList, 0, wListLength * sizeof(awAdapterList[0]));

	for (i = 0; i < numAdapters; i++) {
		uint32_t dwAdapterIndex;
		uint16_t wAdapterType;

		e = HPI_SubSysGetAdapter(phSubSys, i,
			&dwAdapterIndex, &wAdapterType);

		if (e)
			break;

		if (wAdapterType && (dwAdapterIndex < wListLength))
			awAdapterList[dwAdapterIndex] = wAdapterType;

	}

	return e;
}

/** \deprecated Use a combination of HPI_SubSysGetVersionEx() and
    HPI_SubSysGetNumAdapters() and HPI_SubSysGetAdapter() instead.
  */
hpi_err_t HPI_SubSysGetInfo(
	const hpi_hsubsys_t *phSubSys,
	uint32_t *pdwVersion,
	uint16_t *pwNumAdapters,
	uint16_t awAdapterList[],
	uint16_t wListLength
)
{
	hpi_err_t e;
	e = HPI_SubSysGetVersionEx(phSubSys, pdwVersion);
	if (!e)
		e = SubSysFindAdapters(phSubSys, pwNumAdapters,
			awAdapterList, wListLength);
	return e;
}

/** Find all adapters that the HPI subsystem knows about.
  *
  * \deprecated Use a combination of HPI_SubSysGetNumAdapters() and
  * HPI_SubSysGetAdapter() instead
  *
  * The type and adapter number of each adapter is returned in an array of uint16_t
  * pointed to by awAdapterList.  Each position in the array identifies an
  * adapter with an adapter index of the corresponding array index.
  * The value of the array indicates the adapter type.
  * A value of zero indicates that no adapter exists for that adapter number.
  *
  * For example if awAdapterList had a 6114 in position 0, a 0 in position 1
  * and a 6514 in position 2, that would indicate an 6114 adapter set to
  * adapter number 1 and a 6514 adapter set to adapter number 3 in the system.
  * Note that the Adapter number (as set on the card/adapter) will be one
  * more than the array index.
<table border=1 cellspacing=0 cellpadding=5>
<tr>
<td width=200><b>Index:</b>
<td width=80><b>0</b><td width=80><b>1</b>
<td width=80><b>2</b><td width=80><b>3</b>

<tr>
<td width=200>awAdapterList
<td width=80>0x6244<td width=80>0
<td width=80>0x4346<td width=80>0

</table>
  *
  * \return_hpierr
  */
hpi_err_t HPI_SubSysFindAdapters(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	uint16_t *pwNumAdapters,	///< Returned number of initialised adapters
	uint16_t awAdapterList[],	///< Array of adapter types (see description).
	uint16_t wListLength	///< Size (in elements) of *pawAdapterList array
)
{
	return SubSysFindAdapters(phSubSys, pwNumAdapters,
			awAdapterList, wListLength);
}
#endif
/** @} */ /* defgroup subsys */

/*=========================================================================*/
/* placeholeder so dox documentation ends up in the correct spot */
/** \defgroup network Network
\{
*/
/** @} */ /* defgroup network */

/*=========================================================================*/
/** \defgroup adapter Adapter

@{
*/

#ifndef HPI_OS_LINUX_KERNEL
/** Opens an adapter for use.
  * The adapter is specified by wAdapterIndex which corresponds to the adapter
  * index on the adapter hardware (typically set using jumpers or switch).
  * \param phSubSys Vestigial subsys handle (unused), may be set to NULL
  * \param wAdapterIndex Index of the adapter to be opened. For PCI adapters
  * this ranges from 0-15. Network adapter indicies start at 1000.
  *
  * \return_hpierr
  */
hpi_err_t HPI_AdapterOpen(
	const hpi_hsubsys_t *phSubSys,
	uint16_t wAdapterIndex
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ADAPTER, HPI_ADAPTER_OPEN);
	hm.wAdapterIndex = wAdapterIndex;

	HPI_Message(&hm, &hr);

	return hr.wError;

}

/** Closes the adapter associated with the wAdapterIndex.
  * \param phSubSys Vestigial subsys handle (unused), may be set to NULL
  * \param wAdapterIndex Index of the adapter to be opened. For PCI adapters
  * this ranges from 0-15. Network adapter indicies start at 1000.
  * \return_hpierr
  */
hpi_err_t HPI_AdapterClose(
	const hpi_hsubsys_t *phSubSys,
	uint16_t wAdapterIndex
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ADAPTER, HPI_ADAPTER_CLOSE);
	hm.wAdapterIndex = wAdapterIndex;

	HPI_Message(&hm, &hr);

	return hr.wError;
}

#ifdef HPI_BUILD_INCLUDE_DEPRECATED
/** Find the DSP index of a particular object instance.

Only ever implemented for ISTREAM

\deprecated DSP index is not visible above HPI backend level now.
*/
hpi_err_t HPI_AdapterFindObject(
	const hpi_hsubsys_t *phSubSys,
	uint16_t wAdapterIndex,
	uint16_t wObjectType,
	uint16_t wObjectIndex,
	uint16_t *pDspIndex
)
{
	return HPI_ERROR_UNIMPLEMENTED;
}
#endif

/** Sets the operating mode of an adapter. The adapter must be restarted for
  * the mode to take effect.
  * Under Windows this means that the computer must be rebooted.
  *
  * \return_hpierr
 *  \retval HPI_ERROR_BAD_ADAPTER_MODE if an unsupported mode is set
  */

hpi_err_t HPI_AdapterSetMode(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	uint16_t wAdapterIndex,	///< Index of adapter to set mode on.
	uint32_t dwAdapterMode	///< One of the #HPI_ADAPTER_MODES
)
{
	return HPI_AdapterSetModeEx(phSubSys, wAdapterIndex, dwAdapterMode,
		HPI_ADAPTER_MODE_SET);
}

/** Adapter set mode extended. This updated version of HPI_AdapterSetMode()
  * allows querying supported modes.<br>
  * When wQueryOrSet=HPI_ADAPTER_MODE_QUERY doesn't set the mode, but the return
  * value reflects whether the mode is valid for the adapter.<br>
  *  When wQueryOrSet=HPI_ADAPTER_MODE_SET, the mode of the adapter is
  * changed if valid.<br>
  * The adapter must be restarted for the mode to take affect.
  * Under Windows this means that the computer must be rebooted.
  * \return \return_hpierr
  * \retval 0 the adapter supports the given mode - no error
  * \retval HPI_ERROR_BAD_ADAPTER_MODE if an unsupported mode is set or queried
  * \retval HPI_ERROR_XXX code if an error occurs.
  */
hpi_err_t HPI_AdapterSetModeEx(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	uint16_t wAdapterIndex,	///< Index of adapter to set mode on.
	uint32_t dwAdapterMode,	///< One of the #HPI_ADAPTER_MODES
	uint16_t wQueryOrSet	///< set or query the mode
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ADAPTER, HPI_ADAPTER_SET_MODE);
	hm.wAdapterIndex = wAdapterIndex;
	hm.u.ax.mode.adapter_mode = dwAdapterMode;
	hm.u.ax.mode.query_or_set = wQueryOrSet;
	HPI_Message(&hm, &hr);
	return hr.wError;
}

/** Read the current adapter mode setting.
  * \return_hpierr
  */
hpi_err_t HPI_AdapterGetMode(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	uint16_t wAdapterIndex,	///< Index of adapter to get mode from.
	/// The returned adapter mode. Will be one of - #HPI_ADAPTER_MODES.
	uint32_t *pdwAdapterMode
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ADAPTER, HPI_ADAPTER_GET_MODE);
	hm.wAdapterIndex = wAdapterIndex;
	HPI_Message(&hm, &hr);
	if (pdwAdapterMode)
		*pdwAdapterMode = hr.u.ax.mode.adapter_mode;
	return hr.wError;
}
#endif

/** Obtains information about the specified adapter,
  * including the number of output streams
  * and number of input streams, version, serial number and it's type.
  * The adapter is assumed to have one mixer.
  * \param phSubSys Vestigial subsys handle (unused), may be set to NULL
  * \param wAdapterIndex Index of adapter to read adapter information from.
  * \param pwNumOutStreams Number of output streams (play) on the adapter.
  * \param pwNumInStreams Number of input streams (record) on the adapter.
  * \param pwVersion Adapter hardware and DSP software version information.
  * The 16bit word contains the following information:<br>
<table border=1 cellspacing=0 cellpadding=5>
<tr><td><b>Bits</b><td><b>Description</b><td><b>Range</b>
<tr><td>b15-13<td>DSP software major version<td>0..7
<tr><td>b12-b7<td>DSP software minor version<td>0..63
<tr><td>b6-b3<td>Adapter PCB revision<td>A..P represented as 0..15
<tr><td>b2-b0<td>Adapter assembly revision<td>0..7
</table>
  * \param pdwSerialNumber Adapter serial number.  Starts at 1 and goes up.
  * \param pwAdapterType Adapter ID code, defined in HPI.H.
  * Examples are HPI_ADAPTER_ASI6114 (0x6114).
  * \return_hpierr
  */
hpi_err_t HPI_AdapterGetInfo(
	const hpi_hsubsys_t *phSubSys,
	uint16_t wAdapterIndex,
	uint16_t *pwNumOutStreams,
	uint16_t *pwNumInStreams,
	uint16_t *pwVersion,
	uint32_t *pdwSerialNumber,
	uint16_t *pwAdapterType
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ADAPTER, HPI_ADAPTER_GET_INFO);
	hm.wAdapterIndex = wAdapterIndex;

	HPI_Message(&hm, &hr);

	*pwAdapterType = hr.u.ax.info.wAdapterType;
	*pwNumOutStreams = hr.u.ax.info.wNumOStreams;
	*pwNumInStreams = hr.u.ax.info.wNumIStreams;
	*pwVersion = hr.u.ax.info.wVersion;
	*pdwSerialNumber = hr.u.ax.info.dwSerialNumber;
	return hr.wError;
}

#ifndef HPI_OS_LINUX_KERNEL
/** Obtains information about the specified module on an adapter,
  * including the number of outputs and number of inputs,
  * version, serial number and type.
  * \param phSubSys Vestigial subsys handle (unused), may be set to NULL
  * \param wAdapterIndex Index of adapter to read from.
  * \param wModuleIndex Index of module to return information for.
  * Iterate from 0 until an error is returned to get info about all modules
  * \param pwNumOutputs Number of outputs on the module.
  * \param pwNumInputs Number of inputs on the module.
  * \param pwVersion Module hardware and DSP software version information.
  * See HPI_AdapterGetInfo()
  * \param pdwSerialNumber Adapter serial number.  Starts at 1 and goes up.
  * \param pwModuleType Module ID code, defined in HPI.H
  * If a module position is empty, this parameter==0.
  * If a module position contains an unidentified module this parameter==0xFFFF.
  * \param phModule A handle to the module.
  * This parameter is reserved for future use.
  * \return_hpierr
  */

hpi_err_t HPI_AdapterGetModuleByIndex(
	const hpi_hsubsys_t *phSubSys,
	uint16_t wAdapterIndex,
	uint16_t wModuleIndex,
	uint16_t *pwNumOutputs,
	uint16_t *pwNumInputs,
	uint16_t *pwVersion,
	uint32_t *pdwSerialNumber,
	uint16_t *pwModuleType,
	hpi_handle_t * phModule
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ADAPTER, HPI_ADAPTER_MODULE_INFO);
	hm.wAdapterIndex = wAdapterIndex;
	hm.u.ax.module_info.index = wModuleIndex;

	HPI_Message(&hm, &hr);

	*pwModuleType = hr.u.ax.info.wAdapterType;
	*pwNumOutputs = hr.u.ax.info.wNumOStreams;
	*pwNumInputs = hr.u.ax.info.wNumIStreams;
	*pwVersion = hr.u.ax.info.wVersion;
	*pdwSerialNumber = hr.u.ax.info.dwSerialNumber;
	*phModule = 0;

	return hr.wError;
}

/** \internal Get Assert with extra info.

  Retrieve DSP generated assert messages.
  Most ASI adapters have a small buffer that can collect asserts
  that are conditionally generated as the DSP code is running.

  If *pAssertCount is zero, or an HPI error is returned, the remaining values are undefined.
   \return_hpierr

  */
hpi_err_t HPI_AdapterGetAssert2(
	const hpi_hsubsys_t *phSubSys, ///< Vestigial subsys handle (unused), may be set to NULL
	uint16_t wAdapterIndex, ///< Adapter index
	uint16_t *pAssertCount, ///< Number of queued asserts including current one
	char *pszAssert, ///< Pointer to 16 byte message buffer
	uint32_t *pParam1, ///< First assert parameter value
	uint32_t *pParam2, ///< Second assert parameter value
	uint32_t *pDspStringAddr, ///< DSP address of full assert string
	uint16_t *pProcessorId ///< Id of processor on which assert happened
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ADAPTER, HPI_ADAPTER_GET_ASSERT);
	hm.wAdapterIndex = wAdapterIndex;

	HPI_Message(&hm, &hr);

	*pAssertCount = 0;

	if (!hr.wError) {
		*pAssertCount = hr.u.ax.assert.count;

		if (*pAssertCount) {
			*pParam1 = hr.u.ax.assert.p1;
			*pParam2 = hr.u.ax.assert.p2;
			*pProcessorId = hr.u.ax.assert.dsp_index;
			*pDspStringAddr = hr.u.ax.assert.dsp_msg_addr;
			memcpy(pszAssert, hr.u.ax.assert.szMessage, HPI_STRING_LEN);
		} else {
			*pszAssert = 0;
		}
	}
	return hr.wError;
}

#ifdef HPI_BUILD_INCLUDE_DEPRECATED
/** \internal Extended Get Assert
  * The extened assert interface adds 32 bit 'line number' and dsp
  * index to standard assert API.
  * \todo Review whether this is actually implemented anywhere ?
  * \param phSubSys Vestigial subsys handle (unused), may be set to NULL
  * \param wAdapterIndex    Adapter to query.
  * \param wAssertPresent The number of asserts waiting including this one.
  * \param pszAssert      Assert message, traditionally file name.
  * \param pdwLineNumber  Assert number, traditionally line number in file.
  * \param pwAssertOnDsp   The index of the DSP that generated the assert.
  * \return_hpierr
  */
hpi_err_t HPI_AdapterGetAssertEx(
	const hpi_hsubsys_t *phSubSys,
	uint16_t wAdapterIndex,
	uint16_t *wAssertPresent,
	char *pszAssert,
	uint32_t *pdwLineNumber,
	uint16_t *pwAssertOnDsp
)
{
	uint32_t dummy32;

	return HPI_AdapterGetAssert2(phSubSys,
		wAdapterIndex, wAssertPresent, pszAssert,
		pdwLineNumber, &dummy32, &dummy32, pwAssertOnDsp);
}
/** Returns DSP generated assert messages.
  * Most ASI adapters have a small buffer that can collect up to 16 asserts
  * that are conditionally generated as the DSP code is running. This API
  * provides a mechanism for the host to read any asserts pending in the
  * queue.
  * \return_hpierr
  */
hpi_err_t HPI_AdapterGetAssert(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	uint16_t wAdapterIndex,	///< Adpater index to read assert from.
	/// Set to 1 if an assert was returned, otherwise it returns 0.
	uint16_t *pwAssertPresent,
	/** char buffer to contain the returned assert string.
	String should be declared as<br>
	char szAssert[HPI_STRING_LEN].
	*/
	char *pszAssert,
	uint16_t *pwLineNumber	///< The line number that caused the assert.
)
{
	hpi_err_t e;
	uint32_t dummy32;
	uint32_t p1;
	uint16_t dummy16;

	e = HPI_AdapterGetAssert2(phSubSys,
		wAdapterIndex, pwAssertPresent, pszAssert,
		&p1, &dummy32, &dummy32, &dummy16);

	*pwLineNumber = (uint16_t)p1;
	return e;
}
#endif

/** This function tests that asserts are working correctly on the selected
  * adapter.  The message processing code on the target adapter generates
  * an assert when this function is called and that assert can then be read
  * back using the HPI_AdapterGetAssert() function.
  * \return_hpierr
  */
hpi_err_t HPI_AdapterTestAssert(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	uint16_t wAdapterIndex,	///< Index of adapter to generate the assert.
	/** An assert id number that is returned as
	the line number in HPI_AdapterGetAssert().	 */
	uint16_t wAssertId
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ADAPTER, HPI_ADAPTER_TEST_ASSERT);
	hm.wAdapterIndex = wAdapterIndex;
	hm.u.ax.test_assert.value = wAssertId;

	HPI_Message(&hm, &hr);

	return hr.wError;
}

/** \internal Turn on a particular adapter capability.
  *
  * \return_hpierr
  */
hpi_err_t HPI_AdapterEnableCapability(
	const hpi_hsubsys_t *phSubSys,
	uint16_t wAdapterIndex,
	uint16_t wCapability,
	uint32_t dwKey
)
{
#if 1
	return HPI_ERROR_UNIMPLEMENTED;
#else
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ADAPTER, HPI_ADAPTER_ENABLE_CAPABILITY);
	hm.wAdapterIndex = wAdapterIndex;
	hm.u.ax.enable_cap.cap = wCapability;
	hm.u.ax.enable_cap.key = dwKey;

	HPI_Message(&hm, &hr);

	return hr.wError;
#endif
}

/** \internal Unimplemented */
hpi_err_t HPI_AdapterSelfTest(
	const hpi_hsubsys_t *phSubSys,
	uint16_t wAdapterIndex
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ADAPTER, HPI_ADAPTER_SELFTEST);
	hm.wAdapterIndex = wAdapterIndex;
	HPI_Message(&hm, &hr);
	return hr.wError;
}

/* \internal Direct read of adapter memory */
hpi_err_t HPI_AdapterDebugRead2(
	const hpi_hsubsys_t *phSubSys,
	uint16_t adapter_index,
	uint16_t processor_index,
 	uint32_t address,
 	char *buffer,
 	int *byte_count
)
{
	struct hpi_msg_adapter_debug_read hm;
	struct hpi_res_adapter_debug_read hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponseV1(
			&hm.h, sizeof(hm),
			&hr.h, sizeof(hr),
			HPI_OBJ_ADAPTER, HPI_ADAPTER_DEBUG_READ);

	hm.h.wAdapterIndex = adapter_index;
	hm.h.wObjIndex = processor_index;
	hm.dwDspAddress = address;

	if (*byte_count > (int)sizeof(hr.bytes))
		*byte_count = (int)sizeof(hr.bytes);

	hm.dwCountBytes = *byte_count;

	HPI_MessageV1(&hm.h, &hr.h);

	if  (!hr.h.wError) {
		int res_bytes = hr.h.wSize - sizeof(hr.h);
		if (res_bytes > *byte_count)
			res_bytes = *byte_count;
		*byte_count = res_bytes;
		memcpy(buffer, &hr.bytes, res_bytes);
	} else
		*byte_count = 0;

	return hr.h.wError;
}

/** \internal Direct read of adapter memory */
hpi_err_t HPI_AdapterDebugRead(
	const hpi_hsubsys_t *phSubSys,
	uint16_t wAdapterIndex,
 	uint32_t dwDspAddress,
 	char *pBuffer,
 	int *dwCountBytes
)
{
	return HPI_AdapterDebugRead2(phSubSys,
				wAdapterIndex, 0, dwDspAddress,
				pBuffer, dwCountBytes);
}
#endif

/** Set an adapter property to a value.
  * \return_hpierr
  */
hpi_err_t HPI_AdapterSetProperty(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	uint16_t wAdapterIndex,	///< Adapter index.
	uint16_t wProperty,		///< Which of the #HPI_ADAPTER_PROPERTIES to set
	uint16_t wParameter1,	///< Adapter property parameter 1.
	uint16_t wParameter2	///< Adapter property parameter 2.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ADAPTER, HPI_ADAPTER_SET_PROPERTY);
	hm.wAdapterIndex = wAdapterIndex;
	hm.wObjIndex = wProperty; /* helps debug */
	hm.u.ax.property_set.wProperty = wProperty;
	hm.u.ax.property_set.wParameter1 = wParameter1;
	hm.u.ax.property_set.wParameter2 = wParameter2;

	HPI_Message(&hm, &hr);

	return hr.wError;
}

/** Gets the value of an adapter property.
  * \return_hpierr
  */
hpi_err_t HPI_AdapterGetProperty(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	uint16_t wAdapterIndex,	///< Adapter index.
	uint16_t wProperty,		///< One of #HPI_ADAPTER_PROPERTIES to get.
	uint16_t *pwParameter1,	///< Returned adapter property parameter 1.
	uint16_t *pwParameter2	///< Returned adapter property parameter 2.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ADAPTER, HPI_ADAPTER_GET_PROPERTY);
	hm.wAdapterIndex = wAdapterIndex;
	hm.wObjIndex = wProperty; /* helps debug */
	hm.u.ax.property_set.wProperty = wProperty;

	HPI_Message(&hm, &hr);
	if (!hr.wError) {
		if (pwParameter1)
			*pwParameter1 = hr.u.ax.property_get.wParameter1;
		if (pwParameter2)
			*pwParameter2 = hr.u.ax.property_get.wParameter2;
	}

	return hr.wError;
}

#ifndef HPI_OS_LINUX_KERNEL
/** \internal Enumerates adapter properties.
  * To be implemented sometime in the future.
  * This function allows an application to determine what property a particular
  * adapter supports. Furthermore
  * the settings for a particular propery can also be established.
  * \param 	*phSubSys Vestigial subsys handle (unused), may be set to NULL
  * \param 	wAdapterIndex Adapter index.
  * \param 	wIndex Adapter property # to return.
  * \param 	wWhatToEnumerate Either HPI_ADAPTER_PROPERTY_ENUMERATE_PROPERTIES
  *         or HPI_ADAPTER_PROPERTY_ENUMERATE_SETTINGS
  * \param 	wPropertyIndex Property index.
  * \param 	*pdwSetting  Returned adapter property, or property setting,
  *         depending on wWhatToEnumerate.
  * \return_hpierr
  */
hpi_err_t HPI_AdapterEnumerateProperty(
	const hpi_hsubsys_t *phSubSys,
	uint16_t wAdapterIndex,
	uint16_t wIndex,
	uint16_t wWhatToEnumerate,
	uint16_t wPropertyIndex,
	uint32_t *pdwSetting
)
{
	HPI_UNUSED(phSubSys);
	HPI_UNUSED(wAdapterIndex);
	HPI_UNUSED(wIndex);
	HPI_UNUSED(wWhatToEnumerate);
	HPI_UNUSED(wPropertyIndex);
	HPI_UNUSED(pdwSetting);
	return 0;
}
#endif

#ifndef HPI_BUILD_KERNEL_MODE
/** \internal Turn on a particular adapter capability.
  *
  * \return_hpierr
  */
hpi_err_t HPI_AdapterRestart(
	uint16_t adapterIndex
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	// Send a special close message to the adapter to reset it
	HPI_InitMessageResponse(&hm, &hr,
			HPI_OBJ_ADAPTER, HPI_ADAPTER_CLOSE);
	hm.wAdapterIndex = adapterIndex;
	hm.u.ax.restart.key1 = 0x0FF;
	hm.u.ax.restart.key2 = 0xdead;

	HPI_Message(&hm, (HPI_RESPONSE *)&hr);
	return hr.wError;
}
#endif


/** @} */ /* defgroup adapter */

/** \defgroup stream Streams
Perform audio I/O and format conversion
@{
*/
#ifndef HPI_OS_LINUX_KERNEL
static unsigned int roundup_pow_of_two(
	unsigned int size
)
{
	unsigned int n = 0;
	if (size)
		size = size - 1;

	while (size) {
		size >>= 1;
		n++;
	}
	return 1 << n;

}
#endif

/** Initialize an audio format structure, given various defining parameters.

Used in HPI_InStreamSetFormat() and HPI_OutStreamSetFormat()
  * \return_hpierr
  */
hpi_err_t HPI_FormatCreate(
	struct hpi_format *pFormat,	///< Pointer to the format structure to be initialized.
	uint16_t wChannels,		///< From 1 to 8.
	uint16_t wFormat,		///< One of the #HPI_FORMATS.
	uint32_t dwSampleRate,	///< Sample rate in Hz. Samplerate must be between 8000 and 200000.
	uint32_t dwBitRate,		///< Bits per second, must be supplied for MPEG formats, it is calculated for PCM formats.
	uint32_t dwAttributes	///< Format dependent attributes.  E.g. #HPI_MPEG_MODES
)
{
	hpi_err_t err = 0;
	struct hpi_msg_format fmt;

	// check channel count
	switch (wChannels) {
		case 1:
		case 2:
		case 4:
		case 6:
		case 8:
		case 16:
		case 32:
		case 64:
			break;
		default:
			err = HPI_ERROR_INVALID_CHANNELS;
			return err;
	}
	fmt.wChannels = wChannels;

	// make sure we have a valid audio format
	switch (wFormat) {
	case HPI_FORMAT_PCM16_SIGNED:
	case HPI_FORMAT_PCM24_SIGNED:
	case HPI_FORMAT_PCM32_SIGNED:
	case HPI_FORMAT_PCM32_FLOAT:
	case HPI_FORMAT_PCM16_BIGENDIAN:
	case HPI_FORMAT_PCM8_UNSIGNED:
	case HPI_FORMAT_MPEG_L1:
	case HPI_FORMAT_MPEG_L2:
	case HPI_FORMAT_MPEG_L3:
	case HPI_FORMAT_DOLBY_AC2:
	case HPI_FORMAT_AA_TAGIT1_HITS:
	case HPI_FORMAT_AA_TAGIT1_INSERTS:
	case HPI_FORMAT_RAW_BITSTREAM:
	case HPI_FORMAT_AA_TAGIT1_HITS_EX1:
	case HPI_FORMAT_OEM1:
	case HPI_FORMAT_OEM2:
		break;
	default:
		err = HPI_ERROR_INVALID_FORMAT;
		return err;
	}
	fmt.wFormat = wFormat;

	//sample rate can be between 8kHz and 200kHz
	if (dwSampleRate < 8000L) {
		err = HPI_ERROR_INCOMPATIBLE_SAMPLERATE;
		dwSampleRate = 8000L;
	}
	if (dwSampleRate > 200000L) {
		err = HPI_ERROR_INCOMPATIBLE_SAMPLERATE;
		dwSampleRate = 200000L;
	}
	fmt.dwSampleRate = dwSampleRate;

	// for some formats (MPEG) we accept a bitrate
	// for some (PCM) we calculate the bit rate
	switch (wFormat) {
	case HPI_FORMAT_MPEG_L1:
	case HPI_FORMAT_MPEG_L2:
	case HPI_FORMAT_MPEG_L3:
		fmt.dwBitRate = dwBitRate;	// should validate!!!!!!!
		break;
	case HPI_FORMAT_PCM16_SIGNED:
	case HPI_FORMAT_PCM16_BIGENDIAN:
		fmt.dwBitRate = wChannels * dwSampleRate * 2;
		break;
	case HPI_FORMAT_PCM32_SIGNED:
	case HPI_FORMAT_PCM32_FLOAT:
		fmt.dwBitRate = wChannels * dwSampleRate * 4;
		break;
	case HPI_FORMAT_PCM8_UNSIGNED:
		fmt.dwBitRate = wChannels * dwSampleRate;
		break;
	default:
		fmt.dwBitRate = 0;
	}

	// Set the dwAttributes field.
	// The attributes are format dependent.
	switch (wFormat) {
	case HPI_FORMAT_MPEG_L2:
		if ((wChannels == 1)
			&& (dwAttributes != HPI_MPEG_MODE_DEFAULT)) {
			dwAttributes = HPI_MPEG_MODE_DEFAULT;	// correct the error anyway !
			err = HPI_ERROR_INVALID_FORMAT;
		} else if (dwAttributes > HPI_MPEG_MODE_DUALCHANNEL) {
			dwAttributes = HPI_MPEG_MODE_DEFAULT;	// correct the error anyway !
			err = HPI_ERROR_INVALID_FORMAT;
		}
		fmt.dwAttributes = dwAttributes;
		break;
	default:
		fmt.dwAttributes = dwAttributes;
	}

	// Convention is that internal to HPI we manipulate struct hpi_msg_format, but HPI
	// applications use struct hpi_format. Therefore convert "fmt"(Msg) to "pFormat".
	HPI_MsgToFormat(pFormat, &fmt);
	return err;
}

#ifndef HPI_OS_LINUX_KERNEL
/** Given a format and rate that the buffer is processed, return the correct
  * buffer size to support ping-pong buffering of audio.
  *
  * dwRecommendedBufferSize = RoundUpToPowerOf2((bytes per polling period) * 2)
  *
  * Calculate the minimum buffer size for a stream given the audio format that
  * the stream will be set to use and the rate at which the host polls the
  * stream state and reads or writes data. The buffer size returned by this
  * function should be used as the minimum value passed to
  * HPI_OutStreamHostBufferAllocate() or HPI_InStreamHostBufferAllocate().
  * If different formats and samplerates will be used on the stream, buffer size
  * should be calculated for the highest datarate format, or the buffer should
  * be freed and allocated again for each format change.  The size calculated is
  * rounded up to the nearest power of two which is a requirement of background
  * bus mastering (BBM) buffers.
  *
  * For some adapters the physical buffers are preallocated during system
  * initialization.  The preallocation is based on the maximum format bandwidth
  * supported and assumes a 10 millisecond polling rate.  If a slower polling rate 
  * is used this calculation may return a value that exceeds the available
  * preallocated physical buffers.  In which case it is recommended to poll more
  * frequently.
  *
  * \return_hpierr
  */
hpi_err_t HPI_StreamEstimateBufferSize(
	struct hpi_format *pFormat,	///< The format of the stream.
	/** The polling rate of the host thread that
	fills or empties the buffer.*/
	uint32_t dwHostPollingRateInMilliSeconds,
	/** The recommended buffer size in milliseconds.*/
	uint32_t *dwRecommendedBufferSize
)
{
	// compute bytes per second
	uint32_t dwBytesPerSecond;
	uint32_t dwSize;
	uint16_t wChannels;
	struct hpi_format *pF = pFormat;

	wChannels = pF->wChannels;

	switch (pF->wFormat) {
	case HPI_FORMAT_PCM16_BIGENDIAN:
	case HPI_FORMAT_PCM16_SIGNED:
		dwBytesPerSecond = pF->dwSampleRate * 2L * wChannels;
		break;
	case HPI_FORMAT_PCM24_SIGNED:
		dwBytesPerSecond = pF->dwSampleRate * 3L * wChannels;
		break;
	case HPI_FORMAT_PCM32_SIGNED:
	case HPI_FORMAT_PCM32_FLOAT:
		dwBytesPerSecond = pF->dwSampleRate * 4L * wChannels;
		break;
	case HPI_FORMAT_PCM8_UNSIGNED:
		dwBytesPerSecond = pF->dwSampleRate * 1L * wChannels;
		break;
	case HPI_FORMAT_MPEG_L1:
	case HPI_FORMAT_MPEG_L2:
	case HPI_FORMAT_MPEG_L3:
		dwBytesPerSecond = pF->dwBitRate / 8L;
		break;
	case HPI_FORMAT_DOLBY_AC2:
		// Dolby AC-2 is around 256 kBytes/second
		dwBytesPerSecond = 256000L / 8L;
		break;
	default:
		return HPI_ERROR_INVALID_FORMAT;
	}
	dwSize = (dwBytesPerSecond * dwHostPollingRateInMilliSeconds * 2) /
		1000L;
	// round up to nearest 4 K bounday, then round up to power of 2
	*dwRecommendedBufferSize =
		roundup_pow_of_two(((dwSize + 4095L) & ~4095L));
	return 0;
}
#endif

/*=========================================================================*/
/** \defgroup outstream Output Stream

The following section describes the states a stream uses.

\image html outstream_states.png

The state HPI_STATE_DRAINED indicates that the stream has run out of decoded data.

This can happen for two reasons:<br>
Intentionally, when the end of the currently playing audio is reached.<br>
A transient error condition when the adapter DSP was unable to decode audio fast
enough.

The dwDataToPlay count returned from HPI_OutStreamGetInfoEx() measures the
amount of encoded data (MPEG, PCM, etc.) in the stream's buffer.
When there is less than a whole frame of encoded data left,
it cannot be decoded for mixing and output. It is possible that
HPI_OutStreamGetInfoEx() indicates that there is still a small amount
of data to play, but the stream enters the DRAINED state,
and the amount of data to play never gets to zero.

The size of a frame varies depending on the audio format.
Compressed formats such as MPEG require whole frames of data in order to
decode audio.  The size of the input frame depends on the samplerate and bitrate
(e.g. MPEG frame_bytes = bitrate/8 * 1152/samplerate).

AudioScience's implementation of PCM decoding also requires a minimum amount
of input data. ASI4xxx adapters require 4608 bytes, whereas ASI6xxx adapters
require 1536 bytes for stereo, half this for mono.

Conservative conditions to detect end of play:<br>
- Have done the final OutStreamWrite<br>
- Stream state is HPI_STATE_DRAINED<br>
- dwDataToPlay < 4608

Input data requirements for different algorithms.

<table border=1 cellspacing=0 cellpadding=5>
<tr><td><b>Bytes</b><td><b>PCM-16</b><td><b>MP1</b><td><b>MP2</b>
<td><b>MP3</b><td><b>AC-2</b>

<tr><td>AX<td>4608<td><3456<td><3456<td>-<td>380
<tr><td>AX4<td>4608<td><3456<td><3456<td>3456<td>-
<tr><td>AX6<td>1536<td><=700<td><=700<td>1100<td>380
</table>

@{
*/

/** Open and initializes an output stream.  The handle is used for all
  * future calls to that OutStream. An output stream may only be open by only
  * one application at a time.
  * \return_hpierr
  */
hpi_err_t HPI_OutStreamOpen(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	/** Index of adapter to be opened. Ranges from 0 to 15 and
	corresponds to the Adapter Index set on the adapter hardware. */
	uint16_t wAdapterIndex,
	/** Index of the OutStream to be opened. Ranges from 0 to
	wNumOutStreams-1 (returned by HPI_AdapterGetInfo()). */
	uint16_t wOutStreamIndex,
	hpi_handle_t *phOutStream	///< Returned Handle to the OutStream.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_OPEN);
	hm.wAdapterIndex = wAdapterIndex;
	hm.wObjIndex = wOutStreamIndex;

	HPI_Message(&hm, &hr);

	if (hr.wError == 0)
		*phOutStream =
			HPI_IndexesToHandle(HPI_OBJ_OSTREAM, wAdapterIndex,
			wOutStreamIndex);
	else
		*phOutStream = 0;
	return hr.wError;
}

/** Closes an output stream and deallocates host buffers if they are being used.
  * \return_hpierr
  */
hpi_err_t HPI_OutStreamClose(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hOutStream	///< Handle of the OutStream to close.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_HOSTBUFFER_FREE);
	if (hpi_handle_indexes(hOutStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	HPI_Message(&hm, &hr);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_GROUP_RESET);
	hpi_handle_indexes(hOutStream, &hm.wAdapterIndex, &hm.wObjIndex);
	HPI_Message(&hm, &hr);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_CLOSE);
	hpi_handle_indexes(hOutStream, &hm.wAdapterIndex, &hm.wObjIndex);
	HPI_Message(&hm, &hr);

	return hr.wError;
}

#ifdef HPI_BUILD_INCLUDE_DEPRECATED
/**
 \deprecated This function has been superseded by HPI_OutStreamGetInfoEx()
 */
hpi_err_t HPI_OutStreamGetInfo(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hOutStream,
	uint16_t *pwState,
	uint32_t *pdwBufferSize,
	uint32_t *pdwDataToPlay
)
{
	return HPI_OutStreamGetInfoEx(phSubSys,
			hOutStream,
			pwState, pdwBufferSize, pdwDataToPlay, NULL, NULL);
}
#endif

/** Get information about attributes and state of output stream.
  * This is similar to HPI_OutStreamGetInfo() but returns extended information
  * including the size of the streams buffer in pdwBufferSize.
  * It also returns whether the stream is currently playing (the state) and the amount
  * of audio data left to play.
  *
  * \return_hpierr
  */
hpi_err_t HPI_OutStreamGetInfoEx(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hOutStream,	///< Handle to opened OutStream.
	/** State of stream, one of the #HPI_STREAM_STATES */
	uint16_t *pwState,
	uint32_t *pdwBufferSize,	///< Size (in bytes) of stream data buffer.
	uint32_t *pdwDataToPlay,	///< Bytes left in the buffer to play.
	uint32_t *pdwSamplesPlayed,
	/**< The SamplesPlayed parameter returns the number of samples
	played since the last HPI_OutStreamReset command was issued.  It
	reflects the number of stereo samples for a stereo stream and the
	number of mono samples for a mono stream. This means that if a 44.1kHz
	stereo and mono stream were both playing they would both return
	SamplesPlayed=44100 after 1 second.
	*/
	uint32_t *pdwAuxiliaryDataToPlay
	 /**< AuxiliaryDataToPlay is only relevant when BBM is active
	(see HPI_OutStreamHostBufferAllocate). In this case DataToPlay refers to
	the host side buffer state while AuxiliaryDataToPlay refers to the data
	remaining in the card's own buffers.
	*/
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_GET_INFO);
	if (hpi_handle_indexes(hOutStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	HPI_Message(&hm, &hr);

	// only send back data if valid pointers supplied!!
	if (pwState)
		*pwState = hr.u.d.u.stream_info.wState;
	if (pdwBufferSize)
		*pdwBufferSize = hr.u.d.u.stream_info.dwBufferSize;
	if (pdwDataToPlay)
		*pdwDataToPlay = hr.u.d.u.stream_info.dwDataAvailable;
	if (pdwSamplesPlayed)
		*pdwSamplesPlayed = hr.u.d.u.stream_info.dwSamplesTransferred;
	if (pdwAuxiliaryDataToPlay)
		*pdwAuxiliaryDataToPlay =
			hr.u.d.u.stream_info.dwAuxiliaryDataAvailable;
	return hr.wError;
}

/** Writes a block of audio data to the specified output stream.
  * dwBytesToWrite bytes are copied from  *pbData array to the output stream
  * hardware.  On return the memory used to hold that data may be reused.
  *
  * A different format will only be accepted in the first write
  * after the stream is opened or reset.
  *
  * The size of the data block that may be written is limited to half the size
  * of the streams internal data buffer (specified by dwBufferSize returned by
  * HPI_OutStreamGetInfo()).
  *
  *
  * \image html outstream_buf.png
\code
err = HPI_FormatCreate(
			&hpiFormat,
			2,                 // stereo channel
			HPI_FORMAT_MPEG_L2,// MPEG Layer II
			44100L,            //sample rate
			128000L,           //128k bits/sec
			0                  // no attributes
			);

err = HPI_OutStreamWriteBuf(phSubSys, hOutStream, &aData, dwBytes, &hpiFormat);
\endcode
  * \return_hpierr
  * \retval 0 The data was written to the stream
  * \retval HPI_INVALID_DATASIZE tried to write more data than buffer
  * space available - no data was written
  */
hpi_err_t HPI_OutStreamWriteBuf(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hOutStream,	///< Handle to opened OutStream.
	const uint8_t *pbData,		///< Pointer to buffer containing data to be written to the playback buffer.
	uint32_t dwBytesToWrite,	///< Number of bytes to write, must be <= space available.
	const struct hpi_format *pFormat	///< Format of the data (compression,channels,samplerate)
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_WRITE);
	if (hpi_handle_indexes(hOutStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.d.u.data.pbData = (uint8_t *)pbData; /* cast away const, pbData also used for read */
	hm.u.d.u.data.dwDataSize = dwBytesToWrite;

	HPI_FormatToMsg(&hm.u.d.u.data.format, pFormat);

	HPI_Message(&hm, &hr);

	return hr.wError;
}

#ifdef HPI_BUILD_INCLUDE_DEPRECATED
/** Writes a block of audio data to the specified output stream.
  * The data to write is specified by pData. pData also contains the format of
  * the data (channels, compression, sampleRate).
  * The HPI_OutStreamWrite() call copies the data in pData to the
  * output stream hardware.
  * Once the data has been written to the stream, the memory used to
  * hold that data may be reused.
  *
  * \deprecated Use HPI_OutStreamWriteBuf() instead.
  * This function may disappear from a future version.
  *
  * A different format will only be accepted in the first write after the
  * stream is opened or reset.
  *
  * The size of the data block that may be written is limited to half
  * the size of the streams internal data buffer
  * (specified by dwBufferSize returned by HPI_OutStreamGetInfo()).
  *
  * \image html outstream_buf.png
  * \sample
err = HPI_FormatCreate(
			&hpiFormat,
			2,                 // stereo channel
			HPI_FORMAT_MPEG_L2,// MPEG Layer II
			44100L,            //sample rate
			128000L,           //128k bits/sec
			0                  // no attributes
			);

err = HPI_DataCreate(&Data, &hpiFormat, gabBuffer, BLOCK_SIZE);
err = HPI_OutStreamWrite(phSubSys, hOutStream, &Data);
  * \endsample
  * \return_hpierr
  */
hpi_err_t HPI_OutStreamWrite(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hOutStream,	///< Handle to opened OutStream.
	/** Pointer to an struct hpi_data structure containing a description of the
	audio data to be written to the OutStream and played. */
	const struct hpi_data * pData
)
{
	// use one path for WriteBuf.
	struct hpi_format f;
	HPI_MsgToFormat(&f, &((struct hpi_msg_data *)pData)->format);

	return HPI_OutStreamWriteBuf(phSubSys,
		hOutStream,
		((struct hpi_msg_data *)pData)->pbData,
		((struct hpi_msg_data *)pData)->dwDataSize, &f);
}
#endif

/** Starts an output stream playing audio data.
  * Data is taken from the circular buffer on the adapter hardware that has
  * already been partially filled by HPI_OutStreamWrite commands.
  * Audio is played from the current
  * position in the buffer (which may be reset using HPI_OutStreamReset()).
  * \return_hpierr
  */
hpi_err_t HPI_OutStreamStart(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hOutStream	///< Handle to OutStream.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_START);
	if (hpi_handle_indexes(hOutStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	HPI_Message(&hm, &hr);

	return hr.wError;
}

#ifndef HPI_OS_LINUX_KERNEL
/** Sets an output stream to WAIT state, ready for inter-card syncing.
  * Only adapters with inter-card sync headers support this function. This
  * function should be called on each adapter before issueing an
  * HPI_OutStreamStart() call. This function supports
  * grouped streams.
  *
  * \return_hpierr
  */
hpi_err_t HPI_OutStreamWaitStart(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hOutStream	///< Handle to OutStream.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_WAIT_START);
	if (hpi_handle_indexes(hOutStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	HPI_Message(&hm, &hr);

	return hr.wError;
}
#endif

/** Stops an output stream playing audio data.
  * The audio data buffer is not cleared, so a subsequent OutStreamStart will resume playing
  * at the position in the buffer where the playback had been stopped.
  * \return_hpierr
  */
hpi_err_t HPI_OutStreamStop(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hOutStream	///< Handle to OutStream.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_STOP);
	if (hpi_handle_indexes(hOutStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	HPI_Message(&hm, &hr);

	return hr.wError;
}

#ifndef HPI_OS_LINUX_KERNEL
/** \internal
  * Generate a sinewave output on the specified stream.
  * \note This function is unimplemented.
  */
hpi_err_t HPI_OutStreamSinegen(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hOutStream
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_SINEGEN);
	if (hpi_handle_indexes(hOutStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	HPI_Message(&hm, &hr);

	return hr.wError;
}
#endif

/** Clears the audio data buffer of an output stream.
  * If the stream was playing at the time, it will be stopped.
  * \return_hpierr
  */
hpi_err_t HPI_OutStreamReset(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hOutStream	///< Handle to OutStream.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_RESET);
	if (hpi_handle_indexes(hOutStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	HPI_Message(&hm, &hr);

	return hr.wError;
}

/** Queries an OutStream to see whether it supports a certain audio format,
  * described in pFormat.  The result, returned in the error code, is 0 if
  * supported and HPI_ERROR_INVALID_FORMAT if not supported.
  * \return_hpierr
  * \retval HPI_ERROR_INVALID_FORMAT if the format is not supported.
  */
hpi_err_t HPI_OutStreamQueryFormat(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hOutStream,	///< Handle to OutStream.
	struct hpi_format *pFormat	///< Format to query.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_QUERY_FORMAT);
	if (hpi_handle_indexes(hOutStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	HPI_FormatToMsg(&hm.u.d.u.data.format, pFormat);

	HPI_Message(&hm, &hr);

	return hr.wError;
}

/** Sets an OutStream to a certain audio format, described in pFormat.
  * The result, returned in the error code, is 0 if the call succeeded
  * and HPI_ERROR_INVALID_FORMAT if not supported.
  * \return_hpierr
  * \retval HPI_ERROR_INVALID_FORMAT if the format is not supported.
  */
hpi_err_t HPI_OutStreamSetFormat(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hOutStream,	///< Handle to OutStream.
	struct hpi_format *pFormat	///< Format to set, created using HPI_FormatCreate().
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_SET_FORMAT);
	if (hpi_handle_indexes(hOutStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	HPI_FormatToMsg(&hm.u.d.u.data.format, pFormat);

	HPI_Message(&hm, &hr);

	return hr.wError;
}

#ifndef HPI_OS_LINUX_KERNEL
/** Sets the playback velocity for scrubbing. Velocity range is +/- 4.0.
  * nVelocity is set by
  \code
nVelocity = (uint16_t)(fVelocity * HPI_VELOCITY_UNITS);
\endcode
  * where fVelocity is a floating point number in the range of -4.0 to +4.0.
  * This call puts the stream in "scrub" mode. The first call to HPI_OutStreamSetVelocity()
  * should be made while the stream is reset so that scrubbing can be performed after
  * starting playback.
  *
  *\note <b>This functionality is only available on the ASI4300 series adapters.</b>
  *
  * <b>Call sequence</b>
  *
  * A typical playback call sequence without scrubbing is:
\verbatim
Write
Write
Start
Write
.....

Stop
\endverbatim
A typical playback sequence with scrubbing is:
\verbatim
Write
Write
SetVelocity
Start
Write
SetVelocity
.....
\endverbatim
  *
  * Stop - automatically turns of velocity/scrub mode.
  *
  * <b>Data flow</b>
  *
  * The scrubbing approach taken here is to decode audio to a "scrub buffer" that contains
  * many seconds of PCM that can be traversed in at a variable rate.
  * \image html outstream_scrub.png
  * Forward scrubbing does not have any limitations whatsoever, apart from the maximum speed,
  * as specified by HPI_OutStreamSetVelocity().
  *
  * Reverse scrubbing operates under the following constraints:<br>
  * 1) The user may not scrub on audio prior to the HPI_OutStreamStart() data point.<br>
  * 2) The user may not reverse scrub further than -10 seconds from the forward most scrub position.<br>
  * If either of these constraints is broken, the stream state will return HPI_STATE_DRAINED
  * and audio playback will cease. The user can then forward scrub again after this error
  * condition.
  * \return_hpierr
  */
hpi_err_t HPI_OutStreamSetVelocity(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hOutStream,	///< Handle to OutStream.
	short nVelocity		///< The velocity in units HPI_VELOCITY_UNITS.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_SET_VELOCITY);
	if (hpi_handle_indexes(hOutStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.d.u.wVelocity = nVelocity;

	HPI_Message(&hm, &hr);

	return hr.wError;
}

/** \internal
  * Set punch in and out points in a buffer.
  * \note Currently unimplemented.
  */
hpi_err_t HPI_OutStreamSetPunchInOut(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hOutStream,
	uint32_t dwPunchInSample,
	uint32_t dwPunchOutSample
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_SET_PUNCHINOUT);
	if (hpi_handle_indexes(hOutStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	hm.u.d.u.pio.dwPunchInSample = dwPunchInSample;
	hm.u.d.u.pio.dwPunchOutSample = dwPunchOutSample;

	HPI_Message(&hm, &hr);

	return hr.wError;
}

/** Resets MPEG ancillary data extraction.
  * Initializes the MPEG Layer II / III Ancillary data channel to support the extraction
  * of wBytesPerFrame bytes from the MPEG bitstream.
  *
  * \note This call must be made after HPI_OutStreamOpen()
  * or HPI_OutStreamReset() and before the first HPI_OutStreamWrite() call.
  *
  * \param phSubSys Vestigial subsys handle (unused), may be set to NULL
  * \param  hOutStream OutStream handle
  * \param wMode One of the #HPI_MPEG_ANC_MODES
  * The mode for the ancillary data extraction to operate in. Valid settings
  * are HPI_MPEG_ANC_RAW and HPI_MPEG_ANC_HASENERGY. The "RAW" mode indicates that the
  * entire ancillary data field is taken up by data from the Anc data buffer. The "HASENERGY"
  * option tells the decoder that the MPEG frames have energy information stored in them
  * (5 bytes per stereo frame, 3 per mono).
  *
  * \return_hpierr
  */
hpi_err_t HPI_OutStreamAncillaryReset(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hOutStream,
	uint16_t wMode
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_ANC_RESET);
	if (hpi_handle_indexes(hOutStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.d.u.data.format.wChannels = wMode;
	HPI_Message(&hm, &hr);
	return hr.wError;
}

/** Returns information about the Ancillary stream.
  * \return_hpierr
  */
hpi_err_t HPI_OutStreamAncillaryGetInfo(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hOutStream,	///< Handle to OutStream.
	uint32_t *pdwFramesAvailable	///< Number of HPI_ANC_FRAMEs in the hardware buffer available for reading.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_ANC_GET_INFO);
	if (hpi_handle_indexes(hOutStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	HPI_Message(&hm, &hr);
	if (hr.wError == 0) {
		if (pdwFramesAvailable)
			*pdwFramesAvailable =
				hr.u.d.u.stream_info.dwDataAvailable /
				sizeof(struct hpi_anc_frame);
	}
	return hr.wError;
}

/** Reads frames of ancillary data from a stream's ancillary data buffer to
  * pdwBuffer. Note that in the situation where energy level information is
  * embedded in the ancillary data stream along with ancillary data,
  * only the ancillary data will be returned in the ancillary data buffer.
  *
  * Bytes are filled in the bData[] array of the struct hpi_anc_frame structures in
  * the following order:
  *
  * The first bit of ancillary information that follows the valid audio
  * data is placed in bit 7 of  bData[0]. The first 8 bits of ancillary
  * information following valid audio data are all placed in bData[0].
 * In the case where there are 6 bytes total of ancillary
  * information (48 bits) the last byte filled is bData[5].
  *
  * \note If OutStreamAncillaryReset() was called with mode=HPI_MPEG_ANC_RAW,
  * the ancillary data in its entirety is placed in the AncFrameBuffer,
  * so if the file was recorded with energy information in the ancillary
  * data field, the energy information will be included
  * in the extracted ancillary data.
  * \note If OutStreamAncillaryReset() was called with
  * mode=HPI_MPEG_ANC_HASENERGY, the ancillary data minus the energy
  * information is placed in the AncFrameBuffer.
  * \return_hpierr
  */
hpi_err_t HPI_OutStreamAncillaryRead(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hOutStream,	///< Handle to OutStream.
	/** A pointer to a buffer where the read Ancillary
	   data frames should be placed. */
	struct hpi_anc_frame * pAncFrameBuffer,
	/** The size of the Ancillary data buffer in bytes
	    (used for a sanity check). */
	uint32_t dwAncFrameBufferSizeInBytes,
	uint32_t dwNumberOfAncillaryFramesToRead	///< How many frames to read.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_ANC_READ);
	if (hpi_handle_indexes(hOutStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.d.u.data.pbData = (uint8_t *)pAncFrameBuffer;
	hm.u.d.u.data.dwDataSize =
		dwNumberOfAncillaryFramesToRead * sizeof(struct hpi_anc_frame);
	if (hm.u.d.u.data.dwDataSize <= dwAncFrameBufferSizeInBytes)
		HPI_Message(&hm, &hr);
	else
		hr.wError = HPI_ERROR_INVALID_DATASIZE;
	return hr.wError;
}

/** Sets the playback timescale with pitch and content preservation.
  * Range is 0.8-1.2 (
  to 120%) of original time.<br>
  * dwTimeScale in set by:<br>
\code
dwTimeScale = (uint32_t)(fTimeScale * HPI_OSTREAM_TIMESCALE_UNITS);
\endcode
  * where fTimeScale in a floating point number in the range of
  * 0.8 < fTimeScale  < 1.2.
  * The actual granularity of this setting is 1 / 2048  or approximately 0.05%
  * (approx 5 \ref HPI_OSTREAM_TIMESCALE_UNITS).
  *
  * The first call to HPI_OutStreamSetTimeScale should be made while the
  * stream is reset so that time scaling can be performed after starting
  * playback.  Subsequent calls to HPI_OutStreamSetTimeScale can be made
  * when the stream is playing to modify the timescale factor.
  *
  * If the desired timescale factor is not known at stream reset time, the
  * timescale should be set to \ref HPI_OSTREAM_TIMESCALE_PASSTHROUGH.
  * This turns on timescaling, but in passthrough mode. ie no scaling takes place.
  * After OStream buffer preload, the timescale value may be updated with the
  * desired scale facter.
  *
  * \note This functionality is only available on ASI6000 series adapters.
  * \return_hpierr
  */
hpi_err_t HPI_OutStreamSetTimeScale(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hOutStream,	///< Handle to OutStream.
	uint32_t dwTimeScale	///< The time scale in units of \ref HPI_OSTREAM_TIMESCALE_UNITS.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_SET_TIMESCALE);
	if (hpi_handle_indexes(hOutStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	hm.u.d.u.dwTimeScale = dwTimeScale;

	HPI_Message(&hm, &hr);

	return hr.wError;
}

/** Allocates a buffer inside the driver for bus mastering transfers.
 * Once the buffer is allocated, OutStream data will be transferred from it in
 * the background (rather than the application having to wait for the transfer).
 *
 * This function is provided so that the application can match the size of its
 * transfers to the size of the buffer.
 *
 * After a call to HPI_OutStreamHostBufferAllocate(), HPI_OutStreamGetInfoEx()
 * returns the size and data available in host buffer rather than the buffers
 * on the adapter. However, while there is space in the adapter buffers, data
 * will be quickly transferred to the adapter, providing additional buffering
 * against delays in sending more audio data.
 *
 * \note There is a minimum buffer size that will work with a given audio
 * format and polling rate. An appropriate size for the buffer can be calculated
 * using HPI_StreamEstimateBufferSize().
 *
 * If an error occurs or the adapter doesn't support host buffering then no
 * buffer is allocated. Stream transfers still take place using foreground
 * transfers (all drivers pre 2004). Performance will be relatively worse.
 *
 * \return_hpierr
 * \retval HPI_ERROR_INVALID_DATASIZE memory can't be allocated
 *(retrying the call with a smaller size may succeed)
 * \retval HPI_ERROR_INVALID_FUNC the adapter doesn't support busmastering
 */
hpi_err_t HPI_OutStreamHostBufferAllocate(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hOutStream,	///< Handle to OutStream.
	uint32_t dwSizeInBytes	///< Buffer size in bytes to allocate.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_HOSTBUFFER_ALLOC);
	if (hpi_handle_indexes(hOutStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.d.u.buffer.dwBufferSize = dwSizeInBytes;
	HPI_Message(&hm, &hr);
	return hr.wError;
}

#ifdef HPI_BUILD_INCLUDE_INTERNAL
/** \internal
  * Used by hpifunc.c to implement direct buffer access API.
  * \param phSubSys Vestigial subsys handle (unused), may be set to NULL
  * \param hOutStream Pointer to the out stream handle.
  * \param ppBuffer Returned pointer to the BBM buffer.
  * \param ppStatus Returned pointer to the BBM buffer status structure.
  * \return_hpierr
  */
hpi_err_t HPI_OutStreamHostBufferGetInfo(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hOutStream,
	uint8_t ** ppBuffer,
	struct hpi_hostbuffer_status **ppStatus
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_HOSTBUFFER_GET_INFO);
	if (hpi_handle_indexes(hOutStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	HPI_Message(&hm, &hr);

	if (hr.wError == 0) {
		if (ppBuffer)
			*ppBuffer = hr.u.d.u.hostbuffer_info.pBuffer;
		if (ppStatus)
			*ppStatus = hr.u.d.u.hostbuffer_info.pStatus;
	}
	return hr.wError;
}
#endif

/** Free any buffers allocated by HPI_OutStreamHostBufferAllocate().
  * \return_hpierr
  */
hpi_err_t HPI_OutStreamHostBufferFree(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hOutStream	///< Handle to OutStream.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_HOSTBUFFER_FREE);
	if (hpi_handle_indexes(hOutStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	HPI_Message(&hm, &hr);
	return hr.wError;
}
#endif

/** This function adds a stream to a group of streams. Stream groups are
  * used to synchronise starting and stopping of multiple streams at once.
  * The application of this is to support playing (or recording) multiple
  * streams at once, enabling multi-channel recording and playback.
  *
  * When using the "Group" functions all streams that are to be grouped
  * together should be opened. One of the streams should be selected to
  * be the master stream and the other streams should be added to it's group.
  * Both in streams and out streams can be added to the same group.
  * Once a group has been formed, HPI_OutStreamStart() called on the master
  * will cause all streams to start at once.
  *
  * \note This function is only supported on on ASI6000 and ASI5000 adapters.
  * \return_hpierr
  */
hpi_err_t HPI_OutStreamGroupAdd(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hOutStream,	///< Handle to OutStream.
	hpi_handle_t hStream	///< Handle to an In or Out Stream
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	uint16_t wAdapter;
	char cObjType;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_GROUP_ADD);

	if (hpi_handle_indexes(hOutStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	if (hpi_handle_indexes(hStream, &wAdapter,
					&hm.u.d.u.stream.wStreamIndex))
		return HPI_ERROR_INVALID_HANDLE;

	cObjType = HPI_HandleObject(hStream);
	switch (cObjType) {
	case HPI_OBJ_OSTREAM:
	case HPI_OBJ_ISTREAM:
		hm.u.d.u.stream.wObjectType = cObjType;
		break;
	default:
		return HPI_ERROR_INVALID_OBJ;
	}
	if (wAdapter != hm.wAdapterIndex)
		return HPI_ERROR_NO_INTERADAPTER_GROUPS;

	HPI_Message(&hm, &hr);
	return hr.wError;
}

/** This function returns information about the streams that form a group.
  * Given an out stream handle, it returns a bit mapped representation of which
  * streams belong to the group.
  * \note This function is only supported on on ASI6000 and ASI5000 adapters.
  * \return_hpierr
  */
hpi_err_t HPI_OutStreamGroupGetMap(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hOutStream,	///< Handle to OutStream.
	uint32_t *pdwOutStreamMap,
	/**< Bitmapped representation of OutStreams grouped with this output
	    stream. b0 represents OutStream 0, b1 OutStream 1, b2 OutStream 2 etc.
	*/
	uint32_t *pdwInStreamMap
	/**< Bitmapped representation of InStreams grouped with this output stream.
	    b0 represents InStream 0, b1 InStream 1, b2 InStream 2 etc.
				*/
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_GROUP_GETMAP);
	if (hpi_handle_indexes(hOutStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	HPI_Message(&hm, &hr);

	if (pdwOutStreamMap)
		*pdwOutStreamMap = hr.u.d.u.group_info.dwOutStreamGroupMap;
	if (pdwInStreamMap)
		*pdwInStreamMap = hr.u.d.u.group_info.dwInStreamGroupMap;

	return hr.wError;
}

/** Resets stream grouping information for a given out stream.
  * \note This function is only supported on on ASI6000 and ASI5000 adapters.
  * \return_hpierr
  */
hpi_err_t HPI_OutStreamGroupReset(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hOutStream	///< Handle to OutStream.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_GROUP_RESET);
	if (hpi_handle_indexes(hOutStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	HPI_Message(&hm, &hr);
	return hr.wError;
}
#ifndef HPI_BUILD_NO_STREAM_WAIT
/** Returns when the output stream hits the threshold.
  * This function will block in the driver until the count of bytes
  * in the player buffer (BBM buffer plus on adapter) falls at or
  * below the specified threshold.  This should be called from a
  * background thread.
  *
  * \return_hpierr
  */
hpi_err_t HPI_OutStreamWait(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hOutStream,	///< Handle to OutStream.
	uint32_t dwThresholdBytes	///< Threshold
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_WAIT);
	if (hpi_handle_indexes(hOutStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	hm.u.d.u.dwThresholdBytes = dwThresholdBytes;

	HPI_Message(&hm, &hr);

	return hr.wError;
}
#endif
/** Returns the timestamp from the last stream update.
  *
  * \return_hpierr
  */
hpi_err_t HPI_OutStreamGetTimestamp(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hOutStream,	///< OutStream handle
	uint32_t *pdwSample,		///< Sample
	uint32_t *pdwNanosPerSample,	///< Nanoseconds Per Sample
	int64_t *pqwTimestamp		///< Timestamp
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	// contruct the HPI message from the function parameters
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_OSTREAM, HPI_OSTREAM_GET_TIMESTAMP);
	if (hpi_handle_indexes(hOutStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	HPI_Message(&hm, &hr);	// send the message to all the HPIs

	if (pdwSample)
		*pdwSample = hr.u.d.u.timestamp_info.dwSample;
	if (pdwNanosPerSample)
		*pdwNanosPerSample = hr.u.d.u.timestamp_info.dwNsPerSample;
	if (pqwTimestamp)
		*pqwTimestamp = (uint64_t)hr.u.d.u.timestamp_info.qwPtpTimestampHi << 32 |
				(uint64_t)hr.u.d.u.timestamp_info.qwPtpTimestampLo;

	return hr.wError;
}
/** @} */ /* defgroup outstream */

/*=========================================================================*/
/** \defgroup instream Input Stream

The following figure describes the states an InStream uses.

\image html instream_states.png
@{
*/
/** Open and initializes an input stream.
  *
  * An Adapter index and InStream index are passed in
  * and an InStream handle is passed back.  The handle is used for all future calls to
  * that InStream.  A particular input stream may only be open by one application at a time.
  *
  * \note A side effect of HPI_InStreamOpen() is that the following default ancillary data settings are made:
  * 	- Ancillary Bytes Per Frame = 0
  * 	- Ancillary Mode = HPI_MPEG_ANC_HASENERGY
  * 	- Ancillary Alignment = HPI_MPEG_ANC_ALIGN_RIGHT
  *
  * \return_hpierr
  */

hpi_err_t HPI_InStreamOpen(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	uint16_t wAdapterIndex,	///< Index of adapter to be accessed.  Ranges from 0 to 15 and corresponds to the Adapter Index set on the adapter hardware.
	uint16_t wInStreamIndex,	///< Index of the InStream to be opened.  Ranges from 0 to wNumInStreams-1 (returned by HPI_AdapterGetInfo())
	hpi_handle_t * phInStream	///< Returned handle to the opened InStream.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_OPEN);
	hm.wAdapterIndex = wAdapterIndex;
	hm.wObjIndex = wInStreamIndex;

	HPI_Message(&hm, &hr);

	// construct a global (to the audio subsystem) handle from the adapter,DSP
	// and stream index
	if (hr.wError == 0)
		*phInStream =
			HPI_IndexesToHandle(HPI_OBJ_ISTREAM,
			wAdapterIndex, wInStreamIndex);
	else
		*phInStream = 0;

	return hr.wError;
}

/** Closes an input stream. Deallocates allocated host buffer.
  *
  * \return_hpierr
  */
hpi_err_t HPI_InStreamClose(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hInStream	///< Handle to the InStream to close.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_HOSTBUFFER_FREE);
	if (hpi_handle_indexes(hInStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	HPI_Message(&hm, &hr);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_GROUP_RESET);
	hpi_handle_indexes(hInStream, &hm.wAdapterIndex, &hm.wObjIndex);
	HPI_Message(&hm, &hr);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_CLOSE);
	hpi_handle_indexes(hInStream, &hm.wAdapterIndex, &hm.wObjIndex);
	HPI_Message(&hm, &hr);

	return hr.wError;
}

/** Queries an input stream to see whether it supports a certain audio format, described in pFormat.
  *
  * \return_hpierr
  */

hpi_err_t HPI_InStreamQueryFormat(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hInStream,	///< InStream handle.
	const struct hpi_format *pFormat	///< Pointer to an struct hpi_format structure containing info about the audio format to query.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	// contruct the HPI message from the function parameters
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_QUERY_FORMAT);
	if (hpi_handle_indexes(hInStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	HPI_FormatToMsg(&hm.u.d.u.data.format, pFormat);

	HPI_Message(&hm, &hr);	// send the message to all the HPIs

	return hr.wError;
}

/** Sets the recording format for an input stream.
  *
  * The format to set is described by pFormat.
  *
  * \return_hpierr
  *
  * For example, to set an InStream to stereo, MPEG Layer II @ 256kbps, 44.1kHz sample rate:
\code
err = HPI_FormatCreate(
			&hpiFormat,
			2,                 // stereo channel
			HPI_FORMAT_MPEG_L2,// compression format
			44100,             // sample rate
			256000,            // bits/sec (only used for MPEG)
			0                  // currently no attributes
			);

err = HPI_InStreamSetFormat(
			phSubSys,
			hInStream,
			&hpiFormat
			);
\endcode
\n
<b>MP3 Variable Bitrate</b>\n\n
On adapters that support MP3 encoding,  a quality factor between 0 and 100
controls variable bitrate encoding. The quality factor replaces the bitrate
in the dwBitrate parameter of HPI_FormatCreate().
Setting the "bitrate" to 100 or less automatically activates variable bitrate
encoding.
\code
err = HPI_FormatCreate(
			  &hpiFormat,
			  2,                     // stereo channel
			  HPI_FORMAT_MPEG_L2,   // compression format
			  44100,                // sample rate
			  75,                   // VBR quality setting
			  0                     // currently no attributes
			 );

\endcode
\n
<b>InStream interaction with Bitstream</b>\n\n
Where an instream HPI_DESTNODE_ISTREAM is connected to a bitstream input
HPI_SOURCENODE_RAW_BITSTREAM, the bitstream input provides an unformatted
stream of data bits. How this data is treated is affected by
the format chosen when HPI_InStreamSetFormat() is called.
There are two valid choices:\n
\n
(1) \ref HPI_FORMAT_RAW_BITSTREAM\n
The raw bits are copied into the stream without synchronization.
The value returned by HPI_InStreamGetInfoEx() into *pdwSamplesRecorded is
the number of 32 bit words of data recorded.\n
\n
(2) \ref HPI_FORMAT_MPEG_L2
After the InStream has been reset the incoming bitstream is scanned for the
MPEG2 Layer 2 sync pattern (0xFFFE or 0xFFFC), and input (recording) of
the bitstream data is inhibited until this pattern is found.
The first word of recorded or monitored data will be this sync pattern,
and following data will be word-aligned to it.\n
\n
This synchronization process only takes place once after stream reset.
There is a small chance that the sync pattern appears elsewhere in the data
and the mpeg sync in recorded data won't be byte aligned.
\n\n
The value returned by HPI_InStreamGetInfoEx() into *pdwSamplesRecorded is
calculated from the number of bits recorded using the  values for dwBitRate
and dwSampleRate set by HPI_InStreamSetFormat().\n
\code
	samples = bits recorded * dwSampleRate / dwBitRate
\endcode
If the samplerate is 44.1kHz, then the samplecount will not be exact.
  */
hpi_err_t HPI_InStreamSetFormat(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hInStream,	///< InStream handle.
	const struct hpi_format *pFormat	///< Format to set, created using HPI_FormatCreate().
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	// contruct the HPI message from the function parameters
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_SET_FORMAT);
	if (hpi_handle_indexes(hInStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	HPI_FormatToMsg(&hm.u.d.u.data.format, pFormat);

	HPI_Message(&hm, &hr);	// send the message to all the HPIs

	return hr.wError;
}

/** Read data from an InStream into a buffer
  * Reads dwBytesToRead bytes of audio data from the specified InStream into a
  * memory buffer pointed to by pbData
  *
  * The amount of data requested may be any size up to the amount
  * of data available in the hardware buffer specified by dwDataAvailable
  * returned by HPI_InStreamGetInfoEx().
  *
  * \image html instream_fifo.png
  * \par Diagram
  *
  * \return_hpierr
  * \retval HPI_ERROR_INVALID_DATASIZE if trying to read more data than available.
  *
  */
hpi_err_t HPI_InStreamReadBuf(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hInStream,	///< InStream handle.
	uint8_t *pbData,		///< Pointer to buffer for read data.
	uint32_t dwBytesToRead	///< Number of bytes to read, must be <= number available.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	// contruct the HPI message from the function parameters
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_READ);
	if (hpi_handle_indexes(hInStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.d.u.data.dwDataSize = dwBytesToRead;
	hm.u.d.u.data.pbData = pbData;

	HPI_Message(&hm, &hr);	// send the message to all the HPIs

	return hr.wError;
}

#ifdef HPI_BUILD_INCLUDE_DEPRECATED
/** Read data to a buffer described by struct hpi_data

Note that the format part of struct hpi_data is ignored.

\deprecated Use HPI_InStreamReadBuf() instead.
This function may disappear from a future version.
*/

/** Reads a block of audio data from the specified InStream.
  * The memory data buffer to read into is specified by pData.
  * The HPI will copy the data in the InStream hardware buffer to
  * memory buffer pointed to by pData->pbData.
  *
  * The size of the data block that may be read may be any size up to the
  * amount of data available in the hardware
  * buffer (specified by dwDataAvailable returned by HPI_InStreamGetInfoEx()).
  *
  * \image html instream_fifo.png
  * \par Diagram
  *
  * \return_hpierr
  */
hpi_err_t HPI_InStreamRead(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hInStream,	///< InStream handle
	struct hpi_data * pData	///< Pointer to buffer for read data
)
{
	return HPI_InStreamReadBuf(phSubSys,
		hInStream,
		((struct hpi_msg_data *)pData)->pbData,
		((struct hpi_msg_data *)pData)->dwDataSize);
}
#endif

/** Starts an input stream recording audio data.
  * Audio data is written into the adapters hardware buffer
  * using the currently selected audio format
  *(channels, sample rate, compression format etc.).
  * Data is then read from the buffer using HPI_InStreamRead().
  *
  * \return_hpierr
  */

hpi_err_t HPI_InStreamStart(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hInStream	///< InStream handle
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	// contruct the HPI message from the function parameters
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_START);
	if (hpi_handle_indexes(hInStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	HPI_Message(&hm, &hr);	// send the message to all the HPIs

	return hr.wError;
}

/** Get a stream ready for sync'd start across multiple adapters.
  * Only audio adapters with an inter-card sync header support this
  * function. Streams on each adapter should be called with this
  * function. Then streams on each adapater should be called using
  * HPI_InStreamStart() to begin recording. This function supports
  * grouped streams.
  *
  * \return_hpierr
  */

hpi_err_t HPI_InStreamWaitStart(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hInStream	///< InStream handle
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	// contruct the HPI message from the function parameters
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_WAIT_START);
	if (hpi_handle_indexes(hInStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	HPI_Message(&hm, &hr);	// send the message to all the HPIs

	return hr.wError;
}

/**
  * Stops an input stream recording audio data.  The audio data buffers is
  * not cleared, so a subsequent InStreamStart will resume recording at the
  * position in the buffer where the record had been stopped.
  *
  * \return_hpierr
  */

hpi_err_t HPI_InStreamStop(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hInStream	///< InStream handle.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	// contruct the HPI message from the function parameters
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_STOP);
	if (hpi_handle_indexes(hInStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	HPI_Message(&hm, &hr);	// send the message to all the HPIs

	return hr.wError;
}

/** Clears the audio data buffer of an input stream.
  * If the stream was recording at the time, it will be stopped.
  *
  * \return_hpierr
  */

hpi_err_t HPI_InStreamReset(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hInStream	///< InStream handle.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	// contruct the HPI message from the function parameters
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_RESET);
	if (hpi_handle_indexes(hInStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	HPI_Message(&hm, &hr);	// send the message to all the HPIs

	return hr.wError;
}

#ifdef HPI_BUILD_INCLUDE_DEPRECATED
/**  \deprecated This function has been superseded by HPI_InStreamGetInfoEx()
  *
  * Returns information about the input stream.
  *
  * This includes the size of the streams hardware buffer returned in
  * pdwBufferSize.
  * Also includes whether the stream is currently recording (the state)
  * and the amount of audio data currently contained in the buffer.
  * \return_hpierr
  */

hpi_err_t HPI_InStreamGetInfo(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hInStream,	///< InStream handle.
	uint16_t *pwState,		///< State of stream = HPI_STATE_STOPPED or HPI_STATE_RECORDING.
	uint32_t *pdwBufferSize,	///< Size (in bytes) of stream data buffer.
	uint32_t *pdwDataRecorded	///< Amount of data (in bytes) contained in the hardware buffer.
)
{
	return HPI_InStreamGetInfoEx(phSubSys,
			hInStream,
			pwState, pdwBufferSize, pdwDataRecorded, NULL, NULL);
}
#endif

/** Returns extended information about the input stream.
  *
  *\param phSubSys Vestigial subsys handle (unused), may be set to NULL
  *\param hInStream InStream handle.
  *\param pwState State of stream = ::HPI_STATE_STOPPED or ::HPI_STATE_RECORDING.
  *\param pdwBufferSize Size in bytes of stream data buffer.
  *\param pdwSamplesRecorded The SamplesRecorded parameter returns the number
  * of samples recorded since the last HPI_InStreamReset() command was issued.
  * It reflects the number of stereo samples for a stereo stream and the
  * number of mono samples for a mono stream.
  * This means that if a 44.1kHz stereo and mono stream were both recording
  * they would both return SamplesRecorded=44100 after 1 second.
  *\param pdwDataRecorded DataRecorded returns the amount of data available
  * to be read back in the next HPI_InStreamRead() call.
  * When BBM is active this is the data in the host buffer,
  * otherwise it is the amount in the on-card buffer.
  *\param pdwAuxiliaryDataRecorded AuxiliaryDataRecorded is only valid when
  * BBM is being used (see HPI_InStreamHostBufferAllocate()).
  * In BBM mode it returns the amount of data left in the on-card buffers.
  * This can be used to determine if a record overrun has occurred
  * (both BBM and card buffers full), or at the end of recording to ensure that
  * all recorded data has been read.
  *
  * \return_hpierr
  */

hpi_err_t HPI_InStreamGetInfoEx(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hInStream,
	uint16_t *pwState,
	uint32_t *pdwBufferSize,
	uint32_t *pdwDataRecorded,
	uint32_t *pdwSamplesRecorded,
	uint32_t *pdwAuxiliaryDataRecorded
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_GET_INFO);
	if (hpi_handle_indexes(hInStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	HPI_Message(&hm, &hr);

	// only send back data if valid pointers supplied!!
	if (pwState)
		*pwState = hr.u.d.u.stream_info.wState;
	if (pdwBufferSize)
		*pdwBufferSize = hr.u.d.u.stream_info.dwBufferSize;
	if (pdwDataRecorded)
		*pdwDataRecorded = hr.u.d.u.stream_info.dwDataAvailable;
	if (pdwSamplesRecorded)
		*pdwSamplesRecorded =
			hr.u.d.u.stream_info.dwSamplesTransferred;
	if (pdwAuxiliaryDataRecorded)
		*pdwAuxiliaryDataRecorded =
			hr.u.d.u.stream_info.dwAuxiliaryDataAvailable;
	return hr.wError;
}
#ifndef HPI_OS_LINUX_KERNEL
/** Initializes the MPEG Layer II / III Ancillary data channel
  *
  * Initializes the MPEG Layer II / III Ancillary data channel to
  * support the embedding of wBytesPerFrame bytes into the MPEG bitstream.
  *
  * \param phSubSys Vestigial subsys handle (unused), may be set to NULL
  * \param hInStream Handle for InStream.
  * \param wBytesPerFrame Specifies the rate at which data is inserted into
  * the Ancillary data channel.
  * Note that when (wMode== HPI_MPEG_ANC_HASENERGY, see below) an additional
  * 5 bytes per frame are automatically allocated for energy information.
  * \param wMode One of the #HPI_MPEG_ANC_MODES.
  * The mode for the ancillary data extraction to operate in.
  * Valid settings are HPI_MPEG_ANC_RAW and HPI_MPEG_ANC_HASENERGY.
  * The RAW mode indicates that the entire ancillary data field is taken up by
  * data from the Anc data buffer.
  * The HASENERGY option tells the encoder that the MPEG frames have energy
  * information stored in them (5 bytes per stereo frame, 3 per mono).
  * The encoder will insert the energy bytes before filling the remainder of the
  * ancillary data space with data from the ancillary data buffer.
  * \param wAlignment One of the #HPI_ISTREAM_MPEG_ANC_ALIGNS.
  * HPI_MPEG_ANC_ALIGN_LEFT  the wBytesPerFrame data
  * immediately follow the audio data. Spare space is left at the end of the frame.
  * HPI_MPEG_ANC_ALIGN_RIGHT  the wBytesPerFrame data is packed against the end
  * of the frame. Spare space is left at the start of the frame.
  * HPI_MPEG_ANC_ALIGN_FILL  all ancillary data space in the frame is used.
  * wBytesPerFrame or more data is written per frame.
  * There is no spare space.
  * This parameter is ignored for MP3 encoding,
  * effectively it is fixed at HPI_MPEG_ANC_ALIGN_FILL  (See Note 2)
  * \param wIdleBit This field tells the encoder what to set all the bits of
  * the ancillary data field to in the case where there is no data
  * waiting to be inserted.
  * Valid values are 0 or 1.  This parameter is ignored for MP3 encoding,
  * if no data is available, no data will be inserted  (See Note 2)
  *
  * \return_hpierr
  *
  * \image html instream_anc_reset.png
  *
  * See the below table for the relationship between wBytesPerFrame and the
  * datarate on the ancillary data channel.
  * For stereo recording, ancillary data is organized as follows:
<table border=1 cellspacing=0 cellpadding=5>
<tr>
<td><b>Sample rate</b><td><b>Frame rate</b><td><b>Energy rate</b>
<td><b>Bitrate per byte on ancillary data</b>

<tr><td>48 kHz<td>41.66 frames/sec<td>1333.33 bits/sec<td>1333.33 bits/sec
<tr><td>44.1 kHz<td>38.28 frames/sec<td>1224.96 bits/sec<td>306.25 bits/sec
<tr><td>32 kHz<td>27.77 frames/sec<td>888.89 bits/sec<td>222.22 bits/sec
</table>

Ancillary data is embedded at the end of each MPEG frame as follows:\n
\verbatim
Key:
e = ancillary energy bits (correct number of bits not shown)
d = ancillary data bits
a = audio bits
f = fill bits
x = don't care bits\n
HPI_MPEG_ANC_ALIGN_LEFT (fill is at end for "raw" case)
	wMode = HPI_MPEG_ANC_RAW
		a,a,a,d,d,d,d,d,d,f,f,f <eof>
	wMode = HPI_MPEG_ANC_HASENERGY
		a,a,a,d,d,d,d,..,f,f,f,e,e,e,e <eof>

HPI_MPEG_ANC_ALIGN_RIGHT (fill is at front)
	wMode = HPI_MPEG_ANC_RAW
		a,a,a,f,f,f,d,d,d,d,d,d<eof>
	wMode = HPI_MPEG_ANC_HASENERGY
		a,a,a,f,f,f,d,d,d,d,..,e,e,e,e <eof>

HPI_MPEG_ANC_ALIGN_FILL (all available ancillary data spots are used)
	wMode = HPI_MPEG_ANC_RAW
		a,a,a,d,d,d,d,d,d,d,d,d<eof>
	wMode = HPI_MPEG_ANC_HASENERGY
		a,a,a,d,d,d,d,d,d,d,..,e,e,e,e <eof>
\endverbatim

  * <b>Note1 (Calling order):</b>\n
  * This call must be made before an HPI_InStreamSetFormat() call.\n\n
  * <b>Note2 (MP3):</b>\n
  * Embedded energy information in an MPEG1 layer III (MP3) stream is quite
  * difficult to utilize because its position is not constant within the MPEG frame.
  * With MP2, the ancillary data field always appears at the end of the MPEG frame.
  * The parameters wIdleBit and wAlignment are ignored for MP3 due to the inherently more
  * efficient data packing scheme used.\n\n
  * <b>Note3 (Default settings):</b>\n
  * A side effect of HPI_InStreamOpen() is that the following default
  * ancillary data settings are made:\n
  * Ancillary Bytes Per Frame = 0 \n
  * Ancillary Mode = HPI_MPEG_ANC_HASENERGY \n
  * Ancillary Alignment = HPI_MPEG_ANC_ALIGN_RIGHT \n
  */
hpi_err_t HPI_InStreamAncillaryReset(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hInStream,
	uint16_t wBytesPerFrame,
	uint16_t wMode,
	uint16_t wAlignment,
	uint16_t wIdleBit
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_ANC_RESET);
	if (hpi_handle_indexes(hInStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	/*
	 * Format structure packing
	 * dwAttributes = wBytesPerFrame
	 * wFormat = wMode
	 * wMode = wIdleBit
	 */
	hm.u.d.u.data.format.dwAttributes = wBytesPerFrame;
	hm.u.d.u.data.format.wFormat = (wMode << 8) | (wAlignment & 0xff);
	hm.u.d.u.data.format.wChannels = wIdleBit;
	HPI_Message(&hm, &hr);
	return hr.wError;
}

/** Returns information about the ancillary data stream.
  */

hpi_err_t HPI_InStreamAncillaryGetInfo(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hInStream,	///< Handle to the InStream.
	uint32_t *pdwFrameSpace ///< Maximum number of ancillary data frames that can be written to the anc frame buffer.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_ANC_GET_INFO);
	if (hpi_handle_indexes(hInStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	HPI_Message(&hm, &hr);
	if (pdwFrameSpace)
		*pdwFrameSpace =
			(hr.u.d.u.stream_info.dwBufferSize -
			hr.u.d.u.stream_info.dwDataAvailable) /
			sizeof(struct hpi_anc_frame);
	return hr.wError;
}

/** Writes frames to the stream's ancillary data buffer.
  *
  * Writes dwNumberOfAncDataFramesToWrite frames from pAncFrameBuffer to the streams ancillary data buffer.
  * The first bit of ancillary information that follows the valid audio data is bit 7 of bData[0].
  * The first 8 bits of ancillary information following valid audio data are from bData[0].
  * In the case where there are 6 bytes total of ancillary information (48 bits) the last byte
  * inserted in the frame is bData[5].
  * \return_hpierr
  */

hpi_err_t HPI_InStreamAncillaryWrite(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hInStream,	///< Handle to the InStream.
	const struct hpi_anc_frame * pAncFrameBuffer,	///< A pointer to a buffer where the ancillary data frames to write should be placed.
	uint32_t dwAncFrameBufferSizeInBytes,	///< The size of the Ancillary data buffer in bytes (used for a sanity check).
	uint32_t dwNumberOfAncillaryFramesToWrite	///< How many frames to write.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_ANC_WRITE);
	if (hpi_handle_indexes(hInStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.d.u.data.pbData = (uint8_t *)pAncFrameBuffer;
	hm.u.d.u.data.dwDataSize =
		dwNumberOfAncillaryFramesToWrite * sizeof(struct hpi_anc_frame);
	if (hm.u.d.u.data.dwDataSize <= dwAncFrameBufferSizeInBytes)
		HPI_Message(&hm, &hr);
	else
		hr.wError = HPI_ERROR_INVALID_DATASIZE;
	return hr.wError;
}

/** Allocates a buffer on the host PC for bus mastering transfers.
 * Assuming no error:
 * Allocates a buffer on the host PC for bus mastering transfers.
 * Once the buffer is allocated, InStream data will be transferred to it in the
 * background. (rather than the application having to wait for the transfer)
 * This function is provided so that the application can match the size of its
 * transfers to the size of the buffer.
 *
 * From now on, HPI_InStreamGetInfoEx() will return the size and data available
 * in host buffer rather than the buffers on the adapter.
 * However, if the host buffer is allowed to fill up, the adapter buffers will
 * then begin to fill up. In other words you still have the benefit of the
 * larger adapter buffers
 *
 * \note There is a minimum buffer size that will work with a given audio
 * format and polling rate. An appropriate size for the buffer can be
 * calculated using HPI_StreamEstimateHostBufferSize()
 *
 * \return_hpierr
 * \retval HPI_ERROR_INVALID_DATASIZE memory can't be allocated
 *(retrying the call with a smaller size may succeed)
 * \retval HPI_ERROR_MEMORY_ALLOC virtual address of the allocated buffer can't be found.
 * \retval HPI_ERROR_INVALID_FUNC the adapter doesn't support busmastering
 */
hpi_err_t HPI_InStreamHostBufferAllocate(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hInStream,	///< Handle of the InStream.
	uint32_t dwSizeInBytes
)				///< Size of bus mastering buffer to allocate.
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_HOSTBUFFER_ALLOC);
	if (hpi_handle_indexes(hInStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.d.u.buffer.dwBufferSize = dwSizeInBytes;
	HPI_Message(&hm, &hr);
	return hr.wError;
}

#ifdef HPI_BUILD_INCLUDE_INTERNAL
/** \internal
  * Used by hpifunc.c to implement direct buffer access API.
  * \param phSubSys Vestigial subsys handle (unused), may be set to NULL
  * \param hInStream Pointer to the input stream handle.
  * \param ppBuffer Returned pointer to the BBM buffer.
  * \param ppStatus Returned pointer to the BBM buffer status structure.
  * \return_hpierr
  */
hpi_err_t HPI_InStreamHostBufferGetInfo(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hInStream,
	uint8_t ** ppBuffer,
	struct hpi_hostbuffer_status **ppStatus
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_HOSTBUFFER_GET_INFO);
	if (hpi_handle_indexes(hInStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	HPI_Message(&hm, &hr);

	if (hr.wError == 0) {
		if (ppBuffer)
			*ppBuffer = hr.u.d.u.hostbuffer_info.pBuffer;
		if (ppStatus)
			*ppStatus = hr.u.d.u.hostbuffer_info.pStatus;
	}
	return hr.wError;
}
#endif
#endif

/** Free any buffers allocated by HPI_InStreamHostBufferAllocate.
  *
  * \return_hpierr
  *	\retval HPI_ERROR_INVALID_FUNC if the function is not implemented
  */
hpi_err_t HPI_InStreamHostBufferFree(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hInStream
)				///< Handle of the InStream.
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_HOSTBUFFER_FREE);
	if (hpi_handle_indexes(hInStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	HPI_Message(&hm, &hr);
	return hr.wError;
}

/** This function adds a stream to a group of streams. Stream groups are
  * used to synchronise starting and stopping of multiple streams at once.
  * The application of this is to support recording (or playing) multiple
  * streams at once, enabling multi-channel recording and playback.
  *
  * When using the "Group" functions all streams that are to be grouped
  * together should be opened. One of the streams should be selected to
  * be the master stream and the other streams should be added to it's group.
  * Both in streams and out streams can be added to the same group.
  * Once a group has been formed, HPI_InStreamStart() called on the master
  * will cause all streams to start at once.
  *
  * \note This function is only supported on on ASI6000 and ASI5000 adapters.
  * \return_hpierr
  */
hpi_err_t HPI_InStreamGroupAdd(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hInStream,	///< Handle to InStream.
	hpi_handle_t hStream	///< Handle to an InStream or an OutStream.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	uint16_t wAdapter;
	char cObjType;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_GROUP_ADD);
	hr.wError = 0;

	if (hpi_handle_indexes(hInStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	if (hpi_handle_indexes(hStream, &wAdapter,
				&hm.u.d.u.stream.wStreamIndex))
		return HPI_ERROR_INVALID_HANDLE;

	cObjType = HPI_HandleObject(hStream);

	switch (cObjType) {
	case HPI_OBJ_OSTREAM:
	case HPI_OBJ_ISTREAM:
		hm.u.d.u.stream.wObjectType = cObjType;
		break;
	default:
		return HPI_ERROR_INVALID_OBJ;
	}

	if (wAdapter != hm.wAdapterIndex)
		return HPI_ERROR_NO_INTERADAPTER_GROUPS;

	HPI_Message(&hm, &hr);
	return hr.wError;
}

/** This function returns information about the streams that form a group.
  * Given an out stream handle, it returns a bit mapped representation of which
  * streams belong to the group.
  * \note This function is only supported on on ASI6000 and ASI5000 adapters.
  * \return_hpierr
  */
hpi_err_t HPI_InStreamGroupGetMap(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hInStream,	///< Handle to InStream.
	uint32_t *pdwOutStreamMap,
	/**< Bitmapped representation of OutStreams grouped with this output
	    stream. b0 represents OutStream 0, b1 OutStream 1, b2 OutStream 2 etc.
	*/
	uint32_t *pdwInStreamMap
	/**< Bitmapped representation of InStreams grouped with this output stream.
	   b0 represents InStream 0, b1 InStream 1, b2 InStream 2 etc.
	*/
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_HOSTBUFFER_FREE);
	if (hpi_handle_indexes(hInStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	HPI_Message(&hm, &hr);

	if (pdwOutStreamMap)
		*pdwOutStreamMap = hr.u.d.u.group_info.dwOutStreamGroupMap;
	if (pdwInStreamMap)
		*pdwInStreamMap = hr.u.d.u.group_info.dwInStreamGroupMap;

	return hr.wError;
}

/** Resets stream grouping information for a given InStream.
  * \note This function is only supported on on ASI6000 and ASI5000 adapters.
  * \return_hpierr
  */
hpi_err_t HPI_InStreamGroupReset(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hInStream	///< Handle to InStream.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_GROUP_RESET);
	if (hpi_handle_indexes(hInStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	HPI_Message(&hm, &hr);
	return hr.wError;
}
#ifndef HPI_BUILD_NO_STREAM_WAIT
/** Returns when the input stream hits the threshold.
  * This function will block in the driver until the count of bytes
  * in the record stream's BBM buffer meets or exceeds the
  * specified threshold.  This should be called from a background
  * thread.
  *
  * \return_hpierr
  */
hpi_err_t HPI_InStreamWait(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hInStream,		///< InStream handle
	uint32_t dwThresholdBytes	///< Threshold
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	// contruct the HPI message from the function parameters
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_WAIT);
	if (hpi_handle_indexes(hInStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	hm.u.d.u.dwThresholdBytes = dwThresholdBytes;

	HPI_Message(&hm, &hr);	// send the message to all the HPIs

	return hr.wError;
}
#endif
/** Returns the timestamp from the last stream update.
  *
  * \return_hpierr
  */
hpi_err_t HPI_InStreamGetTimestamp(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hInStream,		///< InStream handle
	uint32_t *pdwSample,		///< Sample
	uint32_t *pdwNanosPerSample,	///< Nanoseconds Per Sample
	int64_t *pqwTimestamp		///< Timestamp
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	// contruct the HPI message from the function parameters
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ISTREAM, HPI_ISTREAM_GET_TIMESTAMP);
	if (hpi_handle_indexes(hInStream, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	HPI_Message(&hm, &hr);	// send the message to all the HPIs

	if (pdwSample)
		*pdwSample = hr.u.d.u.timestamp_info.dwSample;
	if (pdwNanosPerSample)
		*pdwNanosPerSample = hr.u.d.u.timestamp_info.dwNsPerSample;
	if (pqwTimestamp)
		*pqwTimestamp = (uint64_t)hr.u.d.u.timestamp_info.qwPtpTimestampHi << 32 |
				(uint64_t)hr.u.d.u.timestamp_info.qwPtpTimestampLo;

	return hr.wError;
}
/** @} */ /* defgroup instream */
/** @} */ /* defgroup stream */

/*=========================================================================*/
/** \defgroup mixer Mixer
@{
    \defgroup mixer_functions Mixer Functions
@{
*/

#ifdef HPI_OS_DELETE
/* Internal function. */
/** \internal
*/
static hpi_err_t mixer_match_control(
	hpi_handle_t hMixer,
	uint16_t i,
	struct hpi_control_t c_spec,
	hpi_handle_t* control,
	int *match
)
{
	hpi_hsubsys_t *phSubSys = NULL;
	struct hpi_control_t c_read;
	hpi_err_t err;

	*match = 0;
	err =  HPI_MixerGetControlByIndex(phSubSys, hMixer,
			i,
			&c_read.wSrcNodeType, &c_read.wSrcNodeIndex,
			&c_read.wDstNodeType, &c_read.wDstNodeIndex,
			&c_read.wControlType,
			control);

	if (err)
		return err;

	if (	(c_read.wSrcNodeType == c_spec.wSrcNodeType ) &&
		(c_read.wSrcNodeIndex == c_spec.wSrcNodeIndex ) &&
		(c_read.wDstNodeType == c_spec.wDstNodeType ) &&
		(c_read.wDstNodeIndex == c_spec.wDstNodeIndex )&&
		(c_read.wControlType == c_spec.wControlType))
		*match = 1;

	return err;
}
#endif

/** Opens and initializes an adapters mixer.
 *
 * An Adapter index is passed in and a Mixer handle is passed back.
 * The handle is used for all future calls to that Mixer.
 *
 * \return_hpierr
 */
hpi_err_t HPI_MixerOpen(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	uint16_t wAdapterIndex,	///< Index of adapter to be opened.  Ranges from 0 to 15 and corresponds to the Adapter Index set on the adapter hardware.
	hpi_handle_t * phMixer	///< Returned handle to the Mixer.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_MIXER, HPI_MIXER_OPEN);
	hm.wAdapterIndex = wAdapterIndex;

	HPI_Message(&hm, &hr);

	if (hr.wError == 0)
		*phMixer =
			HPI_IndexesToHandle(HPI_OBJ_MIXER, wAdapterIndex, 0);
	else
		*phMixer = 0;
	return hr.wError;
}

#ifndef HPI_OS_LINUX_KERNEL
/** Closes a mixer.
  * \return_hpierr
  */

hpi_err_t HPI_MixerClose(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hMixer	///< Handle to the adapter mixer to close.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_MIXER, HPI_MIXER_CLOSE);
	if (hpi_handle_indexes(hMixer, &hm.wAdapterIndex, NULL))
		return HPI_ERROR_INVALID_HANDLE;

	HPI_Message(&hm, &hr);
	return hr.wError;
}
#endif

/** Gets a mixer control.
 *
 * The control maybe located in one of three places:\n
 * -# On a connection between a source node and a destination node.\n
 * You specify both source and destination nodes (via type and type index).
 * -# On a source node.\n
 * You specify the source node and leave the destination node type and index as 0.
 * -# On a destination node.\n
 * You specify the destination node and leave the source node type and index as 0.
 *
 * \note Not all adapters have controls at all nodes, or between all nodes.
 * Consult the Mixer Map for your particular
 * adapter to find out the location and type of controls in its mixer.
 *
 * Say you have an audio adapter with a mixer that has the following layout:
 *
 * \image html mixer_get_control.png
 *
 * You can see that the mixer has two meter controls, located on each of the
 * Outstream source nodes, two volume controls, located between
 * the OutStream sources and the LineOut destination nodes and one
 * level control located on the LineOut destination node.
 *
 * You would use the following code to obtain a handle to the volume control
 * that lies on the connection between OutStream#1 and LineOut#0:
 \sample
err = HPI_MixerGetControl(
		&hSubSys,
		hMixer,
		HPI_SOURCENODE_OSTREAM,
		1,
		HPI_DESTNODE_LINEOUT,
		0,
		HPI_CONTROL_VOLUME,
		&hVolControl
		);
\endsample
 *
 * You would use the following code to obtain a handle to the level control
 * that lies on the LineOut#0 node:
\sample
	err = HPI_MixerGetControl(
			&hSubSys,
			hMixer,
			0,
			0,
			HPI_DESTNODE_LINEOUT,
			0,
			HPI_CONTROL_LEVEL,
			&hLevControl
			);
\endsample
 *
 *\return_hpierr
 */

hpi_err_t HPI_MixerGetControl(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hMixer,	///< Mixer handle.
	uint16_t wSrcNodeType,	///< Source node type i.e. #HPI_SOURCENODE_OSTREAM.
	uint16_t wSrcNodeTypeIndex,	///< Index of particular source node type i.e. the 2nd \ref HPI_SOURCENODE_OSTREAM would be specified as index=1.
	uint16_t wDstNodeType,	///< Destination node type i.e. \ref HPI_DESTNODE_LINEOUT.
	uint16_t wDstNodeTypeIndex,	///< Index of particular source node type i.e. the 3rd \ref HPI_DESTNODE_LINEOUT would be specified as index=2
	uint16_t wControlType,	///< Type of mixer control i.e.  One of the #HPI_CONTROLS
	hpi_handle_t * phControl	///< Handle to the particular control
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_MIXER, HPI_MIXER_GET_CONTROL);
	if (hpi_handle_indexes(hMixer, &hm.wAdapterIndex, NULL))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.m.wNodeType1 = wSrcNodeType;
	hm.u.m.wNodeIndex1 = wSrcNodeTypeIndex;
	hm.u.m.wNodeType2 = wDstNodeType;
	hm.u.m.wNodeIndex2 = wDstNodeTypeIndex;
	hm.u.m.wControlType = wControlType;

	HPI_Message(&hm, &hr);

	// each control in an adapter/mixer has a unique index.
	if (hr.wError == 0)
		*phControl =
			HPI_IndexesToHandle(HPI_OBJ_CONTROL, hm.wAdapterIndex,
			hr.u.m.wControlIndex);
	else
		*phControl = 0;
	return hr.wError;
}

/** Get the location and type of a mixer control by index
  *
  * This function is primarily intended to be used to enumerate all the controls in a mixer.
  * To enumerate all the mixer controls of an adapter, iterate wControlIndex
  * from 0 until the function returns #HPI_ERROR_INVALID_OBJ_INDEX.
  * The iteration should not be terminated if error #HPI_ERROR_CONTROL_DISABLED is returned.
  * This indicates that a control that normally exists is disabled for some reason (possibly hardware
  * failure).  The application should not access such controls, but may for instance display a
  * grayed-out GUI control.
  *
  * A control may exist between two nodes, or on a single node in which case
  * the non-existent node type is set to #HPI_SOURCENODE_NONE or #HPI_DESTNODE_NONE.
  *
  * An invalid source node returns HPI_SOURCENODE_NONE, while an invalid destination node will
  * return HPI_DESTNODE_NONE.
  *
  *\return_hpierr
  *\retval HPI_ERROR_INVALID_OBJ_INDEX when wControlIndex > number of mixer controls
  */
hpi_err_t HPI_MixerGetControlByIndex(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hMixer,	///< Mixer handle.
	uint16_t wControlIndex,	///< Control Index to query.
	uint16_t *pwSrcNodeType,	///< Returned source node type for control at index wControlIndex.
	uint16_t *pwSrcNodeIndex,	///< Returned source node index for control at index wControlIndex.
	uint16_t *pwDstNodeType,	///< Returned destination node type for control at index wControlIndex.
	uint16_t *pwDstNodeIndex,	///< Returned destination node index for control at index wControlIndex.
	uint16_t *pwControlType,	///< Returned control type for control at index wControlIndex.
	hpi_handle_t * phControl	///< Returned control handle for control at index wControlIndex.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_MIXER, HPI_MIXER_GET_CONTROL_BY_INDEX);
	if (hpi_handle_indexes(hMixer, &hm.wAdapterIndex, NULL))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.m.wControlIndex = wControlIndex;
	HPI_Message(&hm, &hr);

	if (pwSrcNodeType) {	/* return all or none of info fields */
		*pwSrcNodeType = hr.u.m.wSrcNodeType + HPI_SOURCENODE_NONE;
		*pwSrcNodeIndex = hr.u.m.wSrcNodeIndex;
		*pwDstNodeType = hr.u.m.wDstNodeType + HPI_DESTNODE_NONE;
		*pwDstNodeIndex = hr.u.m.wDstNodeIndex;
	}
	if (pwControlType)
		*pwControlType = hr.u.m.wControlIndex;	/* Actually Type */

	// each control in an adapter/mixer has a unique index.
	if (phControl) {
		if (hr.wError == 0)
			*phControl =
				HPI_IndexesToHandle(HPI_OBJ_CONTROL,
				hm.wAdapterIndex, wControlIndex);
		else
			*phControl = 0;
	}
	return hr.wError;
}

#ifndef HPI_OS_LINUX_KERNEL
/** Execute a command to store Mixer Controls
  *
  * Valid commands are members of \ref HPI_MIXER_STORE_COMMAND
  *
  *\return_hpierr
  *\retval HPI_ERROR_INVALID_OBJ_INDEX when wControlIndex > number of mixer controls
  */
hpi_err_t HPI_MixerStore(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hMixer,	///< Mixer handle.
	enum HPI_MIXER_STORE_COMMAND command,	///< Command to execute.
	uint16_t wIndex		///< Optional index.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_MIXER, HPI_MIXER_STORE);
	if (hpi_handle_indexes(hMixer, &hm.wAdapterIndex, NULL))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.mx.store.wCommand = command;
	hm.u.mx.store.wIndex = wIndex;
	HPI_Message(&hm, &hr);
	return hr.wError;
}
/** Fetch the number of controls waiting to be stored
  *
  * This function has two uses. It can be used before calling HPI_MixerStore() to
  * determine if there are any controls that need to be stored. Additionally, after
  * calling HPI_MixerStore(), it can be called periodically to determine whether all the
  * controls have been successfully saved.
  *
  *\return_hpierr
  */
hpi_err_t HPI_MixerStoreStatus(
	const hpi_hsubsys_t *phSubSys,   ///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hMixer,             ///< Mixer handle.
	uint16_t *pwControlsToStore      ///< Returned control count to store.
	)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_MIXER, HPI_MIXER_STORE_STATUS);
	if (hpi_handle_indexes(hMixer, &hm.wAdapterIndex, NULL))
		return HPI_ERROR_INVALID_HANDLE;

	if (!pwControlsToStore)
		return HPI_ERROR_INVALID_OPERATION;

	HPI_Message(&hm, &hr);

	if (0 == hr.wError)
		*pwControlsToStore = hr.u.mx.store.wControlCount;

	return hr.wError;
}
#endif
/** @} */ /* defgroup mixer_functions */

/** \defgroup mixer_controls Controls
@{
 */
/*=========================================================================*/
// MIXER CONTROLS
/** \internal
 * General function for setting up to 2 uint32_t return values from a
 * ControlSet call.
 */
#ifdef HPI_OS_LINUX_KERNEL
static
#endif
hpi_err_t HPI_ControlParamSet(
	const hpi_hsubsys_t *phSubSys,
	const hpi_handle_t hControl,
	const uint16_t wAttrib,
	const uint32_t dwParam1,
	const uint32_t dwParam2
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_SET_STATE);
	if (hpi_handle_indexes(hControl, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.c.wAttribute = wAttrib;
	hm.u.c.dwParam1 = dwParam1;
	hm.u.c.dwParam2 = dwParam2;
	HPI_Message(&hm, &hr);
	return hr.wError;
}

static hpi_err_t HPI_ControlLogSet2(
	hpi_handle_t hControl,
	uint16_t wAttrib,
	short sv0, short sv1
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_SET_STATE);
	if (hpi_handle_indexes(hControl, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.c.wAttribute = wAttrib;
	hm.u.c.anLogValue[0] = sv0;
	hm.u.c.anLogValue[1] = sv1;
	HPI_Message(&hm, &hr);
	return hr.wError;
}

/** \internal
 * General function for getting up to 2 uint32_t return values from
 * a ControlGet call.
 */
#ifdef HPI_OS_LINUX_KERNEL
static
#endif
hpi_err_t HPI_ControlParamGet(
	const hpi_hsubsys_t *phSubSys,
	const hpi_handle_t hControl,
	const uint16_t wAttrib,
	uint32_t dwParam1,
	uint32_t dwParam2,
	uint32_t *pdwParam1,
	uint32_t *pdwParam2
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE);
	if (hpi_handle_indexes(hControl, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.c.wAttribute = wAttrib;
	hm.u.c.dwParam1 = dwParam1;
	hm.u.c.dwParam2 = dwParam2;
	HPI_Message(&hm, &hr);

	if (pdwParam1)
		*pdwParam1 = hr.u.c.dwParam1;
	if (pdwParam2)
		*pdwParam2 = hr.u.c.dwParam2;

	return hr.wError;
}

#define HPI_ControlParam1Get(phSubSys, h, a, p1) \
		HPI_ControlParamGet(phSubSys, h, a, 0, 0, p1, NULL)
#define HPI_ControlParam2Get(phSubSys, h, a, p1, p2) \
		HPI_ControlParamGet(phSubSys, h, a, 0, 0, p1, p2)

static hpi_err_t HPI_ControlLogGet2(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hControl,
	uint16_t wAttrib,
	short *sv0, short *sv1
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE);
	if (hpi_handle_indexes(hControl, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.c.wAttribute = wAttrib;

	HPI_Message(&hm, &hr);
	*sv0 = hr.u.c.anLogValue[0];
	if (sv1)
		*sv1 = hr.u.c.anLogValue[1];
	return hr.wError;
}

/** \internal Get the possible settings of an attribute of a mixer control given its index.
  *
  * This is done without disturbing the current setting of the control.
  * Do this by iterating dwIndex from 0 until the function returns
  * HPI_ERROR_INVALID_OBJ_INDEX.
  *
  * For example, to determine which bands are supported by a particular tuner,
  * do the following:
\code
for (dwIndex = 0; dwIndex < 100; dwIndex++) {
	wErr= HPI_ControlQuery(phSS,hC, HPI_TUNER_BAND, dwIndex,
		0 , AvailableBands[dwIndex]);
	if (wErr) break;
}
numBands=dwIndex;
\endcode
  *
  * For attributes that have a range, 3 values will be returned for
  * indices 0 to 2: minimum, maximum and step.
  * The supplementary parameter dwParam is used where the possible settings
  * for one attribute depend on the setting of another attribute,
  * e.g. a tuners frequency range depends on which band is selected.
 */
#ifdef HPI_OS_LINUX_KERNEL
static
#endif
hpi_err_t HPI_ControlQuery(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	const hpi_handle_t hControl,	///< Control to query
	const uint16_t wAttrib,	///< An attribute of the control
	const uint32_t dwIndex,	///< Index for possible attribute values
	const uint32_t dwParam,	///< Supplementary parameter
	uint32_t *pdwSetting	///< One of N possible settings for the control attribute, specified by dwIndex=0..N-1(and possibly depending on the value of the supplementary parameter)
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_INFO);
	if (hpi_handle_indexes(hControl, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	hm.u.c.wAttribute = wAttrib;
	hm.u.c.dwParam1 = dwIndex;
	hm.u.c.dwParam2 = dwParam;

	HPI_Message(&hm, &hr);
	*pdwSetting = hr.u.c.dwParam1;

	return hr.wError;
}

#ifndef HPI_OS_LINUX_KERNEL
/* private function to get a string using multiple HPI transactiosn*/
static hpi_err_t HPI_Control_GetString(
	const hpi_handle_t hControl,
	const uint16_t wAttribute,
	char * pszString,
	const uint32_t wStringLength,
	const uint32_t wIndex  //< Index into array of strings
)
{
	unsigned int subStringIndex;
	hpi_err_t err = 0;

	if (!pszString || !wStringLength) {
		return HPI_ERROR_MEMORY_ALLOC;
	}

	if ((!wStringLength) || (wStringLength > HPI_PAD_COMMENT_LEN)) {
		pszString[0] = 0;
		return HPI_ERROR_INVALID_CONTROL_VALUE;
	}

	/*
		Transfer the PAD field 8 bytes at a time until done.

		The size and content of the field can change between calls so it is not
		possible to avoid inconsistent string content. However, the string buffer
		provided by the caller is always zero-terminated before returning, either
		because a zero byte is encountered while copying the field or by the
		unconditional zero-termination on the last byte of the buffer.
	*/

	for (subStringIndex = 0; subStringIndex < wStringLength;)
	{
		HPI_MESSAGE hm;
		HPI_RESPONSE hr;
		unsigned int j;

		HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE);
		if (hpi_handle_indexes(hControl, &hm.wAdapterIndex, &hm.wObjIndex))
			return HPI_ERROR_INVALID_HANDLE;
		hm.u.c.wAttribute = wAttribute;
		hm.u.c.dwParam1 = subStringIndex;
		hm.u.c.dwParam2 = wIndex;
		HPI_Message(&hm, &hr);

		if (hr.wError) {
			err = hr.wError;
			break;
		}

		/*
			hr.u.cu.chars8.dwRemainingChars is not used to decide when the transfer is complete because:
				1- It is not reliable because it can change (non-monotonically) between calls.
				2- It doesn't include the number of valid characters carried by hr.u.cu.chars8.szData
		*/

		for (j = 0; j < ARRAY_SIZE(hr.u.cu.chars8.szData); subStringIndex++, j++) {
			char c = hr.u.cu.chars8.szData[j];
			if (subStringIndex >= wStringLength) {
				err = HPI_ERROR_INVALID_CONTROL_VALUE;
				break;
			}
			pszString[subStringIndex] = c;
			if (!c) {
				goto done;
			}
		}
	}

done:

	/* ensure the buffer provided by the caller is zero-terminated */
	pszString[wStringLength-1] = 0;
	return err;
}

//#define HPI_MOCK
#ifdef HPI_MOCK
struct sl {
	char * s;
	int l;
};

static char test_data1[] = "This is some test data to \x00\x01return";
static char test_data2[] = "\x01abc";

static struct sl test_table[] = {
	{test_data1, sizeof(test_data1)},
	{test_data2, sizeof(test_data2)}
};

static void HPI_Mock(struct hpi_message_header *m, struct hpi_response_header *r)
{
	struct hpi_res_control_data *pr = (void *)r;
	struct hpi_msg_control_data *pm = (void *)m;
	struct sl *td;
	int remain;
	int count;

	if (pm->p.index > 1) {
		pr->h.wError = HPI_ERROR_INVALID_CONTROL_VALUE;
		return;
	}

	td = &test_table[pm->p.index];

	if (pm->p.offset >= td->l) {
		pr->h.wError = HPI_ERROR_INVALID_CONTROL_VALUE;
		return;
	}

	count = td->l - pm->p.offset;
	count = min(count, 16);
	count = max(count, 0);

	remain = td->l - pm->p.offset - count;

	memcpy(pr->p.bytes, &td->s[pm->p.offset], count);
	pr->p.byte_count = count;
	pr->p.bytes_remaining = remain;
	pr->h.wError = 0;

	printf("Index %d, Offset %d, count %d, remain %d\n",
		pm->p.index, pm->p.offset, count, remain);
}
#endif

/** Get bulk control data
 */
static hpi_err_t HPI_Control_GetData(
	hpi_handle_t hControl,  ///< control handle
	unsigned int attribute,  ///< control attribute to get data from
	char *buffer,  ///< buffer to receive data
	size_t buf_len,  ///< size of buffer at *buffer
	uint32_t index,  ///< Index into array of strings, or DAB service ID
	uint16_t index2,  ///< DAB component ID
	size_t *data_len ///< Returned data length, or required data_len on HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL
)
{
	uint16_t offset = 0;
	struct hpi_res_control_data hr;
	struct hpi_msg_control_data hm;

	if (data_len)
		*data_len = 0;

	if (!buffer)
		return HPI_ERROR_MEMORY_ALLOC;

	do {
		HPI_InitMessageResponseV1(&hm.h, sizeof(hm), &hr.h,  sizeof(hr),
		                          HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE);

		if (hpi_handle_indexes(hControl, &hm.h.wAdapterIndex, &hm.h.wObjIndex))
			return HPI_ERROR_INVALID_HANDLE;

		hm.p.attribute = (uint16_t)attribute;
		hm.p.index = index;
		hm.p.index2 = index2;
		hm.p.offset = offset;

#ifdef HPI_MOCK
		HPI_Mock(&hm.h, &hr.h);
#else
		HPI_MessageV1(&hm.h, &hr.h);
#endif

		if (hr.h.wError)
			return hr.h.wError;

		if ((size_t)(offset + hr.p.byte_count + hr.p.bytes_remaining) > buf_len) {
			if (data_len)
				*data_len = offset + hr.p.byte_count + hr.p.bytes_remaining;
			return HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL;
		}

		memcpy(&buffer[offset], hr.p.bytes, hr.p.byte_count);
		offset += hr.p.byte_count;
	} while (hr.p.bytes_remaining != 0 && offset < buf_len);

	if (data_len)
		*data_len = offset;
	return 0;
}

#endif

/*=========================================================================*/
/** \defgroup aesrx AES/EBU Digital audio receiver controls

The AESEBU receiver  receives audio from a standard digital audio interface
(AESEBU or SPDIF). As well as receiving the audio, status and user bits are
extracted from the digital bitstream.

\image html aesebu_receiver.png

\{
*/
#ifndef HPI_OS_LINUX_KERNEL
/** query formats supported by this aesebu receiver
Iterate dwIndex until an error is returned to get allowed values
for aesebu format in *pwFormat
*/
hpi_err_t HPI_AESEBU_Receiver_QueryFormat(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	const hpi_handle_t hAesRx,	///< Control to query
	const uint32_t dwIndex,	///< Index for possible attribute values
	uint16_t *pwFormat	///< aesebu format, one of the #HPI_AESEBU_FORMATS
)
{
	uint32_t qr;
	hpi_err_t err;

	err = HPI_ControlQuery(phSubSys, hAesRx, HPI_AESEBURX_FORMAT,
				dwIndex, 0, &qr);
	*pwFormat = (uint16_t)qr;
	return err;
}
#endif

/** Sets the physical format of the digital audio input to either the balanced,
 * professional AES/EBU input or the unbalanced, consumer S/PDIF input.
 * Note that not all audio adpaters will have both kinds of inputs.
 * \return_hpierr
 */
hpi_err_t HPI_AESEBU_Receiver_SetFormat(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///<Handle to control of type HPI_CONTROL_AESEBU_RECEIVER
	uint16_t wFormat		///< One of the #HPI_AESEBU_FORMATS
)
{
	return HPI_ControlParamSet(phSubSys, hControl,
		HPI_AESEBURX_FORMAT, wFormat, 0);
}

#ifdef HPI_BUILD_INCLUDE_DEPRECATED
/* Deprecated version of function, don't document! */
hpi_err_t HPI_AESEBU_Receiver_SetSource(
	const hpi_hsubsys_t *phSubSys,	// Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	//Handle to control of type HPI_CONTROL_AESEBU_RECEIVER
	uint16_t wSource		// HPI_AESEBU_FORMAT_AESEBU or HPI_AESEBU_FORMAT_SPDIF
)
{
	return HPI_AESEBU_Receiver_SetFormat(phSubSys, hControl,
		wSource);
}
#endif

/** Gets the physical format of the digital audio input :
 * either the balanced, professional AES/EBU input
 * or the unbalanced, consumer S/PDIF input.
 * \note Not all audio adapters will have both kinds of inputs.
 * \return_hpierr
 */
hpi_err_t HPI_AESEBU_Receiver_GetFormat(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///<Handle to control of type HPI_CONTROL_AESEBU_RECEIVER.
	uint16_t *pwFormat		///< current format, one of the #HPI_AESEBU_FORMATS.
)
{
	uint16_t wErr;
	uint32_t dwParam;

	wErr = HPI_ControlParam1Get(phSubSys, hControl,
		HPI_AESEBURX_FORMAT, &dwParam);
	if (!wErr && pwFormat)
		*pwFormat = (uint16_t)dwParam;

	return wErr;
}

#ifndef HPI_OS_LINUX_KERNEL

#ifdef HPI_BUILD_INCLUDE_DEPRECATED
/* deprecated version of function */
hpi_err_t HPI_AESEBU_Receiver_GetSource(
	const hpi_hsubsys_t *phSubSys,	// Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	// Handle to control of type HPI_CONTROL_AESEBU_RECEIVER
	uint16_t *pwSource
)
{
	return HPI_AESEBU_Receiver_GetFormat(phSubSys, hControl,
		pwSource);
}
#endif

/** Returns the sample rate of the incoming AES/EBU digital audio stream
 * in *pdwSampleRate.
 * This information is obtained from the channel status bits in the
 * digital audio bitstream.
 * \return_hpierr
 * \retval HPI_ERROR_INVALID_OPERATION if PLL unlocked.
 */
hpi_err_t HPI_AESEBU_Receiver_GetSampleRate(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///<Handle to control of type HPI_CONTROL_AESEBU_RECEIVER
	uint32_t *pdwSampleRate	///< samplerate 0, 32000,44100 or 48000 (or x2, x4) returned
)
{
	return HPI_ControlParam1Get(phSubSys, hControl,
		HPI_AESEBURX_SAMPLERATE, pdwSampleRate);
}

/** Get one of 4 userdata bytes from the AES/EBU stream.
  * \return_hpierr
  */
hpi_err_t HPI_AESEBU_Receiver_GetUserData(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///<Handle to control of type HPI_CONTROL_AESEBU_RECEIVER.
	uint16_t wIndex,		///< byte index ranges from 0..3.
	uint16_t *pwData		///< returned user data.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE);
	if (hpi_handle_indexes(hControl, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.c.wAttribute = HPI_AESEBURX_USERDATA;
	hm.u.c.dwParam1 = wIndex;

	HPI_Message(&hm, &hr);

	if (pwData)
		*pwData = (uint16_t)hr.u.c.dwParam2;
	return hr.wError;
}

/** Get one of 24 channel status bytes from the AES/EBU stream.
  * \return_hpierr
  */
hpi_err_t HPI_AESEBU_Receiver_GetChannelStatus(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///<Handle to control of type HPI_CONTROL_AESEBU_RECEIVER.
	uint16_t wIndex,		///< byte index ranges from 0..23.
	uint16_t *pwData		///< returned channel status data.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE);
	if (hpi_handle_indexes(hControl, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.c.wAttribute = HPI_AESEBURX_CHANNELSTATUS;
	hm.u.c.dwParam1 = wIndex;

	HPI_Message(&hm, &hr);

	if (pwData)
		*pwData = (uint16_t)hr.u.c.dwParam2;
	return hr.wError;
}
#endif

/** Get error status from the AES/EBU stream
  * \return_hpierr
  */

hpi_err_t HPI_AESEBU_Receiver_GetErrorStatus(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///<Handle to control of type #HPI_CONTROL_AESEBU_RECEIVER.
	uint16_t *pwErrorData	///< returned error status bitfields defined by #HPI_AESEBU_ERRORS.
)
{
	uint32_t dwErrorData = 0;
	hpi_err_t err = 0;

	err = HPI_ControlParam1Get(phSubSys, hControl,
		HPI_AESEBURX_ERRORSTATUS, &dwErrorData);
	if (pwErrorData)
		*pwErrorData = (uint16_t)dwErrorData;
	return err;
}
/** @} */ /* defgroup aesrx */

/*=========================================================================*/
/** \defgroup aestx AES/EBU Digital audio transmitter control

The AESEBU transmitter transmits audio via a standard digital audio interface
(AESEBU or SPDIF).

\image html aesebu_transmitter.png

\{
*/
#ifndef HPI_OS_LINUX_KERNEL
/** Set the AES/EBU transmitters sample rate.
  * This is only valid if the source is the analog mixer
  * if the source is an outstream, then the samplerate will
  * be that of the outstream.
  * \return_hpierr
  * \note Not all audio adapters will have both kinds of inputs.
  */
hpi_err_t HPI_AESEBU_Transmitter_SetSampleRate(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///<Handle to control of type HPI_CONTROL_AESEBU_TRANSMITTER.
	uint32_t dwSampleRate	///< 32000, 44100 or 48000.
)
{
	return HPI_ControlParamSet(phSubSys, hControl,
		HPI_AESEBUTX_SAMPLERATE, dwSampleRate, 0);
}

/** Set one of 4 userdata bytes in the AES/EBU stream.
  * \return_hpierr
  */
hpi_err_t HPI_AESEBU_Transmitter_SetUserData(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///<Handle to control of type HPI_CONTROL_AESEBU_TRANSMITTER
	uint16_t wIndex,		///< byte index ranges from 0..3
	uint16_t wData		///< user data to set (? only byte values 0..255 allowed?)
)
{
	return HPI_ControlParamSet(phSubSys, hControl,
		HPI_AESEBUTX_USERDATA, wIndex, wData);
}

/** Set one of 24 channel status bytes in the AES/EBU stream.
  * \return_hpierr
  */
hpi_err_t HPI_AESEBU_Transmitter_SetChannelStatus(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///<Handle to control of type HPI_CONTROL_AESEBU_TRANSMITTER.
	uint16_t wIndex,		///< byte index ranges from 0..23.
	uint16_t wData		///< channel status data to write (? only byte values 0..255 allowed?).
)
{
	return HPI_ControlParamSet(phSubSys, hControl,
		HPI_AESEBUTX_CHANNELSTATUS, wIndex, wData);
}

/** Get a byte of channel status in the AES/EBU stream.
  * \warning Currently disabled pending debug of DSP code.
  * \return_hpierr
  * \retval Always HPI_ERROR_INVALID_OPERATION.
  */
hpi_err_t HPI_AESEBU_Transmitter_GetChannelStatus(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///<Handle to control of type HPI_CONTROL_AESEBU_TRANSMITTER.
	uint16_t wIndex,		///< byte index ranges from 0..23.
	uint16_t *pwData		///< read channel status data.
)
{
	HPI_UNUSED(phSubSys);
	HPI_UNUSED(hControl);
	HPI_UNUSED(wIndex);
	HPI_UNUSED(pwData);
	return HPI_ERROR_INVALID_OPERATION;
}

/** Query the formats supported by this AESEBU transmitter
Iterate dwIndex until an error is returned to get allowed values
for aesebu format in *pwFormat
*/
hpi_err_t HPI_AESEBU_Transmitter_QueryFormat(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	const hpi_handle_t hAesTx,	///< Control to query
	const uint32_t dwIndex,	///< Index for possible attribute values
	uint16_t *pwFormat	///< aesebu format one of the #HPI_AESEBU_FORMATS
)
{
	uint32_t qr;
	hpi_err_t err;

	err = HPI_ControlQuery(phSubSys, hAesTx, HPI_AESEBUTX_FORMAT,
				dwIndex, 0, &qr);
	*pwFormat = (uint16_t)qr;
	return err;
}
#endif

/** Set the output electrical format for the AESEBU transmitter.
  *
  * Some adapters only support one of the two formats (usually AESEBU).
  * \return_hpierr
  */
hpi_err_t HPI_AESEBU_Transmitter_SetFormat(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///<Handle to control of type HPI_CONTROL_AESEBU_TRANSMITTER.
	uint16_t wOutputFormat	///< one of the #HPI_AESEBU_FORMATS
)
{
	return HPI_ControlParamSet(phSubSys, hControl,
		HPI_AESEBUTX_FORMAT, wOutputFormat, 0);
}

/** Get the current output electrical format for the AESEBU transmitter.
  * \return_hpierr
  */
hpi_err_t HPI_AESEBU_Transmitter_GetFormat(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///<Handle to control of type HPI_CONTROL_AESEBU_TRANSMITTER.
	uint16_t *pwOutputFormat	///< Current format, one of the #HPI_AESEBU_FORMATS.
)
{
	uint16_t wErr;
	uint32_t dwParam;

	wErr = HPI_ControlParam1Get(phSubSys, hControl,
		HPI_AESEBUTX_FORMAT, &dwParam);
	if (!wErr && pwOutputFormat)
		*pwOutputFormat = (uint16_t)dwParam;

	return wErr;
}

/** @} */ /* defgroup aestx */

#ifndef HPI_OS_LINUX_KERNEL
/*=========================================================================*/
/** \defgroup bitstream Bitstream control
 Control synchronous bitstream I/O.  Only valid on ASI4346 adapters.
\{
*/
hpi_err_t HPI_Bitstream_SetClockEdge(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hControl,
	uint16_t wEdgeType
)
{
	return HPI_ControlParamSet(phSubSys, hControl,
		HPI_BITSTREAM_CLOCK_EDGE, wEdgeType, 0);
}

hpi_err_t HPI_Bitstream_SetDataPolarity(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hControl,
	uint16_t wPolarity
)
{
	return HPI_ControlParamSet(phSubSys, hControl,
		HPI_BITSTREAM_DATA_POLARITY, wPolarity, 0);
}

/**
 * Returns 2 indicative measurements of the incoming data stream.
 *
 * The clock input is deemed inactive if no data bytes are received within a
 * certain number of calls to a polling routine.
 * The time this takes varies according to the number of active streams.
 * If there is clock activity, the data activity indicator
 * is a sample of 16 bits from the incoming data.
 * If this is persistently 0 or 0xFFFF,
 * this may indicate that the data input is inactive.
 *
 * \return_hpierr
 */

hpi_err_t HPI_Bitstream_GetActivity(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///<Handle to bitstream control
	uint16_t *pwClkActivity,	///< 1==clock is active, 0==clock is inactive
	uint16_t *pwDataActivity	///< 1 word sampled from the incoming raw data
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE);
	if (hpi_handle_indexes(hControl, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.c.wAttribute = HPI_BITSTREAM_ACTIVITY;
	HPI_Message(&hm, &hr);
	if (pwClkActivity)
		*pwClkActivity = (uint16_t)hr.u.c.dwParam1;
	if (pwDataActivity)
		*pwDataActivity = (uint16_t)hr.u.c.dwParam2;
	return hr.wError;
}

/** @} */ /* defgroup bitstream */
#endif

/*=========================================================================*/
/** \defgroup channelmode Channel Mode control
A Channel Mode allows you to swap the left and right channels,
mix the left and right channels into the left or right channel only, or
send the left or right channels to both left and right.

@{
*/
/** Query the available channel modes for this control
Iterate dwIndex until an error is returned to get allowed values
for  channel mode in *pwMode
*/
hpi_err_t HPI_ChannelMode_QueryMode(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	const hpi_handle_t hMode,	///< Control to query
	const uint32_t dwIndex,	///< Index for possible attribute values
	uint16_t *pwMode	///< mode
)
{
	uint32_t qr;
	hpi_err_t err;

	err = HPI_ControlQuery(phSubSys, hMode, HPI_CHANNEL_MODE_MODE,
				dwIndex, 0, &qr);
	*pwMode = (uint16_t)qr;
	return err;
}

/** Set the channel mode
\return_hpierr
*/
hpi_err_t HPI_ChannelModeSet(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///<Handle of a Channel Mode control
	uint16_t wMode		///< One of the supported #HPI_CHANNEL_MODES
)
{
	return HPI_ControlParamSet(phSubSys, hControl,
		HPI_CHANNEL_MODE_MODE, wMode, 0);
}

/** Get the current channel mode
\return_hpierr
*/
hpi_err_t HPI_ChannelModeGet(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///<Handle of a Channel Mode control
	uint16_t *wMode		///< One of the supported #HPI_CHANNEL_MODES.
)
{
	uint32_t dwMode32 = 0;
	hpi_err_t err = HPI_ControlParam1Get(phSubSys, hControl,
		HPI_CHANNEL_MODE_MODE, &dwMode32);
	if (wMode)
		*wMode = (uint16_t)dwMode32;
	return err;
}

/** @} */ /* defgroup channelmode */
#ifndef HPI_OS_LINUX_KERNEL
/*=========================================================================*/
/** \defgroup cobranet Cobranet control
A cobranet adapter has one cobranet control for each cobranet interface
(usually only one).
The cobranet control is located on
(HPI_SOURCENODE_COBRANET,0,HPI_DESTNODE_COBRANET,0)
The cobranet control allows reading and writing of the cobranet HMI variables
(See Cirrus cobranet documentation @ www.cobranet.info for details)

@{
*/

/** Write to an HMI variable.
\return_hpierr
\retval HPI_ERROR_INVALID_CONTROL if type is not cobranet
\retval HPI_ERROR_INVALID_OPERATION if HMI variable is not writeable
\retval HPI_ERROR_INVALID_DATASIZE if requested size is greater than the HMI variable size
*/

hpi_err_t HPI_Cobranet_HmiWrite(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///<Handle of a Cobranet control
	uint32_t dwHmiAddress,	///< dwHmiAddress HMI address
	uint32_t dwByteCount,	///<Number of bytes to send to the control
	uint8_t *pbData		///<pointer to data to send
)
{
	struct hpi_msg_cobranet_hmiwrite hm;
	struct hpi_response_header hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponseV1(&hm.h, sizeof(hm), &hr, sizeof(hr),
				HPI_OBJ_CONTROL, HPI_CONTROL_SET_STATE);

	if (hpi_handle_indexes(hControl, &hm.h.wAdapterIndex, &hm.h.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	if (dwByteCount > sizeof(hm.bytes))
		return HPI_ERROR_MESSAGE_BUFFER_TOO_SMALL;

	hm.p.wAttribute = HPI_COBRANET_SET;
	hm.p.dwByteCount = dwByteCount;
	hm.p.dwHmiAddress = dwHmiAddress;
	memcpy(hm.bytes, pbData, dwByteCount);
	hm.h.wSize = (uint16_t)(sizeof(hm.h) + sizeof(hm.p) + dwByteCount);

	HPI_MessageV1(&hm.h, &hr);
	return hr.wError;
}

/** Read from an HMI variable.
\return_hpierr
\retval HPI_ERROR_INVALID_CONTROL if type is not cobranet

The amount of data returned will be the minimum of the input dwMaxByteCount
and the actual size of the variable reported by the HMI
*/
hpi_err_t HPI_Cobranet_HmiRead(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle of a cobranet control
	uint32_t dwHmiAddress,	///< HMI address
	uint32_t dwMaxByteCount,	///< maximum number of bytes to return (<= buffer size of pbData)
	uint32_t *pdwByteCount,	///< actual number of bytes returned
	uint8_t *pbData		///< data read from HMI variable
)
{
	struct hpi_msg_cobranet_hmiread hm;
	struct hpi_res_cobranet_hmiread hr;
	HPI_UNUSED(phSubSys);

	if (!dwMaxByteCount)
		return HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL;

	if (!pbData)
		return HPI_ERROR_MEMORY_ALLOC;

	HPI_InitMessageResponseV1(&hm.h, sizeof(hm), &hr.h, sizeof(hr),
				HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE);

	if (hpi_handle_indexes(hControl, &hm.h.wAdapterIndex, &hm.h.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	if (dwMaxByteCount > sizeof(hr.bytes))
		return HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL;

	hm.p.wAttribute = HPI_COBRANET_GET;
	hm.p.dwByteCount = dwMaxByteCount;
	hm.p.dwHmiAddress = dwHmiAddress;

	HPI_MessageV1(&hm.h, &hr.h);

	if (!hr.h.wError && pbData) {
		if (hr.dwByteCount > sizeof(hr.bytes))
			/* shoud not happen! */
			return HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL;

		*pdwByteCount = hr.dwByteCount;

		if (hr.dwByteCount < dwMaxByteCount)
			dwMaxByteCount = *pdwByteCount;

		memcpy(pbData, hr.bytes, dwMaxByteCount);
	}
	return hr.h.wError;
}

/** Get the status of the last cobranet operation
  * \return_hpierr
  */
hpi_err_t HPI_Cobranet_HmiGetStatus(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle of a cobranet control
	uint32_t *pdwStatus,	///< the raw status word from the HMI
	uint32_t *pdwReadableSize,	///< the reported readable size from the last variable access
	uint32_t *pdwWriteableSize	///< the reported writeable size from the last variable access
)
{
	struct hpi_message hm;
	struct hpi_response hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr,
				HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE);
	if (hpi_handle_indexes(hControl, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	hm.u.c.wAttribute = HPI_COBRANET_GET_STATUS;

	HPI_Message(&hm, &hr);
	if (!hr.wError) {
		if (pdwStatus)
			*pdwStatus = hr.u.cu.cobranet.status.dwStatus;
		if (pdwReadableSize)
			*pdwReadableSize =
				hr.u.cu.cobranet.status.dwReadableSize;
		if (pdwWriteableSize)
			*pdwWriteableSize =
				hr.u.cu.cobranet.status.dwWriteableSize;
	}
	return hr.wError;
}

/** Get the CobraNet node's current IP address.
  * Allows the user to get the current IP address of the CobraNet node.
  * \return_hpierr
  * \retval HPI_ERROR_INVALID_CONTROL if type is not cobranet
  */
hpi_err_t HPI_Cobranet_GetIPaddress(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle of a cobranet control
	uint32_t *pdwIPaddress	///< the current IP address
)
{
	uint32_t dwByteCount;
	uint32_t dwIP;
	hpi_err_t err;
	HPI_UNUSED(phSubSys);

	err = HPI_Cobranet_HmiRead(phSubSys, hControl,
		HPI_COBRANET_HMI_cobraIpMonCurrentIP,
		4, &dwByteCount, (uint8_t *)&dwIP);
	// byte swap the IP address
	*pdwIPaddress =
		((dwIP & 0xff000000) >> 8) |
		((dwIP & 0x00ff0000) << 8) |
		((dwIP & 0x0000ff00) >> 8) | ((dwIP & 0x000000ff) << 8);

	if (err)
		*pdwIPaddress = 0;

	return err;

}

/** Set the CobraNet node's current IP address.
  * Allows the user to set the current IP address of the CobraNet node.
  * \return_hpierr
  * \retval HPI_ERROR_INVALID_CONTROL if type is not cobranet
  */
hpi_err_t HPI_Cobranet_SetIPaddress(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle of a cobranet control
	uint32_t dwIPaddress	///< the new current IP address
)
{
	uint32_t dwIP;
	hpi_err_t err;
	HPI_UNUSED(phSubSys);

	// byte swap the IP address
	dwIP = ((dwIPaddress & 0xff000000) >> 8) |
		((dwIPaddress & 0x00ff0000) << 8) |
		((dwIPaddress & 0x0000ff00) >> 8) |
		((dwIPaddress & 0x000000ff) << 8);

	err = HPI_Cobranet_HmiWrite(phSubSys, hControl,
		HPI_COBRANET_HMI_cobraIpMonCurrentIP, 4, (uint8_t *)&dwIP);

	return err;

}

/** Get the CobraNet node's static IP address.
\return_hpierr
\retval HPI_ERROR_INVALID_CONTROL if type is not cobranet

Allows the user to get the static IP address of the CobraNet node.
*/
hpi_err_t HPI_Cobranet_GetStaticIPaddress(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle of a cobranet control
	uint32_t *pdwIPaddress	///< the static IP address
)
{
	uint32_t dwByteCount;
	uint32_t dwIP;
	hpi_err_t err;
	HPI_UNUSED(phSubSys);
	err = HPI_Cobranet_HmiRead(phSubSys, hControl,
		HPI_COBRANET_HMI_cobraIpMonStaticIP,
		4, &dwByteCount, (uint8_t *)&dwIP);
	// byte swap the IP address
	*pdwIPaddress =
		((dwIP & 0xff000000) >> 8) |
		((dwIP & 0x00ff0000) << 8) |
		((dwIP & 0x0000ff00) >> 8) | ((dwIP & 0x000000ff) << 8);

	if (err)
		*pdwIPaddress = 0;

	return err;

}

/** Set the CobraNet node's static IP address.
\return_hpierr
\retval HPI_ERROR_INVALID_CONTROL if type is not cobranet

Allows the user to set the static IP address of the CobraNet node.
*/
hpi_err_t HPI_Cobranet_SetStaticIPaddress(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle of a cobranet control
	uint32_t dwIPaddress	///< the new static IP address
)
{
	uint32_t dwIP;
	hpi_err_t err;
	HPI_UNUSED(phSubSys);

	// byte swap the IP address
	dwIP = ((dwIPaddress & 0xff000000) >> 8) |
		((dwIPaddress & 0x00ff0000) << 8) |
		((dwIPaddress & 0x0000ff00) >> 8) |
		((dwIPaddress & 0x000000ff) << 8);

	err = HPI_Cobranet_HmiWrite(phSubSys, hControl,
		HPI_COBRANET_HMI_cobraIpMonStaticIP, 4, (uint8_t *)&dwIP);

	return err;

}

/** Get the CobraNet node's MAC address.
\return_hpierr
\retval HPI_ERROR_INVALID_CONTROL if type is not cobranet

Allows the user to get the MAC address of the CobraNet node.
*/
hpi_err_t HPI_Cobranet_GetMACaddress(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle of a cobranet control
	uint32_t *pdwMAC_MSBs,	///< the first 4 bytes of the MAC address.
	uint32_t *pdwMAC_LSBs	///< the last 2 bytes of the MAC address returned in the upper 2 bytes.
)
{
	uint32_t dwByteCount;
	hpi_err_t err;
	uint32_t mac;
	HPI_UNUSED(phSubSys);

	err = HPI_Cobranet_HmiRead(phSubSys, hControl,
		HPI_COBRANET_HMI_cobraIfPhyAddress,
		4, &dwByteCount, (uint8_t *)&mac);

	if (!err) {
		*pdwMAC_MSBs =
			((mac & 0xff000000) >> 8) |
			((mac & 0x00ff0000) << 8) |
			((mac & 0x0000ff00) >> 8) | ((mac & 0x000000ff) << 8);

		err = HPI_Cobranet_HmiRead(phSubSys, hControl,
			HPI_COBRANET_HMI_cobraIfPhyAddress + 1,
			4, &dwByteCount, (uint8_t *)&mac);
	}

	if (!err) {
		*pdwMAC_LSBs =
			((mac & 0xff000000) >> 8) |
			((mac & 0x00ff0000) << 8) |
			((mac & 0x0000ff00) >> 8) | ((mac & 0x000000ff) << 8);
	} else {
		*pdwMAC_MSBs = 0;
		*pdwMAC_LSBs = 0;
	}

	return err;
}

/** @} */ /* defgroup cobranet */
#endif
#ifndef HPI_OS_LINUX_KERNEL
/*=========================================================================*/
/** \defgroup compand  Compressor Expander control
\{
The compander multiplies its input signal by a factor that is dependent on the
amplitude of that signal.
*/

#ifdef HPI_BUILD_INCLUDE_DEPRECATED
/** Set up a compressor expander
	\return_hpierr

	\deprecated Please use individual HPI_Compander functions instead
*/
hpi_err_t HPI_Compander_Set(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Compander control handle
	uint16_t wAttack,		///< attack time in milliseconds
	uint16_t wDecay,		///< decay time in milliseconds
	short wRatio100,	///< gain ratio * 100
	short nThreshold0_01dB,	///< threshold in 100ths of a dB
	short nMakeupGain0_01dB	///< makeup gain in 100ths of a dB
)
{
	hpi_err_t err;
	int index = 1;
	HPI_UNUSED(phSubSys);

	if (!wRatio100 || (wRatio100 == 100))
		return HPI_Compander_SetEnable(phSubSys, hControl, HPI_SWITCH_OFF);

	err = HPI_Compander_SetEnable(phSubSys, hControl, HPI_SWITCH_ON);
	if (!err)
		err = HPI_Compander_SetMakeupGain(phSubSys, hControl, nMakeupGain0_01dB);

	if (wRatio100 < 0) { // setting the noisegate part
		wRatio100 = -wRatio100;
		index = 0;
	}
	if (!err)
		err = HPI_Compander_SetRatio(phSubSys, hControl, index, wRatio100);
	if (!err) // disable the other part
		err = HPI_Compander_SetRatio(phSubSys, hControl, (1 - index), 100);
	if (!err)
		err = HPI_Compander_SetThreshold(phSubSys, hControl, index, nThreshold0_01dB);

	if (!err) {
		err = HPI_Compander_SetAttackTimeConstant(phSubSys, hControl, index, wAttack);
		err = HPI_Compander_SetDecayTimeConstant(phSubSys, hControl, index, wDecay);
	}
	return err;
}

/*! Get the settings of a compressor expander
	\return_hpierr

	\deprecated Please use individual HPI_Compander functions instead
*/
hpi_err_t HPI_Compander_Get(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Compander control handle
	uint16_t *pwAttack,		///<attack time in milliseconds
	uint16_t *pwDecay,		///<decay time in milliseconds
	short *pwRatio100,	///<gain ratio * 100
	short *pnThreshold0_01dB,	///<threshold in 100ths of a dB
	short *pnMakeupGain0_01dB	///<makeup gain in 100ths of a dB
)
{
	uint32_t enable;
	hpi_err_t err;
	uint32_t a,d,r;
	short ratio = 0;
	int index = 1;
	HPI_UNUSED(phSubSys);

	err = HPI_Compander_GetEnable(phSubSys, hControl, &enable);
	if (err)
		return err;

	err = HPI_Compander_GetMakeupGain(phSubSys, hControl, pnMakeupGain0_01dB);
	err = HPI_Compander_GetRatio(phSubSys, hControl, index, &r);
	ratio = (short)r;
	if (r == 100) { // compressor is disabled, try the noisegate
		index = 0;
		err = HPI_Compander_GetRatio(phSubSys, hControl, 0, &r);
		ratio = -(short)r;
	}
	err = HPI_Compander_GetThreshold(phSubSys, hControl, index, pnThreshold0_01dB);
	err = HPI_Compander_GetAttackTimeConstant(phSubSys, hControl, index, &a);
	err = HPI_Compander_GetDecayTimeConstant(phSubSys, hControl, index, &d);
	*pwAttack = (uint16_t)a;
	*pwDecay = (uint16_t)d;

	if (!enable || (r == 100))
		ratio = 0;
	*pwRatio100 = ratio;
	return err;
}
#endif

/** Enable or disable the compander.
 */
hpi_err_t HPI_Compander_SetEnable(
	const hpi_hsubsys_t *phSubSys, ///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl, ///< Compander control handle
	uint32_t enable ///< 1=enable, 0=disable
)
{
	return HPI_ControlParamSet(phSubSys, hControl,
				HPI_GENERIC_ENABLE, enable, 0);
}

/** Get the enable state of the compander.
 */
hpi_err_t HPI_Compander_GetEnable(
	const hpi_hsubsys_t *phSubSys, ///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,///< Compander control handle
	uint32_t *enable ///< 1=enabled, 0=disabled

)
{
	return HPI_ControlParam1Get(phSubSys, hControl,
				HPI_GENERIC_ENABLE, enable);
}


/** Set the makeup gain of a compander control.

The makeup gain is applied independent of signal level, usually to
compensate for level loss from compression.
 */
hpi_err_t HPI_Compander_SetMakeupGain(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Compander control handle
	short nMakeupGain0_01dB	///< makeup gain in 100ths of a dB
)
{
	HPI_UNUSED(phSubSys);
	return HPI_ControlLogSet2(hControl, HPI_COMPANDER_MAKEUPGAIN,
				  nMakeupGain0_01dB, 0);
}
/** Get the makeup gain of a compander control. */
hpi_err_t HPI_Compander_GetMakeupGain(
	const hpi_hsubsys_t *phSubSys, ///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Compander control handle
	short *nMakeupGain0_01dB	///< makeup gain in 100ths of a dB
)
{
	return HPI_ControlLogGet2(phSubSys, hControl, HPI_COMPANDER_MAKEUPGAIN,
				  nMakeupGain0_01dB, NULL);
}

/** Set the attack timeconstants.

The attack timeconstant applies to the RMS detector on the
compander or noise gate input.
 */
hpi_err_t HPI_Compander_SetAttackTimeConstant(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Compander control handle
	unsigned int index,	///< index of knee point
	uint32_t wAttack		///< attack time in milliseconds
)
{
	return HPI_ControlParamSet(phSubSys, hControl, HPI_COMPANDER_ATTACK,
		wAttack, index);
}

/** Get the compander attack timeconstant
 */
hpi_err_t HPI_Compander_GetAttackTimeConstant(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Compander control handle
	unsigned int index,	///< index of knee point
	uint32_t *attack		///< attack time in milliseconds
)
{
	/* Note, the index is passed and returned in dwParam2
	   Here we are not interested in the returned value,
	   but it is required for control save/restore on 2416 */
	return HPI_ControlParamGet(phSubSys, hControl, HPI_COMPANDER_ATTACK,
				   0, index, attack, NULL);
}

/** Set the decay timeconstants.

The decay timeconstant applies to the RMS detector on the
compander or noise gate input.
 */
hpi_err_t HPI_Compander_SetDecayTimeConstant(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Compander control handle
	unsigned int index,	///< index of knee point
	uint32_t decay		///< decay time in milliseconds
)
{
	return HPI_ControlParamSet(phSubSys, hControl, HPI_COMPANDER_DECAY,
		decay, index);
}

/** Get the compander attack timeconstant
 */
hpi_err_t HPI_Compander_GetDecayTimeConstant(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Compander control handle
	unsigned int index,	///< index of knee point
	uint32_t *decay		///< decay time in milliseconds
)
{
	return HPI_ControlParamGet(phSubSys, hControl, HPI_COMPANDER_DECAY,
				   0, index, decay, NULL);

}

/** Set the threshold for one of the compander knee points.

When index==0, the ratio is applied below the set threshold,
otherwise the ratio is applied above the set threshold and
below threshold with higher index (if any).

The threshold for index 0 must be less than that for index 1 etc.

/retval #HPI_ERROR_INVALID_CONTROL_VALUE threshold outside valid range -100 to 0dB
/retval #HPI_ERROR_INVALID_OPERATION trying to set threshold 0 > threshold 1
*/
hpi_err_t HPI_Compander_SetThreshold(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Compander control handle
	unsigned int index,	///< index of knee point
	short nThreshold0_01dB	///< threshold in 100ths of a dB
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_SET_STATE);
	if (hpi_handle_indexes(hControl, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.c.wAttribute = HPI_COMPANDER_THRESHOLD;
	hm.u.c.dwParam2 = index;
	hm.u.c.anLogValue[0] = nThreshold0_01dB;

	HPI_Message(&hm, &hr);

	return hr.wError;
}

/** Get the threshold for one of the compander knee points.
 */
hpi_err_t HPI_Compander_GetThreshold(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Compander control handle
	unsigned int index,	///< index of
	short *nThreshold0_01dB	///< threshold in 100ths of a dB
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE);
	if (hpi_handle_indexes(hControl, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.c.wAttribute = HPI_COMPANDER_THRESHOLD;
	hm.u.c.dwParam2 = index;

	HPI_Message(&hm, &hr);
	*nThreshold0_01dB = hr.u.c.anLogValue[0] ;

	return hr.wError;
}

/** Set the ratio for one of the compander knee points

The actual ratio R is wRatio100/100.0
if index==0,
  for every R dB below the threshold, the output decreases by 1dB,
else
  for every R dB above the threshold, the output increases by 1dB
*/
hpi_err_t HPI_Compander_SetRatio(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Compander control handle
	uint32_t index,	///< index of knee point
	uint32_t wRatio100	///< gain ratio * 100
)
{
	return HPI_ControlParamSet(phSubSys, hControl, HPI_COMPANDER_RATIO,
				   wRatio100, index);
}

/** Set the ratio for one of the compander knee points
 */
hpi_err_t HPI_Compander_GetRatio(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Compander control handle
	uint32_t index,	///< index of
	uint32_t *wRatio100	///< gain ratio * 100
)
{
	return HPI_ControlParamGet(phSubSys, hControl, HPI_COMPANDER_RATIO,
				   0, index, wRatio100, NULL);
}
/** @} */ /* defgroup compand */
#endif

/*=========================================================================*/
/** \defgroup level Level Control
 * The level control value determines the full scale reference level (also known as
 * trim) of an analog input or output.
 *
 * This is the signal level that corresponds to digital full scale (0dB peak).
 *
 * The level control value is set in units of 0.01dBu (0dBu = 0.775 VRMS).
 * For example a value of 1400 is 14dBu.
 *
 * \note The level control is not a gain control. In particular, increasing an
 * input level reduces the apparent input signal.
 *
 * Control type: #HPI_CONTROL_LEVEL
 *
 * Found on node types: #HPI_SOURCENODE_LINEIN, #HPI_SOURCENODE_ANALOG,
 * #HPI_SOURCENODE_MICROPHONE, #HPI_DESTNODE_LINEOUT, #HPI_DESTNODE_ANALOG,
 * \{
 */

/** Query the allowed values of a level control.
  * Gets the minimum, maximum and step of the specified level control.
  * \return_hpierr
  */
hpi_err_t HPI_LevelQueryRange(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to level control.
	short *nMinGain_01dB,	///< Minimum level setting in 100ths of a dBu.
	short *nMaxGain_01dB,	///< Maximum level setting in 100ths of a dBu.
	short *nStepGain_01dB	///< Step size in 100ths of a dBu.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE);
	if (hpi_handle_indexes(hControl, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.c.wAttribute = HPI_LEVEL_RANGE;

	HPI_Message(&hm, &hr);
	if (hr.wError) {
		hr.u.c.anLogValue[0] = 0;
		hr.u.c.anLogValue[1] = 0;
		hr.u.c.dwParam1 = 0;
	}
	if (nMinGain_01dB)
		*nMinGain_01dB = hr.u.c.anLogValue[0];
	if (nMaxGain_01dB)
		*nMaxGain_01dB = hr.u.c.anLogValue[1];
	if (nStepGain_01dB)
		*nStepGain_01dB = (short)hr.u.c.dwParam1;
	return hr.wError;
}


/** Sets the value of a level control.
 * \return_hpierr
 */
hpi_err_t HPI_LevelSetGain(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to control of type #HPI_CONTROL_LEVEL.
	short anGain0_01dB[HPI_MAX_CHANNELS]
       /**< Array containing the stereo level control value.
	Index 0 is the left channel and index 1 is the right channel. */
)
{
	HPI_UNUSED(phSubSys);
	return HPI_ControlLogSet2(hControl, HPI_LEVEL_GAIN,
				  anGain0_01dB[0], anGain0_01dB[1]);
}

/** Gets the value of a level control.
 * \return_hpierr
 */

hpi_err_t HPI_LevelGetGain(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to control of type #HPI_CONTROL_LEVEL.
	short anGain0_01dB[HPI_MAX_CHANNELS]
       /**< Array containing the stereo level control value.
	Index 0 is the left channel and index 1 is the right channel. */
)
{
	return HPI_ControlLogGet2(phSubSys, hControl, HPI_LEVEL_GAIN,
			   &anGain0_01dB[0], &anGain0_01dB[1]);
}
/** @} */ /* defgroup level */

/*=========================================================================*/
/** \defgroup meter  Meter Control
 The meter control returns information about the level of the audio signal
 at the position of the control.

Depending on the adapter, Peak and RMS readings are available.
On some adapters meter ballistics may be set (on older adapters the meter gives
an instantaneous reading)

Readings are in units of 0.01dB

\todo Implement linear to log conversion in fixed point math so that it can be
included with kernel code.

@{
*/

/** Get the number of channels supported by this meter.
*/
hpi_err_t HPI_Meter_QueryChannels(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	const hpi_handle_t hMeter,	///< Control to query
	uint32_t *pChannels	///< number of channels
)
{
	return HPI_ControlQuery(phSubSys, hMeter,  HPI_METER_NUM_CHANNELS,
				0, 0, pChannels);
}
/** Get the meter peak reading
  * \return_hpierr
  */
hpi_err_t HPI_MeterGetPeak(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< meter control handle
	short anPeakdB[HPI_MAX_CHANNELS]	///< meter peaks in millibels
)
{
#ifndef HPI_BUILD_KERNEL_MODE
	short nLinear = 0;
#endif
	short i = 0;

	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE);
	if (hpi_handle_indexes(hControl, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.wObjIndex = hm.wObjIndex;
	hm.u.c.wAttribute = HPI_METER_PEAK;

	HPI_Message(&hm, &hr);

	if (!hr.wError)
#ifndef HPI_BUILD_KERNEL_MODE
	{
		// Method to return meter values from DSP
		// If return value is -ve (-1 to -32767) then treat as Log(peak)
		// range of log values will be -1 to -20000 (-0.01 to -200.00 dB)
		// 0 will never be returned for log (-1 = -0.01dB is max)

		/// \bug Kernel mode cant do floating point log.
		/// \bug +ve linear value is returned instead.

		// convert 0..32767 level to 0.01dB (20log10), 0 is -100.00dB
		for (i = 0; i < HPI_MAX_CHANNELS; i++) {
			nLinear = hr.u.c.anLogValue[i];
			// don't have to touch the LogValue when it is -ve
			// since it is already a log value
			if (nLinear >= 0) {
				if (nLinear == 0)
					hr.u.c.anLogValue[i] =
						HPI_METER_MINIMUM;
				else
					hr.u.c.anLogValue[i] = (short)((float)(20 * log10((float)nLinear / 32767.0)) * 100.0);	// units are 0.01dB
			}
		}
		memcpy(anPeakdB, hr.u.c.anLogValue,
			sizeof(short) * HPI_MAX_CHANNELS);
	}
#else
		memcpy(anPeakdB, hr.u.c.anLogValue,
			sizeof(short) * HPI_MAX_CHANNELS);
#endif
	else
		for (i = 0; i < HPI_MAX_CHANNELS; i++)
			anPeakdB[i] = HPI_METER_MINIMUM;
	return hr.wError;
}

#ifndef HPI_OS_LINUX_KERNEL
/** Get the meter RMS reading in 100ths of a dB
  * \return_hpierr
  */
hpi_err_t HPI_MeterGetRms(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< meter control handle
	short anRmsdB[HPI_MAX_CHANNELS]	///< meter RMS values in millibels
)
{
#ifndef HPI_BUILD_KERNEL_MODE
	short nLinear = 0;
#endif
	short i = 0;

	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE);
	if (hpi_handle_indexes(hControl, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.c.wAttribute = HPI_METER_RMS;

	HPI_Message(&hm, &hr);

	if (!hr.wError)
#ifndef HPI_BUILD_KERNEL_MODE
	{
		/// \bug Kernel mode cant do floating point log.  +ve linear value is returned instead.
		// convert 0..32767 level to 0.01dB (20log10), 0 is -100.00dB
		for (i = 0; i < HPI_MAX_CHANNELS; i++) {
			nLinear = hr.u.c.anLogValue[i];
			// don't have to touch the LogValue when it is -ve since it is already a log value
			if (nLinear >= 0) {
				if (nLinear == 0)
					hr.u.c.anLogValue[i] =
						HPI_METER_MINIMUM;
				else
					hr.u.c.anLogValue[i] = (short)((float)(20 * log10((float)nLinear / 32767.0)) * 100.0);	// units are 0.01dB
			}
		}
		memcpy(anRmsdB, hr.u.c.anLogValue,
			sizeof(short) * HPI_MAX_CHANNELS);
	}
#else
	memcpy(anRmsdB, hr.u.c.anLogValue,
			sizeof(short) * HPI_MAX_CHANNELS);
#endif
	else
		for (i = 0; i < HPI_MAX_CHANNELS; i++)
			anRmsdB[i] = HPI_METER_MINIMUM;	// return -100dB in case function is not supported.

	return hr.wError;
}

/** Set the ballistics of the RMS part of a meter.

The attack and decay values represent the time constants of the equivalent
single pole low pass filter used to create the ballistics.
With a time constant of T, if the meter is stable at full scale and the input
is suddenly removed, the meter will decay.

Setting nAttack to 0 gives the meter instantaneous rise time.
Setting nDecay to a value smaller than a few times your  meter polling interval
is not advised.
The meter will appear to read something approaching the instantaneous value at
the time of polling rather than the maximum peak since the previous reading.

Driver versions up to and including version 4.04.xx implement a single global ballistics
setting for all meters, i.e. if you change the ballistics on one meter, the ballistics
on all meters are updated. Driver versions in the 4.05.xx series and later implement
independent ballistics for each meter.

\return_hpierr
*/
hpi_err_t HPI_MeterSetRmsBallistics(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to control of type HPI_CONTROL_METER
	uint16_t nAttack,	///< Attack timeconstant in milliseconds
	uint16_t nDecay	///< Decay timeconstant in milliseconds
)
{
	return HPI_ControlParamSet(phSubSys, hControl,
		HPI_METER_RMS_BALLISTICS, nAttack, nDecay);
}

/** Get the ballistics settings of the RMS part of a meter.
\return_hpierr
*/
hpi_err_t HPI_MeterGetRmsBallistics(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to control of type HPI_CONTROL_METER
	uint16_t *pnAttack,	///< Attack timeconstant in milliseconds
	uint16_t *pnDecay	///< Decay timeconstant in milliseconds
)
{
	uint32_t dwAttack;
	uint32_t dwDecay;
	uint16_t nError;

	nError = HPI_ControlParam2Get(phSubSys, hControl,
		HPI_METER_RMS_BALLISTICS, &dwAttack, &dwDecay);

	if (pnAttack)
		*pnAttack = (unsigned short)dwAttack;
	if (pnDecay)
		*pnDecay = (unsigned short)dwDecay;

	return nError;
}

/** Set the ballistics of the Peak part of a meter.
\return_hpierr
*/
hpi_err_t HPI_MeterSetPeakBallistics(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to control of type HPI_CONTROL_METER
	uint16_t nAttack,	///< Attack timeconstant in milliseconds
	uint16_t nDecay	///< Decay timeconstant in milliseconds
)
{
	return HPI_ControlParamSet(phSubSys, hControl,
		HPI_METER_PEAK_BALLISTICS, nAttack, nDecay);
}

/** Get the ballistics settings of the Peak part of a meter.
\return_hpierr
*/
hpi_err_t HPI_MeterGetPeakBallistics(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to control of type HPI_CONTROL_METER
	uint16_t *pnAttack,	///< Attack timeconstant in milliseconds
	uint16_t *pnDecay	///< Decay timeconstant in milliseconds
)
{
	uint32_t dwAttack;
	uint32_t dwDecay;
	uint16_t nError;

	nError = HPI_ControlParam2Get(phSubSys, hControl,
		HPI_METER_PEAK_BALLISTICS, &dwAttack, &dwDecay);

	if (pnAttack)
		*pnAttack = (short)dwAttack;
	if (pnDecay)
		*pnDecay = (short)dwDecay;

	return nError;
}

/** @} */ /* defgroup meter */

/* future function to convert linear to log using integers

long lLogC[16] = {
5184138,
-95824768,
-196833680,
-297842592,
-398851488,
-499860384,
-600869312,
-701878208,
-802887104,
-903896000,
-1004904896,
-1105913856,
-1206922752,
-1307931648,
-1408940544,
30};
double __fastcall TFLogDemo::LinearToLogQuadApproxInt(short nPeak)
{
     short   nBeforeFirstBit;
     short   nR;
     short nSample;
     long    lAcc;

     nBeforeFirstBit = 0;
     nSample=nPeak;
     while (!(nSample&0x8000))
     {
	  nSample <<= 1;
	  nBeforeFirstBit++;
     }
     nSample &= ~0x8000;
     nR = nBeforeFirstBit;
     lAcc = (long)nSample * -1047L;  // Q15 * Q9 = Q24
     lAcc = lAcc + 135336768L;
     lAcc = lAcc >> 15;              // Q24 > Q9
     lAcc = lAcc * (long)nSample;    // Q9 * Q15 = Q24
     lAcc = lAcc + lLogC[nR];        // lAcc has result in Q24
     // lAcc = (lAcc>>8)*100;        // result is 100ths of dB in Q16 format
     // dB100 = (short)(lAcc>>16L);
     return (double)lAcc / pow(2.0,24.0);
}
*/
#endif

#ifndef HPI_OS_LINUX_KERNEL
/*=========================================================================*/
/** \defgroup mic Microphone control
A Microphone control of type HPI_CONTROL_MICROPHONE is always located on a
source node of type HPI_SOURCE_NODE_MICROPHONE.
This node type receives an audio signal from a microphone.
If the microphone has adjustable gain, then a VOLUME control will also be
present on the node. Currently the Microphone
control is only used to turn on/off the microphone's phantom power.
\{
*/

/** Sets the microphone phantom power on or off.
\return_hpierr
*/
hpi_err_t HPI_Microphone_SetPhantomPower(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Control handle to type HPI_CONTROL_MICROPHONE
	uint16_t wOnOff		///< Should be set to 1 to turn on the microphone phantom power and 0 to turn it off.
)
{
	return HPI_ControlParamSet(phSubSys, hControl,
		HPI_MICROPHONE_PHANTOM_POWER, (uint32_t)wOnOff, 0);
}

/** Gets the current microphone phantom power setting.
\return_hpierr
*/
hpi_err_t HPI_Microphone_GetPhantomPower(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Control handle to type HPI_CONTROL_MICROPHONE
	uint16_t *pwOnOff		///< Returns 1 if the microphone phantom power is on, 0 if off.
)
{
	uint16_t nError = 0;
	uint32_t dwOnOff = 0;
	nError = HPI_ControlParam1Get(phSubSys, hControl,
		HPI_MICROPHONE_PHANTOM_POWER, &dwOnOff);
	if (pwOnOff)
		*pwOnOff = (uint16_t)dwOnOff;
	return nError;
}

/** @} */ /* defgroup mic */
#endif
/*=========================================================================*/
/** \defgroup mux Multiplexer control
 This control allows one of many sources to be connected to a destination
 Typically used on the instream (record) side to select a record input
or on the linein to select analog or digital input.
\{
*/

/** Set the signal source that the multiplexer will send to the destination
\return_hpierr
*/
hpi_err_t HPI_Multiplexer_SetSource(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Control handle to type HPI_CONTROL_MULTIPLEXER
	uint16_t wSourceNodeType,	///< source node type - one of #HPI_SOURCENODES
	uint16_t wSourceNodeIndex	///< a 0 based index
)
{
	return HPI_ControlParamSet(phSubSys, hControl,
		HPI_MULTIPLEXER_SOURCE, wSourceNodeType, wSourceNodeIndex);
}

/** Get the signal source that the multiplexer is currently connected to
\return_hpierr
*/
hpi_err_t HPI_Multiplexer_GetSource(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Control handle to type HPI_CONTROL_MULTIPLEXER
	uint16_t *wSourceNodeType,	///< source node type - one of #HPI_SOURCENODES
	uint16_t *wSourceNodeIndex	///< a 0 based index
)
{
	uint32_t dwNode, dwIndex;
	hpi_err_t err = HPI_ControlParam2Get(phSubSys, hControl,
		HPI_MULTIPLEXER_SOURCE, &dwNode,
		&dwIndex);
	if (wSourceNodeType)
		*wSourceNodeType = (uint16_t)dwNode;
	if (wSourceNodeIndex)
		*wSourceNodeIndex = (uint16_t)dwIndex;
	return err;
}

/** Establish valid source node settings for this multiplexer.
  * Call with wIndex starting at zero, incrementing until a non-zero return
  * value indicates that the current wIndex is invalid, so querying should be
 * terminated.
  * \return_hpierr
*/
hpi_err_t HPI_Multiplexer_QuerySource(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Control handle to type HPI_CONTROL_MULTIPLEXER
	uint16_t wIndex,		///< a 0 based index
	uint16_t *wSourceNodeType,	///< source node type - one of #HPI_SOURCENODES
	uint16_t *wSourceNodeIndex	///< a 0 based index
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE);
	if (hpi_handle_indexes(hControl, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.c.wAttribute = HPI_MULTIPLEXER_QUERYSOURCE;
	hm.u.c.dwParam1 = wIndex;

	HPI_Message(&hm, &hr);

	if (wSourceNodeType)
		*wSourceNodeType = (uint16_t)hr.u.c.dwParam1;
	if (wSourceNodeIndex)
		*wSourceNodeIndex = (uint16_t)hr.u.c.dwParam2;
	return hr.wError;
}
/** @} */ /* defgroup mux */

#ifndef HPI_OS_LINUX_KERNEL
/*=========================================================================*/
/**\addtogroup parmeq  Parametric Equalizer control

  The parametric equalizer control consists of a series of filters that are
  applied successively to the signal. The number of filters available is
  obtained by calling HPI_ParametricEQ_GetInfo(), then the characteristics
  of each filter are configured using HPI_ParametricEQ_SetBand().

  The equalizer as a whole can be turned on and off using
  HPI_ParametricEQ_SetState().
  Filters can still be set up when the equalizer is switched off.

  Equalizers are typically located on a LineIn input node or an OutStream node.

  Obtain a control handle to an equalizer like this:

\code
err = HPI_MixerGetControl(
		phSubSys,
		hMixer,
		HPI_SOURCENODE_LINEIN, 0,
		0,0,  // No destination node
		HPI_CONTROL_PARAMETRIC_EQ,
		&hControl
		);
\endcode
\{
*/

/** Find out the number of available bands of a parametric equalizer,
and whether it is enabled or not.
  * \return_hpierr
  */
hpi_err_t HPI_ParametricEQ_GetInfo(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Equalizer control handle.
	uint16_t *pwNumberOfBands,	///< Returned number of bands available.
	uint16_t *pwOnOff		///< Returned enabled status. 1 indicates enabled and 0 indicates disabled.
)
{
	uint32_t dwNOB = 0;
	uint32_t dwOO = 0;
	uint16_t nError = 0;

	nError = HPI_ControlParam2Get(phSubSys, hControl,
		HPI_EQUALIZER_NUM_FILTERS, &dwOO, &dwNOB);
	if (pwNumberOfBands)
		*pwNumberOfBands = (uint16_t)dwNOB;
	if (pwOnOff)
		*pwOnOff = (uint16_t)dwOO;
	return nError;
}

/**	Turn a parametric equalizer on or off.
  *\return_hpierr
  */
hpi_err_t HPI_ParametricEQ_SetState(
	const hpi_hsubsys_t *phSubSys,	///<  Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///<  Equalizer control handle
	uint16_t wOnOff		///<  State setting, one of the #HPI_SWITCH_STATES
)
{
	return HPI_ControlParamSet(phSubSys,
		hControl, HPI_EQUALIZER_NUM_FILTERS, wOnOff, 0);
}

/** Get the settings of one of the filters in a parametric equalizer.
See HPI_ParametricEQ_SetBand() for details of parameter interpretation.
\return_hpierr
*/
hpi_err_t HPI_ParametricEQ_GetBand(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Equalizer control handle
	uint16_t wIndex,		///< Index of band to Get.
	uint16_t *pnType,		///< Returned band type.
	uint32_t *pdwFrequencyHz,	///< Returned band frequency.
	short *pnQ100,		///< Returned filter Q * 100.
	short *pnGain0_01dB	///< Returned filter gain in 100ths of a dB.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE);
	if (hpi_handle_indexes(hControl, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.c.wAttribute = HPI_EQUALIZER_FILTER;
	hm.u.c.dwParam2 = wIndex;

	HPI_Message(&hm, &hr);

	if (pdwFrequencyHz)
		*pdwFrequencyHz = hr.u.c.dwParam1;
	if (pnType)
		*pnType = (uint16_t)(hr.u.c.dwParam2 >> 16);
	if (pnQ100)
		*pnQ100 = hr.u.c.anLogValue[1];
	if (pnGain0_01dB)
		*pnGain0_01dB = hr.u.c.anLogValue[0];

	return hr.wError;
}

/** Set up one of the filters in a parametric equalizer.
Set the parameters for one equalizer filter.
The overall equalizer response will be a product of all its filter responses.

\return_hpierr

\param phSubSys Vestigial subsys handle (unused), may be set to NULL
\param hControl Equalizer control handle.
\param wIndex Index of band to set.
\param nType The kind of filter that the band will implement has many different
options.
In the following descriptions, low and high frequencies mean relative to
dwBandFrequency.
"Elsewhere" means at frequencies different from dwBandFrequency,
how different depends on Q (look at the following figures)
- HPI_FILTER_TYPE_BYPASS\n
The filter is bypassed (turned off). Other parameters are ignored
- HPI_FILTER_TYPE_LOWPASS\n
Has unity gain at low frequencies, and attenuation tending towards infinite at
high frequencies. nGain0_01dB parameter is ignored.
See "lp" in the following diagrams.
- HPI_FILTER_TYPE_HIGHPASS\n
Has unity gain at high frequencies, and attenuation tending towards infinite at
low frequencies. nGain0_01dB parameter is ignored.
(Not illustrated, basically the opposite of lowpass)
- HPI_FILTER_TYPE_BANDPASS\n
	Has unity gain at dwFrequencyHz and tends towards infinite attenuation
elsewhere. nGain0_01dB parameter is ignored.
See "bp" in the following diagrams.
- HPI_FILTER_TYPE_BANDSTOP\n
Maximum attenuation at dwFrequencyHz, tends towards unity gain elsewhere.
nGain0_01dB parameter is ignored.
See "bs" in the following diagrams.
- HPI_FILTER_TYPE_LOWSHELF\n
Has gain of nGain0_01dB at low frequencies and unity gain at high frequencies.
See "ls" in the following diagrams.
- HPI_FILTER_TYPE_HIGHSHELF\n
	Has gain of nGain0_01dB at high frequencies and unity gain at
low frequencies.
See "hs" in the following diagrams.
- HPI_FILTER_TYPE_EQ_BAND \n
	Has gain of nGain0_01dB at dwFrequencyHz and unity gain elsewhere.
See "eq" in the following diagrams.

\param dwFrequencyHz is the defining frequency of the filter.
It is the center frequency of types
HPI_FILTER_TYPE_ BANDPASS, HPI_FILTER_TYPE_BANDSTOP, HPI_FILTER_TYPE_EQ_BAND.
It is the -3dB frequency of HPI_FILTER_TYPE_LOWPASS, HPI_FILTER_TYPE_HIGHPASS
when Q=1 or resonant frequency when Q>1 and it is the half gain frequency of HPI_FILTER_TYPE_LOWSHELF, HPI_FILTER_TYPE_HIGHSHELF.
The maximum allowable value is half the current adapter samplerate i.e. Fs/2.
When the adapter samplerate is changed, the equalizer filters will be
recalculated.  If this results in the band frequency being greater
than Fs/2, then the filter will be turned off.

\param nQ100 controls filter sharpness.
To allow the use of an integer parameter, Filter Q = dwQ100/100.\n
In the following figure, gain is 20dB (10x) (nGain0_01dB=2000) and sampling
frequency is normalized to
1Hz (10^0) and nFrequency is 0.1 x sampling frequency. Q=[0.2 0.5 1 2 4 8].\n
Q can also be thought of as affecting bandwidth or shelf slope of some of
these filters.\n
Bandwidth is measured in octaves
(between -3 dB frequencies for BPF and notch or between midpoint (dBgain/2)
gain frequencies for peaking EQ).\n
The relationship between bandwidth and Q is:
\code
 1/Q = 2*sinh[ln(2)/2*bandwidth*omega/sin(omega)]  (digital filter using BLT)
 or      1/Q = 2*sinh[ln(2)/2*bandwidth])           (analog filter prototype)
Where omega = 2*pi*frequency/sampleRate
\endcode
Shelf slope S, a "shelf slope" parameter (for shelving EQ only).
When S = 1, the shelf slope is as steep as it can be and remain monotonically
increasing or decreasing gain with frequency.  The shelf slope, in
dB/octave, remains proportional to S for all other values.\n
The relationship between shelf slope and Q is
1/Q = sqrt[(A + 1/A)*(1/S - 1) + 2]\n
where A  = 10^(dBgain/40)\n
Effect of Q on EQ filters \image html EQ_effect_of_Q.png

\param nGain0_01dB The gain is expressed in milliBels (100ths of a decibel).
Allowable range is -1000 to +1000 mB. Usable range will likely be less than this.
This parameter is only applicable to the equalizer filter types
HPI_FILTER_TYPE_LOWSHELF,HPI_FILTER_TYPE_HIGHSHELF and HPI_FILTER_TYPE_EQ_BAND.
Other filters always have unity gain in the passband.\n
In the following figure, Q=1.0 and sampling frequency is normalized
to 1Hz (10^0) and nFrequency is 0.1 x sampling frequency.
dBgain=[-20 -10 0 10 20]\n
For example, to produce the upper (red) curve in the "Filtertype_eq_band" graph:
\code
err= HPI_ParametricEQ_SetBand(
		phSubsys,
		hMicrophoneControl,
		HPI_FILTER_TYPE_EQ_BAND,
		4410	// 4.41khz
		100			// Q=1
		20*100,	// 20dB
		);
\endcode
Effect of gain on EQ filters \image html EQ_effect_of_gain.png
*/
hpi_err_t HPI_ParametricEQ_SetBand(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hControl,
	uint16_t wIndex,
	uint16_t nType,
	uint32_t dwFrequencyHz,
	short nQ100,
	short nGain0_01dB
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_SET_STATE);
	if (hpi_handle_indexes(hControl, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	hm.u.c.dwParam1 = dwFrequencyHz;
	hm.u.c.dwParam2 = (wIndex & 0xFFFFL) + ((uint32_t)nType << 16);
	hm.u.c.anLogValue[0] = nGain0_01dB;
	hm.u.c.anLogValue[1] = nQ100;
	hm.u.c.wAttribute = HPI_EQUALIZER_FILTER;

	HPI_Message(&hm, &hr);

	return hr.wError;
}

/** Retrieve the calculated IIR filter coefficients
(scaled by 1000 into integers).
\return_hpierr
*/
hpi_err_t HPI_ParametricEQ_GetCoeffs(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Equalizer control handle.
	uint16_t wIndex,		///< Index of band to Get.
	short coeffs[5]		///< Returned IIR filter coefficients * 1000 a1,a2,b0,b1,b2 (a0==0).
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE);
	if (hpi_handle_indexes(hControl, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.c.wAttribute = HPI_EQUALIZER_COEFFICIENTS;
	hm.u.c.dwParam2 = wIndex;

	HPI_Message(&hm, &hr);

	coeffs[0] = (short)hr.u.c.anLogValue[0];
	coeffs[1] = (short)hr.u.c.anLogValue[1];
	coeffs[2] = (short)hr.u.c.dwParam1;
	coeffs[3] = (short)(hr.u.c.dwParam1 >> 16);
	coeffs[4] = (short)hr.u.c.dwParam2;

	return hr.wError;
}

///\}
#endif
/*=========================================================================*/
/** \defgroup sampleclock SampleClock control
The SampleClock control is used to control the clock source for the adapter.

@{
*/
/** Query valid SampleClock sources
Iterate dwIndex until an error is returned to get allowed values
for SampleClock source in *pwFormat
*/
hpi_err_t HPI_SampleClock_QuerySource(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	const hpi_handle_t hClock,	///< Control to query
	const uint32_t dwIndex,	///< Index for possible attribute values
	uint16_t *pwSource	///< source
)
{
	uint32_t qr;
	hpi_err_t err;

	err = HPI_ControlQuery(phSubSys, hClock, HPI_SAMPLECLOCK_SOURCE,
				dwIndex, 0, &qr);
	*pwSource = (uint16_t)qr;
	return err;
}

/** Sets the clock source for the sample clock.
\return_hpierr
*/
hpi_err_t HPI_SampleClock_SetSource(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///<Handle to control of type #HPI_CONTROL_SAMPLECLOCK
	uint16_t wSource		///<Sample clock source - one of #HPI_SAMPLECLOCK_SOURCES.
)
{
	return HPI_ControlParamSet(phSubSys, hControl,
		HPI_SAMPLECLOCK_SOURCE, wSource, 0);
}

/** Gets the current sample clock source.
\return_hpierr
*/
hpi_err_t HPI_SampleClock_GetSource(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///<Handle to control of type #HPI_CONTROL_SAMPLECLOCK
	uint16_t *pwSource		///<Sample clock source - one of #HPI_SAMPLECLOCK_SOURCES.
)
{
	hpi_err_t err = 0;
	uint32_t dwSource = 0;
	err = HPI_ControlParam1Get(phSubSys, hControl,
		HPI_SAMPLECLOCK_SOURCE, &dwSource);
	if (!err)
		if (pwSource)
			*pwSource = (uint16_t)dwSource;
	return err;
}

/** Query the available source indices.
Currently the only source with indices is HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT.
Iterate dwIndex until an error is returned to get allowed values
for SampleClock source index in *pwSourceIndex.
*/
hpi_err_t HPI_SampleClock_QuerySourceIndex(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	const hpi_handle_t hClock,	///< Control to query
	const uint32_t dwIndex,	///< Index for possible attribute values
	const uint32_t dwSource,	///< Source type
	uint16_t *pwSourceIndex	///< source index
)
{
	uint32_t qr;
	hpi_err_t err;

	err = HPI_ControlQuery(phSubSys, hClock, HPI_SAMPLECLOCK_SOURCE_INDEX,
				dwIndex, dwSource, &qr);
	*pwSourceIndex = (uint16_t)qr;
	return err;
}

/** Sets the index for the currently selected source.

Note, to use this function the source must already be set to a source that
supports indices (Currently only HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT).
The source retains its index setting if a different source is selected.
\return_hpierr
*/
hpi_err_t HPI_SampleClock_SetSourceIndex(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///<Handle to control of type HPI_CONTROL_SAMPLECLOCK
	uint16_t wSourceIndex	///<Index of the source to use
)
{
	return HPI_ControlParamSet(phSubSys, hControl,
		HPI_SAMPLECLOCK_SOURCE_INDEX, wSourceIndex, 0);
}


/** Gets the AES/EBU input used to source the adapter clock.
Note, to use this function the source must already be set to a source that
supports indices (Currently only HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT)
On error, *pwSource index is NOT updated.
\return_hpierr
*/
hpi_err_t HPI_SampleClock_GetSourceIndex(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///<Handle to control of type HPI_CONTROL_SAMPLECLOCK
	uint16_t *pwSourceIndex	///<Index of the current source
)
{
	hpi_err_t err = 0;
	uint32_t dwSourceIndex = 0;
	err = HPI_ControlParam1Get(phSubSys, hControl,
		HPI_SAMPLECLOCK_SOURCE_INDEX, &dwSourceIndex);
	if (!err)
		if (pwSourceIndex)
			*pwSourceIndex = (uint16_t)dwSourceIndex;
	return err;
}

/** Query samplerates supported by the local PLL of this sampleclock
Iterate dwIndex until an error is returned to get the list of valid
PLL samplerates.
*/
hpi_err_t HPI_SampleClock_QueryLocalRate(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	const hpi_handle_t hClock,	///< Control to query
	const uint32_t dwIndex,	///< Index for possible attribute values
	uint32_t *pdwRate	///< rate
)
{
	hpi_err_t err;
	err = HPI_ControlQuery(phSubSys, hClock,  HPI_SAMPLECLOCK_LOCAL_SAMPLERATE,
				dwIndex, 0, pdwRate);

	return err;
}

/** Set the samplerate of the sample clock's local PLL
This samplerate becomes the sampleclock output rate when the
HPI_SAMPLECLOCK_SOURCE_LOCAL source is selected

\return_hpierr
\retval HPI_ERROR_INVALID_CONTROL_ATTRIBUTE if the adapter has no local PLL
*/
hpi_err_t HPI_SampleClock_SetLocalRate(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hControl,	///< Sample rate in Hz
	uint32_t dwSampleRate
)
{
	return HPI_ControlParamSet(phSubSys, hControl,
		HPI_SAMPLECLOCK_LOCAL_SAMPLERATE, dwSampleRate, 0);
}

/** Set the samplerate of the sample clock's local PLL in Hz and parts per billion.
This samplerate becomes the sampleclock output rate when the
HPI_SAMPLECLOCK_SOURCE_LOCAL source is selected. Note that not
all adapters support this call.

\note To set the sample rate to 48000.010 Hz the dwSampleRateHz would
be set to 48000 and the dwSampleRatePPB to 10000000 (i.e. 0.010 * 1000000000).

\return_hpierr
\retval HPI_ERROR_INVALID_CONTROL_ATTRIBUTE if the adapter has no local PLL
*/
hpi_err_t HPI_SampleClock_SetLocalRateEx(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hControl,
	uint32_t dwSampleRateHz,	///< Sample rate in Hz
	uint32_t dwSampleRatePPB	///< Fractional Hz in parts per billion
)
{
	return HPI_ControlParamSet(phSubSys, hControl,
		HPI_SAMPLECLOCK_LOCAL_SAMPLERATE_EX, dwSampleRateHz, dwSampleRatePPB);
}

/** Get the samplerate of the sample clock's local PLL
\return_hpierr
\retval HPI_ERROR_INVALID_CONTROL_ATTRIBUTE if the adapter has no local PLL
*/
hpi_err_t HPI_SampleClock_GetLocalRate(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hControl,
	uint32_t *pdwSampleRate
)
{
	hpi_err_t err = 0;
	uint32_t dwSampleRate = 0;
	err = HPI_ControlParam1Get(phSubSys, hControl,
		HPI_SAMPLECLOCK_LOCAL_SAMPLERATE, &dwSampleRate);
	if (!err)
		if (pdwSampleRate)
			*pdwSampleRate = dwSampleRate;
	return err;
}


#ifdef HPI_BUILD_INCLUDE_DEPRECATED
/** Sets the adapter samplerate when the SampleClock source is HPI_SAMPLECLOCK_SOURCE_ADAPTER or HPI_SAMPLECLOCK_SOURCE_LOCAL
\return_hpierr
\deprecated Use HPI_SampleClock_SetLocalRate() instead
*/
hpi_err_t HPI_SampleClock_SetSampleRate(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///<Handle to control of type HPI_CONTROL_SAMPLECLOCK
	uint32_t dwSampleRate	///<Sample rate to set. Valid values depend on adapter type.
)
{
	return HPI_ControlParamSet(phSubSys, hControl,
		HPI_SAMPLECLOCK_SAMPLERATE, dwSampleRate, 0);
}
#endif

/** Gets the current adapter samplerate
\return_hpierr
*/
hpi_err_t HPI_SampleClock_GetSampleRate(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///<Handle to control of type HPI_CONTROL_SAMPLECLOCK
	uint32_t *pdwSampleRate	///<Current sample rate
)
{
	hpi_err_t err = 0;
	uint32_t dwSampleRate = 0;
	err = HPI_ControlParam1Get(phSubSys, hControl,
		HPI_SAMPLECLOCK_SAMPLERATE, &dwSampleRate);
	if (!err)
		if (pdwSampleRate)
			*pdwSampleRate = dwSampleRate;
	return err;
}

#ifndef HPI_OS_LINUX_KERNEL
/** Enable/disable clock autoswitching.

When enabled, the adapter clock will be derived from the first
valid AESEBU input, or from the local PLL if no inputs are valid.

Adapters supporting auto switch are ASI5111,
and ASI50xx family members with AESEBU inputs.

\return hpierr
*/
hpi_err_t HPI_SampleClock_SetAuto(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///<Handle to control of type HPI_CONTROL_SAMPLECLOCK
	uint32_t dwEnable ///< Auto enable. Either HPI_SWITCH_OFF, or HPI_SWITCH_ON
)
{
	return HPI_ControlParamSet(phSubSys, hControl,
		HPI_SAMPLECLOCK_AUTO, dwEnable, 0);
}

hpi_err_t HPI_SampleClock_GetAuto(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///<Handle to control of type HPI_CONTROL_SAMPLECLOCK
	uint32_t *pdwEnable ///< Current auto enable state. Either HPI_SWITCH_OFF, or HPI_SWITCH_ON
)
{
	return HPI_ControlParam1Get(phSubSys, hControl,
		HPI_SAMPLECLOCK_AUTO, pdwEnable);
}

/** Enable/disable local rate changing.


Adapters supporting local clock locking are in the
ASI50xx (PCM), ASI54xx (CobraNet), ASI63xx (CobraNet)
and ASI64xx (CobraNet) families.

For the ASI50xx family, when enabled (locked), the local
clock will be locked to the last rate set by a call to
HPI_SampleClock_SetLocalRate().

For the ASI53xx, ASI54xx, ASI63xx and ASI64xx families the meaning is
a little different. The lock operation disables the software
sample rate converters, thereby locking the sample rate of
all play and record device to the network clock rate of 48 kHz.

When a ASI53xx or ASI63xx is in low latency mode, locked always returns
true and is readonly. An attempt to set the lock will return
HPI_ERROR_CONTROL_DISABLED.

\return hpierr
*/
hpi_err_t HPI_SampleClock_SetLocalRateLock(
	const hpi_hsubsys_t *phSubSys, ///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl, ///<Handle to control of type HPI_CONTROL_SAMPLECLOCK
	uint32_t dwLock ///< Locked state. Either HPI_SWITCH_OFF, or HPI_SWITCH_ON
)
{
	return HPI_ControlParamSet(phSubSys, hControl,
		HPI_SAMPLECLOCK_LOCAL_LOCK, dwLock, 0);
}

hpi_err_t HPI_SampleClock_GetLocalRateLock(
	const hpi_hsubsys_t *phSubSys, ///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl, ///<Handle to control of type HPI_CONTROL_SAMPLECLOCK
	uint32_t *pdwLock ///< Locked state. Either HPI_SWITCH_OFF, or HPI_SWITCH_ON
)
{
	return HPI_ControlParam1Get(phSubSys, hControl,
		HPI_SAMPLECLOCK_LOCAL_LOCK, pdwLock);
}
#endif
/** @} */ /* defgroup sampleclock */

#ifndef HPI_OS_LINUX_KERNEL
/*=========================================================================*/
/** \defgroup tonedetector Tone Detector control
\{
The tone detector monitors its inputs for the presence of any of a number
of tones.

Currently 25Hz and 35Hz tones can be detected independently on left and right
channels. Tones that exceed the threshold set by HPI_ToneDetector_SetThreshold()
are detected. The result of the detection is reflected in the controls state,
and optionally by sending an async event with the new state.
*/

/**  Enumerate the detection frequencies of the tone detector control
  * \param phSubSys Vestigial subsys handle (unused), may be set to NULL
  * \param hControl Handle to tone detector control.
  * \param nIndex iterate this from zero to get detector frequencies in *dwFreqency
  * \param *dwFrequency  detection frequency of tone detector band number nIndex
  * \return_hpierr
  * \retval #HPI_ERROR_INVALID_CONTROL_VALUE if nIndex >= number of frequencies supported
  */
hpi_err_t HPI_ToneDetector_GetFrequency(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hControl,
	uint32_t nIndex,
	uint32_t *dwFrequency
)
{
	return HPI_ControlParamGet(phSubSys, hControl,
		HPI_TONEDETECTOR_FREQUENCY, nIndex, 0, dwFrequency, NULL);
}

/**  Get tone detector state.
  * \param phSubSys Vestigial subsys handle (unused), may be set to NULL
  * \param hControl Handle to tone detector control.
  * \param *state Tonedetector state reflected in the bits of *State.
  *            Upper 16 bits is right channel, lower 16 bits is left channel.
  *            LSB represents lowest frequency detector (25Hz)
  * \return_hpierr
  */
hpi_err_t HPI_ToneDetector_GetState(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hControl,
	uint32_t *state
)
{
	return HPI_ControlParam1Get(phSubSys, hControl, HPI_TONEDETECTOR_STATE, state);
}

#ifdef HPI_BUILD_INCLUDE_DEPRECATED
/* Note: this function should not exist, it is left here because it already has been included in released DLL
Removed from hpi.h about 2007/05/09
*/
hpi_err_t HPI_ToneDetector_SetState(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hControl,
	uint32_t state
)
{
	return HPI_ERROR_UNIMPLEMENTED;
}
#endif

/** Enable (or disable) a ToneDetector control
  * \param phSubSys Vestigial subsys handle (unused), may be set to NULL
  * \param hControl Handle to tone detector control.
  * \param enable 1=enable, 0=disable
  */
hpi_err_t HPI_ToneDetector_SetEnable(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hControl,
	uint32_t enable
)
{
	return HPI_ControlParamSet(phSubSys, hControl, HPI_GENERIC_ENABLE,
		enable, 0);
}

/** Get the Enable state of a ToneDetector control
 * \param phSubSys Vestigial subsys handle (unused), may be set to NULL
 * \param hControl Handle to tone detector control.
 * \param *enable 1=enable, 0=disable
 */
hpi_err_t HPI_ToneDetector_GetEnable(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hControl,
	uint32_t *enable
)
{
	return HPI_ControlParam1Get(phSubSys, hControl, HPI_GENERIC_ENABLE, enable);
}

/** Enable ToneDetector control event generation
  * \param phSubSys Vestigial subsys handle (unused), may be set to NULL
  * \param hControl Handle to tone detector control.
  * \param EventEnable 1=enable, 0=disable
*/
hpi_err_t HPI_ToneDetector_SetEventEnable(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hControl,
	uint32_t EventEnable
)
{
	return HPI_ControlParamSet(phSubSys, hControl,
		HPI_GENERIC_EVENT_ENABLE, (uint32_t)EventEnable, 0);
}

/** Get the event generation enable state of a ToneDetector control
  * \param phSubSys Vestigial subsys handle (unused), may be set to NULL
  * \param hControl Handle to tone detector control.
  * \param EventEnable 1=enable, 0=disable
*/
hpi_err_t HPI_ToneDetector_GetEventEnable(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hControl,
	uint32_t *EventEnable
)
{
	return HPI_ControlParam1Get(phSubSys, hControl,	HPI_GENERIC_EVENT_ENABLE, EventEnable);
}

/** Set the Threshold of a ToneDetector control.
  * \param phSubSys Vestigial subsys handle (unused), may be set to NULL
  * \param hControl Handle to tone detector control.
  * \param threshold in millibels wrt full scale.  E.g. -2000 -> -20dBFS threshold.
  * Tones with level above this threshold are detected.
  * \return_hpierr
  */
hpi_err_t HPI_ToneDetector_SetThreshold(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hControl,
	int threshold
)
{
	return HPI_ControlParamSet(phSubSys, hControl,
		HPI_TONEDETECTOR_THRESHOLD, (uint32_t)threshold, 0);
}

/** Get the Threshold of a ToneDetector control
  * \param phSubSys Vestigial subsys handle (unused), may be set to NULL
  * \param hControl Handle to tone detector control.
  * \param *threshold current threshold, \sa HPI_ToneDetector_SetThreshold()
  */
hpi_err_t HPI_ToneDetector_GetThreshold(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hControl,
	int *threshold
)
{
	return HPI_ControlParam1Get(phSubSys, hControl,
				    HPI_TONEDETECTOR_THRESHOLD, (uint32_t *)threshold);
}
/** @} */ /* defgroup tonedetector */
#endif
#ifndef HPI_OS_LINUX_KERNEL
/*=========================================================================*/
/** \defgroup silence Silence Detector Controls
  *
  * The silence detector control monitors its input for silence exceeding
  * a set duration.
  * Silence is defined as signal below a specified threshold set by HPI_SilenceDetector_SetThreshold()
  * The duration is specified by  HPI_SilenceDetector_SetDelay()
  * silence-detected state is reset immediately the signal exceeds the
  * threshold (no delay)
\{
*/
#ifdef HPI_BUILD_INCLUDE_DEPRECATED
/* Note: this function should not exist, it is left here because it already has been included in released DLL
Removed from hpi.h about 2007/05/09
*/
hpi_err_t HPI_SilenceDetector_SetState(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hControl,
	uint32_t state
)
{
	return HPI_ERROR_UNIMPLEMENTED;
}
#endif

/** Get the State of a SilenceDetector control
  * \param phSubSys Vestigial subsys handle (unused), may be set to NULL
  * \param hControl silence detector handle
  * \param *state The state is a bitmap corresponding to the channels
  *               being monitored (LSB=left channel, LSB+1=right channel)
  * \return_hpierr
  */
hpi_err_t HPI_SilenceDetector_GetState(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hControl,
	uint32_t *state
)
{
	return HPI_ControlParam1Get(phSubSys, hControl,
		HPI_SILENCEDETECTOR_STATE, state);
}

/**  Enable a SilenceDetector control
  * \param phSubSys Vestigial subsys handle (unused), may be set to NULL
  * \param hControl silence detector handle
  * \param *enable 1=enable, 0=disable
  * \return_hpierr
  */
hpi_err_t HPI_SilenceDetector_SetEnable(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hControl,
	uint32_t enable
)
{
	return HPI_ControlParamSet(phSubSys, hControl, HPI_GENERIC_ENABLE,
		enable, 0);
}

/** Get the Enable setting of a SilenceDetector control
  * \param phSubSys Vestigial subsys handle (unused), may be set to NULL
  * \param hControl silence detector handle
  * \param enable 1=enable, 0=disable
  * \return_hpierr
  */
hpi_err_t HPI_SilenceDetector_GetEnable(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hControl,
	uint32_t *enable
)
{
	return HPI_ControlParam1Get(phSubSys, hControl, HPI_GENERIC_ENABLE, enable);
}

/** Set the event generation by a SilenceDetector control
  * \param phSubSys Vestigial subsys handle (unused), may be set to NULL
  * \param hControl silence detector handle
  * \param EventEnable 1=enable, 0=disable
  * \return_hpierr
*/
hpi_err_t HPI_SilenceDetector_SetEventEnable(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hControl,
	uint32_t EventEnable
)
{
	return HPI_ControlParamSet(phSubSys, hControl,
		HPI_GENERIC_EVENT_ENABLE, EventEnable, 0);
}

/** Get the event generation enable setting of a SilenceDetector control
  * \param phSubSys Vestigial subsys handle (unused), may be set to NULL
  * \param hControl silence detector handle
  * \param *EventEnable 1=enable, 0=disable
  * \return_hpierr
*/
hpi_err_t HPI_SilenceDetector_GetEventEnable(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hControl,
	uint32_t *EventEnable
)
{
	return HPI_ControlParam1Get(phSubSys, hControl,
		HPI_GENERIC_EVENT_ENABLE, EventEnable);
}

/** Set the Delay of a SilenceDetector control
  * \param phSubSys Vestigial subsys handle (unused), may be set to NULL
  * \param hControl silence detector handle
  * \param delay  Trigger delay in milliseconds, max 60000 (60 seconds)
  * \return_hpierr
  */
hpi_err_t HPI_SilenceDetector_SetDelay(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hControl,
	uint32_t delay
)
{
	return HPI_ControlParamSet(phSubSys, hControl,
		HPI_SILENCEDETECTOR_DELAY, delay, 0);
}

/** Get the trigger delay of a SilenceDetector control
  * \param phSubSys Vestigial subsys handle (unused), may be set to NULL
  * \param hControl silence detector handle
  * \param *delay see HPI_SilenceDetector_SetDelay()
  * \return_hpierr
*/
hpi_err_t HPI_SilenceDetector_GetDelay(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hControl,
	uint32_t *delay
)
{
	return HPI_ControlParam1Get(phSubSys, hControl,
		HPI_SILENCEDETECTOR_DELAY, delay);
}

/** Set the Threshold of a SilenceDetector control
  * \param phSubSys Vestigial subsys handle (unused), may be set to NULL
  * \param hControl silence detector handle
  * \param threshold in millibels wrt full scale.  E.g. -4000 -> -40dBFS threshold.
  * \return_hpierr
  */
hpi_err_t HPI_SilenceDetector_SetThreshold(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hControl,
	int threshold
)
{
	return HPI_ControlParamSet(phSubSys, hControl,
		HPI_SILENCEDETECTOR_THRESHOLD, threshold, 0);
}

/** Get the Threshold of a SilenceDetector control
  * \param phSubSys Vestigial subsys handle (unused), may be set to NULL
  * \param hControl silence detector handle
  * \param threshold see HPI_SilenceDetector_SetThreshold()
  * \return_hpierr
  */
hpi_err_t HPI_SilenceDetector_GetThreshold(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hControl,
	int *threshold
)
{
	return HPI_ControlParam1Get(phSubSys, hControl,
				    HPI_SILENCEDETECTOR_THRESHOLD, (uint32_t *)threshold);
}

/** @} */ /* defgroup silence */
#endif
/*=========================================================================*/
/** \defgroup tuner Tuner Controls

The tuner control sets the band and frequency of a tuner,
and measures the RF level.

\image html tuner.png

\{
*/
/** Query tuner for supported bands.
Iterate dwIndex until an error is returned to get the list of
bands supported by this tuner
*/
hpi_err_t HPI_Tuner_QueryBand(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	const hpi_handle_t hTuner,	///< Control to query
	const uint32_t dwIndex,	///< Index for possible attribute values
	uint16_t *pwBand	///< band
)
{
	uint32_t qr;
	hpi_err_t err;

	err = HPI_ControlQuery(phSubSys, hTuner, HPI_TUNER_BAND,
				dwIndex, 0, &qr);
	*pwBand = (uint16_t)qr;
	return err;
}

/** Set the band that the tuner recieves.
  *
  * Not all tuners support all bands, e.g. AM+FM or TV+FM.
  *
  * \note That with the exception of #HPI_TUNER_BAND_AUX, the tuner frequency
  * must subsequently be set using HPI_Tuner_SetFrequency().
  * \note The tuner band may not change immediately.  It may take up to 300ms to change.
  * To determine when the band has changed, call HPI_Tuner_GetBand() until it returns the new band.
  * \sa HPI_ControlQuery() for details on determining the
  * bands supported by a particular tuner.
  * \return_hpierr
  */
hpi_err_t HPI_Tuner_SetBand(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to tuner control.
	uint16_t wBand		///< One of the supported bands #HPI_TUNER_BAND.
)
{
	return HPI_ControlParamSet(phSubSys, hControl, HPI_TUNER_BAND,
		wBand, 0);
}

/** Get the current tuner band.
  * \return_hpierr
  */
hpi_err_t HPI_Tuner_GetBand(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to tuner control.
	uint16_t *pwBand		///< Current tuner band - one of #HPI_TUNER_BAND bands.
)
{
	uint32_t dwBand = 0;
	uint16_t nError = 0;

	nError = HPI_ControlParam1Get(phSubSys, hControl,
		HPI_TUNER_BAND, &dwBand);
	if (pwBand)
		*pwBand = (uint16_t)dwBand;
	return nError;
}

/** Query tuner band for supported frequency range and steps
Calling with dwIndex=0,1,2 returns frequency minimum, maximum
and step respectively (in kHz), for the specified band.
For example, to determine the frequency range of the AM band, do the following:
\code
wErr= HPI_Tuner_QueryFrequency(phSS,hC, HPI_TUNER_FREQ, 0, HPI_TUNER_BAND_AM , pdwMinFreq);
wErr= HPI_Tuner_QueryFrequency(phSS,hC, HPI_TUNER_FREQ, 1, HPI_TUNER_BAND_AM , pdwMaxFreq);
wErr= HPI_Tuner_QueryFrequency(phSS,hC, HPI_TUNER_FREQ, 2, HPI_TUNER_BAND_AM , pdwFreqStep);
\endcode

*/
hpi_err_t HPI_Tuner_QueryFrequency(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	const hpi_handle_t hTuner,	///< Control to query
	const uint32_t dwIndex,	///< Index for possible attribute values
	const uint16_t band,	///< Band for which to query frequency range
	uint32_t *pdwFreq	///< frequency min,max, step for given band
)
{
	return HPI_ControlQuery(phSubSys, hTuner, HPI_TUNER_FREQ,
				dwIndex, band, pdwFreq);
}

/** Set the tuner frequency.
  *
  * \note See HPI_ControlQuery() to determine how to find the frequency
  * supported by a particular tuner band.
  * \note The tuner frequency may not change immediately.  It may take up to 300ms to change.
  * To determine when the frequency has changed, call HPI_Tuner_GetFrequency() until it returns the new frequency.
  * \return_hpierr
  */
hpi_err_t HPI_Tuner_SetFrequency(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Vestigial subsys handle (unused), may be set to NULL
	uint32_t wFreqInkHz		///< Tuner frequncy in kHz. Valid values depend on the tuner band setting.
)
{
	return HPI_ControlParamSet(phSubSys, hControl, HPI_TUNER_FREQ,
		wFreqInkHz, 0);
}

/** Get the current tuner frequency.
  * \return_hpierr
  */
hpi_err_t HPI_Tuner_GetFrequency(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to tuner control.
	uint32_t *pwFreqInkHz	///< Returned tuner frequency in kHz.
)
{
	return HPI_ControlParam1Get(phSubSys, hControl, HPI_TUNER_FREQ,
		pwFreqInkHz);
}

/** Query possible settings of tuner gain control
Iterate dwIndex until an error is returned to get the list of
gains supported by this tuner
*/
hpi_err_t HPI_Tuner_QueryGain(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	const hpi_handle_t hTuner,	///< Control to query
	const uint32_t dwIndex,	///< Index for possible attribute values
	uint16_t *pwGain	///<  Tuner gain in millibels
)
{
	uint32_t qr;
	hpi_err_t err;

	err = HPI_ControlQuery(phSubSys, hTuner, HPI_TUNER_BAND,
				dwIndex, 0, &qr);
	*pwGain = (uint16_t)qr;
	return err;
}

/** Set the RF attenuator gain of the tuner front end.
  * \return_hpierr
  */
hpi_err_t HPI_Tuner_SetGain(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to tuner control.
	short nGain		///< Valid values depend on the adapter type. For the ASI8700: 0dB or -20 x HPI_UNITS_PER_dB.
)
{
	return HPI_ControlParamSet(phSubSys, hControl, HPI_TUNER_GAIN,
		nGain, 0);
}
/** Get the current tuner gain
  * \return_hpierr
  */
hpi_err_t HPI_Tuner_GetGain(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to tuner control.
	short *pnGain		///< Current tuner gain in milliBels
)
{
	uint32_t dwGain = 0;
	uint16_t nError = 0;

	nError = HPI_ControlParam1Get(phSubSys, hControl,
		HPI_TUNER_GAIN, &dwGain);
	if (pnGain)
		*pnGain = (uint16_t)dwGain;
	return nError;
}

#ifndef HPI_OS_LINUX_KERNEL
/** Get the RF level of a tuner input in millibel microvolts.
  * Divide the return value by HPI_UNITS_PER_dB to get the level in dBuV.

  * \return_hpierr
  */
hpi_err_t HPI_Tuner_GetRFLevel(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to tuner control.
	short *pwLevel		///< Return level. The units are mBuV  (mB micro volts). Range is +/- 100 dBuV
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE);
	if (hpi_handle_indexes(hControl, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.cu.wAttribute = HPI_TUNER_LEVEL_AVG;
	HPI_Message(&hm, &hr);
	if (pwLevel)
		*pwLevel = hr.u.cu.tuner.sLevel;
	return hr.wError;
}

/** Get the RF raw level of a tuner. This is a "raw" value and it will depend
  * on the type of tuner being accessed. This function only applies to certain
  * bands on certain tuners.
  * \b ASI87xx - Supports this function.<br>
  * <b> ASI89xx with ASI1711 tuner </b> - Does not support this function.
  * It will return #HPI_ERROR_INVALID_CONTROL_ATTRIBUTE.
  * \return_hpierr
  */
hpi_err_t HPI_Tuner_GetRawRFLevel(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to tuner control.
	short *pwLevel		///< The units of this depend on the tuner type. This is the raw level reading that the tuner returns.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE);
	if (hpi_handle_indexes(hControl, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.cu.wAttribute = HPI_TUNER_LEVEL_RAW;
	HPI_Message(&hm, &hr);
	if (pwLevel)
		*pwLevel = hr.u.cu.tuner.sLevel;
	return hr.wError;
}
/** Query tuner band for supported deemphasis settings.
Some tuner bands don't support deemphasis, and will return an empty list.
Iterate dwIndex until an error is returned to get the list of
deemphasis supported by this tuner band.
*/
hpi_err_t HPI_Tuner_QueryDeemphasis(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	const hpi_handle_t hTuner,	///< Control to query
	const uint32_t dwIndex,	///< Index for possible deemphasis values
	const uint16_t band,	///< Band for which to query deemphasis
	uint32_t *pdwDeemphasis	///< deemphasis in us
)
{
	return HPI_ControlQuery(phSubSys, hTuner, HPI_TUNER_DEEMPHASIS,
				dwIndex, band, pdwDeemphasis);
}

/** Set the audio de-emphasis.
  *
  * Not all tuners support de-emphasis. USA FM channels require
  * de-emphasis of 75 usec, while Europe requires 50 usec.
  * Some AM tuners may also have a deemphasis setting.
  * \return_hpierr
  */
hpi_err_t HPI_Tuner_SetDeemphasis(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to tuner control.
	uint32_t dwDeemphasis	///< Pre-emphasis - 0, 50 or 75 (usec)
)
{
	return HPI_ControlParamSet(phSubSys, hControl, HPI_TUNER_DEEMPHASIS,
		dwDeemphasis, 0);
}
/** Get the audio de-emphasis setting.
  *
  * Not all tuners support de-emphasis.
  *
  * \return_hpierr
  */
hpi_err_t HPI_Tuner_GetDeemphasis(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to tuner control.
	uint32_t *pdwDeemphasis	///< Returns pre-emphasis value of, 0, 50 or 75 (usec)
)
{
	return HPI_ControlParam1Get(phSubSys, hControl, HPI_TUNER_DEEMPHASIS,
		pdwDeemphasis);
}

/** Get list of the HD Radio programs supported by this tuner as a bitfield
*/
hpi_err_t HPI_Tuner_QueryProgram(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	const hpi_handle_t hTuner,	///< Control to query
	uint32_t *pbitmapProgram	///<  Tuner program bitfield. Each bit position, when set indicates an available HD Radio program.  Bits 0..7 are valid
)
{
	return HPI_ControlQuery(phSubSys, hTuner, HPI_TUNER_PROGRAM,
				0, 0, pbitmapProgram);
}

/** Sets the tuner HD Radio program.
  *
  * HD Radio tuners and transmitting stations support multiple programs on a
  * single tuner frequency. This API allows the user to set the program number
  * on the currently tuned channel. See HPI_ControlQuery() documentation for how to find
  * the list of supported programs.
  *
  * \return_hpierr
  */
hpi_err_t HPI_Tuner_SetProgram(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to tuner control.
	uint32_t dwProgram		///< Program number to set. Range is 0-7.
)
{
	return HPI_ControlParamSet(phSubSys, hControl, HPI_TUNER_PROGRAM,
		dwProgram, 0);
}

/** Gets the tuner HD Radio program.
  *
  * HD Radio tuners and transmitting stations support multiple programs on a
  * single tuner frequency. This API allows the user to get the current
  * program number on the currently tuned channel.
  *
  * \return_hpierr
  */
hpi_err_t HPI_Tuner_GetProgram(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to tuner control.
	uint32_t *pdwProgram	///< Returns current program. Range is 0-7.
)
{
	return HPI_ControlParam1Get(phSubSys, hControl, HPI_TUNER_PROGRAM,
		pdwProgram);
}

/** Gets the DSP firmware verison of an HD Radio tuner.
  *
  * \return_hpierr
  */
hpi_err_t HPI_Tuner_GetHdRadioDspVersion(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to tuner control.
	/** Returns current version of DSP software running on the tuner. */
	char *pszDspVersion,
	const uint32_t dwStringSize ///< Length on the string in bytes.
)
{
	HPI_UNUSED(phSubSys);
	return HPI_Control_GetString(
			hControl,
			HPI_TUNER_HDRADIO_DSP_VERSION,
			pszDspVersion,
			dwStringSize, 0);
}
/** Gets the SDK firmware verison of an HD Radio tuner.
  *
  * \return_hpierr
  */
hpi_err_t HPI_Tuner_GetHdRadioSdkVersion(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to tuner control.
	/** Returns current version of SDK software running on the tuner. */
	char *pszSdkVersion,
	const uint32_t dwStringSize ///< Length on the string in bytes.
)
{
	HPI_UNUSED(phSubSys);
	return HPI_Control_GetString(
			hControl,
			HPI_TUNER_HDRADIO_SDK_VERSION,
			pszSdkVersion,
			dwStringSize, 0);
}

/** Gets the Firmware firmware verison of a HD or DAB Radio tuner.
  *
  * \return_hpierr
  */
hpi_err_t HPI_Tuner_GetFirmwareVersion(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to tuner control.
	/** Returns current version of SDK software running on the tuner. */
	char *pszFWVersion,
	const uint32_t dwStringSize ///< Length of the string buffer in bytes.
)
{
	HPI_UNUSED(phSubSys);
	return HPI_Control_GetString(
			hControl,
			HPI_TUNER_HDRADIO_SDK_VERSION,
			pszFWVersion,
			dwStringSize, 0);
}

#ifdef HPI_BUILD_INCLUDE_DEPRECATED
/** Get the video status of a tuner input.
  *
  * \deprecated This function has been superceded by HPI_Tuner_GetStatus()
  * \return_hpierr
  */
hpi_err_t HPI_Tuner_GetVideoStatus(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to tuner control.
	uint16_t *pwStatus		/**< A bitfield with the following bits
				HPI_TUNER_VIDEO_COLOR_PRESENT,
				HPI_TUNER_VIDEO_HORZ_SYNC_MISSING,HPI_TUNER_VIDEO_IS_60HZ.
				*/
)
{
	uint16_t dummyStatusMask;

	return HPI_Tuner_GetStatus(phSubSys, hControl,
		&dummyStatusMask,
		pwStatus);
}
#endif

/** Get the status of various Boolean attributes of a tuner control.
  * The pwStatusMask returns which bits in wStatus are valid, as not all
  * tuners support all the status attributes.
  * \param phSubSys Vestigial subsys handle (unused), may be set to NULL
  * \param hControl Handle to tuner control.
  * \param *pwStatusMask A returned bitfield indicating which of the bits in
  * pwStatus contain valid status information. See #HPI_TUNER_STATUS_BITS for
  * valid bit settings.
  * \param *pwStatus A returned bitfield containing current status flags.
  * See #HPI_TUNER_STATUS_BITS for bit meanings.
  * \return_hpierr
  */
hpi_err_t HPI_Tuner_GetStatus(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hControl,
	uint16_t *pwStatusMask,
	uint16_t *pwStatus
)
{
	uint32_t dwStatus = 0;
	uint16_t nError = 0;

	// wStatusmask is in high 16bit word, wStatus is in low 16bit word
	nError = HPI_ControlParam1Get(phSubSys, hControl,
		HPI_TUNER_STATUS, &dwStatus);
	if (pwStatus) {
		if (!nError) {
			*pwStatusMask = (uint16_t)(dwStatus >> 16);
			*pwStatus = (uint16_t)(dwStatus & 0xFFFF);
		} else {
			*pwStatusMask = 0;
			*pwStatus = 0;
		}
	}
	return nError;
}

/** This function turns off the RSS (FM FR level reading) capability for the
  * specified tuner.
  * This only applies to certain bands on certain tuners.<br>
  * ASI87xx - Supports nMode = #HPI_TUNER_MODE_RSS<br>
  * ASI89xx with ASI1721 tuner - Supports nMode = #HPI_TUNER_MODE_RDS<br>
  * Otherwise will return #HPI_ERROR_INVALID_CONTROL_ATTRIBUTE.
  * \return_hpierr
  */
hpi_err_t HPI_Tuner_SetMode(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to tuner control.
	uint32_t nMode,		///< One of the #HPI_TUNER_MODES
	uint32_t nValue		///< One of the #HPI_TUNER_MODE_VALUES, according to the mode
)
{
	return HPI_ControlParamSet(phSubSys, hControl, HPI_TUNER_MODE,
		nMode, nValue);
}

/** Get the current tuner mode.
  * Currently supports checking whether RSS is enabled or disabled.
  * There are some dependancies across adapters for this function.<br>
  * \b ASI87xx - Supports nMode = #HPI_TUNER_MODE_RSS.<br>
  * <b> ASI89xx with ASI1711 tuner </b> - RSS is always enabled. Supports nMode = #HPI_TUNER_MODE_RDS<br>
  * Otherwise will return #HPI_ERROR_INVALID_CONTROL_ATTRIBUTE.
  *
  * \return_hpierr
  */
hpi_err_t HPI_Tuner_GetMode(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to tuner control.
	uint32_t nMode,		///< Currently only supports #HPI_TUNER_MODE_RSS.
	uint32_t *pnValue		///< Returned value is either #HPI_TUNER_MODE_RSS_DISABLE or #HPI_TUNER_MODE_RSS_ENABLE.
)
{
	return HPI_ControlParamGet(phSubSys, hControl, HPI_TUNER_MODE,
		nMode, 0, pnValue, NULL);
}

/** Get the digital signal quality from an HDRadio tuner.
  * \return_hpierr
  */
hpi_err_t HPI_Tuner_GetHdRadioSignalQuality(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to tuner control.
	uint32_t *pdwQuality	///< Pointer to returned HDRadio signal quality.
)
{
	return HPI_ControlParam1Get(phSubSys, hControl, HPI_TUNER_HDRADIO_SIGNAL_QUALITY,
		pdwQuality);
}

/** Get the signal blend from an HDRadio tuner.
  * \return_hpierr
  */
hpi_err_t HPI_Tuner_GetHdRadioSignalBlend(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to tuner control.
	uint32_t *pdwBlend	///< Pointer to returned HDRadio blend (0 = automatic, 1 = force analog only).
)
{
	return HPI_ControlParam1Get(phSubSys, hControl, HPI_TUNER_HDRADIO_BLEND,
		pdwBlend);
}

/** Set the signal blend from an HDRadio tuner.
  * \return_hpierr
  */
hpi_err_t HPI_Tuner_SetHdRadioSignalBlend(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to tuner control.
	const uint32_t dwBlend	///< HDRadio blend (0 = automatic, 1 = force analog only).
)
{
	return HPI_ControlParamSet(phSubSys, hControl, HPI_TUNER_HDRADIO_BLEND,
		dwBlend, 0);
}

/** Sets the DAB audio service.
  *
  \deprecated This function is no longer supported. Use HPI_Tuner_SetDabService() instead.
  *
  * DAB/DAB+ supports multiple services on a single tuner frequency (multiplex/ensemble).
  * This API allows the user to set the service number on the currently tuned multiplex/ensemble.
  * \return_hpierr
  */
hpi_err_t HPI_Tuner_SetDabAudioService(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to tuner control.
	const uint32_t dwIndex	///< Service number to set
)
{
	return HPI_ControlParamSet(phSubSys, hControl, HPI_TUNER_DABRADIO_AS,
		dwIndex, 0);
}

/** Gets the DAB audio service name.
  *
  \deprecated This function is no longer supported. Use HPI_Tuner_GetDabServiceInfo() instead.
  *
  * DAB/DAB+ supports multiple services on a single tuner frequency (multiplex/ensemble)
  * This API allows the user to get the service name of the specified service index.
  * \return_hpierr
  */
hpi_err_t HPI_Tuner_GetDabAudioServiceName(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to tuner control.
	/** Returns name of indexed programme (max length 16 chars) */
	char *pszAudioServiceName, ///< pointer to name of queried service
	const uint32_t dwStringSize, ///< Length of the string buffer in bytes.
	const uint32_t nIndex ///< index of service
)
{
	HPI_UNUSED(phSubSys);
	return HPI_Control_GetString(
			hControl,
			HPI_TUNER_DABRADIO_AS,
			pszAudioServiceName,
			dwStringSize,
			nIndex);
}

/** Gets the number of DAB audio services.
  *
  * Certain tuners and transmitting stations support multiple services on a
  * single tuner frequency. This API allows the user to get the number of
  * services on the currently tuned channel.
  * \return_hpierr
  */
hpi_err_t HPI_Tuner_GetDabAudioServiceCount(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to tuner control.
	uint32_t *pdwIndex, ///< Use of this argument is deprecated and its value should be ignored.
	uint32_t *pdwCount ///< # of services on ensemble
)
{
	HPI_UNUSED(phSubSys);
	return HPI_ControlParam2Get(phSubSys, hControl, HPI_TUNER_DABRADIO_ASC,
		pdwIndex, pdwCount);
}


/** Gets the DAB Multiplex name.
  *
  * \return_hpierr
  */
hpi_err_t HPI_Tuner_GetDabMultiplexName(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to tuner control.
	char *pszMultiplexName, ///< pointer to string to return the multiplex/ensemble name
	const uint32_t dwStringSize ///< Length of the string buffer in bytes.
)
{
	HPI_UNUSED(phSubSys);
	return HPI_Control_GetString(
			hControl,
			HPI_TUNER_DABRADIO_MULTIPLEX_NAME,
			pszMultiplexName,
			dwStringSize, 0);
}

/** Gets the current DAB audio service information - bitrate, mode etc.
  *
  * \return_hpierr
  */
hpi_err_t HPI_Tuner_GetDabAudioInfo(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to tuner control.
	char *pszInfo, ///< pointer to string to return the DAB audio information
	const uint32_t dwStringSize ///< Length of the string buffer in bytes.
)
{
	HPI_UNUSED(phSubSys);
	return HPI_Control_GetString(
			hControl,
			HPI_TUNER_DABRADIO_AUDIO_INFO,
			pszInfo,
			dwStringSize, 0);
}

#ifndef HPI_BUILD_KERNEL_MODE
/** Get DAB service info
 *
 * This API is called with a particular service index and returns a structure,
 * dab_service_info, that contains information about the service, including id,
 * name, number of components and their ids.
 *
 * \return_hpierr
 * \param hControl Tuner handle
 * \param index Enumeration index, 0 .. num_services-1
 * \param service_info dab_service_info structure
 */
hpi_err_t HPI_Tuner_GetDabServiceInfo(
	hpi_handle_t hControl,
	unsigned int index,
	struct hpi_dab_service_info *service_info
)
{
	return HPI_Control_GetData(hControl, HPI_TUNER_DABRADIO_SERVICE_INFO,
		(char *)service_info, sizeof(*service_info), index, 0, NULL);
}

/** Get DAB component info
 *
 * This API used to find out information about each component. It returns this
 * in the dab_component_info structure including its id, name and its
 * User Application (UA) types of Program Associated Data (PAD) contained in the
 * component. For example a UA type of 7 is EPG and UA type of 2 is a MOT slideshow.
 *
 * \return_hpierr
 *
 * \note This function uses deferred HPI processing.  This means that it must be
 * repeatedly called with the same parameters until it returns something other than
 * HPI_ERROR_CONTROL_NOT_READY. One deferred call per tuner can be in progress
 * simultaneously.
 *
 * \param hControl Tuner handle
 * \param service_id Service ID from service list
 * \param component_id Component ID from service list
 * \param component_info Pointer to struct dab_component_info for result
 */
hpi_err_t HPI_Tuner_GetDabComponentInfo(
	hpi_handle_t hControl,
	uint32_t service_id,
	uint16_t component_id,
	struct hpi_dab_component_info *component_info
)
{
	return HPI_Control_GetData(hControl, HPI_TUNER_DABRADIO_COMPONENT_INFO,
		(char *)component_info, sizeof(*component_info), service_id, component_id, NULL);
}

/** Get DAB data packet
 *
 * This returns data into a user-supplied buffer (A buffer of 8KiB will accomodate
 * the maximum size packet).
 * This function should be polled at least every 50ms.
 *
 * The beginning of the buffer is a struct hpi_service_data_packet_info,
 * containing information about how to interpret the rest of the buffer data.
 *
 * hpi_service_data_packet_info.data_type indicates the kind of data packet;
 * For example, a .data_type of 128, means that the rest of the buffer contains DLS text.
 * hpi_service_data_packet_info.buff_count is number of remaining packets on the tuner.
 *
 * \return_hpierr
 * The function will return HPI_ERROR_CONTROL_NOT_READY if there is no packet available.
 *
 * \param hControl - tuner handle
 * \param buffer - buffer for component info structure, recommend 8KiB
 * \param buf_len - size of buffer
 * \param data_len - actual number of bytes returned into buffer OR
 *                   if retval==HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL, the minimum
 *                   buffer size needed
 * \param next_poll_interval_ms - recommended delay before calling this function
 *                                again, typically 50ms.
 */
hpi_err_t HPI_Tuner_GetDabDataPacket(
	hpi_handle_t hControl,
	char *buffer,
	const size_t buf_len,
	size_t *data_len,
	int *next_poll_interval_ms
)
{
	hpi_err_t err;
	int poll_ms = 50;

	err = HPI_Control_GetData(hControl, HPI_TUNER_DABRADIO_DIGITAL_DATA,
		buffer, buf_len, 0, 0, data_len);

	/* In case of overflow or pending packets set poll interval to 0 */
	if (!err) {
		struct hpi_service_data_packet_info *pb = (struct hpi_service_data_packet_info *)buffer;
		if ((pb->flags & 0x3) || pb->buff_count)
			poll_ms = 0;
	}

	if (next_poll_interval_ms)
		*next_poll_interval_ms = poll_ms;

	return err;
}

/** Start or stop a DAB data service
 *
 * A particular service/component is then started (or stopped) using this API.
 * If the component contains audio, then it will start being rendered (and any
 * previous audio component will be stopped). If the component contains PAD or
 * is a data-only component, data packets will start accumulating in a buffer.
 * If the component is non-audio, it can be enabled concurrently to other components.
 *
 * \return_hpierr
 *
 * \note This function uses deferred HPI processing.  This means that it must be repeatedly
 * called with the same parameters until it returns something other than
 * HPI_ERROR_CONTROL_NOT_READY. One deferred call per tuner can be in progress
 * simultaneously.
 *
 * \param hControl - tuner handle
 * \param service_id - from service list
 * \param component_id - from service list
 * \param start - 1=start service, 0=stop service
 */
hpi_err_t HPI_Tuner_SetDabService(
	hpi_handle_t hControl,
	uint32_t service_id,
	uint16_t component_id,
	int start
)
{
	struct hpi_response_header hr;
	struct hpi_msg_control_data hm;

	HPI_InitMessageResponseV1(&hm.h, sizeof(hm), &hr,  sizeof(hr),
				  HPI_OBJ_CONTROL, HPI_CONTROL_SET_STATE);

	if (hpi_handle_indexes(hControl, &hm.h.wAdapterIndex, &hm.h.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	hm.p.attribute = HPI_TUNER_DABRADIO_SERVICE;
	hm.p.index = service_id;
	hm.p.index2 = component_id;
	hm.p.offset = (uint16_t)start;

	HPI_MessageV1(&hm.h, &hr);
	return hr.wError;
}

/** Get the currently selected Audio service.
 *
 * \return_hpierr
 *
 * This returns the ID of the last audio service that was set.  It will have a zero MSB,
 * indicating that it is an audio service.
 *
 * \param hControl - tuner handle
 * \param service_id - 32 bit service ID
 * \param component_id - 16 bit component ID
 */
hpi_err_t HPI_Tuner_GetDabService(
	hpi_handle_t hControl,
	uint32_t *service_id,
	uint16_t *component_id
)
{
	struct hpi_res_payload_v0 hr;
	struct hpi_msg_control_data hm;

	HPI_InitMessageResponseV1(&hm.h, sizeof(hm), &hr.h,  sizeof(hr),
				  HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE);

	if (hpi_handle_indexes(hControl, &hm.h.wAdapterIndex, &hm.h.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	hm.p.attribute = HPI_TUNER_DABRADIO_SERVICE;

	HPI_MessageV1(&hm.h, &hr.h);

	*service_id = hr.u.c.dwParam1;
	*component_id = (uint16_t)hr.u.c.dwParam2;

	return hr.h.wError;
}
#endif

hpi_err_t HPI_Tuner_SendMsg(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL.
	hpi_handle_t hControl,	///< Handle to tuner control.
	uint8_t *pBuf,	///< pointer to message buffer.
	uint16_t msgSize,	///< Number of bytes to send.
	uint16_t resSize	///< Number of bytes to read back.
)
{
	struct hpi_msg_tuner_msg hm;
	struct hpi_res_tuner_msg hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponseV1(&hm.h, sizeof(hm), &hr.h, sizeof(hr), HPI_OBJ_CONTROL, HPI_CONTROL_SET_STATE);
	if (hpi_handle_indexes(hControl, &hm.h.wAdapterIndex, &hm.h.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	if (msgSize > sizeof(hm.msg_bytes)) {
		return HPI_ERROR_MESSAGE_BUFFER_TOO_SMALL;
	}
	if (resSize > sizeof(hr.res_bytes)) {
		return HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL;
	}
	hm.u.cu.wAttribute = HPI_TUNER_DIRECT_MSG;
	memcpy(hm.msg_bytes, pBuf, msgSize);
	hm.tx_byte_count = msgSize;
	hm.rx_byte_count = resSize;
	HPI_MessageV1(&hm.h, &hr.h);
	memcpy(pBuf, hr.res_bytes, resSize);
	return hr.h.wError;
}

hpi_err_t HPI_Tuner_SetBackgroundProc(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL.
	hpi_handle_t hControl,	///< Handle to tuner control.
	uint8_t enabled	///< Enable/disable flag for background processing.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_SET_STATE);
	if (hpi_handle_indexes(hControl, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.c.wAttribute = HPI_TUNER_BACKGROUND_PROC;
	hm.u.c.dwParam1 = enabled;
	HPI_Message(&hm, &hr);
	return hr.wError;
}

/** Get tuner RDS data. Returns RDS data if there is any.
  * \b ASI87xx - Does not support this function.<br>
  * <b> ASI8821/8921 with ASI172x tuner </b> - Does support this function.
  * \return_hpierr
  * \retval HPI_ERROR_INVALID_DATASIZE if the RDS buffer is now empty.
  */
hpi_err_t HPI_Tuner_GetRDS(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL.
	hpi_handle_t hControl,	///< Handle to tuner control.
	char *pData		///< pointer to 12 element array for returned RDS data (i.e. an array of 12 chars).
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE);
	if (hpi_handle_indexes(hControl, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.c.wAttribute = HPI_TUNER_RDS;
	HPI_Message(&hm, &hr);
	if (pData) {
		*(uint32_t *)&pData[0] = hr.u.cu.tuner.rds.dwData[0];
		*(uint32_t *)&pData[4] = hr.u.cu.tuner.rds.dwData[1];
		*(uint32_t *)&pData[8] = hr.u.cu.tuner.rds.dwBLER;
	}
	return hr.wError;
}
#endif
/** @} */ /* defgroup tuner */
#ifndef HPI_OS_LINUX_KERNEL
/*=========================================================================*/
/** \defgroup pad PAD Control

The PAD control supports the reading of "Program Associated Data" from
a tuner node.  

It supports Radio Data System (RDS) in FM analog mode and Program Services Data (PSD) in HD-Radio mode.

For DAB PAD data support, see the HPI_Tuner_DAB functions
\{
*/
#ifndef HPI_HAS_NO_PAD
/** Get an tuners's HD-Radio station name.
  *
  * This function returns:
  * - nothing when the tuner is in FM analog mode
  * - Station name when the tuner is in HD-Radio mode
  * \return_hpierr
  */
hpi_err_t HPI_PAD_GetChannelName(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to PAD control.
	char *pszString,	///< Pointer to channel/station name string buffer.
	/** The length of the passed in pszString in bytes.
	    Minimum length is HPI_PAD_CHANNEL_NAME_LEN. */
	const uint32_t dwDataLength
)
{
	HPI_UNUSED(phSubSys);
	return HPI_Control_GetString(
		hControl,
		HPI_PAD_CHANNEL_NAME,
		pszString,
		dwDataLength, 0);
}

/** Get tuner's PAD artist information.
  *
  * This function returns:
  * - RDS RT field when the tuner is in FM analog mode
  * - Artist field when the tuner is in HD-Radio mode
  * \return_hpierr
  */
hpi_err_t HPI_PAD_GetArtist(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to PAD control.
	char *pszString,	///< Pointer to artist string buffer.
	/** The length of the passed in pszString in bytes.
		Minimum length is HPI_PAD_ARTIST_LEN. */
	const uint32_t dwDataLength
)
{
	HPI_UNUSED(phSubSys);
	return HPI_Control_GetString(
		hControl,
		HPI_PAD_ARTIST,
		pszString,
		dwDataLength, 0);
}

/** Get tuner's PAD title information.
  *
  * This function returns:
  * - RDS PS field when the tuner is in FM analog mode
  * - Title field when the tuner is in HD-Radio mode
  * \return_hpierr
  */
hpi_err_t HPI_PAD_GetTitle(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to PAD control.
	char *pszString,	///< Pointer to title string buffer.
	/** The length of the passed in pszData in bytes.
		Minimum length is HPI_PAD_TITLE_LEN. */
	const uint32_t dwDataLength
)
{
	HPI_UNUSED(phSubSys);
	return HPI_Control_GetString(
		hControl,
		HPI_PAD_TITLE,
		pszString,
		dwDataLength, 0);
}

/** Get tuner's PAD comment information.
  *
  * \note This function is unimplemented.
  * \return_hpierr
  */
hpi_err_t HPI_PAD_GetComment(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to PAD control.
	char *pszString,	///< Pointer to comment string buffer.
	/** The length of the passed in pszData in bytes.
		Minimum length is HPI_PAD_COMMENT_LEN. */
	const uint32_t dwDataLength
)
{
	HPI_UNUSED(phSubSys);
	return HPI_Control_GetString(
		hControl,
		HPI_PAD_COMMENT,
		pszString,
		dwDataLength, 0);
}

/** Get tuner's PAD program type (PTY).
  *
  * This function returns:
  * - RDS PTY field when the tuner is in FM analog mode
  * - Station Type when the tuner is in HD-Radio mode
  *
  * If the PTY field has not been set by the tuner, HPI_PAD_PROGRAM_TYPE_INVALID (0xffff) is returned.
  * \return_hpierr
  */
hpi_err_t HPI_PAD_GetProgramType(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to PAD control.
	uint32_t *pdwPTY		///< Pointer to returned Program Type.
)
{
	return HPI_ControlParam1Get(phSubSys, hControl, HPI_PAD_PROGRAM_TYPE,
		pdwPTY);
}

/** Get tuner's PAD RDS program identification (PI).
  *
  * This function returns:
  * - RDS PI when the tuner is in FM analog and HD-Radiomode
  *
  * \return_hpierr
  */
hpi_err_t HPI_PAD_GetRdsPI(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to PAD control.
	uint32_t *pdwPI		///< Pointer to returned Program Identification.
)
{
	return HPI_ControlParam1Get(phSubSys, hControl, HPI_PAD_PROGRAM_ID,
		pdwPI);
}

#ifndef HPI_BUILD_KERNEL_MODE
#include <hpirds.h>
/** Translate tuner's PTY into a string.
  * \return_hpierr
  */
hpi_err_t HPI_PAD_GetProgramTypeString(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to PAD control.
	const uint32_t dwDataType,	///< Data type. One of #eHPI_RDS_type.
	const uint32_t nPTY,		///< The PTY value to translate.
	char *pszData,		///< Pointer to the returned data string.
	const uint32_t dwDataLength	///< String length. Should be 32 or larger.
)
{
	HPI_UNUSED(phSubSys);
	HPI_UNUSED(hControl);

	strncpy(pszData,HPI_RDS_Get_PTY_Translate(dwDataType,nPTY),dwDataLength);
	if (nPTY==HPI_PAD_PROGRAM_TYPE_INVALID)
		return HPI_ERROR_INVALID_OPERATION;
	else
		return 0;
}
#endif
#endif /* #ifndef HPI_HAS_NO_PAD */
/** @} */ /* defgroup pads */
#endif

/*=========================================================================*/
/** \defgroup volume Volume Control
Volume controls are usually situated on a "connection" between a source node
and a destination node.
They can also be present on source nodes, such as Ostream or LineIn.
They control the gain/attenuation of the signal passing through them.

For example if you have a -10dB (relative to digital full-scale) signal passing
through a volume control with a gain set to -6dB,
then the output signal would be -16dB.

The units of gain parameters are milliBels (mB),
equivalent to 1/1000 of a Bel, or 1/100 of a decibel.
For example a gain of -14.00dB is represented as -1400,
or (-14 * #HPI_UNITS_PER_dB)

Gain parameters are stereo, stored in a 2 element array, element 0 is the
left channel and element 1 is the right channel.

A gain value of #HPI_GAIN_OFF will set the gain to its maximum attenuation or
mute if the adapter supports it.

While most volume controls are attenuation only, some volume controls support
gain as well. This is adapter and control dependant.
Use the HPI_VolumeGetRange() function to determine if the control supports gain.

\{
*/

/** Get the number of channels supported by this volume control
*/
hpi_err_t HPI_Volume_QueryChannels(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	const hpi_handle_t hVolume,	///< Control to query
	uint32_t *pChannels	///< number of channels
)
{
	return HPI_ControlQuery(phSubSys, hVolume,  HPI_VOLUME_NUM_CHANNELS,
				0, 0, pChannels);
}

/** Set the gain of a volume control.

Setting the gain of a volume control has the side effect that any autofades
currently underway are terminated.
The volume would become that of the current Set command.

\return_hpierr
*/
hpi_err_t HPI_VolumeSetGain(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to volume control.
	short anLogGain[HPI_MAX_CHANNELS]	///< Gain in 100ths of a dB.
)
{
	HPI_UNUSED(phSubSys);
	return HPI_ControlLogSet2(hControl, HPI_VOLUME_GAIN,
				  anLogGain[0], anLogGain[1]);
}

/** Gets the current gain of a volume control.
  * \return_hpierr
  */
hpi_err_t HPI_VolumeGetGain(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to volume control.
	short anLogGain[HPI_MAX_CHANNELS]	///< Gain in 100ths of a dB. If an autofade is in progess, it will be reflected here.
)
{
	return HPI_ControlLogGet2(phSubSys, hControl, HPI_VOLUME_GAIN,
			   &anLogGain[0], &anLogGain[1]);
}

/** Set the mute of a volume control.

Mutes the volume control without affecting the current volume setting.

\return_hpierr
*/
hpi_err_t HPI_VolumeSetMute(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to volume control.
	uint32_t mute	///< mute
)
{
	return HPI_ControlParamSet(phSubSys, hControl, HPI_VOLUME_MUTE, mute, 0);
}

/** Gets the current mute setting of a volume control.
  * \return_hpierr
  */
hpi_err_t HPI_VolumeGetMute(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to volume control.
	uint32_t *mute	///< mute
)
{
	return HPI_ControlParam1Get(phSubSys, hControl, HPI_VOLUME_MUTE,
			   mute);
}

/** Query the range of a volume or level control.
  * Gets the max,min and step of the specified volume control.
  * \return_hpierr
  */
hpi_err_t HPI_VolumeQueryRange(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to volume control.
	short *nMinGain_01dB,	///< Minimum gain setting in 100ths of a dB.
	short *nMaxGain_01dB,	///< Maximum gain setting in 100ths of a dB.
	short *nStepGain_01dB	///< Step size in 100ths of a dB.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE);
	if (hpi_handle_indexes(hControl, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.c.wAttribute = HPI_VOLUME_RANGE;

	HPI_Message(&hm, &hr);
	if (hr.wError) {
		hr.u.c.anLogValue[0] = 0;
		hr.u.c.anLogValue[1] = 0;
		hr.u.c.dwParam1 = 0;
	}
	if (nMinGain_01dB)
		*nMinGain_01dB = hr.u.c.anLogValue[0];
	if (nMaxGain_01dB)
		*nMaxGain_01dB = hr.u.c.anLogValue[1];
	if (nStepGain_01dB)
		*nStepGain_01dB = (short)hr.u.c.dwParam1;
	return hr.wError;
}

#ifndef HPI_OS_LINUX_KERNEL
/** Starts an automatic ramp of the volume control from the current gain
  * setting to the specified setting over the specified duration (in milliseconds).
  * The gain starts at the current gain value and fades up/down
  * to anStopGain0_01dB[] over the specified duration.
  *
  *	The fade profile can be either log or linear.
  *
  * When wProfile==HPI_VOLUME_AUTOFADE_LOG the
  * gain in dB changes linearly over time.
  *
  * When wProfile==HPI_VOLUME_AUTOFADE_LINEAR the gain multiplier changes
  * linearly over time. For example half way through the fade time of a fade
  * from 0dB (100%) to -100dB
  * (approx 0%) the gain will be -6dB (50%).
  *
  * \image html volume_fade_profile.png
  *
  * \return_hpierr
  *
  */

hpi_err_t HPI_VolumeAutoFadeProfile(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to volume control.
	short anStopGain0_01dB[HPI_MAX_CHANNELS],	///< End point of the fade in 0.01ths of a dB.
	uint32_t dwDurationMs,
	/**< Duration of fade in milliseconds. Minimum duration is 20 ms.
	Maximum duration is 100 seconds or 100 000 ms. Durations outside this
	range will be converted to the nearest limit.
	*/
	uint16_t wProfile
	/**< The profile, or shape of the autofade curve.
	Allowed values are #HPI_VOLUME_AUTOFADE_LOG or #HPI_VOLUME_AUTOFADE_LINEAR.
	*/
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);

	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_SET_STATE);
	if (hpi_handle_indexes(hControl, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	memcpy(hm.u.c.anLogValue, anStopGain0_01dB,
		sizeof(short) * HPI_MAX_CHANNELS);

	hm.u.c.wAttribute = HPI_VOLUME_AUTOFADE;
	hm.u.c.dwParam1 = dwDurationMs;
	hm.u.c.dwParam2 = wProfile;

	HPI_Message(&hm, &hr);

	return hr.wError;
}

/** \deprecated See HPI_VolumeAutoFadeProfile().
  */
hpi_err_t HPI_VolumeAutoFade(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to a volume control.
	short anStopGain0_01dB[HPI_MAX_CHANNELS],	///< The endpoint of the fade.
	uint32_t dwDurationMs	///< Duration of fade in milliseconds.
)
{
	return HPI_VolumeAutoFadeProfile(phSubSys,
		hControl,
		anStopGain0_01dB, dwDurationMs, HPI_VOLUME_AUTOFADE_LOG);
}

/** Enumerate autofade profiles supported by this controls
 (may be none).
*/
hpi_err_t HPI_Volume_QueryAutoFadeProfile(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	const hpi_handle_t hVolume,	///< Control to query
	const uint32_t i,	///< Enumeration index
	uint16_t *wProfile ///< Autofade profile value
)
{
	hpi_err_t e;
	uint32_t u;
	e = HPI_ControlQuery(phSubSys, hVolume, HPI_VOLUME_AUTOFADE, i, 0, &u);
	*wProfile = (uint16_t)u;
	return e;
}
#endif
/** @} */ /* defgroup volume */

#ifndef HPI_OS_LINUX_KERNEL
/*=========================================================================*/
/** \defgroup Vox Vox control
VOX controls are always situated on a destination node of type HPI_DESTNODE_ISTREAM.
The VOX control specifies the signal level required for recording to begin.
Until the signal exceeds the VOX level no samples are recorded to the record stream.
After the VOX level has been exceeded, recording continues until the stream is stopped or reset.

A trigger level of -100 dB indicates that recording begins with any level audio signal.
A single threshold level is used for both left and right channels.
If either channel exceeds the threshold, recording will proceed.

@{
 */

/**
  * Sets the threshold of a VOX control.  Note the threshold is in units of 0.01dB.
  * The threshold will typically range between 0 and -100dB.
  * On startup the VOX control is set to -100 dB.
  * \return_hpierr
  */
hpi_err_t HPI_VoxSetThreshold(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to a VOX control.
	short anGain0_01dB	///< Trigger level in 100ths of a dB. For example a gain of -1400 is -14.00dB.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_SET_STATE);
	if (hpi_handle_indexes(hControl, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.c.wAttribute = HPI_VOX_THRESHOLD;

#ifndef HPI_BUILD_KERNEL_MODE
	/// \bug Requires floating point operations, not usable in kernel mode. Kernel mode uses raw linear value instead.

	// convert db range 0..-100 dB , 0.01dB (20log10) to 0..32767 (-96 dB = 0)
	{
		/*
		   fDB = 20 * log10(fLinear/32767)

		   fLinear = 10^(fDB/20) * 32767

		   Want to avoid using pow() routine, because math libraries in the past
		   have not handled it well.

		   Re-arrange to us log()/exp() (natural log) routines.

		   Re-write 10^(fDB/20) using natural logs.

		   fLinear = exp(log(10.0) * fDB/20.0) * 32767.0;

		 */
		float fDB = (float)anGain0_01dB / 100.0f;	// units are 0.01dB
		float fLinear = 0.0;
		fLinear = (float)(exp(log(10.0) * fDB / 20.0) * 32767.0);
		hm.u.c.anLogValue[0] = (short)(fLinear);	// only use the first index (LEFT)
	}
#else

	hm.u.c.anLogValue[0] = anGain0_01dB;	// only use the first index (LEFT)
#endif

	HPI_Message(&hm, &hr);

	return hr.wError;
}

/**
  * Gets the current threshold of a VOX control. Note the threshold is in units of 0.01dB.
  * The threshold will typically range between 0 and -100dB.
  * On startup the VOX control is set to -100 dB.
  * \return_hpierr
  */
hpi_err_t HPI_VoxGetThreshold(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hControl,	///< Handle to a VOX control.
	short *anGain0_01dB	///< Returned trigger level in 100ths of a dB. For example a gain of -1400 is -14.00dB.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_CONTROL, HPI_CONTROL_GET_STATE);
	if (hpi_handle_indexes(hControl, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.c.wAttribute = HPI_VOX_THRESHOLD;

	HPI_Message(&hm, &hr);

#ifndef HPI_BUILD_KERNEL_MODE
	/// \bug Requires floating point operations, not usable in kernel mode

	// convert db range 0..-100 dB , 0.01dB (20log10) to 0..32767 (-96 dB = 0)
	{
		/*
		   fDB = 20 * log10(fLinear/32767)

		   fLinear = 10^(fDB/20) * 32767

		   Want to avoid using pow() routine, because math libraries in the past
		   have not handled it well.

		   Re-arrange to us log()/exp() (natural log) routines.

		   Re-write 10^(fDB/20) using natural logs.

		   fLinear = exp(log(10.0) * fDB/20.0) * 32767.0;

		 */
		float fDB, fLinear;

		if (hr.u.c.anLogValue[0] == 0) {
			fDB = -100.0;
		} else {
			fLinear = (float)hr.u.c.anLogValue[0];
			fDB = (float)(log(fLinear / 32767.0) * 20.0 /
				log(10.0));
		}

		*anGain0_01dB = (short)(fDB * 100.0);	// only use the first index (LEFT)
	}
#else

	*anGain0_01dB = hr.u.c.anLogValue[0];	// only use the first index (LEFT)
#endif

	return hr.wError;
}

/** @} */ /* defgroup Vox */
#endif

#ifndef HPI_OS_LINUX_KERNEL
/** \defgroup Universal Universal Control
A universal control is a control type that supports abstract mechanisms
for accessing data, thereby making it capable of representing any controllable
parameter.

Multiple related universal controls are created in sequence to form a block.
For example, a tuner block might use 2 universal controls to
represent the band and the frequency of the tuner. Carrying this description
further, the band and frequency would be referred to as parameters of the
tuner block.

A header universal control is used to name the block and specify the number
and location of parameters that follow. The universal control list for the
above tuner example looks like:

<table border=1 cellspacing=0 cellpadding=5>
<tr><td><b>Index</b></td><td><b>Name</b></td><td><b>Description</b></td></tr>
<tr><td>n</td><td>Tuner</td><td>Block header, count = 2</td></tr>
<tr><td>n+1</td><td>Band</td><td>Parameter for the band</td></tr>
<tr><td>n+2</td><td>Frequency</td><td>Parameter for the frequency</td></tr>
</table>

A call to HPI_MixerGetControl() will return only the first control of type
HPI_CONTROL_UNIVERSAL.

Need to add stuff about how to unpack the remaining parameter controls...

@{
 */

/** Retrieve information associated with an universal control

\param phSubSys Vestigial subsys handle (unused), may be set to NULL
\param hC Control handle.
\param info Location where a pointer to the newly allocated buffer is returned.
Info must always be freed by the caller because it may be allocated and contain a (partial)
result even if the function returns an error. \sa HPI_Entity_Free()
\return_hpierr
*/
hpi_err_t HPI_Universal_Info(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hC,
	struct hpi_entity **info
)
{
	uint16_t wAdapterIndex, wObjIndex;

	if (hpi_handle_indexes(hC, &wAdapterIndex, &wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	return HPI_Entity_Get(phSubSys, wAdapterIndex, wObjIndex,
				HPI_CONTROL_GET_INFO, info);
}

/** Returns the current value of an universal control as an entity

\param phSubSys Vestigial subsys handle (unused), may be set to NULL
\param hC Control handle.
\param value Location where a pointer to the newly allocated buffer is returned.
value must always be freed by the caller because it may be allocated and contain a (partial)
result even if the function returns an error.  \sa HPI_Entity_Free()
\return_hpierr
*/
hpi_err_t HPI_Universal_Get(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hC,
	struct hpi_entity **value
)
{
	uint16_t wAdapterIndex, wObjIndex;

	if (hpi_handle_indexes(hC, &wAdapterIndex, &wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	return HPI_Entity_Get(phSubSys, wAdapterIndex, wObjIndex,
				HPI_CONTROL_GET_STATE, value);
}
/** Set the current value of an universal control

\param phSubSys Vestigial subsys handle (unused), may be set to NULL
\param hC Control handle.
\param value Pointer to the entity containing the new value for the universal control.
\return_hpierr
*/
hpi_err_t HPI_Universal_Set(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hC,
	struct hpi_entity *value
)
{
	uint16_t wAdapterIndex, wObjIndex;

	if (hpi_handle_indexes(hC, &wAdapterIndex, &wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;

	return HPI_Entity_Set(phSubSys, wAdapterIndex, wObjIndex,
				HPI_CONTROL_SET_STATE, value);
}


/** @} */ /* defgroup Universal */

/** @} */ /* defgroup mixer_controls */

/** @} */ /* defgroup mixer */

/*=========================================================================*/
/** \defgroup clock Clock
The clock object is a 24 hour clock that runs on the DSP. It keeps time in terms of
hours, minutes, seconds and milliseconds. AudioScience's standard audio adapters do
not support a clcok object. It is used for custom OEM specific tuner code.
@{
*/
/* Opens the clock on a particular adapter.  It takes as input the handle to
the subsystem (phSubSys) and the adapter index (wAdapterIndex) and returns a
handle to the clock (hClock).
\return_hpierr
*/
hpi_err_t HPI_ClockOpen(
	const hpi_hsubsys_t *phSubSys,	//< Vestigial subsys handle (unused), may be set to NULL
	uint16_t wAdapterIndex,	//< Adapter index.
	hpi_handle_t * phClock	//< Returned handle to a clock object.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_CLOCK, HPI_CLOCK_OPEN);
	hm.wAdapterIndex = wAdapterIndex;

	HPI_Message(&hm, &hr);

	if (hr.wError == 0)
		*phClock = HPI_IndexesToHandle(HPI_OBJ_CLOCK, wAdapterIndex, 0);	// only 1 clock obj per adapter
	else
		*phClock = 0;

	return hr.wError;
}

/* Set the time on the DSP's 24 hour clock.
\return_hpierr
*/
hpi_err_t HPI_ClockSetTime(
	const hpi_hsubsys_t *phSubSys,	//< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hClock,	//< Handle to clock object.
	uint16_t wHour,		//< The hour in the range of 0-23 (this is a 24 hour clock).
	uint16_t wMinute,		//< The minute in the range of 0-59.
	uint16_t wSecond,		//< The second in the range 0-59.
	uint16_t wMilliSeconds	//< The milliseconds in the range of 0-1000.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_CLOCK, HPI_CLOCK_SET_TIME);
	if (hpi_handle_indexes(hClock, &hm.wAdapterIndex, NULL))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.t.wMilliSeconds = wMilliSeconds;
	hm.u.t.wSeconds = wSecond;
	hm.u.t.wMinutes = wMinute;
	hm.u.t.wHours = wHour;
	HPI_Message(&hm, &hr);

	return hr.wError;
}

/* Get the time from the DSPs 24 hour clock.
\return_hpierr
*/
hpi_err_t HPI_ClockGetTime(
	const hpi_hsubsys_t *phSubSys,	//< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hClock,	//< Handle to clock object.
	uint16_t *pwHour,		//< Returned hour in the range of 0-23 (this is a 24 hour clock).
	uint16_t *pwMinute,		//< Returned minute in the range of 0-59.
	uint16_t *pwSecond,		//< Returned second in the range 0-59.
	uint16_t *pwMilliSecond	//< Returned milliseconds in the range of 0-1000.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_CLOCK, HPI_CLOCK_GET_TIME);
	if (hpi_handle_indexes(hClock, &hm.wAdapterIndex, NULL))
		return HPI_ERROR_INVALID_HANDLE;
	HPI_Message(&hm, &hr);
	if (pwMilliSecond)
		*pwMilliSecond = hr.u.t.wMilliSeconds;
	if (pwSecond)
		*pwSecond = hr.u.t.wSeconds;
	if (pwMinute)
		*pwMinute = hr.u.t.wMinutes;
	if (pwHour)
		*pwHour = hr.u.t.wHours;

	return hr.wError;
}
/** @} */ /* defgroup clcck */

/*===========================================================================*/
/** \defgroup gpio GPIO
 The GPIO object on an adapter reperesents a number of input bits
 that may be individually sensed and a number of digital output bits that
 may be individually set.

There is at most one GPIO object per adapter.

On an adapter such as an ASI4346, the bit outputs control relay closurers.
HPI_GpioWriteBit() can be used to set the state of each of the relays.
Similarly, the inputs on the ASI4346 are mapped 1 to 1 to opto isolated inputs.
 \{
*/

/**  Opens the GPIO on a particular adapter for reading and writing.
  It returns a handle to the GPIO object (hGpio) and the number of input
  and output bits (*pwNumberInputBits,*pwNumberOutputBits).
  If the adapter does not have any GPIO functionality, the function will
  return an error.
  * \return_hpierr
  */
hpi_err_t HPI_GpioOpen(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	uint16_t wAdapterIndex,	///< Get GPIO handle for this adapter.
	hpi_handle_t * phGpio,	///< Returned handle to GPIO object.
	uint16_t *pwNumberInputBits,///< Returned number of GPIO inputs.
	uint16_t *pwNumberOutputBits///< Returned number of GPIO outputs.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_GPIO, HPI_GPIO_OPEN);
	hm.wAdapterIndex = wAdapterIndex;

	HPI_Message(&hm, &hr);

	if (hr.wError == 0) {
		*phGpio = HPI_IndexesToHandle(HPI_OBJ_GPIO, wAdapterIndex, 0);	// only 1 digital i/o obj per adapter
		if (pwNumberInputBits)
			*pwNumberInputBits = hr.u.l.wNumberInputBits;
		if (pwNumberOutputBits)
			*pwNumberOutputBits = hr.u.l.wNumberOutputBits;
	} else
		*phGpio = 0;
	return hr.wError;
}

/** Read a particular bit from an adapter's GPIO input port.
  * \return_hpierr
  */
hpi_err_t HPI_GpioReadBit(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hGpio,	///< Handle to GPIO object.
	uint16_t wBitIndex,		///< Bit index to read.
	uint16_t *pwBitData		///< Returned state of the input.  A "1" means the input has been set.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_GPIO, HPI_GPIO_READ_BIT);
	if (hpi_handle_indexes(hGpio, &hm.wAdapterIndex, NULL))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.l.wBitIndex = wBitIndex;

	HPI_Message(&hm, &hr);

	*pwBitData = hr.u.l.wBitData[0];
	return hr.wError;
}

/** Read all bits from an adapter's GPIO input ports.
  * \return_hpierr
  */
hpi_err_t HPI_GpioReadAllBits(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hGpio,	///< Handle to GPIO object.
	/** Returned input states.  pwBitData
	  should point to an array of uint16_t bits[4].
	  Bit 0 refers to the 1st GPIO input.*/
	uint16_t awAllBitData[4]
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_GPIO, HPI_GPIO_READ_ALL);
	if (hpi_handle_indexes(hGpio, &hm.wAdapterIndex, NULL))
		return HPI_ERROR_INVALID_HANDLE;

	HPI_Message(&hm, &hr);

	if (awAllBitData) {
		awAllBitData[0] = hr.u.l.wBitData[0];
		awAllBitData[1] = hr.u.l.wBitData[1];
		awAllBitData[2] = hr.u.l.wBitData[2];
		awAllBitData[3] = hr.u.l.wBitData[3];
	}
	return hr.wError;
}

/** Write a particular bit to an adapter's GPIO output port.
  * \return_hpierr
  */
hpi_err_t HPI_GpioWriteBit(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hGpio,	///< Handle to GPIO object.
	uint16_t wBitIndex, ///< An index which addresses one of the input bits.
	uint16_t wBitData		///<The state to set the output to. A "1" turns the output on.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_GPIO, HPI_GPIO_WRITE_BIT);
	if (hpi_handle_indexes(hGpio, &hm.wAdapterIndex, NULL))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.l.wBitIndex = wBitIndex;
	hm.u.l.wBitData = wBitData;

	HPI_Message(&hm, &hr);

	return hr.wError;
}

/** Read back the current status of all GPIO outputs on an adapter.
  * \return_hpierr
  */
hpi_err_t HPI_GpioWriteStatus(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hGpio,	///< Handle to GPIO object.
	/** Returned output states.  pwBitData
	  should point to an array of uint16_t bits[4].
	  Bit 0 refers to the 1st GPIO output.*/
	uint16_t awAllBitData[4]
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_GPIO, HPI_GPIO_WRITE_STATUS);
	if (hpi_handle_indexes(hGpio, &hm.wAdapterIndex, NULL))
		return HPI_ERROR_INVALID_HANDLE;

	HPI_Message(&hm, &hr);

	if (awAllBitData) {
		awAllBitData[0] = hr.u.l.wBitData[0];
		awAllBitData[1] = hr.u.l.wBitData[1];
		awAllBitData[2] = hr.u.l.wBitData[2];
		awAllBitData[3] = hr.u.l.wBitData[3];
	}
	return hr.wError;
}
/** @} */ /* defgroup gpio */

#ifdef HPI_BUILD_ASYNC_EVENTS
/*=========================================================================*/
/** \defgroup async Asynchronous Event Handling Functions
The asynchronous event module is designed to report events that occur on an adapter to an
\em interested application.

An Async object can be used to reciev notifications for both GPIO and other signal detection events.
A typical coding sequence would look something like:

\code

// The below code represents the bits that would go in an application.

	...
	// application startup section
	HPI_AsyncEventOpen(pSS,0,&hAsync);
	CreateThread(ThreadAsync);
	...

	...
	// application shutdown
	HPI_AsyncEventOpen(pSS,hAsync);
	...

#define MAX_EVENTS 10
void ThreadAsync()
{
	int ExitSignalled=0;
	hpi_err_t err;
	tHPIAsyncEvent e[MAX_EVENTS]

	while(!ExitSignalled)
	{
		err = hpi_err_t HPI_AsyncEventWait(pSS, hAsync,
			MAX_EVENTS,
			&e[0],
			&wEvents);

		 if (wError==HPI_ASYNC_TERMINATE_WAIT)
			ExitSignalled=1;
		 else
		 {
			 if (!err)
				for(i=0;i<wEvents;i++)
					CallCodeToProcessEvent(e[i]);
		 }

	}
}

\endcode

 \{
*/

/** Open an ASync object.
Opens a GPIO object and returns a handle to the same.
\return_hpierr
*/
hpi_err_t HPI_AsyncEventOpen(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL.
	uint16_t wAdapterIndex,	///< The adapter index to open the Async object.
	hpi_handle_t * phAsync	///< Returned handle of an ASync object.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ASYNCEVENT, HPI_ASYNCEVENT_OPEN);
	hm.wAdapterIndex = wAdapterIndex;

	HPI_Message(&hm, &hr);

	if (hr.wError == 0)
		// only 1 async obj per adapter
		*phAsync =
			HPI_IndexesToHandle(HPI_OBJ_ASYNCEVENT, wAdapterIndex,
			0);
	else
		*phAsync = 0;
	return hr.wError;

}

/** Closes an ASync object.
\return_hpierr
*/
hpi_err_t HPI_AsyncEventClose(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL.
	hpi_handle_t hAsync	///< Handle of the Async object to close.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ASYNCEVENT, HPI_ASYNCEVENT_CLOSE);
	if (hpi_handle_indexes(hAsync, &hm.wAdapterIndex, NULL))
		return HPI_ERROR_INVALID_HANDLE;

	HPI_Message(&hm, &hr);

	return hr.wError;
}

/** Waits for a asynchronous events.
This call waits for any async event. The calling thread is suspended until an ASync event
is detected. After the async event is detected the call completes and returns information
about the event(s) that occured.
\return_hpierr
*/
hpi_err_t HPI_AsyncEventWait(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL.
	hpi_handle_t hAsync,	///< Handle of an Async object.
	uint16_t wMaximumEvents,	///< Maximum number of events matches size of array passed in pEvents.
	struct hpi_async_event * pEvents,	///< Events are returned here.
	uint16_t *pwNumberReturned	///< Number events returned.
)
{
	HPI_UNUSED(phSubSys);

	return 0;
}

/** Returns the number of asynchronous events waiting.
\return_hpierr
*/
hpi_err_t HPI_AsyncEventGetCount(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL.
	hpi_handle_t hAsync,	///< Handle of an Async object.
	uint16_t *pwCount		/// Returned number of events waiting.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ASYNCEVENT, HPI_ASYNCEVENT_GETCOUNT);
	if (hpi_handle_indexes(hAsync, &hm.wAdapterIndex, NULL))
		return HPI_ERROR_INVALID_HANDLE;

	HPI_Message(&hm, &hr);

	if (hr.wError == 0)
		if (pwCount)
			*pwCount = hr.u.as.u.count.wCount;

	return hr.wError;
}

/** Returns single or many asynchronous events.
This call will read any waiting events from the asynchronous event queue and
return a description of the event. It is non-blocking.
\return_hpierr
*/
hpi_err_t HPI_AsyncEventGet(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL.
	hpi_handle_t hAsync,	///< Handle of an Async object.
	uint16_t wMaximumEvents,	///< Maximum number of events matches size of array passed in pEvents.
	struct hpi_async_event * pEvents,	///< Events are returned here.
	uint16_t *pwNumberReturned	///< Number events returned.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_ASYNCEVENT, HPI_ASYNCEVENT_GET);
	if (hpi_handle_indexes(hAsync, &hm.wAdapterIndex, NULL))
		return HPI_ERROR_INVALID_HANDLE;

	HPI_Message(&hm, &hr);
	if (!hr.wError) {
		memcpy(pEvents, &hr.u.as.u.event,
			sizeof(struct hpi_async_event));
		*pwNumberReturned = 1;
	}

	return hr.wError;
}

/** @} */ /* defgroup async */
#else
hpi_err_t HPI_AsyncEventOpen(const hpi_hsubsys_t *s, uint16_t i, hpi_handle_t *a)
{
	HPI_UNUSED(s);
	HPI_UNUSED(i);
	HPI_UNUSED(a);

	return HPI_ERROR_UNIMPLEMENTED;
}

hpi_err_t HPI_AsyncEventClose(const hpi_hsubsys_t *s, hpi_handle_t h)
{
	HPI_UNUSED(s);
	HPI_UNUSED(h);

	return HPI_ERROR_UNIMPLEMENTED;
}

hpi_err_t HPI_AsyncEventWait(const hpi_hsubsys_t *s, hpi_handle_t h,
		uint16_t m, struct hpi_async_event *e, uint16_t *n)
{
	HPI_UNUSED(s);
	HPI_UNUSED(h);
	HPI_UNUSED(e);
	HPI_UNUSED(n);

	return HPI_ERROR_UNIMPLEMENTED;
}

hpi_err_t HPI_AsyncEventGetCount(const hpi_hsubsys_t *s, hpi_handle_t h,
		uint16_t *c)
{
	HPI_UNUSED(s);
	HPI_UNUSED(h);
	HPI_UNUSED(c);

	return HPI_ERROR_UNIMPLEMENTED;
}

hpi_err_t HPI_AsyncEventGet(const hpi_hsubsys_t *s, hpi_handle_t h,
		uint16_t m, struct hpi_async_event *e, uint16_t *n)
{
	HPI_UNUSED(s);
	HPI_UNUSED(h);
	HPI_UNUSED(m);
	HPI_UNUSED(e);
	HPI_UNUSED(n);

	return HPI_ERROR_UNIMPLEMENTED;
}

#endif

/*=========================================================================*/
/** \defgroup nvmem Nonvolatile memory

 Some adapters contain non-volatile memory containing a number of bytes
 The number of data words is adapter dependant and can be obtained
 from the *pwSizeInBytes parameter returned from the Open function

There can be at most one nvmemory object per adapter.

@{
*/
/**
Opens the non-volatile memory on a particular adapter for reading and writing.
It takes as input the handle to the subsytem (phSubSys) and the adapter index
(wAdapterIndex) and returns a handle to the non-volatile memory (hNvMemory)
and the size of the memory in bytes (wSizeInBytes). If the adapter does not
have any non-volatile memory, the function will return an error.
* \return_hpierr
*/

hpi_err_t HPI_NvMemoryOpen(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	uint16_t wAdapterIndex,	///< Get nvmemory handle on this adapter
	hpi_handle_t * phNvMemory,	///< Handle to an HPI_NVMEMORY object
	uint16_t *pwSizeInBytes	///< size of the nv memory in bytes
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_NVMEMORY, HPI_NVMEMORY_OPEN);
	hm.wAdapterIndex = wAdapterIndex;

	HPI_Message(&hm, &hr);

	if (hr.wError == 0) {
		*phNvMemory = HPI_IndexesToHandle(HPI_OBJ_NVMEMORY, wAdapterIndex, 0);	// only 1 nv-memory obj per adapter
		if (pwSizeInBytes)
			*pwSizeInBytes = hr.u.n.wSizeInBytes;
	} else
		*phNvMemory = 0;
	return hr.wError;
}

/**
Reads a byte from an adapters non-volatile memory. The input is a handle to
the non-volatile memory (hNvMemory - returned from HPI_NvMemoryOpen())
and an index which addresses one of the bytes in the memory (wIndex).
The index may range from 0 to SizeInBytes-1 (returned by HPI_NvMemoryOpen()).
The byte is returned in *pwData.). An error return of HPI_ERROR_NVMEM_BUSY
indicates that an attempt to access the NvMem was made before the previous
operation has completed. The call should be re-tried.
* \return_hpierr
*/

hpi_err_t HPI_NvMemoryReadByte(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hNvMemory,	///< Handle to an HPI_NVMEMORY object
	uint16_t wIndex,		///< An Index that may range from 0 to SizeInBytes-1 (returned by HPI_NvMemoryOpen())
	uint16_t *pwData		///< Returned data byte
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_NVMEMORY, HPI_NVMEMORY_READ_BYTE);
	if (hpi_handle_indexes(hNvMemory, &hm.wAdapterIndex, NULL))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.n.wAddress = wIndex;

	HPI_Message(&hm, &hr);

	*pwData = hr.u.n.wData;
	return hr.wError;
}

/**
Writes a byte to an adapters non-volatile memory. The input is a handle to
the non-volatile memory (hNvMemory - returned from HPI_NvMemoryOpen()),
an index which addresses one of the bytes in the memory (wIndex) and the
data to write (wData). The index may range from 0 to SizeInBytes-1 (returned
by HPI_NvMemoryOpen()). An error return of HPI_ERROR_NVMEM_BUSY indicates
that an attempt to access the NvMem was made before the previous operation
has completed. The call should be re-tried.
\return_hpierr
*/
hpi_err_t HPI_NvMemoryWriteByte(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hNvMemory,	///< Handle to an HPI_NVMEMORY object
	uint16_t wIndex,		///< An Index that may range from 0 to SizeInBytes-1 (returned by HPI_NvMemoryOpen())
	uint16_t wData		///> Byte of data to write
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_NVMEMORY, HPI_NVMEMORY_WRITE_BYTE);
	if (hpi_handle_indexes(hNvMemory, &hm.wAdapterIndex, NULL))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.n.wAddress = wIndex;
	hm.u.n.wData = wData;

	HPI_Message(&hm, &hr);

	return hr.wError;
}

/** @} */ /* defgroup nvmem */
/*===========================================================================*/
/** \defgroup profile Profile
The Profile object supports profiling of the DSP code.
It should be used as a development tool for measuring DSP code operation.
Comments in AXPROF.H describe the DSP side of the profiling operation. In
general this set of functions is intended for AudioScience internel use.
@{
*/
/** Open all the profiles on a particular adapter.

If the adapter does not have profiling enabled, the function will return an
error, and *phProfile is set to NULL

The complete profile set of all profiles can be thought of as any array of
execution timings/profiles. Each indexed profile corresponds to the execution
of a particular segment of DSP code.

Note that HPI_ProfileStartAll() must be called after HPI_ProfileOpenAll() to
start the profiling operation on the DSP.

\return_hpierr
*/
hpi_err_t HPI_ProfileOpenAll(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	uint16_t wAdapterIndex,	///< Adapter index.
	uint16_t wProfileIndex,	///< Corresponds to DSP index.
	hpi_handle_t * phProfile,	///< Returned profile handle.
	uint16_t *pwMaxProfiles	///< Returned maximum number of profile bins supported.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_PROFILE, HPI_PROFILE_OPEN_ALL);
	hm.wAdapterIndex = wAdapterIndex;
	hm.wObjIndex = wProfileIndex;
	HPI_Message(&hm, &hr);

	*pwMaxProfiles = hr.u.p.u.o.wMaxProfiles;
	if (hr.wError == 0)
		*phProfile =
			HPI_IndexesToHandle(HPI_OBJ_PROFILE, wAdapterIndex,
			wProfileIndex);
	else
		*phProfile = 0;
	return hr.wError;
}

/** Reads a single profile from the DSP's profile store.
The input is a handle to the profiles (hProfiles - returned from HPI_ProfileOpenAll())
and an index that addresses one of the profiles (wIndex).  The index may range from 0 to
wMaxProfiles  (returned by HPI_ProfileOpenAll()). The return parameters describe the execution of
the profiled section of DSP code.

\return_hpierr
*/
hpi_err_t HPI_ProfileGet(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hProfile,	///< Handle of profile object.
	uint16_t wBinIndex,		///< Index of the profile to retrieve.
	uint16_t *pwProfileInterval,	///< Return number of seconds over which profile counts are accumulated
	uint32_t *pdwTotalTickCount,	///< Return total time spent executing the profiled code (measured in ticks)
	uint32_t *pdwCallCount,	///< Return number of times the profiled code was executed.
	uint32_t *pdwMaxTickCount, ///< Returned maximum ticks for one pass through the profiled code (range 0-8,388,608,).
	uint32_t *pdwTicksPerMillisecond ///< Return the number of ticks in one millisecond.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_PROFILE, HPI_PROFILE_GET);
	if (hpi_handle_indexes(hProfile, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.p.wBinIndex = wBinIndex;
	HPI_Message(&hm, &hr);
	if (pwProfileInterval)
		*pwProfileInterval = hr.u.p.u.t.wProfileInterval;
	if (pdwTotalTickCount)
		*pdwTotalTickCount = hr.u.p.u.t.dwTotalTickCount;
	if (pdwCallCount)
		*pdwCallCount = hr.u.p.u.t.dwCallCount;
	if (pdwMaxTickCount)
		*pdwMaxTickCount = hr.u.p.u.t.dwMaxTickCount;
	if (pdwTicksPerMillisecond)
		*pdwTicksPerMillisecond = hr.u.p.u.t.dwTicksPerMillisecond;
	return hr.wError;
}

#ifdef HPI_BUILD_INCLUDE_DEPRECATED
/**
\deprecated this function is no longer supported. Please use HPI_ProfileGetUtilization() instead.
*/
hpi_err_t HPI_ProfileGetIdleCount(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hProfile,
	uint16_t wIndex,
	uint32_t *pdwIdleCycles,
	uint32_t *pdwCount
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_PROFILE, HPI_PROFILE_GET_IDLECOUNT);
	if (hpi_handle_indexes(hProfile, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.p.wBinIndex = wIndex;
	HPI_Message(&hm, &hr);
	if (hr.wError) {
		if (pdwIdleCycles)
			*pdwIdleCycles = 0;
		if (pdwCount)
			*pdwCount = 0;
	} else {
		if (pdwIdleCycles)
			*pdwIdleCycles = hr.u.p.u.t.dwTotalTickCount;
		if (pdwCount)
			*pdwCount = hr.u.p.u.t.dwCallCount;
	}
	return hr.wError;
}
#endif

/** Get the DSP utilization in 1/100 of a percent.
\return_hpierr
*/
hpi_err_t HPI_ProfileGetUtilization(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hProfile,	///<  Handle of profile object.
	uint32_t *pdwUtilization	///< Returned DSP utilization in 100ths of a percent.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_PROFILE, HPI_PROFILE_GET_UTILIZATION);
	if (hpi_handle_indexes(hProfile, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	HPI_Message(&hm, &hr);
	if (hr.wError) {
		if (pdwUtilization)
			*pdwUtilization = 0;
	} else {
		if (pdwUtilization)
			*pdwUtilization = hr.u.p.u.t.dwCallCount;
	}
	return hr.wError;
}

/** Get the name of a profile.
A typical adapter can support multiple "named" profiles simultaneously.
This function allows an application (or GUI) to read the names of the
profiles from the DSP so as to correctly label the returned timing information.
\return_hpierr
*/
hpi_err_t HPI_ProfileGetName(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hProfile,	///< Handle of profile object.
	uint16_t wBinIndex,		///< Index of the profile to retrieve the name of.
	char *szName,		///< Pointer to a string that will have the name returned in it.
	uint16_t nNameLength	///< Length of the szName string.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_PROFILE, HPI_PROFILE_GET_NAME);
	if (hpi_handle_indexes(hProfile, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.p.wBinIndex = wBinIndex;
	HPI_Message(&hm, &hr);
	if (hr.wError) {
		if (szName)
			strcpy(szName, "??");
	} else {
		if (szName)
			memcpy(szName, (char *)hr.u.p.u.n.szName,
				nNameLength);
	}
	return hr.wError;
}

/** Start profiling running.
This starts profile counters and timers running. It is up to the user
to periodically call HPI_ProfileGet() to retrieve timing information.
\return_hpierr
*/
hpi_err_t HPI_ProfileStartAll(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hProfile	///<  Handle of profile object.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_PROFILE, HPI_PROFILE_START_ALL);
	if (hpi_handle_indexes(hProfile, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	HPI_Message(&hm, &hr);

	return hr.wError;
}

/** Stop profiling.
When profiling is stopped counters are no longer updated.
\return_hpierr
*/
hpi_err_t HPI_ProfileStopAll(
	const hpi_hsubsys_t *phSubSys,	///< Vestigial subsys handle (unused), may be set to NULL
	hpi_handle_t hProfile	///<  Handle of profile object.
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_PROFILE, HPI_PROFILE_STOP_ALL);
	if (hpi_handle_indexes(hProfile, &hm.wAdapterIndex, &hm.wObjIndex))
		return HPI_ERROR_INVALID_HANDLE;
	HPI_Message(&hm, &hr);

	return hr.wError;
}
/** @} */ /* defgroup profile */

/*=========================================================================*/
// Hide this section from Doxygen since it is not implemented yet.
hpi_err_t HPI_WatchdogOpen(
	const hpi_hsubsys_t *phSubSys,
	uint16_t wAdapterIndex,
	hpi_handle_t * phWatchdog
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_WATCHDOG, HPI_WATCHDOG_OPEN);
	hm.wAdapterIndex = wAdapterIndex;

	HPI_Message(&hm, &hr);

	if (hr.wError == 0)
		*phWatchdog = HPI_IndexesToHandle(HPI_OBJ_WATCHDOG, wAdapterIndex, 0);	// only 1 watchdog obj per adapter
	else
		*phWatchdog = 0;
	return hr.wError;
}

hpi_err_t HPI_WatchdogSetTime(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hWatchdog,
	uint32_t dwTimeMillisec
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_WATCHDOG, HPI_WATCHDOG_SET_TIME);
	if (hpi_handle_indexes(hWatchdog, &hm.wAdapterIndex, NULL))
		return HPI_ERROR_INVALID_HANDLE;
	hm.u.w.dwTimeMs = dwTimeMillisec;

	HPI_Message(&hm, &hr);

	return hr.wError;
}

hpi_err_t HPI_WatchdogPing(
	const hpi_hsubsys_t *phSubSys,
	hpi_handle_t hWatchdog
)
{
	HPI_MESSAGE hm;
	HPI_RESPONSE hr;
	HPI_UNUSED(phSubSys);
	HPI_InitMessageResponse(&hm, &hr, HPI_OBJ_WATCHDOG, HPI_WATCHDOG_PING);
	if (hpi_handle_indexes(hWatchdog, &hm.wAdapterIndex, NULL))
		return HPI_ERROR_INVALID_HANDLE;

	HPI_Message(&hm, &hr);

	return hr.wError;
}
#endif


#ifndef HPI_OS_LINUX_KERNEL
#if defined(HPI_OS_WIN32_USER) || defined(INCLUDE_WINDOWS_ON_LINUX) || defined(HPI_OS_OSX)

#if defined(WAVE_FORMAT_EXTENSIBLE) && defined(_WAVEFORMATEXTENSIBLE_)
static unsigned char PcmSubformatGUID[16] =
	{ 1, 0, 0, 0, 0, 0, 0x10, 0, 0x80, 0, 0, 0xAA, 0, 0x38, 0x9B, 0x71 };

static unsigned char IeeeFloatSubformatGUID[16] =
	{ 3, 0, 0, 0, 0, 0, 0x10, 0, 0x80, 0, 0, 0xAA, 0, 0x38, 0x9B, 0x71 };
#endif

hpi_err_t HPI_WaveFormatToHpiFormat(
	const PWAVEFORMATEX lpFormatEx,
	struct hpi_format *pHpiFormat
)
{
	hpi_err_t err = 0;

	switch (lpFormatEx->wFormatTag) {
#if defined(WAVE_FORMAT_EXTENSIBLE) && defined(_WAVEFORMATEXTENSIBLE_)
	case WAVE_FORMAT_EXTENSIBLE:
		// Make sure the subformat is PCM or IEEE float
		if (memcmp
			(&((PWAVEFORMATEXTENSIBLE) lpFormatEx)->SubFormat,
				IeeeFloatSubformatGUID, 16) == 0) {
			if (lpFormatEx->wBitsPerSample == 32)
				pHpiFormat->wFormat = HPI_FORMAT_PCM32_FLOAT;
			else {
				pHpiFormat->wFormat = 0;
				err = HPI_ERROR_INVALID_FORMAT;
			}
			break;
		}else if (memcmp
			(&((PWAVEFORMATEXTENSIBLE) lpFormatEx)->SubFormat,
				PcmSubformatGUID, 16) != 0) {
			err = HPI_ERROR_INVALID_FORMAT;
			break;
		}
		// else fallthrough for PcmSubformatGUID
#endif
	case WAVE_FORMAT_PCM:
		//DBGPRINTF0(DEBUG_MASK_WOD_CUSTOM,TEXT("PCM"));
		if (lpFormatEx->wBitsPerSample == 16)
			pHpiFormat->wFormat = HPI_FORMAT_PCM16_SIGNED;
		else if (lpFormatEx->wBitsPerSample == 8)
			pHpiFormat->wFormat = HPI_FORMAT_PCM8_UNSIGNED;
		else if (lpFormatEx->wBitsPerSample == 24)
			pHpiFormat->wFormat = HPI_FORMAT_PCM24_SIGNED;
		else if (lpFormatEx->wBitsPerSample == 32)
			pHpiFormat->wFormat = HPI_FORMAT_PCM32_SIGNED;
		else {
			pHpiFormat->wFormat = 0;
			err = HPI_ERROR_INVALID_FORMAT;
		}
		break;
	case WAVE_FORMAT_IEEE_FLOAT:
		if (lpFormatEx->wBitsPerSample == 32)
			pHpiFormat->wFormat = HPI_FORMAT_PCM32_FLOAT;
		else {
			pHpiFormat->wFormat = 0;
			err = HPI_ERROR_INVALID_FORMAT;
		}
		break;

	case WAVE_FORMAT_DOLBY_AC2:
		//DBGPRINTF0(DEBUG_MASK_WOD_CUSTOM,TEXT("AC2"));
		pHpiFormat->wFormat = HPI_FORMAT_DOLBY_AC2;
		break;

	case WAVE_FORMAT_MPEG:
		switch (((MPEG1WAVEFORMAT *) lpFormatEx)->fwHeadLayer) {
		case ACM_MPEG_LAYER1:
			pHpiFormat->wFormat = HPI_FORMAT_MPEG_L1;
			break;
		case ACM_MPEG_LAYER2:
			pHpiFormat->wFormat = HPI_FORMAT_MPEG_L2;
			break;
		case ACM_MPEG_LAYER3:
			pHpiFormat->wFormat = HPI_FORMAT_MPEG_L3;
			break;
		default:
			pHpiFormat->wFormat = HPI_FORMAT_MPEG_L2;
			break;	// really should be error
		}
		pHpiFormat->dwBitRate =
			((MPEG1WAVEFORMAT *) lpFormatEx)->dwHeadBitrate;
		if (pHpiFormat->dwBitRate == 0)
			pHpiFormat->dwBitRate = 256000L;	// must have a default
		break;
	case WAVE_FORMAT_MPEGLAYER3:
		pHpiFormat->wFormat = HPI_FORMAT_MPEG_L3;
		pHpiFormat->dwBitRate =
			((MPEGLAYER3WAVEFORMAT *) lpFormatEx)->wfx.
			nAvgBytesPerSec * 8;
		if (pHpiFormat->dwBitRate == 0)
			pHpiFormat->dwBitRate = 256000L;	// must have a default
		break;

	default:
		err = HPI_ERROR_INVALID_FORMAT;
	}
	pHpiFormat->wChannels = lpFormatEx->nChannels;
	pHpiFormat->dwSampleRate = lpFormatEx->nSamplesPerSec;
	pHpiFormat->dwAttributes = 0;
	pHpiFormat->wModeLegacy = 0;
	pHpiFormat->wUnused = 0;

	return err;
}

hpi_err_t HPI_HpiFormatToWaveFormat(
	const struct hpi_format *pHpiFormat,
	PWAVEFORMATEX lpFormatEx
)
{
	hpi_err_t err = 0;

	lpFormatEx->cbSize = 0;

	lpFormatEx->nChannels = pHpiFormat->wChannels;
	lpFormatEx->nSamplesPerSec = pHpiFormat->dwSampleRate;

	switch (pHpiFormat->wFormat) {
	case HPI_FORMAT_PCM8_UNSIGNED:
		lpFormatEx->nAvgBytesPerSec =
			pHpiFormat->wChannels * pHpiFormat->dwSampleRate;
		lpFormatEx->nBlockAlign = pHpiFormat->wChannels;
		lpFormatEx->wBitsPerSample = 8;
		lpFormatEx->wFormatTag = WAVE_FORMAT_PCM;
		break;
	case HPI_FORMAT_PCM16_SIGNED:
		lpFormatEx->nAvgBytesPerSec =
			2 * pHpiFormat->wChannels * pHpiFormat->dwSampleRate;
		lpFormatEx->nBlockAlign = 2 * pHpiFormat->wChannels;
		lpFormatEx->wBitsPerSample = 16;
		lpFormatEx->wFormatTag = WAVE_FORMAT_PCM;
		break;
	case HPI_FORMAT_PCM24_SIGNED:
		lpFormatEx->nAvgBytesPerSec =
			3 * pHpiFormat->wChannels * pHpiFormat->dwSampleRate;
		lpFormatEx->nBlockAlign = 3 * pHpiFormat->wChannels;
		lpFormatEx->wBitsPerSample = 24;
		lpFormatEx->wFormatTag = WAVE_FORMAT_PCM;
		break;

	case HPI_FORMAT_PCM32_SIGNED:
		lpFormatEx->nAvgBytesPerSec =
			4 * pHpiFormat->wChannels * pHpiFormat->dwSampleRate;
		lpFormatEx->nBlockAlign = 4 * pHpiFormat->wChannels;
		lpFormatEx->wBitsPerSample = 32;
		lpFormatEx->wFormatTag = WAVE_FORMAT_PCM;
		break;

	case HPI_FORMAT_PCM32_FLOAT:
		lpFormatEx->nAvgBytesPerSec =
			4 * pHpiFormat->wChannels * pHpiFormat->dwSampleRate;
		lpFormatEx->nBlockAlign = 4 * pHpiFormat->wChannels;
		lpFormatEx->wBitsPerSample = 32;
		lpFormatEx->wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
		break;

	case HPI_FORMAT_AA_TAGIT1_HITS:
		lpFormatEx->nAvgBytesPerSec =
			2 * pHpiFormat->wChannels * pHpiFormat->dwSampleRate;
		lpFormatEx->nBlockAlign = 2 * pHpiFormat->wChannels;
		lpFormatEx->wBitsPerSample = 16;
		lpFormatEx->wFormatTag = WAVE_FORMAT_DEVELOPMENT;
		break;

		//this is setup as MPEG layer 2
	case HPI_FORMAT_MPEG_L2:
	case HPI_FORMAT_MPEG_L3:
		lpFormatEx->wFormatTag = WAVE_FORMAT_MPEG;
		lpFormatEx->wBitsPerSample = 0;
		// this is for 44.1kHz, stereo, 256kbs
		lpFormatEx->nBlockAlign
			=
			(unsigned short)((144 * pHpiFormat->dwBitRate) /
			pHpiFormat->dwSampleRate);
		lpFormatEx->nAvgBytesPerSec = pHpiFormat->dwBitRate / 8;
		lpFormatEx->cbSize = 22;
		if (pHpiFormat->wFormat == HPI_FORMAT_MPEG_L2)
			((MPEG1WAVEFORMAT *) lpFormatEx)->fwHeadLayer =
				ACM_MPEG_LAYER2;
		else
			((MPEG1WAVEFORMAT *) lpFormatEx)->fwHeadLayer =
				ACM_MPEG_LAYER3;
		//this makes the desired rather than actual
		((MPEG1WAVEFORMAT *) lpFormatEx)->dwHeadBitrate =
			pHpiFormat->dwBitRate;
		if (pHpiFormat->wChannels == 2)
			((MPEG1WAVEFORMAT *) lpFormatEx)->fwHeadMode =
				ACM_MPEG_JOINTSTEREO;
		else
			((MPEG1WAVEFORMAT *) lpFormatEx)->fwHeadMode =
				ACM_MPEG_SINGLECHANNEL;

		((MPEG1WAVEFORMAT *) lpFormatEx)->fwHeadModeExt = 0;	//0x000F;   //??
		((MPEG1WAVEFORMAT *) lpFormatEx)->wHeadEmphasis = 0;
		((MPEG1WAVEFORMAT *) lpFormatEx)->fwHeadFlags = 0;	//ACM_MPEG_ID_MPEG1;
		((MPEG1WAVEFORMAT *) lpFormatEx)->dwPTSLow = 0;
		((MPEG1WAVEFORMAT *) lpFormatEx)->dwPTSHigh = 0;
		break;

	default:
		err = HPI_ERROR_INVALID_FORMAT;
	}

	return err;
}

#endif
/* defined(HPI_OS_WIN32_USER) */
#endif /* define HPI_OS_LINUX_KERNEL */

#ifdef HPI_BUILD_INCLUDE_DEPRECATED
/** Initialize a struct hpi_data structure
\deprecated struct hpi_data struct may be removed from a future version.
To avoid using struct hpi_data switch to using HPI_OutStreamWriteBuf() and HPI_InStreamReadBuf()
*/
hpi_err_t HPI_DataCreate(
	struct hpi_data * pData,	///<[inout] Structure to be initialised
	struct hpi_format *pFormat,	///<[in] format of the data
	uint8_t *pbData,		///<[in] pointer to data buffer
	uint32_t dwDataSize		///<[in] amount of data in buffer
)
{
	struct hpi_msg_data *pMD = (struct hpi_msg_data *)pData;

	HPI_FormatToMsg(&pMD->format, pFormat);

	pMD->pbData = pbData;
	pMD->dwDataSize = dwDataSize;
	return 0;
}
#endif
/** @} */ /* defgroup utility */
