/*
 * hpiudp_cache.c
 *
 * Control caching module.
 *

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 "hpiudp_cache.c"

#define HPIUDP_USE_CACHE

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include "hpi_internal.h"
#include "hpimsginit.h"
#include "hpinet.h"
#include "hpidebug.h"
#include "hpicmn.h"
#include "hpiudp_cache.h"

enum {eAllChanged=1, eLimitsChanged = 2, eValueChanged =4};

static int hpiudp_cache_update_required(
		struct hpiudp_cache_context *cache_context,
		unsigned int nCurrent_ms
);


/*=================================================================*/
/* get control index */
static uint16_t ControlIndex(const struct hpi_message *phm)
{
	if (phm->wObject == HPI_OBJ_CONTROL)
		return phm->wObjIndex;

	if (phm->wObject == HPI_OBJ_MIXER)
		return phm->u.m.wControlIndex;

	return 0;
}

/*=================================================================*/
/* look up channel info */
static int ControlsGetChannelInfo(
		struct hpiudp_cache_context *cache,
		struct hpi_response *phr,
		uint32_t control_index,
		int *nHandled
)
{
	unsigned int nChannels = cache->controlDef[control_index].wChannels;
	HPI_InitResponse(phr, HPI_OBJ_CONTROL,
		 HPI_CONTROL_GET_INFO, 0);
	if (nChannels == 0)
		nChannels = 2;
	phr->u.c.dwParam1 = nChannels;
	*nHandled = 1;
	return 0;
}

/*=================================================================*/
/* check for return information already being cached */
int hpiudp_cache_check_for_control(struct hpi_adapter *pA,	///< Adapter structure
		uint16_t adapter_index,
		struct hpiudp_cache_context *cache,  ///< cache structure
		struct hpi_message *phm, ///< HPI message
		struct hpi_response *phr, ///< HPI response, maybe from cache
		int *nHandled, ///< boolean, non zero if response is from cache
		int *nCachingError ///< problem with control lookup in cache
)
{
	int nRet = 0;
	uint16_t wControlIndex = ControlIndex(phm);

	*nHandled = 0;
	*nCachingError = 0;

	switch (phm->wFunction) {
	case HPI_MIXER_GET_CONTROL_BY_INDEX:
		HPI_InitResponse(phr, HPI_OBJ_MIXER, HPI_MIXER_GET_CONTROL_BY_INDEX, 0);
		if (wControlIndex < cache->controlCache->dwControlCount) {
			struct hpi_control_defn *pC =
				&cache->controlDef[wControlIndex];
			if (pC->wSrcNodeType >= HPI_SOURCENODE_NONE)
				pC->wSrcNodeType -= HPI_SOURCENODE_NONE;
			if (pC->wDestNodeType >= HPI_DESTNODE_NONE)
				pC->wDestNodeType -= HPI_DESTNODE_NONE;
			phr->u.m.wSrcNodeType = pC->wSrcNodeType;
			phr->u.m.wSrcNodeIndex = pC->wSrcNodeIndex;
			phr->u.m.wDstNodeType = pC->wDestNodeType;
			phr->u.m.wDstNodeIndex = pC->wDestNodeIndex;
			phr->u.m.wControlIndex = pC->wType; // actually type !
			HPI_DEBUG_LOG1(DEBUG,
				"HPI_MIXER_GET_CONTROL_BY_INDEX cached control # %d\n",
				wControlIndex);
		} else
			phr->wError = HPI_ERROR_INVALID_OBJ_INDEX;
		*nHandled = 1;
		return 0;
		break;
	// handle HPI_ControlQuery() for volume and meter channels
	case HPI_CONTROL_GET_INFO:
		if (wControlIndex < cache->controlCache->dwControlCount) {
			switch (cache->controlDef[wControlIndex].wType)
			{
			case HPI_CONTROL_METER:
				if (phm->u.c.wAttribute == HPI_METER_NUM_CHANNELS)
					return ControlsGetChannelInfo (
						cache, phr,
						wControlIndex, nHandled);
				break;
			case HPI_CONTROL_VOLUME:
				if (phm->u.c.wAttribute == HPI_VOLUME_NUM_CHANNELS)
					return ControlsGetChannelInfo (
						cache, phr,
						wControlIndex, nHandled);
				if (phm->u.c.wAttribute == HPI_VOLUME_AUTOFADE) {
					struct hpi_control_cache_single *pC;
					if (!HpiFindControlCache(cache->controlCache, phm->wObjIndex, &pC)) {
						return 0;
					}

					HPI_InitResponse(phr, HPI_OBJ_CONTROL,
						HPI_CONTROL_GET_INFO, 0);

					if (0 == phm->u.c.dwParam1) {
						if (pC->u.vol.flags & HPI_VOLUME_AUTOFADE) {
							phr->u.c.dwParam1 = HPI_VOLUME_AUTOFADE_LOG;
						}
					} else if (1 == phm->u.c.dwParam1) {
						if (pC->u.vol.flags & HPI_VOLUME_AUTOFADE) {
							phr->u.c.dwParam1 = HPI_VOLUME_AUTOFADE_LINEAR;
						}
					} else {
						phr->wError = HPI_ERROR_INVALID_OBJ_INDEX;
					}
					*nHandled = 1;
					return 0;
				}
				break;
			case HPI_CONTROL_UNIVERSAL:
				*nHandled = HpiCheckControlCache(
						cache->controlCache,
						phm,phr) == 1;
				break;
			default:
				break;
			}
		}
		// the following for debug only
		HPI_DEBUG_LOG3(DEBUG,"HPI_MIXER_GET_CONTROL_BY_INDEX control type %d, Attrib %d, index %d\n",
				cache->controlDef[wControlIndex].
				wType, phm->u.c.wAttribute,
				wControlIndex);
		break;

	case HPI_CONTROL_GET_STATE:
		if (phr->wError == HPI_ERROR_NETWORK_TOO_MANY_CLIENTS)
			*nCachingError = HPI_ERROR_NETWORK_TOO_MANY_CLIENTS;
		/* returns 1 if found, otherwise 0 */
		if (HpiCheckControlCache(cache->controlCache,phm,phr) == 1) {
			*nHandled = 1;
			return 0;
		}
		/* check for control cache not working for some reason */
		if ( (cache->controlDef[wControlIndex].wType==HPI_CONTROL_METER) &&
			((phm->u.c.wAttribute==HPI_METER_PEAK) || (phm->u.c.wAttribute==HPI_METER_RMS))
			)
			// meter peak and rms attributes should always be cached, so this is an error condition.
			*nCachingError = HPI_ERROR_CONTROL_CACHING;
		break;

	default:
		break;
	}

	return nRet;
}

#define strv_size(a) hpi_entity_size(a)
/** Search STRV struct for STRV of specific role */
static struct hpi_entity *search_strv(
		struct hpi_entity *ptr,
		enum e_entity_role role,
		struct hpi_entity *guard)
{
	while (ptr && ptr < guard) {
		if (ptr->role == role)
			break;
		ptr = (struct hpi_entity *)((char *)ptr + strv_size(ptr));
	}
	if (ptr >= guard)
		ptr = NULL;
	return ptr;
}


/*=================================================================*/
/** Update control cache
  *
  */
int hpiudp_cache_update_now(struct hpi_adapter *pA,
		uint16_t adapter_index,
		struct hpiudp_cache_context *cache_context,
		ptr_fn_messsage msg_fn,
		struct hpi_response *phr,
		const unsigned int nTimeOut_ms,
		unsigned int timestamp)
{
	struct hpi_message hm;
	HPI_RESPONSEX hr;
	unsigned int nMoreToDo = 1;
	int i = 0;
	int nRet = 0;
	struct hpi_control_cache * cache = cache_context->controlCache;
	char *cache_data = (char *)&cache->pCache[0];
	unsigned int init = cache_context->controlCache->dwInit;
	char *c = NULL;
	int bytes_to_process = 0;

	while (nMoreToDo) {
		struct hpi_control_cache_info *ci;
		
		HPI_InitMessageResponse(&hm,  (struct hpi_response *)&hr, HPI_OBJ_MIXER,
				HPI_MIXER_GET_CONTROL_MULTIPLE_VALUES);
		hm.wAdapterIndex = adapter_index;
		hm.u.mx.gcabi.wStartingIndex = (uint16_t)i;
		hm.u.mx.gcabi.dwLengthInBytes = HPINET_RESP_SIZEOF_DATA;
		hm.u.mx.gcabi.wFlags = HPI_MIXER_GET_CONTROL_MULTIPLE_CHANGED;
		hr.resp.wSize = sizeof(hr);
		nRet = msg_fn(pA, &hm, (struct hpi_response *)&hr, nTimeOut_ms);

		if (nRet || hr.resp.wError) {
			HPI_DEBUG_LOG3(ERROR,
				"hpiudp_cache_update_now() HPI_MIXER_GET_CONTROL_MULTIPLE_VALUES Adap=%d nRet %d, resp.wError %d\n",
				adapter_index, nRet, hr.resp.wError);
		}

		if (nRet != 0) {
			memcpy(phr, &hr, hr.resp.wSize);
			return nRet;
		}
		/* exit while loop on error */
		if (hr.resp.wError == HPI_ERROR_NETWORK_TOO_MANY_CLIENTS) {
			phr->wError = hr.resp.wError;
			break;
		} else if (hr.resp.wError)
			break;

		/* process the response */
		c = &(((HPI_RESPONSEX *) & hr)->data[0]);
		ci = (struct hpi_control_cache_info *)c;
		bytes_to_process = (int)hr.resp.u.mx.gcabi.dwBytesReturned;

		if (bytes_to_process == 0) {
			HPI_DEBUG_LOG0(ERROR,"Control cache update returning 0 bytes\n");
			break;
		}
		else if (!ci->nSizeIn32bitWords) {
			HPI_DEBUG_LOG0(ERROR, "hpiudp_cache_update_now() invalid cache size\n");
			break;
		} 
		else {
			HPI_DEBUG_LOG2(DEBUG,"strv - Control cache update %d bytes, starting index %d\n",
				bytes_to_process, ((struct hpi_control_cache_info *)c)[0].ControlIndex );
		}

		while ( bytes_to_process > 0 ) {
			struct hpi_control_cache_info *cache_dest;
			int flags = 0;

			size_t this_size = ci->nSizeIn32bitWords * sizeof(uint32_t);
			if (!this_size) {
				HPI_DEBUG_LOG0(ERROR, "hpiudp_cache_update_now() invalid cache size\n");
				break;
			}

			/*
			 * If the control cache has been initialized already, can use
			 * pInfo[] to look up the position of the cached control. Otherwise
			 * we have to use cache_data and keep updating cache_date so that
			 * it moves up through the pCache[] data buffer.
			 */
			if (init) {
				cache_dest = cache->pInfo[ci->ControlIndex];
			} else {
				cache_dest = (struct hpi_control_cache_info *)cache_data;
			}

			if (!cache_dest) {
				HPI_DEBUG_LOG1(ERROR, "Missing control cache->pInfo[%d]\n", ci->ControlIndex);
				goto next_control;
			}
			/* entity caching update checking */
			if ( (ci->ControlType==HPI_CONTROL_UNIVERSAL) && 1) {
				HPI_DEBUG_LOG3(VERBOSE,
					"strv update entity info for[%d] role %d size %d\n",
					ci->ControlIndex,
					((struct hpi_entity *)(ci+1))->role,
					(int)(ci->nSizeIn32bitWords * sizeof(int)));
			}

			if (!init || (cache_dest->nSizeIn32bitWords==ci->nSizeIn32bitWords)) {
				// This also handles strv entity_role_block and entity_role_parameter_port
				// since those sizes match the cached size.
				memcpy(cache_dest, ci, this_size);
				flags = eAllChanged;
			}
			else {
				// mis-matched cache size
				if (ci->ControlType==HPI_CONTROL_UNIVERSAL) {
					struct hpi_entity *recvd_entity = (struct hpi_entity *)(ci+1);
					// check for entity_role_value | entity_role_range
					enum e_entity_role r = recvd_entity->role;
					if ((r!=entity_role_value)&&(r!=entity_role_range)) {
						HPI_DEBUG_LOG2(	DEBUG, "unexpected role c[%d] rcv %d\n", ci->ControlIndex, r);
						r = entity_role_null;
					}
					while (r) {	// use while to assist exit on error
						struct hpi_entity *guard;
						struct hpi_entity *cache_entity;

						cache_entity = (struct hpi_entity *)(cache_dest+1);
						guard = hpi_entity_ptr_to_next(cache_entity);

						if (r==entity_role_range) {
							cache_entity = search_strv(
								hpi_entity_value_ptr(cache_entity),
								entity_role_value_constraint,
								guard);
							guard = hpi_entity_ptr_to_next(cache_entity);
							flags = eLimitsChanged;
						} else {
							flags = eValueChanged;
						}

						cache_entity = search_strv(
								hpi_entity_value_ptr(cache_entity),
								r,
								guard);
						if (!cache_entity) {
							HPI_DEBUG_LOG2(ERROR, "strv role not found c[%d] role %d\n", ci->ControlIndex, r);
							break;
						}
						if (cache_entity->size!=recvd_entity->size) {
							HPI_DEBUG_LOG3(	ERROR, "strv mis-matched cache size c[%d] cache %d, rcv %d\n", ci->ControlIndex, cache_entity->size,recvd_entity->size);
							break;
						}
						HPI_DEBUG_LOG1(DEBUG, "strv update VALUE|LIMITS entity, bytes copied %d\n", cache_entity->size);
						#ifdef HPI_BUILD_DEBUG
						if (cache_entity->size) {
							unsigned int k;
							for (k=0; k<cache_entity->size/sizeof(uint32_t); k++) {
								uint32_t data = ((uint32_t *)recvd_entity)[k];
								HPI_DEBUG_LOG3(DEBUG, "strv data[%d] 0x%08X %d\n", k, data, data);
							}
						}
						#endif
						memcpy(cache_entity, recvd_entity, cache_entity->size);
						break;
					}
				} else {
					HPI_DEBUG_LOG3(
						ERROR,
						"mis-matched cache size c[%d] cache %d, rcv %d\n",
						ci->ControlIndex,
						cache_dest->nSizeIn32bitWords,
						ci->nSizeIn32bitWords
						);
				}
			}

next_control:
			c += this_size;
			ci = (struct hpi_control_cache_info *)c;
			bytes_to_process -= (int)this_size;
			i++;
			// first time through add up all the sizes
			if (!init)
				cache_data += this_size;
			else if (cache_context->pTimeStamp) {
				struct hpiudp_cache_update_time  *t =
					&cache_context->pTimeStamp[ci->ControlIndex];

				if (flags & eAllChanged)
					t->all_info = timestamp;
				if (flags & eValueChanged)
					t->value = timestamp;
				if (flags & eLimitsChanged)
					t->limits = timestamp;
			}
		}
		nMoreToDo = hr.resp.u.mx.gcabi.wMoreToDo;
	}
	/*
	 * The first time through init is false and we force a call below to
	 * HpiCheckControlCache() which in turn causes the cache->pInfo[] array
	 * of pointers to be initialized.
	 */
	if (!init) {
		struct hpi_message hm;
		struct hpi_response hr;
		memset(&hm, 0, sizeof(struct hpi_message));
		memset(&hr, 0, sizeof(struct hpi_response));
		HpiCheckControlCache( cache, &hm, &hr);
	}

	return nRet;
}
/*=================================================================*/
/** Check if control cache update is required
  *
  */
static int hpiudp_cache_update_required(
		struct hpiudp_cache_context *cache_context,
		unsigned int nCurrent_ms
)
{
	// if time delta is < 50ms don't do anything.
	if ((cache_context->timeStamp_ms != 0) &&
	    (nCurrent_ms - cache_context->timeStamp_ms < 50)) {
		return 0;
	} else {
		// we are going to update cache, update the timestamp
		cache_context->timeStamp_ms = nCurrent_ms;
		return 1;
	}
}
/*=================================================================*/
/**
 init the control cache
\retval 0 = success
\retval non-zero = failure
*/
int hpiudp_cache_update(struct hpi_adapter *pA,
		uint16_t adapter_index,
		struct hpiudp_cache_context *cache_context,
		ptr_fn_messsage msg_fn,
		struct hpi_response *phr,
		const unsigned int nTimeOut_ms)
{
	unsigned int nCurrent_ms = GetTickCount();

	if (!hpiudp_cache_update_required(cache_context, nCurrent_ms))
		return 0;
	return hpiudp_cache_update_now(pA,adapter_index,
		cache_context, msg_fn, phr, nTimeOut_ms, nCurrent_ms);
}
/*=================================================================*/
/**
 init the control cache
\retval 0 = success
\retval non-zero = failure
*/
int hpiudp_cache_init(
	struct hpi_adapter *pA,
	uint16_t adapter_index,
	struct hpiudp_cache_context **ptr_cache,
	ptr_fn_messsage msg_fn,
	struct hpi_response *phr
)
{
	int nRet = 0;
	int nThisTimeout_ms = 200; /* 200 ms*/
	uint32_t max_control_defs;
	struct hpi_message hm;
	HPI_RESPONSEX hr;
	struct hpiudp_cache_context *cache;
	int i = 0;
	size_t cache_size = 0;
	unsigned int control_count = 0;

	// alloc a context cache
	cache = (struct hpiudp_cache_context *)calloc(1, sizeof(*cache));
	*ptr_cache = cache;

	// 1. Call MixerOpen()
	HPI_InitMessageResponse(&hm, (struct hpi_response *)&hr,
			HPI_OBJ_MIXER, HPI_MIXER_OPEN);
	hm.wAdapterIndex = adapter_index;
	hr.resp.wSize = sizeof(hr);
	nRet = msg_fn(pA, &hm, (struct hpi_response *)&hr, nThisTimeout_ms);
	if (nRet) {
		memcpy(phr, &hr, hr.resp.wSize);
		return nRet;
	}

	HPI_InitMessageResponse(&hm, (struct hpi_response *)&hr,
			HPI_OBJ_MIXER, HPI_MIXER_GET_CACHE_INFO);
	hm.wAdapterIndex = adapter_index;
	hr.resp.wSize = sizeof(hr);
	nRet = msg_fn(pA, &hm, (struct hpi_response *)&hr, nThisTimeout_ms);
	if (nRet) {
		memcpy(phr, &hr, hr.resp.wSize);
		return nRet;
	}
	if (!hr.resp.wError) {
		max_control_defs = hr.resp.u.mx.cache_info.total_controls;
		cache_size = hr.resp.u.mx.cache_info.cache_bytes;
	} else {
		max_control_defs = 4096; // Could probe for this using MGCABI, or realloc afterwards
		cache_size = 0; // will determine later after reading control info
	}

	cache->controlDef = calloc(sizeof(*cache->controlDef), max_control_defs);
	assert(cache->controlDef);

	// 2. Call MixerGetControlArrayByIndex() to load all the available controls.
	while (1) {
		uint16_t newControls;

		HPI_InitMessageResponse(&hm, (struct hpi_response *)&hr, HPI_OBJ_MIXER,
				HPI_MIXER_GET_CONTROL_ARRAY_BY_INDEX);
		hm.wAdapterIndex = adapter_index;
		hm.u.mx.gcabi.wStartingIndex = (uint16_t)control_count;
		hm.u.mx.gcabi.dwLengthInBytes = HPINET_RESP_SIZEOF_DATA;
		hr.resp.wSize = sizeof(hr);

		nRet = msg_fn(pA, &hm, (struct hpi_response *)&hr, nThisTimeout_ms);
		if (nRet || hr.resp.wError) {
			memcpy(phr, &hr, hr.resp.wSize);
			return nRet;
		}

		newControls = (uint16_t)(hr.resp.u.mx.gcabi.dwBytesReturned /
						sizeof(struct hpi_control_defn));

		assert(control_count + newControls <= max_control_defs);

		memcpy(&cache->controlDef[control_count],
				&hr.data,
				newControls * sizeof(struct hpi_control_defn));

		control_count += newControls;

		/* We have all the controls if a less than full packet is
		returned. */
		if (newControls <
			HPINET_RESP_SIZEOF_DATA /
			sizeof(struct hpi_control_defn))
		{
			HPI_DEBUG_LOG1(INFO,
				"MixerGetControlArrayByIndex(): total controls found = %d\n",
				control_count);
			break;
		}
	}

	cache->controlDef = realloc(cache->controlDef,
			control_count * sizeof(*cache->controlDef));

	// 3. Load the control cache for the first time and timestamp it.
	// First reset the cache.
	HPI_InitMessageResponse(&hm,  (struct hpi_response *)&hr, HPI_OBJ_MIXER,
			HPI_MIXER_GET_CONTROL_MULTIPLE_VALUES);
	hm.wAdapterIndex = adapter_index;
	hm.u.mx.gcabi.wStartingIndex = (uint16_t)i;
	hm.u.mx.gcabi.dwLengthInBytes = HPINET_RESP_SIZEOF_DATA;
	hm.u.mx.gcabi.wFlags = HPI_MIXER_GET_CONTROL_MULTIPLE_RESET;

	hr.resp.wSize = sizeof(hr);

	nRet = msg_fn(pA, &hm, (struct hpi_response *)&hr, nThisTimeout_ms);
	if (nRet) {
		memcpy(phr, &hr, hr.resp.wSize);
		return nRet;
	}

	if (!cache_size)
		cache_size = control_count * sizeof(struct hpi_control_cache_single);

	cache->controlCacheData = calloc(1, cache_size);
	assert(cache->controlCacheData);

	cache->controlCache = HpiAllocControlCache(
		control_count,
		(uint32_t)cache_size,
		(uint8_t *)&cache->controlCacheData[0]);

	assert(cache->controlCache);

	cache->controlCache->adap_idx = adapter_index;

	return nRet;
}

/*=================================================================*/
/* are controls cached ? */
static int ControlsCached(const struct hpiudp_cache_context *cache)
{
	if (NULL == cache)
		return 0;
	if (NULL == cache->controlCache)
		return 0;
	return cache->controlCache->dwControlCount;
}

/*=================================================================*/
/* process an HPI message ? */
int hpiudp_cache_message(
		struct hpi_adapter *pA,	///< Adapter structure
		uint16_t adapter_index,
		uint32_t *mixer_is_open,
		struct hpiudp_cache_context **ptr_cache,  ///< cache structure
		ptr_fn_messsage msg_fn,
		struct hpi_message *phm, ///< HPI message
		struct hpi_response *phr, ///< HPI response, maybe from cache
		int *nHandled, ///< boolean, non zero if response is from cache
		int *nCachingError ///< problem with control lookup in cache
)
{
	struct hpiudp_cache_context *cache=*ptr_cache;
	int nThisTimeout_ms = 100; /* 100 ms*/
	int nRet = 0;

	if ((phm->wObject == HPI_OBJ_MIXER) ||
	    (phm->wObject == HPI_OBJ_CONTROL)) {
		/* cache only applies to mixer and control messages */
		if(cache==NULL) {
			nRet = hpiudp_cache_init(
					pA,
					adapter_index,
					ptr_cache,
					msg_fn,
					phr);
			cache=*ptr_cache;
			if( nRet )
				return nRet;
			nRet = hpiudp_cache_update(
					pA,
					adapter_index,
					cache,
					msg_fn,
					phr,
					nThisTimeout_ms);
			if( nRet )
				return nRet;
		}

		if ((phm->wFunction == HPI_MIXER_OPEN) && mixer_is_open) {
			HPI_InitResponse(phr, HPI_OBJ_MIXER, HPI_MIXER_OPEN, 0);
			return 0;
		}

		if (ControlsCached(cache)) {
			if (phm->wFunction == HPI_CONTROL_GET_STATE)
				nRet = hpiudp_cache_update(pA,
						adapter_index,
						cache,
						msg_fn,
						phr,
						nThisTimeout_ms);

			if (nRet != 0) {
				*nHandled = 1;
				return nRet;
			}
			nRet = hpiudp_cache_check_for_control(pA,
					adapter_index,
					cache,
					phm, phr,
					nHandled, nCachingError);
			if(*nHandled)
				return nRet;
		} else if ((phm->wFunction == HPI_CONTROL_GET_STATE) &&
			(phm->u.c.wAttribute == HPI_METER_PEAK))
			*nCachingError = HPI_ERROR_NETWORK_TOO_MANY_CLIENTS;
	}
	return 0;
}


/*=================================================================*/
/** Free any control caching
  */

void hpiudp_cache_free(struct hpiudp_cache_context **ptr_cache)
{
	struct hpiudp_cache_context *cache = *ptr_cache;
	if (cache)
	{
		HPI_DEBUG_LOG0(INFO, "Control cache free\n");
		if (cache->controlCache) {
			if(cache->controlDef) {
				free(cache->controlDef);
				cache->controlDef = NULL;
			}
			if(cache->controlCacheData) {
				free(cache->controlCacheData);
				cache->controlCacheData = NULL;
			}
			HpiFreeControlCache(cache->controlCache);
			cache->controlCache = NULL;
		}
		free(cache);
		*ptr_cache = NULL;
	}
}
