/****************************************************************************** AudioScience HPI driver Copyright (C) 1997-2010 AudioScience Inc. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation; This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA \file hpicmn.c Common functions used by hpixxxx.c modules (C) Copyright AudioScience Inc. 1998-2003 *******************************************************************************/ #define SOURCEFILE_NAME "hpicmn.c" #include "hpi_internal.h" #include "hpidebug.h" #include "hpicmn.h" struct hpi_adapters_list { struct hpios_spinlock list_lock; struct hpi_adapter_obj adapter[HPI_MAX_ADAPTERS]; u16 gw_num_adapters; }; static struct hpi_adapters_list adapters; /** * 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 **/ u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr) { u16 error = 0; if ((phr->type != HPI_TYPE_RESPONSE) || (phr->object != phm->object) || (phr->function != phm->function)) error = HPI_ERROR_INVALID_RESPONSE; return error; } u16 hpi_add_adapter(struct hpi_adapter_obj *pao) { u16 retval = 0; /*HPI_ASSERT(pao->wAdapterType); */ hpios_alistlock_lock(&adapters); if (pao->index >= HPI_MAX_ADAPTERS) { retval = HPI_ERROR_BAD_ADAPTER_NUMBER; goto unlock; } if (adapters.adapter[pao->index].adapter_type) { { retval = HPI_DUPLICATE_ADAPTER_NUMBER; goto unlock; } } adapters.adapter[pao->index] = *pao; hpios_dsplock_init(&adapters.adapter[pao->index]); adapters.gw_num_adapters++; unlock: hpios_alistlock_un_lock(&adapters); return retval; } void hpi_delete_adapter(struct hpi_adapter_obj *pao) { memset(pao, 0, sizeof(struct hpi_adapter_obj)); hpios_alistlock_lock(&adapters); adapters.gw_num_adapters--; /* dec the number of adapters */ hpios_alistlock_un_lock(&adapters); } /** * FindAdapter returns a pointer to the struct hpi_adapter_obj with * index wAdapterIndex in an HPI_ADAPTERS_LIST structure. * */ struct hpi_adapter_obj *hpi_find_adapter(u16 adapter_index) { struct hpi_adapter_obj *pao = NULL; if (adapter_index >= HPI_MAX_ADAPTERS) { HPI_DEBUG_LOG(VERBOSE, "find_adapter invalid index %d ", adapter_index); return NULL; } pao = &adapters.adapter[adapter_index]; if (pao->adapter_type != 0) { /* HPI_DEBUG_LOG(VERBOSE, "Found adapter index %d\n", wAdapterIndex); */ return pao; } else { /* HPI_DEBUG_LOG(VERBOSE, "No adapter index %d\n", wAdapterIndex); */ return NULL; } } /** * * wipe an HPI_ADAPTERS_LIST structure. * **/ static void wipe_adapter_list(void ) { memset(&adapters, 0, sizeof(adapters)); } /** * SubSysGetAdapters fills awAdapterList in an struct hpi_response structure * with all adapters in the given HPI_ADAPTERS_LIST. * */ static void subsys_get_adapters(struct hpi_response *phr) { /* fill in the response adapter array with the position */ /* identified by the adapter number/index of the adapters in */ /* this HPI */ /* i.e. if we have an A120 with it's jumper set to */ /* Adapter Number 2 then put an Adapter type A120 in the */ /* array in position 1 */ /* NOTE: AdapterNumber is 1..N, Index is 0..N-1 */ /* input: NONE */ /* output: wNumAdapters */ /* awAdapter[] */ /* */ short i; struct hpi_adapter_obj *pao = NULL; HPI_DEBUG_LOG(VERBOSE, "subsys_get_adapters\n"); /* for each adapter, place it's type in the position of the array */ /* corresponding to it's adapter number */ for (i = 0; i < adapters.gw_num_adapters; i++) { pao = &adapters.adapter[i]; if (phr->u.s.aw_adapter_list[pao->index] != 0) { phr->error = HPI_DUPLICATE_ADAPTER_NUMBER; phr->specific_error = pao->index; return; } phr->u.s.aw_adapter_list[pao->index] = pao->adapter_type; } phr->u.s.num_adapters = adapters.gw_num_adapters; phr->error = 0; /* the function completed OK; */ } static unsigned int control_cache_alloc_check(struct hpi_control_cache *pC) { unsigned int i; int cached = 0; if (!pC) return 0; if ((!pC->init) && (pC->p_cache != NULL) && (pC->control_count) && (pC->cache_size_in_bytes) ) { u32 *p_master_cache; pC->init = 1; p_master_cache = (u32 *)pC->p_cache; HPI_DEBUG_LOG(VERBOSE, "check %d controls\n", pC->control_count); for (i = 0; i < pC->control_count; i++) { struct hpi_control_cache_info *info = (struct hpi_control_cache_info *) p_master_cache; if (info->control_type) { pC->p_info[i] = info; cached++; } else pC->p_info[i] = NULL; if (info->size_in32bit_words) p_master_cache += info->size_in32bit_words; else p_master_cache += sizeof(struct hpi_control_cache_single) / sizeof(u32); HPI_DEBUG_LOG(VERBOSE, "cached %d, pinfo %p index %d type %d\n", cached, pC->p_info[i], info->control_index, info->control_type); } /* We didn't find anything to cache, so try again later ! */ if (!cached) pC->init = 0; } return pC->init; } /** Find a control. */ static short find_control(struct hpi_message *phm, struct hpi_control_cache *p_cache, struct hpi_control_cache_info **pI, u16 *pw_control_index) { *pw_control_index = phm->obj_index; if (!control_cache_alloc_check(p_cache)) { HPI_DEBUG_LOG(VERBOSE, "control_cache_alloc_check() failed. adap%d ci%d\n", phm->adapter_index, *pw_control_index); return 0; } *pI = p_cache->p_info[*pw_control_index]; if (!*pI) { HPI_DEBUG_LOG(VERBOSE, "uncached adap %d, control %d\n", phm->adapter_index, *pw_control_index); return 0; } else { HPI_DEBUG_LOG(VERBOSE, "find_control() type %d\n", (*pI)->control_type); } return 1; } /** Used by the kernel driver to figure out if a buffer needs mapping. */ short hpi_check_buffer_mapping(struct hpi_control_cache *p_cache, struct hpi_message *phm, void **p, unsigned int *pN) { *pN = 0; *p = NULL; if ((phm->function == HPI_CONTROL_GET_STATE) && (phm->object == HPI_OBJ_CONTROLEX) ) { u16 control_index; struct hpi_control_cache_info *pI; if (!find_control(phm, p_cache, &pI, &control_index)) return 0; } return 0; } /* 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 struct pad_ofs_size pad_desc[] = { HPICMN_PAD_OFS_AND_SIZE(c_channel), /* HPI_PAD_CHANNEL_NAME */ HPICMN_PAD_OFS_AND_SIZE(c_artist), /* HPI_PAD_ARTIST */ HPICMN_PAD_OFS_AND_SIZE(c_title), /* HPI_PAD_TITLE */ HPICMN_PAD_OFS_AND_SIZE(c_comment), /* HPI_PAD_COMMENT */ }; /** CheckControlCache checks the cache and fills the struct hpi_response * accordingly. It returns one if a cache hit occurred, zero otherwise. */ short hpi_check_control_cache(struct hpi_control_cache *p_cache, struct hpi_message *phm, struct hpi_response *phr) { short found = 1; u16 control_index; struct hpi_control_cache_info *pI; struct hpi_control_cache_single *pC; struct hpi_control_cache_pad *p_pad; if (!find_control(phm, p_cache, &pI, &control_index)) return 0; phr->error = 0; /* 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; p_pad = (struct hpi_control_cache_pad *)pI; switch (pI->control_type) { case HPI_CONTROL_METER: if (phm->u.c.attribute == HPI_METER_PEAK) { phr->u.c.an_log_value[0] = pC->u.p.an_log_peak[0]; phr->u.c.an_log_value[1] = pC->u.p.an_log_peak[1]; } else if (phm->u.c.attribute == HPI_METER_RMS) { phr->u.c.an_log_value[0] = pC->u.p.an_logRMS[0]; phr->u.c.an_log_value[1] = pC->u.p.an_logRMS[1]; } else found = 0; break; case HPI_CONTROL_VOLUME: if (phm->u.c.attribute == HPI_VOLUME_GAIN) { phr->u.c.an_log_value[0] = pC->u.v.an_log[0]; phr->u.c.an_log_value[1] = pC->u.v.an_log[1]; } else found = 0; break; case HPI_CONTROL_MULTIPLEXER: if (phm->u.c.attribute == HPI_MULTIPLEXER_SOURCE) { phr->u.c.param1 = pC->u.x.source_node_type; phr->u.c.param2 = pC->u.x.source_node_index; } else { found = 0; } break; case HPI_CONTROL_CHANNEL_MODE: if (phm->u.c.attribute == HPI_CHANNEL_MODE_MODE) phr->u.c.param1 = pC->u.m.mode; else found = 0; break; case HPI_CONTROL_LEVEL: if (phm->u.c.attribute == HPI_LEVEL_GAIN) { phr->u.c.an_log_value[0] = pC->u.l.an_log[0]; phr->u.c.an_log_value[1] = pC->u.l.an_log[1]; } else found = 0; break; case HPI_CONTROL_TUNER: if (phm->u.c.attribute == HPI_TUNER_FREQ) phr->u.c.param1 = pC->u.t.freq_ink_hz; else if (phm->u.c.attribute == HPI_TUNER_BAND) phr->u.c.param1 = pC->u.t.band; else if ((phm->u.c.attribute == HPI_TUNER_LEVEL) && (phm->u.c.param1 == HPI_TUNER_LEVEL_AVERAGE)) phr->u.c.param1 = pC->u.t.level; else found = 0; break; case HPI_CONTROL_AESEBU_RECEIVER: if (phm->u.c.attribute == HPI_AESEBURX_ERRORSTATUS) phr->u.c.param1 = pC->u.aes3rx.error_status; else if (phm->u.c.attribute == HPI_AESEBURX_FORMAT) phr->u.c.param1 = pC->u.aes3rx.source; else found = 0; break; case HPI_CONTROL_AESEBU_TRANSMITTER: if (phm->u.c.attribute == HPI_AESEBUTX_FORMAT) phr->u.c.param1 = pC->u.aes3tx.format; else found = 0; break; case HPI_CONTROL_TONEDETECTOR: if (phm->u.c.attribute == HPI_TONEDETECTOR_STATE) phr->u.c.param1 = pC->u.tone.state; else found = 0; break; case HPI_CONTROL_SILENCEDETECTOR: if (phm->u.c.attribute == HPI_SILENCEDETECTOR_STATE) { phr->u.c.param1 = pC->u.silence.state; phr->u.c.param2 = pC->u.silence.count; } else found = 0; break; case HPI_CONTROL_MICROPHONE: if (phm->u.c.attribute == HPI_MICROPHONE_PHANTOM_POWER) phr->u.c.param1 = pC->u.phantom_power.state; else found = 0; break; case HPI_CONTROL_SAMPLECLOCK: if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE) phr->u.c.param1 = pC->u.clk.source; else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE_INDEX) { if (pC->u.clk.source_index == HPI_ERROR_ILLEGAL_CACHE_VALUE) { phr->u.c.param1 = 0; phr->error = HPI_ERROR_INVALID_OPERATION; } else phr->u.c.param1 = pC->u.clk.source_index; } else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SAMPLERATE) phr->u.c.param1 = pC->u.clk.sample_rate; else found = 0; break; case HPI_CONTROL_PAD: if (!(p_pad->field_valid_flags & (1 << HPI_CTL_ATTR_INDEX(phm->u.c. attribute)))) { phr->error = HPI_ERROR_INVALID_CONTROL_ATTRIBUTE; break; } if (phm->u.c.attribute == HPI_PAD_PROGRAM_ID) phr->u.c.param1 = p_pad->pI; else if (phm->u.c.attribute == HPI_PAD_PROGRAM_TYPE) phr->u.c.param1 = p_pad->pTY; else { unsigned int index = HPI_CTL_ATTR_INDEX(phm->u.c.attribute) - 1; unsigned int offset = phm->u.c.param1; unsigned int pad_string_len, field_size; char *pad_string; unsigned int tocopy; HPI_DEBUG_LOG(VERBOSE, "PADS HPI_PADS_ %d\n", phm->u.c.attribute); if (index > ARRAY_SIZE(pad_desc) - 1) { phr->error = HPI_ERROR_INVALID_CONTROL_ATTRIBUTE; break; } pad_string = ((char *)p_pad) + pad_desc[index].offset; field_size = pad_desc[index].field_size; /* Ensure null terminator */ pad_string[field_size - 1] = 0; pad_string_len = strlen(pad_string) + 1; if (offset > pad_string_len) { phr->error = HPI_ERROR_INVALID_CONTROL_VALUE; break; } tocopy = pad_string_len - offset; if (tocopy > sizeof(phr->u.cu.chars8.sz_data)) tocopy = sizeof(phr->u.cu.chars8.sz_data); HPI_DEBUG_LOG(VERBOSE, "PADS memcpy(%d), offset %d \n", tocopy, offset); memcpy(phr->u.cu.chars8.sz_data, &pad_string[offset], tocopy); phr->u.cu.chars8.remaining_chars = pad_string_len - offset - tocopy; } break; default: found = 0; break; } if (found) HPI_DEBUG_LOG(VERBOSE, "cached adap %d, ctl %d, type %d, attr %d\n", phm->adapter_index, pI->control_index, pI->control_type, phm->u.c.attribute); else HPI_DEBUG_LOG(VERBOSE, "uncached adap %d, ctl %d, ctl type %d\n", phm->adapter_index, pI->control_index, pI->control_type); if (found) phr->size = sizeof(struct hpi_response_header) + sizeof(struct hpi_control_res); return found; } /** 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 hpi_sync_control_cache(struct hpi_control_cache *p_cache, struct hpi_message *phm, struct hpi_response *phr) { u16 control_index; struct hpi_control_cache_single *pC; struct hpi_control_cache_info *pI; if (phr->error) return; if (!find_control(phm, p_cache, &pI, &control_index)) 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; switch (pI->control_type) { case HPI_CONTROL_VOLUME: if (phm->u.c.attribute == HPI_VOLUME_GAIN) { pC->u.v.an_log[0] = phr->u.c.an_log_value[0]; pC->u.v.an_log[1] = phr->u.c.an_log_value[1]; } break; case HPI_CONTROL_MULTIPLEXER: /* mux does not return its setting on Set command. */ if (phm->u.c.attribute == HPI_MULTIPLEXER_SOURCE) { pC->u.x.source_node_type = (u16)phm->u.c.param1; pC->u.x.source_node_index = (u16)phm->u.c.param2; } break; case HPI_CONTROL_CHANNEL_MODE: /* mode does not return its setting on Set command. */ if (phm->u.c.attribute == HPI_CHANNEL_MODE_MODE) pC->u.m.mode = (u16)phm->u.c.param1; break; case HPI_CONTROL_LEVEL: if (phm->u.c.attribute == HPI_LEVEL_GAIN) { pC->u.v.an_log[0] = phr->u.c.an_log_value[0]; pC->u.v.an_log[1] = phr->u.c.an_log_value[1]; } break; case HPI_CONTROL_MICROPHONE: if (phm->u.c.attribute == HPI_MICROPHONE_PHANTOM_POWER) pC->u.phantom_power.state = (u16)phm->u.c.param1; break; case HPI_CONTROL_AESEBU_TRANSMITTER: if (phm->u.c.attribute == HPI_AESEBUTX_FORMAT) pC->u.aes3tx.format = phm->u.c.param1; break; case HPI_CONTROL_AESEBU_RECEIVER: if (phm->u.c.attribute == HPI_AESEBURX_FORMAT) pC->u.aes3rx.source = phm->u.c.param1; break; case HPI_CONTROL_SAMPLECLOCK: if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE) pC->u.clk.source = (u16)phm->u.c.param1; else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SOURCE_INDEX) pC->u.clk.source_index = (u16)phm->u.c.param1; else if (phm->u.c.attribute == HPI_SAMPLECLOCK_SAMPLERATE) pC->u.clk.sample_rate = phm->u.c.param1; break; default: break; } } struct hpi_control_cache *hpi_alloc_control_cache(const u32 number_of_controls, const u32 size_in_bytes, struct hpi_control_cache_info *pDSP_control_buffer) { struct hpi_control_cache *p_cache = kmalloc(sizeof(*p_cache), GFP_KERNEL); p_cache->cache_size_in_bytes = size_in_bytes; p_cache->control_count = number_of_controls; p_cache->p_cache = (struct hpi_control_cache_single *)pDSP_control_buffer; p_cache->init = 0; p_cache->p_info = kmalloc(sizeof(*p_cache->p_info) * p_cache->control_count, GFP_KERNEL); return p_cache; } void hpi_free_control_cache(struct hpi_control_cache *p_cache) { if (p_cache->init) { kfree(p_cache->p_info); p_cache->p_info = NULL; p_cache->init = 0; kfree(p_cache); } } static void subsys_message(struct hpi_message *phm, struct hpi_response *phr) { switch (phm->function) { case HPI_SUBSYS_OPEN: case HPI_SUBSYS_CLOSE: case HPI_SUBSYS_DRIVER_UNLOAD: phr->error = 0; break; case HPI_SUBSYS_DRIVER_LOAD: wipe_adapter_list(); hpios_alistlock_init(&adapters); phr->error = 0; break; case HPI_SUBSYS_GET_INFO: subsys_get_adapters(phr); break; case HPI_SUBSYS_CREATE_ADAPTER: case HPI_SUBSYS_DELETE_ADAPTER: phr->error = 0; break; default: phr->error = HPI_ERROR_INVALID_FUNC; break; } } void HPI_COMMON(struct hpi_message *phm, struct hpi_response *phr) { switch (phm->type) { case HPI_TYPE_MESSAGE: switch (phm->object) { case HPI_OBJ_SUBSYSTEM: subsys_message(phm, phr); break; } break; default: phr->error = HPI_ERROR_INVALID_TYPE; break; } }