Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View file

@ -0,0 +1,5 @@
snd-asihpi-objs := asihpi.o hpioctl.o hpimsginit.o\
hpicmn.o hpifunc.o hpidebug.o hpidspcd.o\
hpios.o hpi6000.o hpi6205.o hpimsgx.o
obj-$(CONFIG_SND_ASIHPI) += snd-asihpi.o

2995
sound/pci/asihpi/asihpi.c Normal file

File diff suppressed because it is too large Load diff

1727
sound/pci/asihpi/hpi.h Normal file

File diff suppressed because it is too large Load diff

1809
sound/pci/asihpi/hpi6000.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,70 @@
/*****************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
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
Public declarations for DSP Proramming Interface to TI C6701
Shared between hpi6000.c and DSP code
(C) Copyright AudioScience Inc. 1998-2003
******************************************************************************/
#ifndef _HPI6000_H_
#define _HPI6000_H_
#define HPI_NMIXER_CONTROLS 200
/*
* Control caching is always supported in the HPI code.
* The DSP should make sure that dwControlCacheSizeInBytes is initialized to 0
* during boot to make it in-active.
*/
struct hpi_hif_6000 {
u32 host_cmd;
u32 dsp_ack;
u32 address;
u32 length;
u32 message_buffer_address;
u32 response_buffer_address;
u32 dsp_number;
u32 adapter_info;
u32 control_cache_is_dirty;
u32 control_cache_address;
u32 control_cache_size_in_bytes;
u32 control_cache_count;
};
#define HPI_HIF_PACK_ADAPTER_INFO(adapter, version_major, version_minor) \
((adapter << 16) | (version_major << 8) | version_minor)
#define HPI_HIF_ADAPTER_INFO_EXTRACT_ADAPTER(adapterinfo) \
((adapterinfo >> 16) & 0xffff)
#define HPI_HIF_ADAPTER_INFO_EXTRACT_HWVERSION_MAJOR(adapterinfo) \
((adapterinfo >> 8) & 0xff)
#define HPI_HIF_ADAPTER_INFO_EXTRACT_HWVERSION_MINOR(adapterinfo) \
(adapterinfo & 0xff)
/* Command/status exchanged between host and DSP */
#define HPI_HIF_IDLE 0
#define HPI_HIF_SEND_MSG 1
#define HPI_HIF_GET_RESP 2
#define HPI_HIF_DATA_MASK 0x10
#define HPI_HIF_SEND_DATA 0x13
#define HPI_HIF_GET_DATA 0x14
#define HPI_HIF_SEND_DONE 5
#define HPI_HIF_RESET 9
#endif /* _HPI6000_H_ */

2208
sound/pci/asihpi/hpi6205.c Normal file

File diff suppressed because it is too large Load diff

103
sound/pci/asihpi/hpi6205.h Normal file
View file

@ -0,0 +1,103 @@
/*****************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
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
Host Interface module for an ASI6205 based
bus mastering PCI adapter.
Copyright AudioScience, Inc., 2003
******************************************************************************/
#ifndef _HPI6205_H_
#define _HPI6205_H_
#include "hpi_internal.h"
/***********************************************************
Defines used for basic messaging
************************************************************/
#define H620_HIF_RESET 0
#define H620_HIF_IDLE 1
#define H620_HIF_GET_RESP 2
#define H620_HIF_DATA_DONE 3
#define H620_HIF_DATA_MASK 0x10
#define H620_HIF_SEND_DATA 0x14
#define H620_HIF_GET_DATA 0x15
#define H620_HIF_UNKNOWN 0x0000ffff
/***********************************************************
Types used for mixer control caching
************************************************************/
#define H620_MAX_ISTREAMS 32
#define H620_MAX_OSTREAMS 32
#define HPI_NMIXER_CONTROLS 2048
/*********************************************************************
This is used for dynamic control cache allocation
**********************************************************************/
struct controlcache_6205 {
u32 number_of_controls;
u32 physical_address32;
u32 size_in_bytes;
};
/*********************************************************************
This is used for dynamic allocation of async event array
**********************************************************************/
struct async_event_buffer_6205 {
u32 physical_address32;
u32 spare;
struct hpi_fifo_buffer b;
};
/***********************************************************
The Host located memory buffer that the 6205 will bus master
in and out of.
************************************************************/
#define HPI6205_SIZEOF_DATA (16*1024)
struct message_buffer_6205 {
struct hpi_message message;
char data[256];
};
struct response_buffer_6205 {
struct hpi_response response;
char data[256];
};
union buffer_6205 {
struct message_buffer_6205 message_buffer;
struct response_buffer_6205 response_buffer;
u8 b_data[HPI6205_SIZEOF_DATA];
};
struct bus_master_interface {
u32 host_cmd;
u32 dsp_ack;
u32 transfer_size_in_bytes;
union buffer_6205 u;
struct controlcache_6205 control_cache;
struct async_event_buffer_6205 async_buffer;
struct hpi_hostbuffer_status
instream_host_buffer_status[H620_MAX_ISTREAMS];
struct hpi_hostbuffer_status
outstream_host_buffer_status[H620_MAX_OSTREAMS];
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,32 @@
/** HPI Version Definitions
Development releases have odd minor version.
Production releases have even minor version.
\file hpi_version.h
*/
#ifndef _HPI_VERSION_H
#define _HPI_VERSION_H
/* Use single digits for versions less that 10 to avoid octal. */
/* *** HPI_VER is the only edit required to update version *** */
/** HPI version */
#define HPI_VER HPI_VERSION_CONSTRUCTOR(4, 10, 1)
/** HPI version string in dotted decimal format */
#define HPI_VER_STRING "4.10.01"
/** Library version as documented in hpi-api-versions.txt */
#define HPI_LIB_VER HPI_VERSION_CONSTRUCTOR(10, 2, 0)
/** Construct hpi version number from major, minor, release numbers */
#define HPI_VERSION_CONSTRUCTOR(maj, min, r) ((maj << 16) + (min << 8) + r)
/** Extract major version from hpi version number */
#define HPI_VER_MAJOR(v) ((int)(v >> 16))
/** Extract minor version from hpi version number */
#define HPI_VER_MINOR(v) ((int)((v >> 8) & 0xFF))
/** Extract release from hpi version number */
#define HPI_VER_RELEASE(v) ((int)(v & 0xFF))
#endif

703
sound/pci/asihpi/hpicmn.c Normal file
View file

@ -0,0 +1,703 @@
/******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
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 "hpimsginit.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)
{
if (phr->type != HPI_TYPE_RESPONSE) {
HPI_DEBUG_LOG(ERROR, "header type %d invalid\n", phr->type);
return HPI_ERROR_INVALID_RESPONSE;
}
if (phr->object != phm->object) {
HPI_DEBUG_LOG(ERROR, "header object %d invalid\n",
phr->object);
return HPI_ERROR_INVALID_RESPONSE;
}
if (phr->function != phm->function) {
HPI_DEBUG_LOG(ERROR, "header function %d invalid\n",
phr->function);
return HPI_ERROR_INVALID_RESPONSE;
}
return 0;
}
u16 hpi_add_adapter(struct hpi_adapter_obj *pao)
{
u16 retval = 0;
/*HPI_ASSERT(pao->type); */
hpios_alistlock_lock(&adapters);
if (pao->index >= HPI_MAX_ADAPTERS) {
retval = HPI_ERROR_BAD_ADAPTER_NUMBER;
goto unlock;
}
if (adapters.adapter[pao->index].type) {
int a;
for (a = HPI_MAX_ADAPTERS - 1; a >= 0; a--) {
if (!adapters.adapter[a].type) {
HPI_DEBUG_LOG(WARNING,
"ASI%X duplicate index %d moved to %d\n",
pao->type, pao->index, a);
pao->index = a;
break;
}
}
if (a < 0) {
retval = HPI_ERROR_DUPLICATE_ADAPTER_NUMBER;
goto unlock;
}
}
adapters.adapter[pao->index] = *pao;
hpios_dsplock_init(&adapters.adapter[pao->index]);
adapters.gw_num_adapters++;
unlock:
hpios_alistlock_unlock(&adapters);
return retval;
}
void hpi_delete_adapter(struct hpi_adapter_obj *pao)
{
if (!pao->type) {
HPI_DEBUG_LOG(ERROR, "removing null adapter?\n");
return;
}
hpios_alistlock_lock(&adapters);
if (adapters.adapter[pao->index].type)
adapters.gw_num_adapters--;
memset(&adapters.adapter[pao->index], 0, sizeof(adapters.adapter[0]));
hpios_alistlock_unlock(&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\n",
adapter_index);
return NULL;
}
pao = &adapters.adapter[adapter_index];
if (pao->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));
}
static void subsys_get_adapter(struct hpi_message *phm,
struct hpi_response *phr)
{
int count = phm->obj_index;
u16 index = 0;
/* find the nCount'th nonzero adapter in array */
for (index = 0; index < HPI_MAX_ADAPTERS; index++) {
if (adapters.adapter[index].type) {
if (!count)
break;
count--;
}
}
if (index < HPI_MAX_ADAPTERS) {
phr->u.s.adapter_index = adapters.adapter[index].index;
phr->u.s.adapter_type = adapters.adapter[index].type;
} else {
phr->u.s.adapter_index = 0;
phr->u.s.adapter_type = 0;
phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
}
}
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)
return pC->init;
if (!pC->p_cache)
return 0;
if (pC->control_count && pC->cache_size_in_bytes) {
char *p_master_cache;
unsigned int byte_count = 0;
p_master_cache = (char *)pC->p_cache;
HPI_DEBUG_LOG(DEBUG, "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[byte_count];
if (!info->size_in32bit_words) {
if (!i) {
HPI_DEBUG_LOG(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_LOG(ERROR,
"adap %d zero size cache entry %d\n",
pC->adap_idx, i);
break;
}
if (info->control_type) {
pC->p_info[info->control_index] = info;
cached++;
} else { /* dummy cache entry */
pC->p_info[info->control_index] = NULL;
}
byte_count += info->size_in32bit_words * 4;
HPI_DEBUG_LOG(VERBOSE,
"cached %d, pinfo %p index %d type %d size %d\n",
cached, pC->p_info[info->control_index],
info->control_index, info->control_type,
info->size_in32bit_words);
/* quit loop early if whole cache has been scanned.
* dwControlCount is the maximum possible entries
* but some may be absent from the cache
*/
if (byte_count >= pC->cache_size_in_bytes)
break;
/* have seen last control index */
if (info->control_index == pC->control_count - 1)
break;
}
if (byte_count != pC->cache_size_in_bytes)
HPI_DEBUG_LOG(WARNING,
"adap %d bytecount %d != cache size %d\n",
pC->adap_idx, byte_count,
pC->cache_size_in_bytes);
else
HPI_DEBUG_LOG(DEBUG,
"adap %d cache good, bytecount == cache size = %d\n",
pC->adap_idx, byte_count);
pC->init = (u16)cached;
}
return pC->init;
}
/** Find a control.
*/
static short find_control(u16 control_index,
struct hpi_control_cache *p_cache, struct hpi_control_cache_info **pI)
{
if (!control_cache_alloc_check(p_cache)) {
HPI_DEBUG_LOG(VERBOSE,
"control_cache_alloc_check() failed %d\n",
control_index);
return 0;
}
*pI = p_cache->p_info[control_index];
if (!*pI) {
HPI_DEBUG_LOG(VERBOSE, "Uncached Control %d\n",
control_index);
return 0;
} else {
HPI_DEBUG_LOG(VERBOSE, "find_control() type %d\n",
(*pI)->control_type);
}
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 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;
struct hpi_control_cache_info *pI;
struct hpi_control_cache_single *pC;
size_t response_size;
if (!find_control(phm->obj_index, p_cache, &pI)) {
HPI_DEBUG_LOG(VERBOSE,
"HPICMN find_control() failed for adap %d\n",
phm->adapter_index);
return 0;
}
phr->error = 0;
phr->specific_error = 0;
phr->version = 0;
/* set the default response size */
response_size =
sizeof(struct hpi_response_header) +
sizeof(struct hpi_control_res);
/* 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_METER:
if (phm->u.c.attribute == HPI_METER_PEAK) {
phr->u.c.an_log_value[0] = pC->u.meter.an_log_peak[0];
phr->u.c.an_log_value[1] = pC->u.meter.an_log_peak[1];
} else if (phm->u.c.attribute == HPI_METER_RMS) {
if (pC->u.meter.an_logRMS[0] ==
HPI_CACHE_INVALID_SHORT) {
phr->error =
HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
phr->u.c.an_log_value[0] = HPI_METER_MINIMUM;
phr->u.c.an_log_value[1] = HPI_METER_MINIMUM;
} else {
phr->u.c.an_log_value[0] =
pC->u.meter.an_logRMS[0];
phr->u.c.an_log_value[1] =
pC->u.meter.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.vol.an_log[0];
phr->u.c.an_log_value[1] = pC->u.vol.an_log[1];
} else if (phm->u.c.attribute == 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.param1 =
HPI_BITMASK_ALL_CHANNELS;
else
phr->u.c.param1 = 0;
} else {
phr->error =
HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
phr->u.c.param1 = 0;
}
} else {
found = 0;
}
break;
case HPI_CONTROL_MULTIPLEXER:
if (phm->u.c.attribute == HPI_MULTIPLEXER_SOURCE) {
phr->u.c.param1 = pC->u.mux.source_node_type;
phr->u.c.param2 = pC->u.mux.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.mode.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.level.an_log[0];
phr->u.c.an_log_value[1] = pC->u.level.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.tuner.freq_ink_hz;
else if (phm->u.c.attribute == HPI_TUNER_BAND)
phr->u.c.param1 = pC->u.tuner.band;
else if (phm->u.c.attribute == HPI_TUNER_LEVEL_AVG)
if (pC->u.tuner.s_level_avg ==
HPI_CACHE_INVALID_SHORT) {
phr->u.cu.tuner.s_level = 0;
phr->error =
HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
} else
phr->u.cu.tuner.s_level =
pC->u.tuner.s_level_avg;
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.format;
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;
} else
found = 0;
break;
case HPI_CONTROL_MICROPHONE:
if (phm->u.c.attribute == HPI_MICROPHONE_PHANTOM_POWER)
phr->u.c.param1 = pC->u.microphone.phantom_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_CACHE_INVALID_UINT16) {
phr->u.c.param1 = 0;
phr->error =
HPI_ERROR_INVALID_CONTROL_ATTRIBUTE;
} 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:{
struct hpi_control_cache_pad *p_pad;
p_pad = (struct hpi_control_cache_pad *)pI;
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;
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);
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;
}
HPI_DEBUG_LOG(VERBOSE, "%s Adap %d, Ctl %d, Type %d, Attr %d\n",
found ? "Cached" : "Uncached", phm->adapter_index,
pI->control_index, pI->control_type, phm->u.c.attribute);
if (found) {
phr->size = (u16)response_size;
phr->type = HPI_TYPE_RESPONSE;
phr->object = phm->object;
phr->function = phm->function;
}
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_cmn_control_cache_sync_to_msg(struct hpi_control_cache *p_cache,
struct hpi_message *phm, struct hpi_response *phr)
{
struct hpi_control_cache_single *pC;
struct hpi_control_cache_info *pI;
if (phr->error)
return;
if (!find_control(phm->obj_index, p_cache, &pI)) {
HPI_DEBUG_LOG(VERBOSE,
"HPICMN find_control() failed for adap %d\n",
phm->adapter_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.vol.an_log[0] = phr->u.c.an_log_value[0];
pC->u.vol.an_log[1] = phr->u.c.an_log_value[1];
} else if (phm->u.c.attribute == HPI_VOLUME_MUTE) {
if (phm->u.c.param1)
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.attribute == HPI_MULTIPLEXER_SOURCE) {
pC->u.mux.source_node_type = (u16)phm->u.c.param1;
pC->u.mux.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.mode.mode = (u16)phm->u.c.param1;
break;
case HPI_CONTROL_LEVEL:
if (phm->u.c.attribute == HPI_LEVEL_GAIN) {
pC->u.vol.an_log[0] = phr->u.c.an_log_value[0];
pC->u.vol.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.microphone.phantom_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.format = 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;
}
}
/** Allocate control cache.
\return Cache pointer, or NULL if allocation fails.
*/
struct hpi_control_cache *hpi_alloc_control_cache(const u32 control_count,
const u32 size_in_bytes, u8 *p_dsp_control_buffer)
{
struct hpi_control_cache *p_cache =
kmalloc(sizeof(*p_cache), GFP_KERNEL);
if (!p_cache)
return NULL;
p_cache->p_info = kcalloc(control_count, sizeof(*p_cache->p_info),
GFP_KERNEL);
if (!p_cache->p_info) {
kfree(p_cache);
return NULL;
}
p_cache->cache_size_in_bytes = size_in_bytes;
p_cache->control_count = control_count;
p_cache->p_cache = p_dsp_control_buffer;
p_cache->init = 0;
return p_cache;
}
void hpi_free_control_cache(struct hpi_control_cache *p_cache)
{
if (p_cache) {
kfree(p_cache->p_info);
kfree(p_cache);
}
}
static void subsys_message(struct hpi_message *phm, struct hpi_response *phr)
{
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, phm->function, 0);
switch (phm->function) {
case HPI_SUBSYS_OPEN:
case HPI_SUBSYS_CLOSE:
case HPI_SUBSYS_DRIVER_UNLOAD:
break;
case HPI_SUBSYS_DRIVER_LOAD:
wipe_adapter_list();
hpios_alistlock_init(&adapters);
break;
case HPI_SUBSYS_GET_ADAPTER:
subsys_get_adapter(phm, phr);
break;
case HPI_SUBSYS_GET_NUM_ADAPTERS:
phr->u.s.num_adapters = adapters.gw_num_adapters;
break;
case HPI_SUBSYS_CREATE_ADAPTER:
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_REQUEST:
switch (phm->object) {
case HPI_OBJ_SUBSYSTEM:
subsys_message(phm, phr);
break;
}
break;
default:
phr->error = HPI_ERROR_INVALID_TYPE;
break;
}
}

67
sound/pci/asihpi/hpicmn.h Normal file
View file

@ -0,0 +1,67 @@
/**
AudioScience HPI driver
Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
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
*/
struct hpi_adapter_obj;
/* a function that takes an adapter obj and returns an int */
typedef int adapter_int_func(struct hpi_adapter_obj *pao);
struct hpi_adapter_obj {
struct hpi_pci pci; /* PCI info - bus#,dev#,address etc */
u16 type; /* 0x6644 == ASI6644 etc */
u16 index;
struct hpios_spinlock dsp_lock;
u16 dsp_crashed;
u16 has_control_cache;
void *priv;
};
struct hpi_control_cache {
/** indicates whether the structures are initialized */
u16 init;
u16 adap_idx;
u32 control_count;
u32 cache_size_in_bytes;
/** pointer to allocated memory of lookup pointers. */
struct hpi_control_cache_info **p_info;
/** pointer to DSP's control cache. */
u8 *p_cache;
};
struct hpi_adapter_obj *hpi_find_adapter(u16 adapter_index);
u16 hpi_add_adapter(struct hpi_adapter_obj *pao);
void hpi_delete_adapter(struct hpi_adapter_obj *pao);
short hpi_check_control_cache(struct hpi_control_cache *pC,
struct hpi_message *phm, struct hpi_response *phr);
struct hpi_control_cache *hpi_alloc_control_cache(const u32
number_of_controls, const u32 size_in_bytes, u8 *pDSP_control_buffer);
void hpi_free_control_cache(struct hpi_control_cache *p_cache);
void hpi_cmn_control_cache_sync_to_msg(struct hpi_control_cache *pC,
struct hpi_message *phm, struct hpi_response *phr);
u16 hpi_validate_response(struct hpi_message *phm, struct hpi_response *phr);
hpi_handler_func HPI_COMMON;

View file

@ -0,0 +1,78 @@
/************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
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
Debug macro translation.
************************************************************************/
#include "hpi_internal.h"
#include "hpidebug.h"
/* Debug level; 0 quiet; 1 informative, 2 debug, 3 verbose debug. */
int hpi_debug_level = HPI_DEBUG_LEVEL_DEFAULT;
void hpi_debug_init(void)
{
printk(KERN_INFO "debug start\n");
}
int hpi_debug_level_set(int level)
{
int old_level;
old_level = hpi_debug_level;
hpi_debug_level = level;
return old_level;
}
int hpi_debug_level_get(void)
{
return hpi_debug_level;
}
void hpi_debug_message(struct hpi_message *phm, char *sz_fileline)
{
if (phm) {
printk(KERN_DEBUG "HPI_MSG%d,%d,%d,%d,%d\n", phm->version,
phm->adapter_index, phm->obj_index, phm->function,
phm->u.c.attribute);
}
}
void hpi_debug_data(u16 *pdata, u32 len)
{
u32 i;
int j;
int k;
int lines;
int cols = 8;
lines = (len + cols - 1) / cols;
if (lines > 8)
lines = 8;
for (i = 0, j = 0; j < lines; j++) {
printk(KERN_DEBUG "%p:", (pdata + i));
for (k = 0; k < cols && i < len; i++, k++)
printk("%s%04x", k == 0 ? "" : " ", pdata[i]);
printk("\n");
}
}

102
sound/pci/asihpi/hpidebug.h Normal file
View file

@ -0,0 +1,102 @@
/*****************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
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
Debug macros.
*****************************************************************************/
#ifndef _HPIDEBUG_H
#define _HPIDEBUG_H
#include "hpi_internal.h"
/* Define debugging levels. */
enum { HPI_DEBUG_LEVEL_ERROR = 0, /* always log errors */
HPI_DEBUG_LEVEL_WARNING = 1,
HPI_DEBUG_LEVEL_NOTICE = 2,
HPI_DEBUG_LEVEL_INFO = 3,
HPI_DEBUG_LEVEL_DEBUG = 4,
HPI_DEBUG_LEVEL_VERBOSE = 5 /* same printk level as DEBUG */
};
#define HPI_DEBUG_LEVEL_DEFAULT HPI_DEBUG_LEVEL_NOTICE
/* an OS can define an extra flag string that is appended to
the start of each message, eg see linux kernel hpios.h */
#ifdef SOURCEFILE_NAME
#define FILE_LINE SOURCEFILE_NAME ":" __stringify(__LINE__) " "
#else
#define FILE_LINE __FILE__ ":" __stringify(__LINE__) " "
#endif
#define HPI_DEBUG_ASSERT(expression) \
do { \
if (!(expression)) { \
printk(KERN_ERR FILE_LINE \
"ASSERT " __stringify(expression)); \
} \
} while (0)
#define HPI_DEBUG_LOG(level, ...) \
do { \
if (hpi_debug_level >= HPI_DEBUG_LEVEL_##level) { \
printk(HPI_DEBUG_FLAG_##level \
FILE_LINE __VA_ARGS__); \
} \
} while (0)
void hpi_debug_init(void);
int hpi_debug_level_set(int level);
int hpi_debug_level_get(void);
/* needed by Linux driver for dynamic debug level changes */
extern int hpi_debug_level;
void hpi_debug_message(struct hpi_message *phm, char *sz_fileline);
void hpi_debug_data(u16 *pdata, u32 len);
#define HPI_DEBUG_DATA(pdata, len) \
do { \
if (hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE) \
hpi_debug_data(pdata, len); \
} while (0)
#define HPI_DEBUG_MESSAGE(level, phm) \
do { \
if (hpi_debug_level >= HPI_DEBUG_LEVEL_##level) { \
hpi_debug_message(phm, HPI_DEBUG_FLAG_##level \
FILE_LINE __stringify(level)); \
} \
} while (0)
#define HPI_DEBUG_RESPONSE(phr) \
do { \
if (((hpi_debug_level >= HPI_DEBUG_LEVEL_DEBUG) && \
(phr->error)) ||\
(hpi_debug_level >= HPI_DEBUG_LEVEL_VERBOSE)) \
printk(KERN_DEBUG "HPI_RES%d,%d,%d\n", \
phr->version, phr->error, phr->specific_error); \
} while (0)
#ifndef compile_time_assert
#define compile_time_assert(cond, msg) \
typedef char msg[(cond) ? 1 : -1]
#endif
#endif /* _HPIDEBUG_H_ */

144
sound/pci/asihpi/hpidspcd.c Normal file
View file

@ -0,0 +1,144 @@
/***********************************************************************/
/**
AudioScience HPI driver
Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
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
Functions for reading DSP code using
hotplug firmware loader from individual dsp code files
*/
/***********************************************************************/
#define SOURCEFILE_NAME "hpidspcd.c"
#include "hpidspcd.h"
#include "hpidebug.h"
#include "hpi_version.h"
struct dsp_code_private {
/** Firmware descriptor */
const struct firmware *firmware;
struct pci_dev *dev;
};
/*-------------------------------------------------------------------*/
short hpi_dsp_code_open(u32 adapter, void *os_data, struct dsp_code *dsp_code,
u32 *os_error_code)
{
const struct firmware *firmware;
struct pci_dev *dev = os_data;
struct code_header header;
char fw_name[20];
short err_ret = HPI_ERROR_DSP_FILE_NOT_FOUND;
int err;
sprintf(fw_name, "asihpi/dsp%04x.bin", adapter);
err = request_firmware(&firmware, fw_name, &dev->dev);
if (err || !firmware) {
dev_err(&dev->dev, "%d, request_firmware failed for %s\n",
err, fw_name);
goto error1;
}
if (firmware->size < sizeof(header)) {
dev_err(&dev->dev, "Header size too small %s\n", fw_name);
goto error2;
}
memcpy(&header, firmware->data, sizeof(header));
if ((header.type != 0x45444F43) || /* "CODE" */
(header.adapter != adapter)
|| (header.size != firmware->size)) {
dev_err(&dev->dev,
"Invalid firmware header size %d != file %zd\n",
header.size, firmware->size);
goto error2;
}
if ((header.version >> 9) != (HPI_VER >> 9)) {
/* Consider even and subsequent odd minor versions to be compatible */
dev_err(&dev->dev, "Incompatible firmware version DSP image %X != Driver %X\n",
header.version, HPI_VER);
goto error2;
}
if (header.version != HPI_VER) {
dev_info(&dev->dev,
"Firmware: release version mismatch DSP image %X != Driver %X\n",
header.version, HPI_VER);
}
HPI_DEBUG_LOG(DEBUG, "dsp code %s opened\n", fw_name);
dsp_code->pvt = kmalloc(sizeof(*dsp_code->pvt), GFP_KERNEL);
if (!dsp_code->pvt) {
err_ret = HPI_ERROR_MEMORY_ALLOC;
goto error2;
}
dsp_code->pvt->dev = dev;
dsp_code->pvt->firmware = firmware;
dsp_code->header = header;
dsp_code->block_length = header.size / sizeof(u32);
dsp_code->word_count = sizeof(header) / sizeof(u32);
return 0;
error2:
release_firmware(firmware);
error1:
dsp_code->block_length = 0;
return err_ret;
}
/*-------------------------------------------------------------------*/
void hpi_dsp_code_close(struct dsp_code *dsp_code)
{
HPI_DEBUG_LOG(DEBUG, "dsp code closed\n");
release_firmware(dsp_code->pvt->firmware);
kfree(dsp_code->pvt);
}
/*-------------------------------------------------------------------*/
void hpi_dsp_code_rewind(struct dsp_code *dsp_code)
{
/* Go back to start of data, after header */
dsp_code->word_count = sizeof(struct code_header) / sizeof(u32);
}
/*-------------------------------------------------------------------*/
short hpi_dsp_code_read_word(struct dsp_code *dsp_code, u32 *pword)
{
if (dsp_code->word_count + 1 > dsp_code->block_length)
return HPI_ERROR_DSP_FILE_FORMAT;
*pword = ((u32 *)(dsp_code->pvt->firmware->data))[dsp_code->
word_count];
dsp_code->word_count++;
return 0;
}
/*-------------------------------------------------------------------*/
short hpi_dsp_code_read_block(size_t words_requested,
struct dsp_code *dsp_code, u32 **ppblock)
{
if (dsp_code->word_count + words_requested > dsp_code->block_length)
return HPI_ERROR_DSP_FILE_FORMAT;
*ppblock =
((u32 *)(dsp_code->pvt->firmware->data)) +
dsp_code->word_count;
dsp_code->word_count += words_requested;
return 0;
}

106
sound/pci/asihpi/hpidspcd.h Normal file
View file

@ -0,0 +1,106 @@
/***********************************************************************/
/**
AudioScience HPI driver
Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
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
Functions for reading DSP code to load into DSP
*/
/***********************************************************************/
#ifndef _HPIDSPCD_H_
#define _HPIDSPCD_H_
#include "hpi_internal.h"
/** Header structure for dsp firmware file
This structure must match that used in s2bin.c for generation of asidsp.bin
*/
/*#ifndef DISABLE_PRAGMA_PACK1 */
/*#pragma pack(push, 1) */
/*#endif */
struct code_header {
/** Size in bytes including header */
u32 size;
/** File type tag "CODE" == 0x45444F43 */
u32 type;
/** Adapter model number */
u32 adapter;
/** Firmware version*/
u32 version;
/** Data checksum */
u32 checksum;
};
/*#ifndef DISABLE_PRAGMA_PACK1 */
/*#pragma pack(pop) */
/*#endif */
/*? Don't need the pragmas? */
compile_time_assert((sizeof(struct code_header) == 20), code_header_size);
/** Descriptor for dspcode from firmware loader */
struct dsp_code {
/** copy of file header */
struct code_header header;
/** Expected number of words in the whole dsp code,INCL header */
u32 block_length;
/** Number of words read so far */
u32 word_count;
/** internal state of DSP code reader */
struct dsp_code_private *pvt;
};
/** Prepare *psDspCode to refer to the requested adapter's firmware.
Code file name is obtained from HpiOs_GetDspCodePath
\return 0 for success, or error code if requested code is not available
*/
short hpi_dsp_code_open(
/** Code identifier, usually adapter family */
u32 adapter, void *pci_dev,
/** Pointer to DSP code control structure */
struct dsp_code *ps_dsp_code,
/** Pointer to dword to receive OS specific error code */
u32 *pos_error_code);
/** Close the DSP code file */
void hpi_dsp_code_close(struct dsp_code *ps_dsp_code);
/** Rewind to the beginning of the DSP code file (for verify) */
void hpi_dsp_code_rewind(struct dsp_code *ps_dsp_code);
/** Read one word from the dsp code file
\return 0 for success, or error code if eof, or block length exceeded
*/
short hpi_dsp_code_read_word(struct dsp_code *ps_dsp_code,
/**< DSP code descriptor */
u32 *pword /**< Where to store the read word */
);
/** Get a block of dsp code into an internal buffer, and provide a pointer to
that buffer. (If dsp code is already an array in memory, it is referenced,
not copied.)
\return Error if requested number of words are not available
*/
short hpi_dsp_code_read_block(size_t words_requested,
struct dsp_code *ps_dsp_code,
/* Pointer to store (Pointer to code buffer) */
u32 **ppblock);
#endif

2871
sound/pci/asihpi/hpifunc.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,116 @@
/******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
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
Hardware Programming Interface (HPI) Utility functions.
(C) Copyright AudioScience Inc. 2007
*******************************************************************************/
#include "hpi_internal.h"
#include "hpimsginit.h"
/* The actual message size for each object type */
static u16 msg_size[HPI_OBJ_MAXINDEX + 1] = HPI_MESSAGE_SIZE_BY_OBJECT;
/* The actual response size for each object type */
static u16 res_size[HPI_OBJ_MAXINDEX + 1] = HPI_RESPONSE_SIZE_BY_OBJECT;
/* Flag to enable alternate message type for SSX2 bypass. */
static u16 gwSSX2_bypass;
/** \internal
* initialize the HPI message structure
*/
static void hpi_init_message(struct hpi_message *phm, u16 object,
u16 function)
{
memset(phm, 0, sizeof(*phm));
if ((object > 0) && (object <= HPI_OBJ_MAXINDEX))
phm->size = msg_size[object];
else
phm->size = sizeof(*phm);
if (gwSSX2_bypass)
phm->type = HPI_TYPE_SSX2BYPASS_MESSAGE;
else
phm->type = HPI_TYPE_REQUEST;
phm->object = object;
phm->function = function;
phm->version = 0;
phm->adapter_index = HPI_ADAPTER_INDEX_INVALID;
/* Expect actual adapter index to be set by caller */
}
/** \internal
* initialize the HPI response structure
*/
void hpi_init_response(struct hpi_response *phr, u16 object, u16 function,
u16 error)
{
memset(phr, 0, sizeof(*phr));
phr->type = HPI_TYPE_RESPONSE;
if ((object > 0) && (object <= HPI_OBJ_MAXINDEX))
phr->size = res_size[object];
else
phr->size = sizeof(*phr);
phr->object = object;
phr->function = function;
phr->error = error;
phr->specific_error = 0;
phr->version = 0;
}
void hpi_init_message_response(struct hpi_message *phm,
struct hpi_response *phr, u16 object, u16 function)
{
hpi_init_message(phm, object, function);
/* default error return if the response is
not filled in by the callee */
hpi_init_response(phr, object, function,
HPI_ERROR_PROCESSING_MESSAGE);
}
static void hpi_init_messageV1(struct hpi_message_header *phm, u16 size,
u16 object, u16 function)
{
memset(phm, 0, sizeof(*phm));
if ((object > 0) && (object <= HPI_OBJ_MAXINDEX)) {
phm->size = size;
phm->type = HPI_TYPE_REQUEST;
phm->object = object;
phm->function = function;
phm->version = 1;
/* Expect adapter index to be set by caller */
}
}
void hpi_init_responseV1(struct hpi_response_header *phr, u16 size,
u16 object, u16 function)
{
memset(phr, 0, sizeof(*phr));
phr->size = size;
phr->version = 1;
phr->type = HPI_TYPE_RESPONSE;
phr->error = HPI_ERROR_PROCESSING_MESSAGE;
}
void hpi_init_message_responseV1(struct hpi_message_header *phm, u16 msg_size,
struct hpi_response_header *phr, u16 res_size, u16 object,
u16 function)
{
hpi_init_messageV1(phm, msg_size, object, function);
hpi_init_responseV1(phr, res_size, object, function);
}

View file

@ -0,0 +1,46 @@
/******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
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
Hardware Programming Interface (HPI) Utility functions
(C) Copyright AudioScience Inc. 2007
*******************************************************************************/
/* Initialise response headers, or msg/response pairs.
Note that it is valid to just init a response e.g. when a lower level is
preparing a response to a message.
However, when sending a message, a matching response buffer must always be
prepared.
*/
#ifndef _HPIMSGINIT_H_
#define _HPIMSGINIT_H_
void hpi_init_response(struct hpi_response *phr, u16 object, u16 function,
u16 error);
void hpi_init_message_response(struct hpi_message *phm,
struct hpi_response *phr, u16 object, u16 function);
void hpi_init_responseV1(struct hpi_response_header *phr, u16 size,
u16 object, u16 function);
void hpi_init_message_responseV1(struct hpi_message_header *phm, u16 msg_size,
struct hpi_response_header *phr, u16 res_size, u16 object,
u16 function);
#endif /* _HPIMSGINIT_H_ */

800
sound/pci/asihpi/hpimsgx.c Normal file
View file

@ -0,0 +1,800 @@
/******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
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
Extended Message Function With Response Caching
(C) Copyright AudioScience Inc. 2002
*****************************************************************************/
#define SOURCEFILE_NAME "hpimsgx.c"
#include "hpi_internal.h"
#include "hpi_version.h"
#include "hpimsginit.h"
#include "hpicmn.h"
#include "hpimsgx.h"
#include "hpidebug.h"
static struct pci_device_id asihpi_pci_tbl[] = {
#include "hpipcida.h"
};
static struct hpios_spinlock msgx_lock;
static hpi_handler_func *hpi_entry_points[HPI_MAX_ADAPTERS];
static hpi_handler_func *hpi_lookup_entry_point_function(const struct hpi_pci
*pci_info)
{
int i;
for (i = 0; asihpi_pci_tbl[i].vendor != 0; i++) {
if (asihpi_pci_tbl[i].vendor != PCI_ANY_ID
&& asihpi_pci_tbl[i].vendor !=
pci_info->pci_dev->vendor)
continue;
if (asihpi_pci_tbl[i].device != PCI_ANY_ID
&& asihpi_pci_tbl[i].device !=
pci_info->pci_dev->device)
continue;
if (asihpi_pci_tbl[i].subvendor != PCI_ANY_ID
&& asihpi_pci_tbl[i].subvendor !=
pci_info->pci_dev->subsystem_vendor)
continue;
if (asihpi_pci_tbl[i].subdevice != PCI_ANY_ID
&& asihpi_pci_tbl[i].subdevice !=
pci_info->pci_dev->subsystem_device)
continue;
/* HPI_DEBUG_LOG(DEBUG, " %x,%lx\n", i,
asihpi_pci_tbl[i].driver_data); */
return (hpi_handler_func *) asihpi_pci_tbl[i].driver_data;
}
return NULL;
}
static inline void hw_entry_point(struct hpi_message *phm,
struct hpi_response *phr)
{
if ((phm->adapter_index < HPI_MAX_ADAPTERS)
&& hpi_entry_points[phm->adapter_index])
hpi_entry_points[phm->adapter_index] (phm, phr);
else
hpi_init_response(phr, phm->object, phm->function,
HPI_ERROR_PROCESSING_MESSAGE);
}
static void adapter_open(struct hpi_message *phm, struct hpi_response *phr);
static void adapter_close(struct hpi_message *phm, struct hpi_response *phr);
static void mixer_open(struct hpi_message *phm, struct hpi_response *phr);
static void mixer_close(struct hpi_message *phm, struct hpi_response *phr);
static void outstream_open(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner);
static void outstream_close(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner);
static void instream_open(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner);
static void instream_close(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner);
static void HPIMSGX__reset(u16 adapter_index);
static u16 HPIMSGX__init(struct hpi_message *phm, struct hpi_response *phr);
static void HPIMSGX__cleanup(u16 adapter_index, void *h_owner);
#ifndef DISABLE_PRAGMA_PACK1
#pragma pack(push, 1)
#endif
struct hpi_subsys_response {
struct hpi_response_header h;
struct hpi_subsys_res s;
};
struct hpi_adapter_response {
struct hpi_response_header h;
struct hpi_adapter_res a;
};
struct hpi_mixer_response {
struct hpi_response_header h;
struct hpi_mixer_res m;
};
struct hpi_stream_response {
struct hpi_response_header h;
struct hpi_stream_res d;
};
struct adapter_info {
u16 type;
u16 num_instreams;
u16 num_outstreams;
};
struct asi_open_state {
int open_flag;
void *h_owner;
};
#ifndef DISABLE_PRAGMA_PACK1
#pragma pack(pop)
#endif
/* Globals */
static struct hpi_adapter_response rESP_HPI_ADAPTER_OPEN[HPI_MAX_ADAPTERS];
static struct hpi_stream_response
rESP_HPI_OSTREAM_OPEN[HPI_MAX_ADAPTERS][HPI_MAX_STREAMS];
static struct hpi_stream_response
rESP_HPI_ISTREAM_OPEN[HPI_MAX_ADAPTERS][HPI_MAX_STREAMS];
static struct hpi_mixer_response rESP_HPI_MIXER_OPEN[HPI_MAX_ADAPTERS];
static struct adapter_info aDAPTER_INFO[HPI_MAX_ADAPTERS];
/* use these to keep track of opens from user mode apps/DLLs */
static struct asi_open_state
outstream_user_open[HPI_MAX_ADAPTERS][HPI_MAX_STREAMS];
static struct asi_open_state
instream_user_open[HPI_MAX_ADAPTERS][HPI_MAX_STREAMS];
static void subsys_message(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner)
{
if (phm->adapter_index != HPI_ADAPTER_INDEX_INVALID)
HPI_DEBUG_LOG(WARNING,
"suspicious adapter index %d in subsys message 0x%x.\n",
phm->adapter_index, phm->function);
switch (phm->function) {
case HPI_SUBSYS_GET_VERSION:
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_GET_VERSION, 0);
phr->u.s.version = HPI_VER >> 8; /* return major.minor */
phr->u.s.data = HPI_VER; /* return major.minor.release */
break;
case HPI_SUBSYS_OPEN:
/*do not propagate the message down the chain */
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_OPEN, 0);
break;
case HPI_SUBSYS_CLOSE:
/*do not propagate the message down the chain */
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_CLOSE,
0);
HPIMSGX__cleanup(HPIMSGX_ALLADAPTERS, h_owner);
break;
case HPI_SUBSYS_DRIVER_LOAD:
/* Initialize this module's internal state */
hpios_msgxlock_init(&msgx_lock);
memset(&hpi_entry_points, 0, sizeof(hpi_entry_points));
/* Init subsys_findadapters response to no-adapters */
HPIMSGX__reset(HPIMSGX_ALLADAPTERS);
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_DRIVER_LOAD, 0);
/* individual HPIs dont implement driver load */
HPI_COMMON(phm, phr);
break;
case HPI_SUBSYS_DRIVER_UNLOAD:
HPI_COMMON(phm, phr);
HPIMSGX__cleanup(HPIMSGX_ALLADAPTERS, h_owner);
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_DRIVER_UNLOAD, 0);
return;
case HPI_SUBSYS_GET_NUM_ADAPTERS:
case HPI_SUBSYS_GET_ADAPTER:
HPI_COMMON(phm, phr);
break;
case HPI_SUBSYS_CREATE_ADAPTER:
HPIMSGX__init(phm, phr);
break;
default:
/* Must explicitly handle every subsys message in this switch */
hpi_init_response(phr, HPI_OBJ_SUBSYSTEM, phm->function,
HPI_ERROR_INVALID_FUNC);
break;
}
}
static void adapter_message(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner)
{
switch (phm->function) {
case HPI_ADAPTER_OPEN:
adapter_open(phm, phr);
break;
case HPI_ADAPTER_CLOSE:
adapter_close(phm, phr);
break;
case HPI_ADAPTER_DELETE:
HPIMSGX__cleanup(phm->adapter_index, h_owner);
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
HPI_ADAPTER_CLOSE);
hm.adapter_index = phm->adapter_index;
hw_entry_point(&hm, &hr);
}
hw_entry_point(phm, phr);
break;
default:
hw_entry_point(phm, phr);
break;
}
}
static void mixer_message(struct hpi_message *phm, struct hpi_response *phr)
{
switch (phm->function) {
case HPI_MIXER_OPEN:
mixer_open(phm, phr);
break;
case HPI_MIXER_CLOSE:
mixer_close(phm, phr);
break;
default:
hw_entry_point(phm, phr);
break;
}
}
static void outstream_message(struct hpi_message *phm,
struct hpi_response *phr, void *h_owner)
{
if (phm->obj_index >= aDAPTER_INFO[phm->adapter_index].num_outstreams) {
hpi_init_response(phr, HPI_OBJ_OSTREAM, phm->function,
HPI_ERROR_INVALID_OBJ_INDEX);
return;
}
switch (phm->function) {
case HPI_OSTREAM_OPEN:
outstream_open(phm, phr, h_owner);
break;
case HPI_OSTREAM_CLOSE:
outstream_close(phm, phr, h_owner);
break;
default:
hw_entry_point(phm, phr);
break;
}
}
static void instream_message(struct hpi_message *phm,
struct hpi_response *phr, void *h_owner)
{
if (phm->obj_index >= aDAPTER_INFO[phm->adapter_index].num_instreams) {
hpi_init_response(phr, HPI_OBJ_ISTREAM, phm->function,
HPI_ERROR_INVALID_OBJ_INDEX);
return;
}
switch (phm->function) {
case HPI_ISTREAM_OPEN:
instream_open(phm, phr, h_owner);
break;
case HPI_ISTREAM_CLOSE:
instream_close(phm, phr, h_owner);
break;
default:
hw_entry_point(phm, phr);
break;
}
}
/* NOTE: HPI_Message() must be defined in the driver as a wrapper for
* HPI_MessageEx so that functions in hpifunc.c compile.
*/
void hpi_send_recv_ex(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner)
{
HPI_DEBUG_MESSAGE(DEBUG, phm);
if (phm->type != HPI_TYPE_REQUEST) {
hpi_init_response(phr, phm->object, phm->function,
HPI_ERROR_INVALID_TYPE);
return;
}
if (phm->adapter_index >= HPI_MAX_ADAPTERS
&& phm->adapter_index != HPIMSGX_ALLADAPTERS) {
hpi_init_response(phr, phm->object, phm->function,
HPI_ERROR_BAD_ADAPTER_NUMBER);
return;
}
switch (phm->object) {
case HPI_OBJ_SUBSYSTEM:
subsys_message(phm, phr, h_owner);
break;
case HPI_OBJ_ADAPTER:
adapter_message(phm, phr, h_owner);
break;
case HPI_OBJ_MIXER:
mixer_message(phm, phr);
break;
case HPI_OBJ_OSTREAM:
outstream_message(phm, phr, h_owner);
break;
case HPI_OBJ_ISTREAM:
instream_message(phm, phr, h_owner);
break;
default:
hw_entry_point(phm, phr);
break;
}
HPI_DEBUG_RESPONSE(phr);
}
static void adapter_open(struct hpi_message *phm, struct hpi_response *phr)
{
HPI_DEBUG_LOG(VERBOSE, "adapter_open\n");
memcpy(phr, &rESP_HPI_ADAPTER_OPEN[phm->adapter_index],
sizeof(rESP_HPI_ADAPTER_OPEN[0]));
}
static void adapter_close(struct hpi_message *phm, struct hpi_response *phr)
{
HPI_DEBUG_LOG(VERBOSE, "adapter_close\n");
hpi_init_response(phr, HPI_OBJ_ADAPTER, HPI_ADAPTER_CLOSE, 0);
}
static void mixer_open(struct hpi_message *phm, struct hpi_response *phr)
{
memcpy(phr, &rESP_HPI_MIXER_OPEN[phm->adapter_index],
sizeof(rESP_HPI_MIXER_OPEN[0]));
}
static void mixer_close(struct hpi_message *phm, struct hpi_response *phr)
{
hpi_init_response(phr, HPI_OBJ_MIXER, HPI_MIXER_CLOSE, 0);
}
static void instream_open(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_response(phr, HPI_OBJ_ISTREAM, HPI_ISTREAM_OPEN, 0);
hpios_msgxlock_lock(&msgx_lock);
if (instream_user_open[phm->adapter_index][phm->obj_index].open_flag)
phr->error = HPI_ERROR_OBJ_ALREADY_OPEN;
else if (rESP_HPI_ISTREAM_OPEN[phm->adapter_index]
[phm->obj_index].h.error)
memcpy(phr,
&rESP_HPI_ISTREAM_OPEN[phm->adapter_index][phm->
obj_index],
sizeof(rESP_HPI_ISTREAM_OPEN[0][0]));
else {
instream_user_open[phm->adapter_index][phm->
obj_index].open_flag = 1;
hpios_msgxlock_unlock(&msgx_lock);
/* issue a reset */
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_RESET);
hm.adapter_index = phm->adapter_index;
hm.obj_index = phm->obj_index;
hw_entry_point(&hm, &hr);
hpios_msgxlock_lock(&msgx_lock);
if (hr.error) {
instream_user_open[phm->adapter_index][phm->
obj_index].open_flag = 0;
phr->error = hr.error;
} else {
instream_user_open[phm->adapter_index][phm->
obj_index].open_flag = 1;
instream_user_open[phm->adapter_index][phm->
obj_index].h_owner = h_owner;
memcpy(phr,
&rESP_HPI_ISTREAM_OPEN[phm->adapter_index]
[phm->obj_index],
sizeof(rESP_HPI_ISTREAM_OPEN[0][0]));
}
}
hpios_msgxlock_unlock(&msgx_lock);
}
static void instream_close(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_response(phr, HPI_OBJ_ISTREAM, HPI_ISTREAM_CLOSE, 0);
hpios_msgxlock_lock(&msgx_lock);
if (h_owner ==
instream_user_open[phm->adapter_index][phm->
obj_index].h_owner) {
/* HPI_DEBUG_LOG(INFO,"closing adapter %d "
"instream %d owned by %p\n",
phm->wAdapterIndex, phm->wObjIndex, hOwner); */
instream_user_open[phm->adapter_index][phm->
obj_index].h_owner = NULL;
hpios_msgxlock_unlock(&msgx_lock);
/* issue a reset */
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_RESET);
hm.adapter_index = phm->adapter_index;
hm.obj_index = phm->obj_index;
hw_entry_point(&hm, &hr);
hpios_msgxlock_lock(&msgx_lock);
if (hr.error) {
instream_user_open[phm->adapter_index][phm->
obj_index].h_owner = h_owner;
phr->error = hr.error;
} else {
instream_user_open[phm->adapter_index][phm->
obj_index].open_flag = 0;
instream_user_open[phm->adapter_index][phm->
obj_index].h_owner = NULL;
}
} else {
HPI_DEBUG_LOG(WARNING,
"%p trying to close %d instream %d owned by %p\n",
h_owner, phm->adapter_index, phm->obj_index,
instream_user_open[phm->adapter_index][phm->
obj_index].h_owner);
phr->error = HPI_ERROR_OBJ_NOT_OPEN;
}
hpios_msgxlock_unlock(&msgx_lock);
}
static void outstream_open(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_response(phr, HPI_OBJ_OSTREAM, HPI_OSTREAM_OPEN, 0);
hpios_msgxlock_lock(&msgx_lock);
if (outstream_user_open[phm->adapter_index][phm->obj_index].open_flag)
phr->error = HPI_ERROR_OBJ_ALREADY_OPEN;
else if (rESP_HPI_OSTREAM_OPEN[phm->adapter_index]
[phm->obj_index].h.error)
memcpy(phr,
&rESP_HPI_OSTREAM_OPEN[phm->adapter_index][phm->
obj_index],
sizeof(rESP_HPI_OSTREAM_OPEN[0][0]));
else {
outstream_user_open[phm->adapter_index][phm->
obj_index].open_flag = 1;
hpios_msgxlock_unlock(&msgx_lock);
/* issue a reset */
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_RESET);
hm.adapter_index = phm->adapter_index;
hm.obj_index = phm->obj_index;
hw_entry_point(&hm, &hr);
hpios_msgxlock_lock(&msgx_lock);
if (hr.error) {
outstream_user_open[phm->adapter_index][phm->
obj_index].open_flag = 0;
phr->error = hr.error;
} else {
outstream_user_open[phm->adapter_index][phm->
obj_index].open_flag = 1;
outstream_user_open[phm->adapter_index][phm->
obj_index].h_owner = h_owner;
memcpy(phr,
&rESP_HPI_OSTREAM_OPEN[phm->adapter_index]
[phm->obj_index],
sizeof(rESP_HPI_OSTREAM_OPEN[0][0]));
}
}
hpios_msgxlock_unlock(&msgx_lock);
}
static void outstream_close(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_response(phr, HPI_OBJ_OSTREAM, HPI_OSTREAM_CLOSE, 0);
hpios_msgxlock_lock(&msgx_lock);
if (h_owner ==
outstream_user_open[phm->adapter_index][phm->
obj_index].h_owner) {
/* HPI_DEBUG_LOG(INFO,"closing adapter %d "
"outstream %d owned by %p\n",
phm->wAdapterIndex, phm->wObjIndex, hOwner); */
outstream_user_open[phm->adapter_index][phm->
obj_index].h_owner = NULL;
hpios_msgxlock_unlock(&msgx_lock);
/* issue a reset */
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_RESET);
hm.adapter_index = phm->adapter_index;
hm.obj_index = phm->obj_index;
hw_entry_point(&hm, &hr);
hpios_msgxlock_lock(&msgx_lock);
if (hr.error) {
outstream_user_open[phm->adapter_index][phm->
obj_index].h_owner = h_owner;
phr->error = hr.error;
} else {
outstream_user_open[phm->adapter_index][phm->
obj_index].open_flag = 0;
outstream_user_open[phm->adapter_index][phm->
obj_index].h_owner = NULL;
}
} else {
HPI_DEBUG_LOG(WARNING,
"%p trying to close %d outstream %d owned by %p\n",
h_owner, phm->adapter_index, phm->obj_index,
outstream_user_open[phm->adapter_index][phm->
obj_index].h_owner);
phr->error = HPI_ERROR_OBJ_NOT_OPEN;
}
hpios_msgxlock_unlock(&msgx_lock);
}
static u16 adapter_prepare(u16 adapter)
{
struct hpi_message hm;
struct hpi_response hr;
/* Open the adapter and streams */
u16 i;
/* call to HPI_ADAPTER_OPEN */
hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
HPI_ADAPTER_OPEN);
hm.adapter_index = adapter;
hw_entry_point(&hm, &hr);
memcpy(&rESP_HPI_ADAPTER_OPEN[adapter], &hr,
sizeof(rESP_HPI_ADAPTER_OPEN[0]));
if (hr.error)
return hr.error;
/* call to HPI_ADAPTER_GET_INFO */
hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
HPI_ADAPTER_GET_INFO);
hm.adapter_index = adapter;
hw_entry_point(&hm, &hr);
if (hr.error)
return hr.error;
aDAPTER_INFO[adapter].num_outstreams = hr.u.ax.info.num_outstreams;
aDAPTER_INFO[adapter].num_instreams = hr.u.ax.info.num_instreams;
aDAPTER_INFO[adapter].type = hr.u.ax.info.adapter_type;
/* call to HPI_OSTREAM_OPEN */
for (i = 0; i < aDAPTER_INFO[adapter].num_outstreams; i++) {
hpi_init_message_response(&hm, &hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_OPEN);
hm.adapter_index = adapter;
hm.obj_index = i;
hw_entry_point(&hm, &hr);
memcpy(&rESP_HPI_OSTREAM_OPEN[adapter][i], &hr,
sizeof(rESP_HPI_OSTREAM_OPEN[0][0]));
outstream_user_open[adapter][i].open_flag = 0;
outstream_user_open[adapter][i].h_owner = NULL;
}
/* call to HPI_ISTREAM_OPEN */
for (i = 0; i < aDAPTER_INFO[adapter].num_instreams; i++) {
hpi_init_message_response(&hm, &hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_OPEN);
hm.adapter_index = adapter;
hm.obj_index = i;
hw_entry_point(&hm, &hr);
memcpy(&rESP_HPI_ISTREAM_OPEN[adapter][i], &hr,
sizeof(rESP_HPI_ISTREAM_OPEN[0][0]));
instream_user_open[adapter][i].open_flag = 0;
instream_user_open[adapter][i].h_owner = NULL;
}
/* call to HPI_MIXER_OPEN */
hpi_init_message_response(&hm, &hr, HPI_OBJ_MIXER, HPI_MIXER_OPEN);
hm.adapter_index = adapter;
hw_entry_point(&hm, &hr);
memcpy(&rESP_HPI_MIXER_OPEN[adapter], &hr,
sizeof(rESP_HPI_MIXER_OPEN[0]));
return 0;
}
static void HPIMSGX__reset(u16 adapter_index)
{
int i;
u16 adapter;
struct hpi_response hr;
if (adapter_index == HPIMSGX_ALLADAPTERS) {
for (adapter = 0; adapter < HPI_MAX_ADAPTERS; adapter++) {
hpi_init_response(&hr, HPI_OBJ_ADAPTER,
HPI_ADAPTER_OPEN, HPI_ERROR_BAD_ADAPTER);
memcpy(&rESP_HPI_ADAPTER_OPEN[adapter], &hr,
sizeof(rESP_HPI_ADAPTER_OPEN[adapter]));
hpi_init_response(&hr, HPI_OBJ_MIXER, HPI_MIXER_OPEN,
HPI_ERROR_INVALID_OBJ);
memcpy(&rESP_HPI_MIXER_OPEN[adapter], &hr,
sizeof(rESP_HPI_MIXER_OPEN[adapter]));
for (i = 0; i < HPI_MAX_STREAMS; i++) {
hpi_init_response(&hr, HPI_OBJ_OSTREAM,
HPI_OSTREAM_OPEN,
HPI_ERROR_INVALID_OBJ);
memcpy(&rESP_HPI_OSTREAM_OPEN[adapter][i],
&hr,
sizeof(rESP_HPI_OSTREAM_OPEN[adapter]
[i]));
hpi_init_response(&hr, HPI_OBJ_ISTREAM,
HPI_ISTREAM_OPEN,
HPI_ERROR_INVALID_OBJ);
memcpy(&rESP_HPI_ISTREAM_OPEN[adapter][i],
&hr,
sizeof(rESP_HPI_ISTREAM_OPEN[adapter]
[i]));
}
}
} else if (adapter_index < HPI_MAX_ADAPTERS) {
rESP_HPI_ADAPTER_OPEN[adapter_index].h.error =
HPI_ERROR_BAD_ADAPTER;
rESP_HPI_MIXER_OPEN[adapter_index].h.error =
HPI_ERROR_INVALID_OBJ;
for (i = 0; i < HPI_MAX_STREAMS; i++) {
rESP_HPI_OSTREAM_OPEN[adapter_index][i].h.error =
HPI_ERROR_INVALID_OBJ;
rESP_HPI_ISTREAM_OPEN[adapter_index][i].h.error =
HPI_ERROR_INVALID_OBJ;
}
}
}
static u16 HPIMSGX__init(struct hpi_message *phm,
/* HPI_SUBSYS_CREATE_ADAPTER structure with */
/* resource list or NULL=find all */
struct hpi_response *phr
/* response from HPI_ADAPTER_GET_INFO */
)
{
hpi_handler_func *entry_point_func;
struct hpi_response hr;
/* Init response here so we can pass in previous adapter list */
hpi_init_response(&hr, phm->object, phm->function,
HPI_ERROR_INVALID_OBJ);
entry_point_func =
hpi_lookup_entry_point_function(phm->u.s.resource.r.pci);
if (entry_point_func) {
HPI_DEBUG_MESSAGE(DEBUG, phm);
entry_point_func(phm, &hr);
} else {
phr->error = HPI_ERROR_PROCESSING_MESSAGE;
return phr->error;
}
if (hr.error == 0) {
/* the adapter was created successfully
save the mapping for future use */
hpi_entry_points[hr.u.s.adapter_index] = entry_point_func;
/* prepare adapter (pre-open streams etc.) */
HPI_DEBUG_LOG(DEBUG,
"HPI_SUBSYS_CREATE_ADAPTER successful,"
" preparing adapter\n");
adapter_prepare(hr.u.s.adapter_index);
}
memcpy(phr, &hr, hr.size);
return phr->error;
}
static void HPIMSGX__cleanup(u16 adapter_index, void *h_owner)
{
int i, adapter, adapter_limit;
if (!h_owner)
return;
if (adapter_index == HPIMSGX_ALLADAPTERS) {
adapter = 0;
adapter_limit = HPI_MAX_ADAPTERS;
} else {
adapter = adapter_index;
adapter_limit = adapter + 1;
}
for (; adapter < adapter_limit; adapter++) {
/* printk(KERN_INFO "Cleanup adapter #%d\n",wAdapter); */
for (i = 0; i < HPI_MAX_STREAMS; i++) {
if (h_owner ==
outstream_user_open[adapter][i].h_owner) {
struct hpi_message hm;
struct hpi_response hr;
HPI_DEBUG_LOG(DEBUG,
"Close adapter %d ostream %d\n",
adapter, i);
hpi_init_message_response(&hm, &hr,
HPI_OBJ_OSTREAM, HPI_OSTREAM_RESET);
hm.adapter_index = (u16)adapter;
hm.obj_index = (u16)i;
hw_entry_point(&hm, &hr);
hm.function = HPI_OSTREAM_HOSTBUFFER_FREE;
hw_entry_point(&hm, &hr);
hm.function = HPI_OSTREAM_GROUP_RESET;
hw_entry_point(&hm, &hr);
outstream_user_open[adapter][i].open_flag = 0;
outstream_user_open[adapter][i].h_owner =
NULL;
}
if (h_owner == instream_user_open[adapter][i].h_owner) {
struct hpi_message hm;
struct hpi_response hr;
HPI_DEBUG_LOG(DEBUG,
"Close adapter %d istream %d\n",
adapter, i);
hpi_init_message_response(&hm, &hr,
HPI_OBJ_ISTREAM, HPI_ISTREAM_RESET);
hm.adapter_index = (u16)adapter;
hm.obj_index = (u16)i;
hw_entry_point(&hm, &hr);
hm.function = HPI_ISTREAM_HOSTBUFFER_FREE;
hw_entry_point(&hm, &hr);
hm.function = HPI_ISTREAM_GROUP_RESET;
hw_entry_point(&hm, &hr);
instream_user_open[adapter][i].open_flag = 0;
instream_user_open[adapter][i].h_owner = NULL;
}
}
}
}

View file

@ -0,0 +1,36 @@
/******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
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
HPI Extended Message Handler Functions
(C) Copyright AudioScience Inc. 1997-2003
******************************************************************************/
#ifndef _HPIMSGX_H_
#define _HPIMSGX_H_
#include "hpi_internal.h"
#define HPIMSGX_ALLADAPTERS (0xFFFF)
void hpi_send_recv_ex(struct hpi_message *phm, struct hpi_response *phr,
void *h_owner);
#define HPI_MESSAGE_LOWER_LAYER hpi_send_recv_ex
#endif /* _HPIMSGX_H_ */

480
sound/pci/asihpi/hpioctl.c Normal file
View file

@ -0,0 +1,480 @@
/*******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
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
Common Linux HPI ioctl and module probe/remove functions
*******************************************************************************/
#define SOURCEFILE_NAME "hpioctl.c"
#include "hpi_internal.h"
#include "hpi_version.h"
#include "hpimsginit.h"
#include "hpidebug.h"
#include "hpimsgx.h"
#include "hpioctl.h"
#include "hpicmn.h"
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/moduleparam.h>
#include <asm/uaccess.h>
#include <linux/pci.h>
#include <linux/stringify.h>
#include <linux/module.h>
#ifdef MODULE_FIRMWARE
MODULE_FIRMWARE("asihpi/dsp5000.bin");
MODULE_FIRMWARE("asihpi/dsp6200.bin");
MODULE_FIRMWARE("asihpi/dsp6205.bin");
MODULE_FIRMWARE("asihpi/dsp6400.bin");
MODULE_FIRMWARE("asihpi/dsp6600.bin");
MODULE_FIRMWARE("asihpi/dsp8700.bin");
MODULE_FIRMWARE("asihpi/dsp8900.bin");
#endif
static int prealloc_stream_buf;
module_param(prealloc_stream_buf, int, S_IRUGO);
MODULE_PARM_DESC(prealloc_stream_buf,
"Preallocate size for per-adapter stream buffer");
/* Allow the debug level to be changed after module load.
E.g. echo 2 > /sys/module/asihpi/parameters/hpiDebugLevel
*/
module_param(hpi_debug_level, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(hpi_debug_level, "debug verbosity 0..5");
/* List of adapters found */
static struct hpi_adapter adapters[HPI_MAX_ADAPTERS];
/* Wrapper function to HPI_Message to enable dumping of the
message and response types.
*/
static void hpi_send_recv_f(struct hpi_message *phm, struct hpi_response *phr,
struct file *file)
{
if ((phm->adapter_index >= HPI_MAX_ADAPTERS)
&& (phm->object != HPI_OBJ_SUBSYSTEM))
phr->error = HPI_ERROR_INVALID_OBJ_INDEX;
else
hpi_send_recv_ex(phm, phr, file);
}
/* This is called from hpifunc.c functions, called by ALSA
* (or other kernel process) In this case there is no file descriptor
* available for the message cache code
*/
void hpi_send_recv(struct hpi_message *phm, struct hpi_response *phr)
{
hpi_send_recv_f(phm, phr, HOWNER_KERNEL);
}
EXPORT_SYMBOL(hpi_send_recv);
/* for radio-asihpi */
int asihpi_hpi_release(struct file *file)
{
struct hpi_message hm;
struct hpi_response hr;
/* HPI_DEBUG_LOG(INFO,"hpi_release file %p, pid %d\n", file, current->pid); */
/* close the subsystem just in case the application forgot to. */
hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_CLOSE);
hpi_send_recv_ex(&hm, &hr, file);
return 0;
}
long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct hpi_ioctl_linux __user *phpi_ioctl_data;
void __user *puhm;
void __user *puhr;
union hpi_message_buffer_v1 *hm;
union hpi_response_buffer_v1 *hr;
u16 res_max_size;
u32 uncopied_bytes;
int err = 0;
if (cmd != HPI_IOCTL_LINUX)
return -EINVAL;
hm = kmalloc(sizeof(*hm), GFP_KERNEL);
hr = kmalloc(sizeof(*hr), GFP_KERNEL);
if (!hm || !hr) {
err = -ENOMEM;
goto out;
}
phpi_ioctl_data = (struct hpi_ioctl_linux __user *)arg;
/* Read the message and response pointers from user space. */
if (get_user(puhm, &phpi_ioctl_data->phm)
|| get_user(puhr, &phpi_ioctl_data->phr)) {
err = -EFAULT;
goto out;
}
/* Now read the message size and data from user space. */
if (get_user(hm->h.size, (u16 __user *)puhm)) {
err = -EFAULT;
goto out;
}
if (hm->h.size > sizeof(*hm))
hm->h.size = sizeof(*hm);
/* printk(KERN_INFO "message size %d\n", hm->h.wSize); */
uncopied_bytes = copy_from_user(hm, puhm, hm->h.size);
if (uncopied_bytes) {
HPI_DEBUG_LOG(ERROR, "uncopied bytes %d\n", uncopied_bytes);
err = -EFAULT;
goto out;
}
if (get_user(res_max_size, (u16 __user *)puhr)) {
err = -EFAULT;
goto out;
}
/* printk(KERN_INFO "user response size %d\n", res_max_size); */
if (res_max_size < sizeof(struct hpi_response_header)) {
HPI_DEBUG_LOG(WARNING, "small res size %d\n", res_max_size);
err = -EFAULT;
goto out;
}
switch (hm->h.function) {
case HPI_SUBSYS_CREATE_ADAPTER:
case HPI_ADAPTER_DELETE:
/* Application must not use these functions! */
hr->h.size = sizeof(hr->h);
hr->h.error = HPI_ERROR_INVALID_OPERATION;
hr->h.function = hm->h.function;
uncopied_bytes = copy_to_user(puhr, hr, hr->h.size);
if (uncopied_bytes)
err = -EFAULT;
else
err = 0;
goto out;
}
hr->h.size = res_max_size;
if (hm->h.object == HPI_OBJ_SUBSYSTEM) {
hpi_send_recv_f(&hm->m0, &hr->r0, file);
} else {
u16 __user *ptr = NULL;
u32 size = 0;
/* -1=no data 0=read from user mem, 1=write to user mem */
int wrflag = -1;
struct hpi_adapter *pa = NULL;
if (hm->h.adapter_index < ARRAY_SIZE(adapters))
pa = &adapters[hm->h.adapter_index];
if (!pa || !pa->adapter || !pa->adapter->type) {
hpi_init_response(&hr->r0, hm->h.object,
hm->h.function, HPI_ERROR_BAD_ADAPTER_NUMBER);
uncopied_bytes =
copy_to_user(puhr, hr, sizeof(hr->h));
if (uncopied_bytes)
err = -EFAULT;
else
err = 0;
goto out;
}
if (mutex_lock_interruptible(&pa->mutex)) {
err = -EINTR;
goto out;
}
/* Dig out any pointers embedded in the message. */
switch (hm->h.function) {
case HPI_OSTREAM_WRITE:
case HPI_ISTREAM_READ:{
/* Yes, sparse, this is correct. */
ptr = (u16 __user *)hm->m0.u.d.u.data.pb_data;
size = hm->m0.u.d.u.data.data_size;
/* Allocate buffer according to application request.
?Is it better to alloc/free for the duration
of the transaction?
*/
if (pa->buffer_size < size) {
HPI_DEBUG_LOG(DEBUG,
"Realloc adapter %d stream "
"buffer from %zd to %d\n",
hm->h.adapter_index,
pa->buffer_size, size);
if (pa->p_buffer) {
pa->buffer_size = 0;
vfree(pa->p_buffer);
}
pa->p_buffer = vmalloc(size);
if (pa->p_buffer)
pa->buffer_size = size;
else {
HPI_DEBUG_LOG(ERROR,
"HPI could not allocate "
"stream buffer size %d\n",
size);
mutex_unlock(&pa->mutex);
err = -EINVAL;
goto out;
}
}
hm->m0.u.d.u.data.pb_data = pa->p_buffer;
if (hm->h.function == HPI_ISTREAM_READ)
/* from card, WRITE to user mem */
wrflag = 1;
else
wrflag = 0;
break;
}
default:
size = 0;
break;
}
if (size && (wrflag == 0)) {
uncopied_bytes =
copy_from_user(pa->p_buffer, ptr, size);
if (uncopied_bytes)
HPI_DEBUG_LOG(WARNING,
"Missed %d of %d "
"bytes from user\n", uncopied_bytes,
size);
}
hpi_send_recv_f(&hm->m0, &hr->r0, file);
if (size && (wrflag == 1)) {
uncopied_bytes =
copy_to_user(ptr, pa->p_buffer, size);
if (uncopied_bytes)
HPI_DEBUG_LOG(WARNING,
"Missed %d of %d " "bytes to user\n",
uncopied_bytes, size);
}
mutex_unlock(&pa->mutex);
}
/* on return response size must be set */
/*printk(KERN_INFO "response size %d\n", hr->h.wSize); */
if (!hr->h.size) {
HPI_DEBUG_LOG(ERROR, "response zero size\n");
err = -EFAULT;
goto out;
}
if (hr->h.size > res_max_size) {
HPI_DEBUG_LOG(ERROR, "response too big %d %d\n", hr->h.size,
res_max_size);
hr->h.error = HPI_ERROR_RESPONSE_BUFFER_TOO_SMALL;
hr->h.specific_error = hr->h.size;
hr->h.size = sizeof(hr->h);
}
uncopied_bytes = copy_to_user(puhr, hr, hr->h.size);
if (uncopied_bytes) {
HPI_DEBUG_LOG(ERROR, "uncopied bytes %d\n", uncopied_bytes);
err = -EFAULT;
goto out;
}
out:
kfree(hm);
kfree(hr);
return err;
}
int asihpi_adapter_probe(struct pci_dev *pci_dev,
const struct pci_device_id *pci_id)
{
int idx, nm;
int adapter_index;
unsigned int memlen;
struct hpi_message hm;
struct hpi_response hr;
struct hpi_adapter adapter;
struct hpi_pci pci;
memset(&adapter, 0, sizeof(adapter));
dev_printk(KERN_DEBUG, &pci_dev->dev,
"probe %04x:%04x,%04x:%04x,%04x\n", pci_dev->vendor,
pci_dev->device, pci_dev->subsystem_vendor,
pci_dev->subsystem_device, pci_dev->devfn);
if (pci_enable_device(pci_dev) < 0) {
dev_err(&pci_dev->dev,
"pci_enable_device failed, disabling device\n");
return -EIO;
}
pci_set_master(pci_dev); /* also sets latency timer if < 16 */
hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_CREATE_ADAPTER);
hpi_init_response(&hr, HPI_OBJ_SUBSYSTEM, HPI_SUBSYS_CREATE_ADAPTER,
HPI_ERROR_PROCESSING_MESSAGE);
hm.adapter_index = HPI_ADAPTER_INDEX_INVALID;
nm = HPI_MAX_ADAPTER_MEM_SPACES;
for (idx = 0; idx < nm; idx++) {
HPI_DEBUG_LOG(INFO, "resource %d %pR\n", idx,
&pci_dev->resource[idx]);
if (pci_resource_flags(pci_dev, idx) & IORESOURCE_MEM) {
memlen = pci_resource_len(pci_dev, idx);
pci.ap_mem_base[idx] =
ioremap(pci_resource_start(pci_dev, idx),
memlen);
if (!pci.ap_mem_base[idx]) {
HPI_DEBUG_LOG(ERROR,
"ioremap failed, aborting\n");
/* unmap previously mapped pci mem space */
goto err;
}
}
}
pci.pci_dev = pci_dev;
hm.u.s.resource.bus_type = HPI_BUS_PCI;
hm.u.s.resource.r.pci = &pci;
/* call CreateAdapterObject on the relevant hpi module */
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
if (hr.error)
goto err;
adapter_index = hr.u.s.adapter_index;
adapter.adapter = hpi_find_adapter(adapter_index);
if (prealloc_stream_buf) {
adapter.p_buffer = vmalloc(prealloc_stream_buf);
if (!adapter.p_buffer) {
HPI_DEBUG_LOG(ERROR,
"HPI could not allocate "
"kernel buffer size %d\n",
prealloc_stream_buf);
goto err;
}
}
hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
HPI_ADAPTER_OPEN);
hm.adapter_index = adapter.adapter->index;
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
if (hr.error)
goto err;
/* WARNING can't init mutex in 'adapter'
* and then copy it to adapters[] ?!?!
*/
adapters[adapter_index] = adapter;
mutex_init(&adapters[adapter_index].mutex);
pci_set_drvdata(pci_dev, &adapters[adapter_index]);
dev_info(&pci_dev->dev, "probe succeeded for ASI%04X HPI index %d\n",
adapter.adapter->type, adapter_index);
return 0;
err:
for (idx = 0; idx < HPI_MAX_ADAPTER_MEM_SPACES; idx++) {
if (pci.ap_mem_base[idx]) {
iounmap(pci.ap_mem_base[idx]);
pci.ap_mem_base[idx] = NULL;
}
}
if (adapter.p_buffer) {
adapter.buffer_size = 0;
vfree(adapter.p_buffer);
}
HPI_DEBUG_LOG(ERROR, "adapter_probe failed\n");
return -ENODEV;
}
void asihpi_adapter_remove(struct pci_dev *pci_dev)
{
int idx;
struct hpi_message hm;
struct hpi_response hr;
struct hpi_adapter *pa;
struct hpi_pci pci;
pa = pci_get_drvdata(pci_dev);
pci = pa->adapter->pci;
hpi_init_message_response(&hm, &hr, HPI_OBJ_ADAPTER,
HPI_ADAPTER_DELETE);
hm.adapter_index = pa->adapter->index;
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
/* unmap PCI memory space, mapped during device init. */
for (idx = 0; idx < HPI_MAX_ADAPTER_MEM_SPACES; idx++) {
if (pci.ap_mem_base[idx])
iounmap(pci.ap_mem_base[idx]);
}
if (pa->p_buffer)
vfree(pa->p_buffer);
if (1)
dev_info(&pci_dev->dev,
"remove %04x:%04x,%04x:%04x,%04x, HPI index %d\n",
pci_dev->vendor, pci_dev->device,
pci_dev->subsystem_vendor, pci_dev->subsystem_device,
pci_dev->devfn, pa->adapter->index);
memset(pa, 0, sizeof(*pa));
}
void __init asihpi_init(void)
{
struct hpi_message hm;
struct hpi_response hr;
memset(adapters, 0, sizeof(adapters));
printk(KERN_INFO "ASIHPI driver " HPI_VER_STRING "\n");
hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_DRIVER_LOAD);
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
}
void asihpi_exit(void)
{
struct hpi_message hm;
struct hpi_response hr;
hpi_init_message_response(&hm, &hr, HPI_OBJ_SUBSYSTEM,
HPI_SUBSYS_DRIVER_UNLOAD);
hpi_send_recv_ex(&hm, &hr, HOWNER_KERNEL);
}

View file

@ -0,0 +1,38 @@
/*******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
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
Linux HPI ioctl, and shared module init functions
*******************************************************************************/
int asihpi_adapter_probe(struct pci_dev *pci_dev,
const struct pci_device_id *pci_id);
void asihpi_adapter_remove(struct pci_dev *pci_dev);
void __init asihpi_init(void);
void __exit asihpi_exit(void);
int asihpi_hpi_release(struct file *file);
long asihpi_hpi_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
/* This is called from hpifunc.c functions, called by ALSA
* (or other kernel process) In this case there is no file descriptor
* available for the message cache code
*/
void hpi_send_recv(struct hpi_message *phm, struct hpi_response *phr);
#define HOWNER_KERNEL ((void *)-1)

83
sound/pci/asihpi/hpios.c Normal file
View file

@ -0,0 +1,83 @@
/******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2012 AudioScience Inc. <support@audioscience.com>
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
HPI Operating System function implementation for Linux
(C) Copyright AudioScience Inc. 1997-2003
******************************************************************************/
#define SOURCEFILE_NAME "hpios.c"
#include "hpi_internal.h"
#include "hpidebug.h"
#include <linux/delay.h>
#include <linux/sched.h>
void hpios_delay_micro_seconds(u32 num_micro_sec)
{
if ((usecs_to_jiffies(num_micro_sec) > 1) && !in_interrupt()) {
/* MUST NOT SCHEDULE IN INTERRUPT CONTEXT! */
schedule_timeout_uninterruptible(usecs_to_jiffies
(num_micro_sec));
} else if (num_micro_sec <= 2000)
udelay(num_micro_sec);
else
mdelay(num_micro_sec / 1000);
}
/** Allocate an area of locked memory for bus master DMA operations.
If allocation fails, return 1, and *pMemArea.size = 0
*/
u16 hpios_locked_mem_alloc(struct consistent_dma_area *p_mem_area, u32 size,
struct pci_dev *pdev)
{
/*?? any benefit in using managed dmam_alloc_coherent? */
p_mem_area->vaddr =
dma_alloc_coherent(&pdev->dev, size, &p_mem_area->dma_handle,
GFP_DMA32 | GFP_KERNEL);
if (p_mem_area->vaddr) {
HPI_DEBUG_LOG(DEBUG, "allocated %d bytes, dma 0x%x vma %p\n",
size, (unsigned int)p_mem_area->dma_handle,
p_mem_area->vaddr);
p_mem_area->pdev = &pdev->dev;
p_mem_area->size = size;
return 0;
} else {
HPI_DEBUG_LOG(WARNING,
"failed to allocate %d bytes locked memory\n", size);
p_mem_area->size = 0;
return 1;
}
}
u16 hpios_locked_mem_free(struct consistent_dma_area *p_mem_area)
{
if (p_mem_area->size) {
dma_free_coherent(p_mem_area->pdev, p_mem_area->size,
p_mem_area->vaddr, p_mem_area->dma_handle);
HPI_DEBUG_LOG(DEBUG, "freed %lu bytes, dma 0x%x vma %p\n",
(unsigned long)p_mem_area->size,
(unsigned int)p_mem_area->dma_handle,
p_mem_area->vaddr);
p_mem_area->size = 0;
return 0;
} else {
return 1;
}
}

165
sound/pci/asihpi/hpios.h Normal file
View file

@ -0,0 +1,165 @@
/******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
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
HPI Operating System Specific macros for Linux Kernel driver
(C) Copyright AudioScience Inc. 1997-2003
******************************************************************************/
#ifndef _HPIOS_H_
#define _HPIOS_H_
#undef HPI_OS_LINUX_KERNEL
#define HPI_OS_LINUX_KERNEL
#define HPI_OS_DEFINED
#define HPI_BUILD_KERNEL_MODE
#include <linux/io.h>
#include <linux/ioctl.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/device.h>
#include <linux/firmware.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/mutex.h>
#define HPI_NO_OS_FILE_OPS
#ifdef CONFIG_64BIT
#define HPI64BIT
#endif
/** Details of a memory area allocated with pci_alloc_consistent
Need all info for parameters to pci_free_consistent
*/
struct consistent_dma_area {
struct device *pdev;
/* looks like dma-mapping dma_devres ?! */
size_t size;
void *vaddr;
dma_addr_t dma_handle;
};
static inline u16 hpios_locked_mem_get_phys_addr(struct consistent_dma_area
*locked_mem_handle, u32 *p_physical_addr)
{
*p_physical_addr = locked_mem_handle->dma_handle;
return 0;
}
static inline u16 hpios_locked_mem_get_virt_addr(struct consistent_dma_area
*locked_mem_handle, void **pp_virtual_addr)
{
*pp_virtual_addr = locked_mem_handle->vaddr;
return 0;
}
static inline u16 hpios_locked_mem_valid(struct consistent_dma_area
*locked_mem_handle)
{
return locked_mem_handle->size != 0;
}
struct hpi_ioctl_linux {
void __user *phm;
void __user *phr;
};
/* Conflict?: H is already used by a number of drivers hid, bluetooth hci,
and some sound drivers sb16, hdsp, emu10k. AFAIK 0xFC is ununsed command
*/
#define HPI_IOCTL_LINUX _IOWR('H', 0xFC, struct hpi_ioctl_linux)
#define HPI_DEBUG_FLAG_ERROR KERN_ERR
#define HPI_DEBUG_FLAG_WARNING KERN_WARNING
#define HPI_DEBUG_FLAG_NOTICE KERN_NOTICE
#define HPI_DEBUG_FLAG_INFO KERN_INFO
#define HPI_DEBUG_FLAG_DEBUG KERN_DEBUG
#define HPI_DEBUG_FLAG_VERBOSE KERN_DEBUG /* kernel has no verbose */
#include <linux/spinlock.h>
#define HPI_LOCKING
struct hpios_spinlock {
spinlock_t lock; /* SEE hpios_spinlock */
int lock_context;
};
/* The reason for all this evilness is that ALSA calls some of a drivers
* operators in atomic context, and some not. But all our functions channel
* through the HPI_Message conduit, so we can't handle the different context
* per function
*/
#define IN_LOCK_BH 1
#define IN_LOCK_IRQ 0
static inline void cond_lock(struct hpios_spinlock *l)
{
if (irqs_disabled()) {
/* NO bh or isr can execute on this processor,
so ordinary lock will do
*/
spin_lock(&((l)->lock));
l->lock_context = IN_LOCK_IRQ;
} else {
spin_lock_bh(&((l)->lock));
l->lock_context = IN_LOCK_BH;
}
}
static inline void cond_unlock(struct hpios_spinlock *l)
{
if (l->lock_context == IN_LOCK_BH)
spin_unlock_bh(&((l)->lock));
else
spin_unlock(&((l)->lock));
}
#define hpios_msgxlock_init(obj) spin_lock_init(&(obj)->lock)
#define hpios_msgxlock_lock(obj) cond_lock(obj)
#define hpios_msgxlock_unlock(obj) cond_unlock(obj)
#define hpios_dsplock_init(obj) spin_lock_init(&(obj)->dsp_lock.lock)
#define hpios_dsplock_lock(obj) cond_lock(&(obj)->dsp_lock)
#define hpios_dsplock_unlock(obj) cond_unlock(&(obj)->dsp_lock)
#ifdef CONFIG_SND_DEBUG
#define HPI_BUILD_DEBUG
#endif
#define HPI_ALIST_LOCKING
#define hpios_alistlock_init(obj) spin_lock_init(&((obj)->list_lock.lock))
#define hpios_alistlock_lock(obj) spin_lock(&((obj)->list_lock.lock))
#define hpios_alistlock_unlock(obj) spin_unlock(&((obj)->list_lock.lock))
struct snd_card;
/** pci drvdata points to an instance of this struct */
struct hpi_adapter {
struct hpi_adapter_obj *adapter;
struct snd_card *snd_card;
/* mutex prevents contention for one card
between multiple user programs (via ioctl) */
struct mutex mutex;
char *p_buffer;
size_t buffer_size;
};
#endif

View file

@ -0,0 +1,37 @@
/******************************************************************************
AudioScience HPI driver
Copyright (C) 1997-2011 AudioScience Inc. <support@audioscience.com>
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
Array initializer for PCI card IDs
(C) Copyright AudioScience Inc. 1998-2003
*******************************************************************************/
/*NOTE: when adding new lines to this header file
they MUST be grouped by HPI entry point.
*/
{
HPI_PCI_VENDOR_ID_TI, HPI_PCI_DEV_ID_DSP6205,
HPI_PCI_VENDOR_ID_AUDIOSCIENCE, PCI_ANY_ID, 0, 0,
(kernel_ulong_t) HPI_6205}
, {
HPI_PCI_VENDOR_ID_TI, HPI_PCI_DEV_ID_PCI2040,
HPI_PCI_VENDOR_ID_AUDIOSCIENCE, PCI_ANY_ID, 0, 0,
(kernel_ulong_t) HPI_6000}
, {
0}