/******************************************************************************
 \file hpicmn.c

 Common functions used by hpixxxx.c modules

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 )

*******************************************************************************/
#define SOURCEFILE_NAME "hpicmn.c"

#include "hpi_internal.h"
#include "hpidebug.h"
#include "hpimsginit.h"

#include "hpicmn.h"
#ifndef HPI_BUILD_SANITISE
#ifndef HPI_BUILD_ALSA
#include "hpicheck.h"
#endif
#endif

#ifndef HPI_BUILD_NO_SHARED_GLOBALS
static struct {
#ifdef HPI_LOCKING
	HpiOs_Mutex aListLock;
#endif
	struct hpi_adapter_obj *adapter[HPI_MAX_ADAPTERS];
	uint16_t gwNumAdapters;
#ifndef HPI_BUILD_REASSIGN_DUPLICATE_ADAPTER_IDX
	struct hpi_duplicate_index_adapter_obj
		dup_idx_adapters[HPI_MAX_ADAPTERS];
	uint16_t gwNumDuplicateIndexAdapters;
#endif
} adapters;

#endif // HPI_BUILD_NO_SHARED_GLOBALS

/**
* Given an HPI Message that was sent out and a response that was received,
* validate that the response has the correct fields filled in,
* i.e ObjectType, Function etc
**/
hpi_err_t HpiValidateResponse(
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
	if (phr->wType != HPI_TYPE_RESPONSE) {
		HPI_DEBUG_LOG1(ERROR, "Header type %d invalid\n", phr->wType);
		return HPI_ERROR_INVALID_RESPONSE;
	}

	if (phr->wObject != phm->wObject) {
		HPI_DEBUG_LOG1(ERROR, "Header object %d invalid\n", phr->wObject);
		return HPI_ERROR_INVALID_RESPONSE;
	}

	if (phr->wFunction != phm->wFunction) {
		HPI_DEBUG_LOG1(ERROR, "Header function %d invalid\n", phr->wFunction);
		return HPI_ERROR_INVALID_RESPONSE;
	}

	return 0;
}
#ifndef HPI_BUILD_NO_SHARED_GLOBALS
hpi_err_t HpiAddAdapter(struct hpi_adapter_obj *pao)
{
	hpi_err_t retval = 0;
	//HPI_ASSERT(pao->type);

	HpiOs_Mutex_Lock(&adapters.aListLock);

	if (pao->index >= HPI_MAX_ADAPTERS) {
		retval = HPI_ERROR_BAD_ADAPTER_NUMBER;
		goto unlock;
	}

	if (adapters.adapter[pao->index]) {
#ifdef HPI_BUILD_REASSIGN_DUPLICATE_ADAPTER_IDX
		int a;
		for (a = HPI_MAX_ADAPTERS - 1; a >= 0; a--) {
			if (!adapters.adapter[a]) {
				HPI_DEBUG_LOG3(WARNING,
					"ASI%X duplicate index %d moved to %d\n",
					pao->type, pao->index, a);
				pao->index = a;
				break;
			}
		}
		if (a < 0)
#else
		if ( adapters.gwNumDuplicateIndexAdapters < HPI_MAX_ADAPTERS ) {
			struct hpi_duplicate_index_adapter_obj * dia =
				&adapters.dup_idx_adapters[
					adapters.gwNumDuplicateIndexAdapters];
			dia->type = pao->type;
			dia->index = pao->index;
			adapters.gwNumDuplicateIndexAdapters++;
		}
#endif
		{
			retval = HPI_ERROR_DUPLICATE_ADAPTER_NUMBER;
			goto unlock;
		}
	}
	adapters.adapter[pao->index] = pao;
	adapters.gwNumAdapters++;

unlock:
	HpiOs_Mutex_Unlock(&adapters.aListLock);
	return retval;
}

void HpiRemoveAdapter(struct hpi_adapter_obj *pao)
{
	if (!pao ||  (pao->index >= HPI_MAX_ADAPTERS))
		return;

	HpiOs_Mutex_Lock(&adapters.aListLock);
	if (adapters.adapter[pao->index])
		adapters.gwNumAdapters--;
	adapters.adapter[pao->index] = NULL;
	HpiOs_Mutex_Unlock(&adapters.aListLock);
}

/** Get adapter data for given adapter index
 * \retval NULL if index is invalid,
 * \retval pointer to valid hpi_adapter_obj struct
 */
struct hpi_adapter_obj *HpiGetAdapter(uint16_t wAdapterIndex)
{
	if (wAdapterIndex >= HPI_MAX_ADAPTERS) {
		HPI_DEBUG_LOG1(VERBOSE,
			"FindAdapter invalid index %d\n",
			wAdapterIndex);
		return NULL;
	}

	return adapters.adapter[wAdapterIndex];
}

/**
*
* wipe an adapters structure.
*
**/
static void WipeAdapterList(void)
{
	memset(&adapters, 0, sizeof(adapters));
}

static void Subsys_GetAdapter(
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
	int nCount = phm->wObjIndex;
	int i;
	uint16_t wIndex = 0;
	uint16_t wType = 0;

	/* find the nCount'th nonzero adapter in array */
	for (i = 0; i < HPI_MAX_ADAPTERS; i++) {
		if (adapters.adapter[i]) {
			if (!nCount) {
				wIndex = adapters.adapter[i]->index;
				wType = adapters.adapter[i]->type;
				break;
			}
			nCount--;
		}
	}
#ifndef HPI_BUILD_REASSIGN_DUPLICATE_ADAPTER_IDX
	for (i = 0; !wType && i < HPI_MAX_ADAPTERS; i++) {
		if (adapters.dup_idx_adapters[i].type) {
			if (!nCount) {
				wIndex = adapters.dup_idx_adapters[i].index;
				wType = adapters.dup_idx_adapters[i].type;
				phr->wError = HPI_ERROR_DUPLICATE_ADAPTER_NUMBER;
				break;
			}
			nCount--;
		}
	}
#endif
	phr->u.s.wAdapterIndex = wIndex;
	phr->u.s.wAdapterType = wType;
	if (!wType)
		phr->wError = HPI_ERROR_INVALID_OBJ_INDEX;
}

#endif // HPI_BUILD_NO_SHARED_GLOBALS

static unsigned int ControlCacheAllocCheck(struct hpi_control_cache *pC )
{
	unsigned int i;
	int nCached=0;
	if (!pC)
		return 0;

	if (pC->dwInit)
		return pC->dwInit;

	if (!pC->pCache)
		return 0;

	if (pC->dwControlCount && pC->dwCacheSizeInBytes) {
		char *pMasterCache;
		unsigned int byteCount = 0;

		pMasterCache = (char *)pC->pCache;
		HPI_DEBUG_LOG1(DEBUG, "check %d controls\n", pC->dwControlCount);
		for (i = 0; i < pC->dwControlCount; i++) {
			struct hpi_control_cache_info *info =
				(struct hpi_control_cache_info *)&pMasterCache[byteCount];
			uint16_t ControlIndex = info->ControlIndex;

			if (ControlIndex >= pC->dwControlCount) {
				HPI_DEBUG_LOG2(INFO, "Adap %d control index %d out of range, cache not ready?\n",
						pC->adap_idx,ControlIndex);
				return 0;
			}

			if (!info->nSizeIn32bitWords) {
				if (!i) {
					HPI_DEBUG_LOG1(INFO, "Adap %d cache not ready?\n", pC->adap_idx);
					return 0;
				}
				/* The cache is invalid.
				 * Minimum valid entry size is
				 * sizeof(struct hpi_control_cache_info)
				 */
				HPI_DEBUG_LOG2(ERROR, "Adap %d zero size cache entry %d\n", pC->adap_idx, i);
				break;
			}

			if (info->ControlType) {
				pC->pInfo[ControlIndex] = info;
				nCached++;
			} else {/* dummy cache entry */
				pC->pInfo[ControlIndex] = NULL;
			}

			byteCount += info->nSizeIn32bitWords * 4;

			HPI_DEBUG_LOG5(VERBOSE,
				"nCached %d, pinfo %p index %d type %d size %d\n",
				nCached,
				pC->pInfo[info->ControlIndex],
				info->ControlIndex,info->ControlType,
				info->nSizeIn32bitWords);

			/* quit loop early if whole cache has been scanned.
			 * dwControlCount is the maximum possible entries
			 * but some may be absent from the cache
			 */
			if (byteCount >= pC->dwCacheSizeInBytes)
				break;
			// have seen last control index
			if (info->ControlIndex == pC->dwControlCount - 1)
				break;
		}

		if (byteCount != pC->dwCacheSizeInBytes)
			HPI_DEBUG_LOG3(WARNING, "Adap %d bytecount %d != cache size %d\n",
				pC->adap_idx, byteCount,  pC->dwCacheSizeInBytes);
		else
			HPI_DEBUG_LOG2(DEBUG, "Adap %d cache good, bytecount == cache size = %d\n", pC->adap_idx, byteCount);

		pC->dwInit = (uint16_t)nCached;
	}
	return pC->dwInit;
}
/** Find a control.
*/
static short FindControl(
	uint16_t wControlIndex,
	struct hpi_control_cache *pCache,
	struct hpi_control_cache_info **pI
)
{
	if (!ControlCacheAllocCheck(pCache)) {
		HPI_DEBUG_LOG1(VERBOSE,
			"ControlCacheAllocCheck() failed %d\n",
			wControlIndex);
		return 0;
	}
	if (wControlIndex >= pCache->dwControlCount) {
		HPI_DEBUG_LOG1(VERBOSE,
			"Control index out of bounds %d\n",
			wControlIndex);
		return 0;
	}
	*pI = pCache->pInfo[wControlIndex];
	if (!*pI) {
		HPI_DEBUG_LOG1(VERBOSE,
			"Uncached Control %d\n",
			wControlIndex);
		return 0;
	} else {
		HPI_DEBUG_LOG1(VERBOSE,
			"FindControl() Type %d\n",
			(*pI)->ControlType);
	}
	return 1;
}

/* allow unified treatment of several string fields within struct */
#define HPICMN_PAD_OFS_AND_SIZE(m)  {\
	offsetof(struct hpi_control_cache_pad, m), \
	sizeof(((struct hpi_control_cache_pad *)(NULL))->m) }

struct pad_ofs_size {
	unsigned int offset;
	unsigned int field_size;
};

static const struct pad_ofs_size aPadDesc[] = {
	HPICMN_PAD_OFS_AND_SIZE(cChannel), /* HPI_PAD_CHANNEL_NAME */
	HPICMN_PAD_OFS_AND_SIZE(cArtist), /* HPI_PAD_ARTIST */
	HPICMN_PAD_OFS_AND_SIZE(cTitle), /* HPI_PAD_TITLE */
	HPICMN_PAD_OFS_AND_SIZE(cComment), /* HPI_PAD_COMMENT */
};

#define HPI_PAD_FIELD_MAXSIZE (\
	sizeof(((struct hpi_control_cache_pad *)(NULL))->cComment) )

static struct hpi_entity *find_value_entity( struct hpi_entity *t)
{
	struct hpi_entity *g = hpi_entity_ptr_to_next(t);
	t = hpi_entity_value_ptr(t);
	while (t && t<g){
		if (t->role == entity_role_value)
			break;
		t = hpi_entity_ptr_to_next(t);
	}
	if (t>=g) {
		t = NULL;
	}
	return t;
}

/** CheckControlCache checks the cache and fills the struct hpi_response
 * accordingly. It returns one if a cache hit occurred, zero otherwise.
 */
short HpiCheckControlCache_Single(
	struct hpi_control_cache_single *pC,
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
	size_t response_size;
	short found = 1;

	/* set the default response size */
	response_size = sizeof(struct hpi_response_header) +
			sizeof(struct hpi_control_res);

	switch (pC->u.i.ControlType) {

	case HPI_CONTROL_METER:
		if (phm->u.c.wAttribute == HPI_METER_PEAK) {
			phr->u.c.anLogValue[0] = pC->u.meter.anLogPeak[0];
			phr->u.c.anLogValue[1] = pC->u.meter.anLogPeak[1];
		} else if (phm->u.c.wAttribute == HPI_METER_RMS) {
			if (pC->u.meter.anLogRMS[0] == HPI_CACHE_INVALID_SHORT) {
				phr->wError = HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
				phr->u.c.anLogValue[0] = HPI_METER_MINIMUM;
				phr->u.c.anLogValue[1] = HPI_METER_MINIMUM;
			} else {
				phr->u.c.anLogValue[0] = pC->u.meter.anLogRMS[0];
				phr->u.c.anLogValue[1] = pC->u.meter.anLogRMS[1];
			}
		} else
			found = 0;
		break;
	case HPI_CONTROL_VOLUME:
		if (phm->u.c.wAttribute == HPI_VOLUME_GAIN) {
			phr->u.c.anLogValue[0] = pC->u.vol.anLog[0];
			phr->u.c.anLogValue[1] = pC->u.vol.anLog[1];
		} else if (phm->u.c.wAttribute == HPI_VOLUME_MUTE) {
			if ( pC->u.vol.flags & HPI_VOLUME_FLAG_HAS_MUTE) {
				if (pC->u.vol.flags & HPI_VOLUME_FLAG_MUTED)
					phr->u.c.dwParam1 = HPI_BITMASK_ALL_CHANNELS;
				else
					phr->u.c.dwParam1 = 0;
			} else {
				phr->wError = HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
				phr->u.c.dwParam1 = 0;
			}
		} else {
			found = 0;
		}
		break;
	case HPI_CONTROL_MULTIPLEXER:
		if (phm->u.c.wAttribute == HPI_MULTIPLEXER_SOURCE) {
			phr->u.c.dwParam1 = pC->u.mux.wSourceNodeType;
			phr->u.c.dwParam2 = pC->u.mux.wSourceNodeIndex;
		} else {
			found = 0;
		}
		break;
	case HPI_CONTROL_CHANNEL_MODE:
		if (phm->u.c.wAttribute ==  HPI_CHANNEL_MODE_MODE)
			phr->u.c.dwParam1 = pC->u.mode.wMode;
		else
			found = 0;
		break;
	case HPI_CONTROL_LEVEL:
		if (phm->u.c.wAttribute == HPI_LEVEL_GAIN) {
			phr->u.c.anLogValue[0] = pC->u.level.anLog[0];
			phr->u.c.anLogValue[1] = pC->u.level.anLog[1];
		} else
			found = 0;
		break;
	case HPI_CONTROL_TUNER:
		if (phm->u.c.wAttribute == HPI_TUNER_FREQ)
			phr->u.c.dwParam1 = pC->u.tuner.dwFreqInkHz;
		else if (phm->u.c.wAttribute == HPI_TUNER_BAND)
			phr->u.c.dwParam1 = pC->u.tuner.wBand;
		else if (phm->u.c.wAttribute == HPI_TUNER_LEVEL_AVG)
			if (pC->u.tuner.sLevelAvg == HPI_CACHE_INVALID_SHORT) {
				phr->u.cu.tuner.sLevel = 0;
				phr->wError = HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
			} else
				phr->u.cu.tuner.sLevel = pC->u.tuner.sLevelAvg;
		else
			found = 0;
		break;
	case HPI_CONTROL_AESEBU_RECEIVER:
		if (phm->u.c.wAttribute == HPI_AESEBURX_ERRORSTATUS)
			phr->u.c.dwParam1 = pC->u.aes3rx.dwErrorStatus;
		else if (phm->u.c.wAttribute == HPI_AESEBURX_FORMAT)
			phr->u.c.dwParam1 = pC->u.aes3rx.dwFormat;
		else
			found = 0;
		break;
	case HPI_CONTROL_AESEBU_TRANSMITTER:
		if (phm->u.c.wAttribute == HPI_AESEBUTX_FORMAT)
			phr->u.c.dwParam1 = pC->u.aes3tx.dwFormat;
		else
			found = 0;
		break;
	case HPI_CONTROL_TONEDETECTOR:
		if (phm->u.c.wAttribute == HPI_TONEDETECTOR_STATE)
			phr->u.c.dwParam1 = pC->u.tone.wState;
		else
			found = 0;
		break;
	case HPI_CONTROL_SILENCEDETECTOR:
		if (phm->u.c.wAttribute == HPI_SILENCEDETECTOR_STATE) {
			phr->u.c.dwParam1 = pC->u.silence.dwState;
		} else
			found = 0;
		break;
	case HPI_CONTROL_MICROPHONE:
		if (phm->u.c.wAttribute == HPI_MICROPHONE_PHANTOM_POWER)
			phr->u.c.dwParam1 = pC->u.microphone.phantom_state;
		else
			found = 0;
		break;
	case HPI_CONTROL_SAMPLECLOCK:
		if (phm->u.c.wAttribute == HPI_SAMPLECLOCK_SOURCE)
			phr->u.c.dwParam1 = pC->u.clk.wSource;
		else if (phm->u.c.wAttribute == HPI_SAMPLECLOCK_SOURCE_INDEX) {
			if (pC->u.clk.wSourceIndex ==
				HPI_CACHE_INVALID_UINT16) {
				phr->u.c.dwParam1 = 0;
				phr->wError = HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
			} else
				phr->u.c.dwParam1 = pC->u.clk.wSourceIndex;
		} else if (phm->u.c.wAttribute == HPI_SAMPLECLOCK_SAMPLERATE)
			phr->u.c.dwParam1 = pC->u.clk.dwSampleRate;
		else
			found = 0;
		break;
	case HPI_CONTROL_PAD: {
		struct hpi_control_cache_pad *pPad;
		pPad = (struct hpi_control_cache_pad *)pC;

		if( !(pPad->dwFieldValidFlags &
			(1 << HPI_CTL_ATTR_INDEX(phm->u.c.wAttribute))))
		{
			phr->wError =
				HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
			break;
		}

		if (phm->u.c.wAttribute == HPI_PAD_PROGRAM_ID)
			phr->u.c.dwParam1 = pPad->dwPI;
		else if (phm->u.c.wAttribute == HPI_PAD_PROGRAM_TYPE)
			phr->u.c.dwParam1 = pPad->dwPTY;
		else {
			unsigned int index = HPI_CTL_ATTR_INDEX(phm->u.c.wAttribute) - 1;
			size_t offset = phm->u.c.dwParam1;
			size_t pad_string_len, field_size;
			char * pad_string;
			size_t tocopy;

			if (index >= ARRAY_SIZE(aPadDesc)) {
				phr->wError = HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
				break;
			}

			pad_string = ((char *)pPad) + aPadDesc[index].offset;
			field_size = aPadDesc[index].field_size;
			{
				/* 
					Make a local copy of the buffer and operate on the copy
					to reduce the time window in which a race a can occur.

					The PAD field copy is zero-terminated unconditionally
					on the last byte to provide a sentinel.
				*/

				char field_buf[HPI_PAD_FIELD_MAXSIZE];
				
				if (field_size > HPI_PAD_FIELD_MAXSIZE){
					field_size = HPI_PAD_FIELD_MAXSIZE;
					HPI_DEBUG_LOG0(ERROR,
						"HpiCheckControlCache_Single() error in definition of HPI_PAD_FIELD_MAXSIZE\n");
				}
				memcpy(field_buf, pad_string, field_size);

				/* Ensure null terminator */
				field_buf[field_size - 1] = 0;
				pad_string_len = min(strlen(field_buf) + 1, field_size);

				if (offset >= pad_string_len) {
					phr->wError = HPI_ERROR_INVALID_CONTROL_VALUE;
					break;
				}

				tocopy = pad_string_len - offset;
				if (tocopy > sizeof(phr->u.cu.chars8.szData))
					tocopy = sizeof(phr->u.cu.chars8.szData);

				memcpy(phr->u.cu.chars8.szData,
					&field_buf[offset], tocopy);

				phr->u.cu.chars8.dwRemainingChars =
						(uint32_t)(pad_string_len - offset - tocopy);
			}
		}
		}
		break;
	case HPI_CONTROL_UNIVERSAL: {
		struct hpi_control_cache_strv *pSTRV = &pC->u.strv;
		struct hpi_res_strv *resp = (struct hpi_res_strv *)phr;
		if (phm->wFunction == HPI_CONTROL_GET_INFO) {
			if (phr->wSize <
					pC->u.i.nSizeIn32bitWords * sizeof(uint32_t) +
					sizeof(struct hpi_response_header) ) {
				phr->wError = HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL;
				break;
			}
			memcpy( &resp->strv, &pSTRV->strv, pSTRV->strv.size );
			response_size = sizeof(resp->h) + pSTRV->strv.size;
		} else if (phm->wFunction == HPI_CONTROL_GET_STATE) {
			struct hpi_entity *t = find_value_entity(&pSTRV->strv);
			if (t) {
				HPI_DEBUG_LOG1(VERBOSE, "strv [%d] value read from cache\n", phm->wObjIndex);
				memcpy( &resp->strv, t, t->size );
				response_size = sizeof(resp->h) + t->size;
			} else {
				found = 0;
			}
		}
		else
			found = 0;
		}
		break;
	default:
		found = 0;
		break;
	}

	HPI_DEBUG_LOG5(VERBOSE,
		"%s Adap %d, Ctl %d, Type %d, Attr %d\n",
		found ? "Cached" : "Uncached",
		phm->wAdapterIndex, pC->u.i.ControlIndex,
		pC->u.i.ControlType, phm->u.c.wAttribute);

	if (found) {
		phr->wSize = (uint16_t)response_size;
		phr->wType = HPI_TYPE_RESPONSE;
		phr->wObject = phm->wObject;
		phr->wFunction = phm->wFunction;
	}

	return found;
}

short HpiFindControlCache(
	struct hpi_control_cache *pCache,
	int index,
	struct hpi_control_cache_single **pC)
{
	struct hpi_control_cache_info *pI;

	if (!FindControl((uint16_t)index, pCache, &pI)) {
		return 0;
	}
	*pC = (struct hpi_control_cache_single *)pI;

	return 1;
}


short HpiCheckControlCache(
	struct hpi_control_cache *pCache,
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
	struct hpi_control_cache_single *pC;

	if (!HpiFindControlCache(pCache, phm->wObjIndex, &pC)) {
		HPI_DEBUG_LOG1(VERBOSE,
			"HPICMN FindControl() failed for adap %d\n",
			phm->wAdapterIndex);
		return 0;
	}

	phr->wError = 0;
	phr->wSpecificError = 0;
	phr->version = 0;

	return HpiCheckControlCache_Single(
			pC,
			phm,
			phr);
}

/** Updates the cache with Set values.

Only update if no error.
Volume and Level return the limited values in the response, so use these
Multiplexer does so use sent values
*/
void HpiCmn_ControlCache_SyncToMsg_Single(
	struct hpi_control_cache_single *pC,
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
	switch (pC->u.i.ControlType) {
	case HPI_CONTROL_VOLUME:
		if (phm->u.c.wAttribute == HPI_VOLUME_GAIN) {
			pC->u.vol.anLog[0] = phr->u.c.anLogValue[0];
			pC->u.vol.anLog[1] = phr->u.c.anLogValue[1];
		} else if (phm->u.c.wAttribute == HPI_VOLUME_MUTE) {
			if (phm->u.c.dwParam1)
				pC->u.vol.flags |= HPI_VOLUME_FLAG_MUTED;
			else
				pC->u.vol.flags &= ~HPI_VOLUME_FLAG_MUTED;
		}
		break;
	case HPI_CONTROL_MULTIPLEXER:
		// mux does not return its setting on Set command.
		if (phm->u.c.wAttribute == HPI_MULTIPLEXER_SOURCE) {
			pC->u.mux.wSourceNodeType = (uint16_t)phm->u.c.dwParam1;
			pC->u.mux.wSourceNodeIndex = (uint16_t)phm->u.c.dwParam2;
		}
		break;
	case HPI_CONTROL_CHANNEL_MODE:
		// mode does not return its setting on Set command.
		if (phm->u.c.wAttribute == HPI_CHANNEL_MODE_MODE)
			pC->u.mode.wMode = (uint16_t)phm->u.c.dwParam1;
		break;
	case HPI_CONTROL_LEVEL:
		if (phm->u.c.wAttribute == HPI_LEVEL_GAIN) {
			pC->u.vol.anLog[0] = phr->u.c.anLogValue[0];
			pC->u.vol.anLog[1] = phr->u.c.anLogValue[1];
		}
		break;
	case HPI_CONTROL_MICROPHONE:
		if (phm->u.c.wAttribute == HPI_MICROPHONE_PHANTOM_POWER)
			pC->u.microphone.phantom_state = (uint16_t)phm->u.c.dwParam1;
		break;
	case HPI_CONTROL_AESEBU_TRANSMITTER:
		if (phm->u.c.wAttribute == HPI_AESEBUTX_FORMAT)
			pC->u.aes3tx.dwFormat = phm->u.c.dwParam1;
		break;
	case HPI_CONTROL_AESEBU_RECEIVER:
		if (phm->u.c.wAttribute == HPI_AESEBURX_FORMAT)
			pC->u.aes3rx.dwFormat = phm->u.c.dwParam1;
		break;
	case HPI_CONTROL_SAMPLECLOCK:
		if (phm->u.c.wAttribute == HPI_SAMPLECLOCK_SOURCE)
			pC->u.clk.wSource = (uint16_t)phm->u.c.dwParam1;
		else if (phm->u.c.wAttribute == HPI_SAMPLECLOCK_SOURCE_INDEX)
			pC->u.clk.wSourceIndex = (uint16_t)phm->u.c.dwParam1;
		else if (phm->u.c.wAttribute == HPI_SAMPLECLOCK_SAMPLERATE)
			pC->u.clk.dwSampleRate = phm->u.c.dwParam1;
		break;
#ifndef HPI_OS_LINUX_KERNEL
	case HPI_CONTROL_UNIVERSAL:
	{
		struct hpi_control_cache_strv *pSTRV;
		struct hpi_msg_strv *msg;
		struct hpi_entity *t;

		if (phm->wFunction != HPI_CONTROL_SET_STATE)
			break;

		pSTRV = &pC->u.strv;
		msg = (struct hpi_msg_strv *)phm;
		t = find_value_entity(&pSTRV->strv);

		if (!t)
			break;

		if (t->role!=msg->strv.role) {
			HPI_DEBUG_LOG3(ERROR, "strv [%d] value msg role %d != cache role %d\n",
				phm->wObjIndex, msg->strv.role, t->role);
			break;
		}
		if (t->type!=msg->strv.type) {
			HPI_DEBUG_LOG3(ERROR, "strv [%d] value msg type %d != cache type %d\n",
				phm->wObjIndex, msg->strv.type, t->type);
			break;
		}
		/* size check, supports caching a "shorter" cstring */
		if (	((t->type==entity_type_cstring) &&
				(hpi_entity_size(t)  < hpi_entity_size(&msg->strv))) ||
			((t->type!=entity_type_cstring) &&
				(hpi_entity_size(t) != hpi_entity_size(&msg->strv)))   ){
					HPI_DEBUG_LOG3(ERROR, "strv [%d] value msg size %d miss match cache size %d\n",
						phm->wObjIndex, msg->strv.size, t->size);
			break;
		}
		HPI_DEBUG_LOG1(DEBUG, "strv [%d] value set cache\n", phm->wObjIndex);
		/*
		 * Boolean update is a special case because there are "don't cares"
		 * in the value entity.
		 */
		if (t->type==entity_type_boolean) {
			char *m = hpi_entity_value_ptr(&msg->strv); // msg
			char *c = hpi_entity_value_ptr(t); // cache
			size_t i;
			for (i=0; i<hpi_entity_value_size(t); i++) {
				if ( m[i]=='T' || m[i]=='F' )
					c[i] = m[i];
			}
		} else {
			/* only copy the value part of the entity so that cached cstring storage does not shrink */
			memcpy( hpi_entity_value_ptr(t), hpi_entity_value_ptr(&msg->strv), t->size - hpi_entity_header_size() );
		}
		break;
	}
#endif
	default:
		break;
	}
}
void HpiCmn_ControlCache_SyncToMsg(
	struct hpi_control_cache *pCache,
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
	struct hpi_control_cache_single *pC;
	struct hpi_control_cache_info *pI;

	if (phr->wError)
		return;

	if (!FindControl(phm->wObjIndex,pCache,&pI)) {
		HPI_DEBUG_LOG1(VERBOSE,
			"HPICMN FindControl() failed for adap %d\n",
			phm->wAdapterIndex);
		return;
	}

	/* pC is the default cached control strucure.
	May be cast to something else in the following switch statement.
	*/
	pC = (struct hpi_control_cache_single *)pI;

	HpiCmn_ControlCache_SyncToMsg_Single(pC, phm, phr);
}
/** Allocate control cache.

\return Cache pointer, or NULL if allocation fails.
*/
struct hpi_control_cache * HpiAllocControlCache(
	const uint32_t controlCount,
	const uint32_t sizeInBytes,
	uint8_t *pDspControlBuffer
)
{
	struct hpi_control_cache *pCache = HpiOs_MemAlloc(sizeof(*pCache));
	if (!pCache)
		return NULL;

	pCache->pInfo = HpiOs_MemAllocZeroArray(controlCount, sizeof(*pCache->pInfo));
	if (!pCache->pInfo) {
		HpiOs_MemFree(pCache);
		return NULL;
	}

	pCache->dwCacheSizeInBytes = sizeInBytes;
	pCache->dwControlCount = controlCount;
	pCache->pCache = pDspControlBuffer;
	pCache->dwInit = 0;
	return pCache;
}

void HpiFreeControlCache(struct hpi_control_cache *pCache)
{
	if (pCache) {
		HpiOs_MemFree(pCache->pInfo);
		HpiOs_MemFree(pCache);
	}
}

#ifndef HPI_BUILD_NO_SHARED_GLOBALS

static void SubSysMessage(
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
	HPI_InitResponse(phr, HPI_OBJ_SUBSYSTEM,
		phm->wFunction, 0);

	switch (phm->wFunction) {
	case HPI_SUBSYS_OPEN:
	case HPI_SUBSYS_CLOSE:
	case HPI_SUBSYS_DRIVER_UNLOAD:
		break;
	case HPI_SUBSYS_DRIVER_LOAD:
		WipeAdapterList();
		HpiOs_Mutex_Init(&adapters.aListLock);
		break;
	case HPI_SUBSYS_GET_ADAPTER:
		Subsys_GetAdapter(phm, phr);
		break;
	case HPI_SUBSYS_GET_NUM_ADAPTERS:
#ifndef HPI_BUILD_REASSIGN_DUPLICATE_ADAPTER_IDX
		phr->u.s.wNumAdapters = adapters.gwNumAdapters +
				adapters.gwNumDuplicateIndexAdapters;
#else
		phr->u.s.wNumAdapters = adapters.gwNumAdapters;
#endif
		break;
	case HPI_SUBSYS_CREATE_ADAPTER:
		break;
	default:
		phr->wError = HPI_ERROR_INVALID_FUNC;
		break;
	}
}

void HPI_COMMON(
	struct hpi_message *phm,
	struct hpi_response *phr
)
{
	switch (phm->wType) {
	case HPI_TYPE_REQUEST:
		switch (phm->wObject) {
		case HPI_OBJ_SUBSYSTEM:
			SubSysMessage(phm, phr);
			break;
		}
		break;

	default:
		phr->wError = HPI_ERROR_INVALID_TYPE;
		break;
	}
}

#endif // HPI_BUILD_NO_SHARED_GLOBALS
