brintos

brintos / linux-shallow public Read only

0
0
Text · 18.1 KiB · 7d1abae Raw
714 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/******************************************************************************3 4    AudioScience HPI driver5    Copyright (C) 1997-2014  AudioScience Inc. <support@audioscience.com>6 7 8\file hpicmn.c9 10 Common functions used by hpixxxx.c modules11 12(C) Copyright AudioScience Inc. 1998-200313*******************************************************************************/14#define SOURCEFILE_NAME "hpicmn.c"15 16#include "hpi_internal.h"17#include "hpidebug.h"18#include "hpimsginit.h"19 20#include "hpicmn.h"21 22struct hpi_adapters_list {23	struct hpios_spinlock list_lock;24	struct hpi_adapter_obj adapter[HPI_MAX_ADAPTERS];25	u16 gw_num_adapters;26};27 28static struct hpi_adapters_list adapters;29 30/**31 * hpi_validate_response - Given an HPI Message that was sent out and32 * a response that was received, validate that the response has the33 * correct fields filled in, i.e ObjectType, Function etc34 * @phm: message35 * @phr: response36 */37u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr)38{39	if (phr->type != HPI_TYPE_RESPONSE) {40		HPI_DEBUG_LOG(ERROR, "header type %d invalid\n", phr->type);41		return HPI_ERROR_INVALID_RESPONSE;42	}43 44	if (phr->object != phm->object) {45		HPI_DEBUG_LOG(ERROR, "header object %d invalid\n",46			phr->object);47		return HPI_ERROR_INVALID_RESPONSE;48	}49 50	if (phr->function != phm->function) {51		HPI_DEBUG_LOG(ERROR, "header function %d invalid\n",52			phr->function);53		return HPI_ERROR_INVALID_RESPONSE;54	}55 56	return 0;57}58 59u16 hpi_add_adapter(struct hpi_adapter_obj *pao)60{61	u16 retval = 0;62	/*HPI_ASSERT(pao->type); */63 64	hpios_alistlock_lock(&adapters);65 66	if (pao->index >= HPI_MAX_ADAPTERS) {67		retval = HPI_ERROR_BAD_ADAPTER_NUMBER;68		goto unlock;69	}70 71	if (adapters.adapter[pao->index].type) {72		int a;73		for (a = HPI_MAX_ADAPTERS - 1; a >= 0; a--) {74			if (!adapters.adapter[a].type) {75				HPI_DEBUG_LOG(WARNING,76					"ASI%X duplicate index %d moved to %d\n",77					pao->type, pao->index, a);78				pao->index = a;79				break;80			}81		}82		if (a < 0) {83			retval = HPI_ERROR_DUPLICATE_ADAPTER_NUMBER;84			goto unlock;85		}86	}87	adapters.adapter[pao->index] = *pao;88	hpios_dsplock_init(&adapters.adapter[pao->index]);89	adapters.gw_num_adapters++;90 91unlock:92	hpios_alistlock_unlock(&adapters);93	return retval;94}95 96void hpi_delete_adapter(struct hpi_adapter_obj *pao)97{98	if (!pao->type) {99		HPI_DEBUG_LOG(ERROR, "removing null adapter?\n");100		return;101	}102 103	hpios_alistlock_lock(&adapters);104	if (adapters.adapter[pao->index].type)105		adapters.gw_num_adapters--;106	memset(&adapters.adapter[pao->index], 0, sizeof(adapters.adapter[0]));107	hpios_alistlock_unlock(&adapters);108}109 110/**111 * hpi_find_adapter - FindAdapter returns a pointer to the struct112 * hpi_adapter_obj with index wAdapterIndex in an HPI_ADAPTERS_LIST113 * structure.114 * @adapter_index: value in [0, HPI_MAX_ADAPTERS[115 */116struct hpi_adapter_obj *hpi_find_adapter(u16 adapter_index)117{118	struct hpi_adapter_obj *pao = NULL;119 120	if (adapter_index >= HPI_MAX_ADAPTERS) {121		HPI_DEBUG_LOG(VERBOSE, "find_adapter invalid index %d\n",122			adapter_index);123		return NULL;124	}125 126	pao = &adapters.adapter[adapter_index];127	if (pao->type != 0) {128		/*129		   HPI_DEBUG_LOG(VERBOSE, "Found adapter index %d\n",130		   wAdapterIndex);131		 */132		return pao;133	} else {134		/*135		   HPI_DEBUG_LOG(VERBOSE, "No adapter index %d\n",136		   wAdapterIndex);137		 */138		return NULL;139	}140}141 142/**143 * wipe_adapter_list - wipe an HPI_ADAPTERS_LIST structure.144 *145 */146static void wipe_adapter_list(void)147{148	memset(&adapters, 0, sizeof(adapters));149}150 151static void subsys_get_adapter(struct hpi_message *phm,152	struct hpi_response *phr)153{154	int count = phm->obj_index;155	u16 index = 0;156 157	/* find the nCount'th nonzero adapter in array */158	for (index = 0; index < HPI_MAX_ADAPTERS; index++) {159		if (adapters.adapter[index].type) {160			if (!count)161				break;162			count--;163		}164	}165 166	if (index < HPI_MAX_ADAPTERS) {167		phr->u.s.adapter_index = adapters.adapter[index].index;168		phr->u.s.adapter_type = adapters.adapter[index].type;169	} else {170		phr->u.s.adapter_index = 0;171		phr->u.s.adapter_type = 0;172		phr->error = HPI_ERROR_INVALID_OBJ_INDEX;173	}174}175 176static unsigned int control_cache_alloc_check(struct hpi_control_cache *pC)177{178	unsigned int i;179	int cached = 0;180	if (!pC)181		return 0;182 183	if (pC->init)184		return pC->init;185 186	if (!pC->p_cache)187		return 0;188 189	if (pC->control_count && pC->cache_size_in_bytes) {190		char *p_master_cache;191		unsigned int byte_count = 0;192 193		p_master_cache = (char *)pC->p_cache;194		HPI_DEBUG_LOG(DEBUG, "check %d controls\n",195			pC->control_count);196		for (i = 0; i < pC->control_count; i++) {197			struct hpi_control_cache_info *info =198				(struct hpi_control_cache_info *)199				&p_master_cache[byte_count];200			u16 control_index = info->control_index;201 202			if (control_index >= pC->control_count) {203				HPI_DEBUG_LOG(INFO,204					"adap %d control index %d out of range, cache not ready?\n",205					pC->adap_idx, control_index);206				return 0;207			}208 209			if (!info->size_in32bit_words) {210				if (!i) {211					HPI_DEBUG_LOG(INFO,212						"adap %d cache not ready?\n",213						pC->adap_idx);214					return 0;215				}216				/* The cache is invalid.217				 * Minimum valid entry size is218				 * sizeof(struct hpi_control_cache_info)219				 */220				HPI_DEBUG_LOG(ERROR,221					"adap %d zero size cache entry %d\n",222					pC->adap_idx, i);223				break;224			}225 226			if (info->control_type) {227				pC->p_info[control_index] = info;228				cached++;229			} else {	/* dummy cache entry */230				pC->p_info[control_index] = NULL;231			}232 233			byte_count += info->size_in32bit_words * 4;234 235			HPI_DEBUG_LOG(VERBOSE,236				"cached %d, pinfo %p index %d type %d size %d\n",237				cached, pC->p_info[info->control_index],238				info->control_index, info->control_type,239				info->size_in32bit_words);240 241			/* quit loop early if whole cache has been scanned.242			 * dwControlCount is the maximum possible entries243			 * but some may be absent from the cache244			 */245			if (byte_count >= pC->cache_size_in_bytes)246				break;247			/* have seen last control index */248			if (info->control_index == pC->control_count - 1)249				break;250		}251 252		if (byte_count != pC->cache_size_in_bytes)253			HPI_DEBUG_LOG(WARNING,254				"adap %d bytecount %d != cache size %d\n",255				pC->adap_idx, byte_count,256				pC->cache_size_in_bytes);257		else258			HPI_DEBUG_LOG(DEBUG,259				"adap %d cache good, bytecount == cache size = %d\n",260				pC->adap_idx, byte_count);261 262		pC->init = (u16)cached;263	}264	return pC->init;265}266 267/** Find a control.268*/269static short find_control(u16 control_index,270	struct hpi_control_cache *p_cache, struct hpi_control_cache_info **pI)271{272	if (!control_cache_alloc_check(p_cache)) {273		HPI_DEBUG_LOG(VERBOSE,274			"control_cache_alloc_check() failed %d\n",275			control_index);276		return 0;277	}278 279	*pI = p_cache->p_info[control_index];280	if (!*pI) {281		HPI_DEBUG_LOG(VERBOSE, "Uncached Control %d\n",282			control_index);283		return 0;284	} else {285		HPI_DEBUG_LOG(VERBOSE, "find_control() type %d\n",286			(*pI)->control_type);287	}288	return 1;289}290 291/* allow unified treatment of several string fields within struct */292#define HPICMN_PAD_OFS_AND_SIZE(m)  {\293	offsetof(struct hpi_control_cache_pad, m), \294	sizeof(((struct hpi_control_cache_pad *)(NULL))->m) }295 296struct pad_ofs_size {297	unsigned int offset;298	unsigned int field_size;299};300 301static const struct pad_ofs_size pad_desc[] = {302	HPICMN_PAD_OFS_AND_SIZE(c_channel),	/* HPI_PAD_CHANNEL_NAME */303	HPICMN_PAD_OFS_AND_SIZE(c_artist),	/* HPI_PAD_ARTIST */304	HPICMN_PAD_OFS_AND_SIZE(c_title),	/* HPI_PAD_TITLE */305	HPICMN_PAD_OFS_AND_SIZE(c_comment),	/* HPI_PAD_COMMENT */306};307 308/** CheckControlCache checks the cache and fills the struct hpi_response309 * accordingly. It returns one if a cache hit occurred, zero otherwise.310 */311short hpi_check_control_cache_single(struct hpi_control_cache_single *pC,312	struct hpi_message *phm, struct hpi_response *phr)313{314	size_t response_size;315	short found = 1;316 317	/* set the default response size */318	response_size =319		sizeof(struct hpi_response_header) +320		sizeof(struct hpi_control_res);321 322	switch (pC->u.i.control_type) {323 324	case HPI_CONTROL_METER:325		if (phm->u.c.attribute == HPI_METER_PEAK) {326			phr->u.c.an_log_value[0] = pC->u.meter.an_log_peak[0];327			phr->u.c.an_log_value[1] = pC->u.meter.an_log_peak[1];328		} else if (phm->u.c.attribute == HPI_METER_RMS) {329			if (pC->u.meter.an_logRMS[0] ==330				HPI_CACHE_INVALID_SHORT) {331				phr->error =332					HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;333				phr->u.c.an_log_value[0] = HPI_METER_MINIMUM;334				phr->u.c.an_log_value[1] = HPI_METER_MINIMUM;335			} else {336				phr->u.c.an_log_value[0] =337					pC->u.meter.an_logRMS[0];338				phr->u.c.an_log_value[1] =339					pC->u.meter.an_logRMS[1];340			}341		} else342			found = 0;343		break;344	case HPI_CONTROL_VOLUME:345		if (phm->u.c.attribute == HPI_VOLUME_GAIN) {346			phr->u.c.an_log_value[0] = pC->u.vol.an_log[0];347			phr->u.c.an_log_value[1] = pC->u.vol.an_log[1];348		} else if (phm->u.c.attribute == HPI_VOLUME_MUTE) {349			if (pC->u.vol.flags & HPI_VOLUME_FLAG_HAS_MUTE) {350				if (pC->u.vol.flags & HPI_VOLUME_FLAG_MUTED)351					phr->u.c.param1 =352						HPI_BITMASK_ALL_CHANNELS;353				else354					phr->u.c.param1 = 0;355			} else {356				phr->error =357					HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;358				phr->u.c.param1 = 0;359			}360		} else {361			found = 0;362		}363		break;364	case HPI_CONTROL_MULTIPLEXER:365		if (phm->u.c.attribute == HPI_MULTIPLEXER_SOURCE) {366			phr->u.c.param1 = pC->u.mux.source_node_type;367			phr->u.c.param2 = pC->u.mux.source_node_index;368		} else {369			found = 0;370		}371		break;372	case HPI_CONTROL_CHANNEL_MODE:373		if (phm->u.c.attribute == HPI_CHANNEL_MODE_MODE)374			phr->u.c.param1 = pC->u.mode.mode;375		else376			found = 0;377		break;378	case HPI_CONTROL_LEVEL:379		if (phm->u.c.attribute == HPI_LEVEL_GAIN) {380			phr->u.c.an_log_value[0] = pC->u.level.an_log[0];381			phr->u.c.an_log_value[1] = pC->u.level.an_log[1];382		} else383			found = 0;384		break;385	case HPI_CONTROL_TUNER:386		if (phm->u.c.attribute == HPI_TUNER_FREQ)387			phr->u.c.param1 = pC->u.tuner.freq_ink_hz;388		else if (phm->u.c.attribute == HPI_TUNER_BAND)389			phr->u.c.param1 = pC->u.tuner.band;390		else if (phm->u.c.attribute == HPI_TUNER_LEVEL_AVG)391			if (pC->u.tuner.s_level_avg ==392				HPI_CACHE_INVALID_SHORT) {393				phr->u.cu.tuner.s_level = 0;394				phr->error =395					HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;396			} else397				phr->u.cu.tuner.s_level =398					pC->u.tuner.s_level_avg;399		else400			found = 0;401		break;402	case HPI_CONTROL_AESEBU_RECEIVER:403		if (phm->u.c.attribute == HPI_AESEBURX_ERRORSTATUS)404			phr->u.c.param1 = pC->u.aes3rx.error_status;405		else if (phm->u.c.attribute == HPI_AESEBURX_FORMAT)406			phr->u.c.param1 = pC->u.aes3rx.format;407		else408			found = 0;409		break;410	case HPI_CONTROL_AESEBU_TRANSMITTER:411		if (phm->u.c.attribute == HPI_AESEBUTX_FORMAT)412			phr->u.c.param1 = pC->u.aes3tx.format;413		else414			found = 0;415		break;416	case HPI_CONTROL_TONEDETECTOR:417		if (phm->u.c.attribute == HPI_TONEDETECTOR_STATE)418			phr->u.c.param1 = pC->u.tone.state;419		else420			found = 0;421		break;422	case HPI_CONTROL_SILENCEDETECTOR:423		if (phm->u.c.attribute == HPI_SILENCEDETECTOR_STATE) {424			phr->u.c.param1 = pC->u.silence.state;425		} else426			found = 0;427		break;428	case HPI_CONTROL_MICROPHONE:429		if (phm->u.c.attribute == HPI_MICROPHONE_PHANTOM_POWER)430			phr->u.c.param1 = pC->u.microphone.phantom_state;431		else432			found = 0;433		break;434	case HPI_CONTROL_SAMPLECLOCK:435		if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE)436			phr->u.c.param1 = pC->u.clk.source;437		else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE_INDEX) {438			if (pC->u.clk.source_index ==439				HPI_CACHE_INVALID_UINT16) {440				phr->u.c.param1 = 0;441				phr->error =442					HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;443			} else444				phr->u.c.param1 = pC->u.clk.source_index;445		} else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SAMPLERATE)446			phr->u.c.param1 = pC->u.clk.sample_rate;447		else448			found = 0;449		break;450	case HPI_CONTROL_PAD:{451			struct hpi_control_cache_pad *p_pad;452			p_pad = (struct hpi_control_cache_pad *)pC;453 454			if (!(p_pad->field_valid_flags & (1 <<455						HPI_CTL_ATTR_INDEX(phm->u.c.456							attribute)))) {457				phr->error =458					HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;459				break;460			}461 462			if (phm->u.c.attribute == HPI_PAD_PROGRAM_ID)463				phr->u.c.param1 = p_pad->pI;464			else if (phm->u.c.attribute == HPI_PAD_PROGRAM_TYPE)465				phr->u.c.param1 = p_pad->pTY;466			else {467				unsigned int index =468					HPI_CTL_ATTR_INDEX(phm->u.c.469					attribute) - 1;470				unsigned int offset = phm->u.c.param1;471				unsigned int pad_string_len, field_size;472				char *pad_string;473				unsigned int tocopy;474 475				if (index > ARRAY_SIZE(pad_desc) - 1) {476					phr->error =477						HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;478					break;479				}480 481				pad_string =482					((char *)p_pad) +483					pad_desc[index].offset;484				field_size = pad_desc[index].field_size;485				/* Ensure null terminator */486				pad_string[field_size - 1] = 0;487 488				pad_string_len = strlen(pad_string) + 1;489 490				if (offset > pad_string_len) {491					phr->error =492						HPI_ERROR_INVALID_CONTROL_VALUE;493					break;494				}495 496				tocopy = pad_string_len - offset;497				if (tocopy > sizeof(phr->u.cu.chars8.sz_data))498					tocopy = sizeof(phr->u.cu.chars8.499						sz_data);500 501				memcpy(phr->u.cu.chars8.sz_data,502					&pad_string[offset], tocopy);503 504				phr->u.cu.chars8.remaining_chars =505					pad_string_len - offset - tocopy;506			}507		}508		break;509	default:510		found = 0;511		break;512	}513 514	HPI_DEBUG_LOG(VERBOSE, "%s Adap %d, Ctl %d, Type %d, Attr %d\n",515		found ? "Cached" : "Uncached", phm->adapter_index,516		pC->u.i.control_index, pC->u.i.control_type,517		phm->u.c.attribute);518 519	if (found) {520		phr->size = (u16)response_size;521		phr->type = HPI_TYPE_RESPONSE;522		phr->object = phm->object;523		phr->function = phm->function;524	}525 526	return found;527}528 529short hpi_check_control_cache(struct hpi_control_cache *p_cache,530	struct hpi_message *phm, struct hpi_response *phr)531{532	struct hpi_control_cache_info *pI;533 534	if (!find_control(phm->obj_index, p_cache, &pI)) {535		HPI_DEBUG_LOG(VERBOSE,536			"HPICMN find_control() failed for adap %d\n",537			phm->adapter_index);538		return 0;539	}540 541	phr->error = 0;542	phr->specific_error = 0;543	phr->version = 0;544 545	return hpi_check_control_cache_single((struct hpi_control_cache_single546			*)pI, phm, phr);547}548 549/** Updates the cache with Set values.550 551Only update if no error.552Volume and Level return the limited values in the response, so use these553Multiplexer does so use sent values554*/555void hpi_cmn_control_cache_sync_to_msg_single(struct hpi_control_cache_single556	*pC, struct hpi_message *phm, struct hpi_response *phr)557{558	switch (pC->u.i.control_type) {559	case HPI_CONTROL_VOLUME:560		if (phm->u.c.attribute == HPI_VOLUME_GAIN) {561			pC->u.vol.an_log[0] = phr->u.c.an_log_value[0];562			pC->u.vol.an_log[1] = phr->u.c.an_log_value[1];563		} else if (phm->u.c.attribute == HPI_VOLUME_MUTE) {564			if (phm->u.c.param1)565				pC->u.vol.flags |= HPI_VOLUME_FLAG_MUTED;566			else567				pC->u.vol.flags &= ~HPI_VOLUME_FLAG_MUTED;568		}569		break;570	case HPI_CONTROL_MULTIPLEXER:571		/* mux does not return its setting on Set command. */572		if (phm->u.c.attribute == HPI_MULTIPLEXER_SOURCE) {573			pC->u.mux.source_node_type = (u16)phm->u.c.param1;574			pC->u.mux.source_node_index = (u16)phm->u.c.param2;575		}576		break;577	case HPI_CONTROL_CHANNEL_MODE:578		/* mode does not return its setting on Set command. */579		if (phm->u.c.attribute == HPI_CHANNEL_MODE_MODE)580			pC->u.mode.mode = (u16)phm->u.c.param1;581		break;582	case HPI_CONTROL_LEVEL:583		if (phm->u.c.attribute == HPI_LEVEL_GAIN) {584			pC->u.vol.an_log[0] = phr->u.c.an_log_value[0];585			pC->u.vol.an_log[1] = phr->u.c.an_log_value[1];586		}587		break;588	case HPI_CONTROL_MICROPHONE:589		if (phm->u.c.attribute == HPI_MICROPHONE_PHANTOM_POWER)590			pC->u.microphone.phantom_state = (u16)phm->u.c.param1;591		break;592	case HPI_CONTROL_AESEBU_TRANSMITTER:593		if (phm->u.c.attribute == HPI_AESEBUTX_FORMAT)594			pC->u.aes3tx.format = phm->u.c.param1;595		break;596	case HPI_CONTROL_AESEBU_RECEIVER:597		if (phm->u.c.attribute == HPI_AESEBURX_FORMAT)598			pC->u.aes3rx.format = phm->u.c.param1;599		break;600	case HPI_CONTROL_SAMPLECLOCK:601		if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE)602			pC->u.clk.source = (u16)phm->u.c.param1;603		else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE_INDEX)604			pC->u.clk.source_index = (u16)phm->u.c.param1;605		else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SAMPLERATE)606			pC->u.clk.sample_rate = phm->u.c.param1;607		break;608	default:609		break;610	}611}612 613void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *p_cache,614	struct hpi_message *phm, struct hpi_response *phr)615{616	struct hpi_control_cache_single *pC;617	struct hpi_control_cache_info *pI;618 619	if (phr->error)620		return;621 622	if (!find_control(phm->obj_index, p_cache, &pI)) {623		HPI_DEBUG_LOG(VERBOSE,624			"HPICMN find_control() failed for adap %d\n",625			phm->adapter_index);626		return;627	}628 629	/* pC is the default cached control strucure.630	   May be cast to something else in the following switch statement.631	 */632	pC = (struct hpi_control_cache_single *)pI;633 634	hpi_cmn_control_cache_sync_to_msg_single(pC, phm, phr);635}636 637/** Allocate control cache.638 639\return Cache pointer, or NULL if allocation fails.640*/641struct hpi_control_cache *hpi_alloc_control_cache(const u32 control_count,642	const u32 size_in_bytes, u8 *p_dsp_control_buffer)643{644	struct hpi_control_cache *p_cache =645		kmalloc(sizeof(*p_cache), GFP_KERNEL);646	if (!p_cache)647		return NULL;648 649	p_cache->p_info =650		kcalloc(control_count, sizeof(*p_cache->p_info), GFP_KERNEL);651	if (!p_cache->p_info) {652		kfree(p_cache);653		return NULL;654	}655 656	p_cache->cache_size_in_bytes = size_in_bytes;657	p_cache->control_count = control_count;658	p_cache->p_cache = p_dsp_control_buffer;659	p_cache->init = 0;660	return p_cache;661}662 663void hpi_free_control_cache(struct hpi_control_cache *p_cache)664{665	if (p_cache) {666		kfree(p_cache->p_info);667		kfree(p_cache);668	}669}670 671static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)672{673	hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, phm->function, 0);674 675	switch (phm->function) {676	case HPI_SUBSYS_OPEN:677	case HPI_SUBSYS_CLOSE:678	case HPI_SUBSYS_DRIVER_UNLOAD:679		break;680	case HPI_SUBSYS_DRIVER_LOAD:681		wipe_adapter_list();682		hpios_alistlock_init(&adapters);683		break;684	case HPI_SUBSYS_GET_ADAPTER:685		subsys_get_adapter(phm, phr);686		break;687	case HPI_SUBSYS_GET_NUM_ADAPTERS:688		phr->u.s.num_adapters = adapters.gw_num_adapters;689		break;690	case HPI_SUBSYS_CREATE_ADAPTER:691		break;692	default:693		phr->error = HPI_ERROR_INVALID_FUNC;694		break;695	}696}697 698void HPI_COMMON(struct hpi_message *phm, struct hpi_response *phr)699{700	switch (phm->type) {701	case HPI_TYPE_REQUEST:702		switch (phm->object) {703		case HPI_OBJ_SUBSYSTEM:704			subsys_message(phm, phr);705			break;706		}707		break;708 709	default:710		phr->error = HPI_ERROR_INVALID_TYPE;711		break;712	}713}714