Doc updates

This commit is contained in:
Ross Owen
2012-08-30 14:21:17 +01:00
parent e449050b40
commit 99cdee6e2b
45 changed files with 0 additions and 0 deletions

1
module_usb_audio/README Normal file
View File

@@ -0,0 +1 @@
Shared USB Audio Files

18
module_usb_audio/audio.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef __audio_h__
#define __audio_h__
/** The audio driver thread.
*
* This function drives I2S ports and handles samples to/from other digital
* I/O threads.
*
* \param c_in Audio sample channel connected to the mixer() thread or the
* decouple() thread
* \param c_dig channel connected to the clockGen() thread for
* receiving/transmitting samples
* \param c_config An optional channel that will be passed on to the
* CODEC configuration functions.
*/
void audio(chanend c_in, chanend ?c_dig, chanend ?c_config, chanend ?c_adc);
#endif // __audio_h__

693
module_usb_audio/audio.xc Executable file
View File

@@ -0,0 +1,693 @@
/**
* @file audio.xc
* @brief XMOS L1/L2 USB 2,0 Audio Reference Design. Audio Functions.
* @author Ross Owen, XMOS Semiconductor Ltd
*
* This thread handles I2S and pars an additional SPDIF Tx thread. It forwards samples to the SPDIF Tx thread.
* Additionally this thread handles clocking and CODEC/DAC/ADC config.
**/
#include <syscall.h>
#include <platform.h>
#include <xs1.h>
#include <xclib.h>
#include <print.h>
#include <xs1_su.h>
#include "clocking.h"
#include "audioports.h"
#include "codec.h"
#include "devicedefines.h"
#include "SpdifTransmit.h"
extern out port p_test;
unsigned g_adcVal = 0;
//#define RAMP_CHECK 1
//#pragma xta command "analyse path i2s_output_l i2s_output_r"
//#pragma xta command "set required - 2000 ns"
//#pragma xta command "analyse path i2s_output_r i2s_output_l"
//#pragma xta command "set required - 2000 ns"
/* I2S Data I/O*/
#if (I2S_CHANS_DAC != 0)
extern buffered out port:32 p_i2s_dac[I2S_WIRES_DAC];
#endif
#if (I2S_CHANS_ADC != 0)
extern buffered in port:32 p_i2s_adc[I2S_WIRES_ADC];
#endif
/* I2S LR/Bit clock I/O */
#ifdef CODEC_SLAVE
extern buffered out port:32 p_lrclk;
extern buffered out port:32 p_bclk;
#else
extern in port p_lrclk;
extern in port p_bclk;
#endif
/* Master clock input */
extern port p_mclk;
#ifdef SPDIF
extern buffered out port:32 p_spdif_tx;
#endif
extern clock clk_audio_mclk;
extern clock clk_audio_bclk;
extern clock clk_mst_spd;
extern void device_reboot(void);
/* I2S delivery thread */
#pragma unsafe arrays
unsigned deliver(chanend c_out, chanend ?c_spd_out, unsigned divide, chanend ?c_dig_rx, chanend ?c_adc)
{
unsigned sample;
#if NUM_USB_CHAN_OUT > 0
unsigned samplesOut[NUM_USB_CHAN_OUT];
#endif
#if NUM_USB_CHAN_IN > 0
unsigned samplesIn[NUM_USB_CHAN_IN];
unsigned samplesInPrev[NUM_USB_CHAN_IN];
#endif
unsigned tmp;
unsigned index;
#ifdef RAMP_CHECK
unsigned prev=0;
int started = 0;
#endif
unsigned test = 0;
#if NUM_USB_CHAN_IN > 0
for (int i=0;i<NUM_USB_CHAN_IN;i++)
{
samplesIn[i] = 0;
samplesInPrev[i] = 0;
}
#endif
outuint(c_out, 0);
/* Check for sample freq change or new samples from mixer*/
if(testct(c_out))
{
inct(c_out);
return inuint(c_out);
}
else
{
#ifndef MIXER // Interfaces straight to decouple()
(void) inuint(c_out);
#if NUM_USB_CHAN_IN > 0
#pragma loop unroll
for(int i = 0; i < NUM_USB_CHAN_IN; i++)
{
outuint(c_out, samplesIn[i]);
}
#endif
#if NUM_USB_CHAN_OUT > 0
#pragma loop unroll
for(int i = 0; i < NUM_USB_CHAN_OUT; i++)
{
samplesOut[i] = inuint(c_out);
}
#endif
#else
#pragma loop unroll
for(int i = 0; i < NUM_USB_CHAN_OUT; i++)
{
int tmp = inuint(c_out);
#if defined(OUT_VOLUME_IN_MIXER) && defined(OUT_VOLUME_AFTER_MIX)
tmp<<=3;
#endif
samplesOut[i] = tmp;
}
#pragma loop unroll
for(int i = 0; i < NUM_USB_CHAN_IN; i++)
{
outuint(c_out, samplesIn[i]);
}
#endif
}
#ifdef CODEC_SLAVE
/* Clear I2S port buffers */
clearbuf(p_lrclk);
#if (I2S_CHANS_DAC != 0)
for(int i = 0; i < I2S_WIRES_DAC; i++)
{
clearbuf(p_i2s_dac[i]);
}
#endif
#if (I2S_CHANS_ADC != 0)
for(int i = 0; i < I2S_WIRES_ADC; i++)
{
clearbuf(p_i2s_adc[i]);
}
#endif
if(divide == 1)
{
p_lrclk <: 0 @ tmp;
tmp += 30;
/* Prefill the ports so data starts to be input */
#if (I2S_CHANS_DAC != 0)
#pragma loop unroll
for(int i = 0; i < I2S_WIRES_DAC; i++)
{
p_i2s_dac[i] @ tmp <: 0;
}
#endif
p_lrclk @ tmp <: 0x7FFFFFFF;
#if (I2S_CHANS_ADC != 0)
p_i2s_adc[0] @ (tmp - 1) :> void;
#endif
#if (I2S_CHANS_ADC != 0)
#pragma loop unroll
for(int i = 0; i < I2S_WIRES_ADC; i++)
{
clearbuf(p_i2s_adc[i]);
}
#endif
}
else
{
clearbuf(p_bclk);
#if (I2S_CHANS_DAC != 0)
/* Prefill the ports so data is input in advance */
for(int i = 0; i < I2S_WIRES_DAC; i++)
{
p_i2s_dac[i] <: 0;
}
#endif
p_lrclk <: 0x7FFFFFFF;
p_bclk <: 0xAAAAAAAA;
p_bclk <: 0xAAAAAAAA;
}
#else
/* CODEC is master */
/* Wait for LRCLK edge */
p_lrclk when pinseq(0) :> void;
p_lrclk when pinseq(1) :> void;
p_lrclk when pinseq(0) :> void;
p_lrclk when pinseq(1) :> void;
p_lrclk when pinseq(0) :> void @ tmp;
tmp += 33;
#if (I2S_CHANS_DAC != 0)
#pragma loop unroll
for(int i = 0; i < I2S_WIRES_DAC; i++)
{
p_i2s_dac[i] @ tmp <: 0;
}
#endif
p_i2s_adc[0] @ tmp - 1 :> void;
#pragma loop unroll
for(int i = 0; i < I2S_WIRES_ADC; i++)
{
clearbuf(p_i2s_adc[i]);
}
/* TODO In master mode, the i/o loop assumes L/RCLK = 32bit clocks. We should check this every interation
* and resync if we got a bclk glitch */
#endif
/* Main Audio I/O loop */
while (1)
{
outuint(c_out, 0);
/* Check for sample freq change or new samples from mixer*/
if(testct(c_out))
{
inct(c_out);
return inuint(c_out);
}
else
{
#ifndef MIXER // Interfaces straight to decouple()
(void) inuint(c_out);
#if NUM_USB_CHAN_IN > 0
#pragma loop unroll
for(int i = 0; i < NUM_USB_CHAN_IN; i++)
{
outuint(c_out, samplesIn[i]);
}
#endif
#if NUM_USB_CHAN_OUT > 0
#pragma loop unroll
for(int i = 0; i < NUM_USB_CHAN_OUT; i++)
{
samplesOut[i] = inuint(c_out);
}
#endif
#else
#pragma loop unroll
for(int i = 0; i < NUM_USB_CHAN_OUT; i++)
{
int tmp = inuint(c_out);
#if defined(OUT_VOLUME_IN_MIXER) && defined(OUT_VOLUME_AFTER_MIX)
tmp<<=3;
#endif
samplesOut[i] = tmp;
}
#pragma loop unroll
for(int i = 0; i < NUM_USB_CHAN_IN; i++)
{
outuint(c_out, samplesIn[i]);
}
#endif
}
#if defined(SPDIF_RX) || defined(ADAT_RX)
inuint(c_dig_rx);
#endif
#ifdef SPDIF_RX
asm("ldw %0, dp[g_digData]":"=r"(samplesIn[SPDIF_RX_INDEX + 0]));
asm("ldw %0, dp[g_digData+4]":"=r"(samplesIn[SPDIF_RX_INDEX + 1]));
#endif
#ifdef ADAT_RX
asm("ldw %0, dp[g_digData+8]":"=r"(samplesIn[ADAT_RX_INDEX + 0]));
asm("ldw %0, dp[g_digData+12]":"=r"(samplesIn[ADAT_RX_INDEX+ 1]));
asm("ldw %0, dp[g_digData+16]":"=r"(samplesIn[ADAT_RX_INDEX + 2]));
asm("ldw %0, dp[g_digData+20]":"=r"(samplesIn[ADAT_RX_INDEX + 3]));
asm("ldw %0, dp[g_digData+24]":"=r"(samplesIn[ADAT_RX_INDEX + 4]));
asm("ldw %0, dp[g_digData+28]":"=r"(samplesIn[ADAT_RX_INDEX + 5]));
asm("ldw %0, dp[g_digData+32]":"=r"(samplesIn[ADAT_RX_INDEX + 6]));
asm("ldw %0, dp[g_digData+36]":"=r"(samplesIn[ADAT_RX_INDEX + 7]));
#endif
#if defined(SPDIF_RX) || defined(ADAT_RX)
/* Request digital data (with prefill) */
outuint(c_dig_rx, 0);
#endif
tmp = 0;
#pragma xta endpoint "i2s_output_l"
#if (I2S_CHANS_DAC != 0) && (NUM_USB_CHAN_OUT != 0)
#pragma loop unroll
for(int i = 0; i < I2S_CHANS_DAC; i+=2)
{
p_i2s_dac[tmp++] <: bitrev(samplesOut[i]); /* Output LEFT sample to DAC */
}
#endif
#ifdef CODEC_SLAVE
/* Generate clocks LR Clock low - LEFT */
switch (divide)
{
case 8:
/* LR clock delayed by one clock, This is so MSB is output on the falling edge of BCLK
* after the falling edge on which LRCLK was toggled. (see I2S spec) */
p_lrclk <: 0x80000000;
p_bclk <: 0xF0F0F0F0;
p_bclk <: 0xF0F0F0F0;
p_bclk <: 0xF0F0F0F0;
p_bclk <: 0xF0F0F0F0;
p_bclk <: 0xF0F0F0F0;
p_bclk <: 0xF0F0F0F0;
p_bclk <: 0xF0F0F0F0;
p_bclk <: 0xF0F0F0F0;
break;
case 4:
p_lrclk <: 0x80000000;
p_bclk <: 0xCCCCCCCC;
p_bclk <: 0xCCCCCCCC;
p_bclk <: 0xCCCCCCCC;
p_bclk <: 0xCCCCCCCC;
break;
case 2:
p_lrclk <: 0x80000000;
p_bclk <: 0xAAAAAAAA;
p_bclk <: 0xAAAAAAAA;
break;
case 1:
p_lrclk <: 0x80000000;
break;
}
#endif
#if (I2S_CHANS_ADC != 0)
/* Input prevous R sample into R in buffer */
index = 0;
#pragma loop unroll
for(int i = 1; i < I2S_CHANS_ADC; i += 2)
{
p_i2s_adc[index++] :> sample;
#if NUM_USB_CHAN_IN > 0
samplesIn[i] = bitrev(sample);
/* Store the previous left in left */
samplesIn[i-1] = samplesInPrev[i];
#endif
}
#endif
#if defined(SPDIF) && (NUM_USB_CHAN_OUT > 0)
outuint(c_spd_out, samplesOut[SPDIF_TX_INDEX]); /* Forward sample to SPDIF txt thread */
sample = samplesOut[SPDIF_TX_INDEX + 1];
outuint(c_spd_out, sample); /* Forward sample to SPDIF txt thread */
#ifdef RAMP_CHECK
sample >>= 8;
if (started<10000) {
if (sample == prev+1)
started++;
}
else
if (sample != prev+1 && sample != 0) {
printintln(prev);
printintln(sample);
printintln(prev-sample+1);
}
prev = sample;
#endif
#endif
tmp = 0;
#pragma xta endpoint "i2s_output_r"
#if (I2S_CHANS_DAC != 0) && (NUM_USB_CHAN_OUT != 0)
#pragma loop unroll
for(int i = 1; i < I2S_CHANS_DAC; i+=2)
{
p_i2s_dac[tmp++] <: bitrev(samplesOut[i]); /* Output RIGHT sample to DAC */
}
#endif
#ifdef CODEC_SLAVE
/* Clock out data (and LR clock) */
switch (divide)
{
case 8:
p_lrclk <: 0x7FFFFFFF;
p_bclk <: 0xF0F0F0F0;
p_bclk <: 0xF0F0F0F0;
p_bclk <: 0xF0F0F0F0;
p_bclk <: 0xF0F0F0F0;
p_bclk <: 0xF0F0F0F0;
p_bclk <: 0xF0F0F0F0;
p_bclk <: 0xF0F0F0F0;
p_bclk <: 0xF0F0F0F0;
break;
case 4:
p_lrclk <: 0x7FFFFFFF;
p_bclk <: 0xCCCCCCCC;
p_bclk <: 0xCCCCCCCC;
p_bclk <: 0xCCCCCCCC;
p_bclk <: 0xCCCCCCCC;
break;
case 2:
p_lrclk <: 0x7FFFFFFF;
p_bclk <: 0xAAAAAAAA;
p_bclk <: 0xAAAAAAAA;
break;
case 1:
p_lrclk <: 0x7FFFFFFF;
break;
}
#endif
#if (I2S_CHANS_ADC != 0)
/* Input previous L ADC sample */
index = 0;
#pragma loop unroll
for(int i = 1; i < I2S_CHANS_ADC; i += 2)
{
p_i2s_adc[index++] :> sample;
#if NUM_USB_CHAN_IN > 0
samplesInPrev[i] = bitrev(sample);
#endif
}
#ifdef SU1_ADC
{
unsigned x;
x = inuint(c_adc);
inct(c_adc);
asm("stw %0, dp[g_adcVal]"::"r"(x));
}
#endif
#endif
}
return 0;
}
/* This function is a dummy version of the deliver thread that does not
connect to the codec ports. It is used during DFU reset. */
static unsigned dummy_deliver(chanend c_out) {
while (1)
{
outuint(c_out, 0);
/* Check for sample freq change or new samples from mixer*/
if(testct(c_out))
{
inct(c_out);
return inuint(c_out);
}
else
{
#ifndef MIXER // Interfaces straight to decouple()
(void) inuint(c_out);
#pragma loop unroll
for(int i = 0; i < NUM_USB_CHAN_IN; i++)
{
outuint(c_out, 0);
}
#pragma loop unroll
for(int i = 0; i < NUM_USB_CHAN_OUT; i++)
{
(void) inuint(c_out);
}
#else
#pragma loop unroll
for(int i = 0; i < NUM_USB_CHAN_OUT; i++)
{
(void) inuint(c_out);
}
#pragma loop unroll
for(int i = 0; i < NUM_USB_CHAN_IN; i++)
{
outuint(c_out, 0);
}
#endif
}
}
return 0;
}
#define SAMPLE_RATE 200000
#define NUMBER_CHANNELS 1
#define NUMBER_SAMPLES 100
#define NUMBER_WORDS ((NUMBER_SAMPLES * NUMBER_CHANNELS+1)/2)
#define SAMPLES_PER_PRINT 1
void audio(chanend c_mix_out, chanend ?c_dig_rx, chanend ?c_config, chanend ?c)
{
#ifdef SPDIF
chan c_spdif_out;
#endif
unsigned curSamFreq = DEFAULT_FREQ;
unsigned mClk;
unsigned divide;
unsigned firstRun = 1;
#ifdef SU1_ADC
/* Setup galaxian ADC */
unsigned data[1], channel;
int r;
unsigned int vals[NUMBER_WORDS];
int cnt = 0;
int div;
unsigned val = 0;
int val2 = 0;
int adcOk = 0;
/* Enable adc on channel */
enable_xs1_su_adc_input(0, c);
/* General ADC control (enabled, 1 samples per packet, 32 bits per sample) */
data[0] = 0x10201;
data[0] = 0x30101;
r = write_periph_32(xs1_su, 2, 0x20, 1, data);
/* ADC needs a few clocks before it starts pumping out samples */
for(int i = 0; i< 10; i++)
{
p_lrclk <: val;
val = ~val;
{
timer t;
unsigned time;
t :> time;
t when timerafter(time+1000):> void;
}
}
#endif
#ifdef SPDIF
SpdifTransmitPortConfig(p_spdif_tx, clk_mst_spd, p_mclk);
#endif
/* Initialise master clock generation */
ClockingInit(c_config);
/* Perform required CODEC/ADC/DAC initialisation */
CodecInit(c_config);
{
}
while(1)
{
/* Calculate what master clock we should be using */
if ((curSamFreq % 22050) == 0)
{
mClk = MCLK_441;
}
else if ((curSamFreq % 24000) == 0)
{
mClk = MCLK_48;
}
/* Calculate divide required for bit clock e.g. 11.289600 / (176400 * 64) = 1 */
divide = mClk / ( curSamFreq * 64 );
/* Configure clocking for required master clock */
ClockingConfig(mClk, c_config);
if(!firstRun)
{
/* TODO wait for good mclk instead of delay */
/* No delay for DFU modes */
if ((curSamFreq != AUDIO_REBOOT_FROM_DFU) && (curSamFreq != AUDIO_STOP_FOR_DFU))
{
timer t;
unsigned time;
t :> time;
t when timerafter(time+AUDIO_PLL_LOCK_DELAY) :> void;
/* Handshake back */
outct(c_mix_out, XS1_CT_END);
}
}
firstRun = 0;
/* Configure CODEC/DAC/ADC for SampleFreq/MClk */
CodecConfig(curSamFreq, mClk, c_config);
/* Configure audio ports */
ConfigAudioPorts(divide);
par
{
#ifdef SPDIF
{
set_thread_fast_mode_on();
SpdifTransmit(p_spdif_tx, c_spdif_out);
}
#endif
{
#ifdef SPDIF
/* Communicate master clock and sample freq to S/PDIF thread */
outuint(c_spdif_out, curSamFreq);
outuint(c_spdif_out, mClk);
#endif
curSamFreq = deliver(c_mix_out,
#ifdef SPDIF
c_spdif_out,
#else
null,
#endif
divide, c_dig_rx, c);
// Currently no more audio will happen after this point
if (curSamFreq == AUDIO_STOP_FOR_DFU)
{
outct(c_mix_out, XS1_CT_END);
while (1)
{
curSamFreq = dummy_deliver(c_mix_out);
if (curSamFreq == AUDIO_START_FROM_DFU)
{
outct(c_mix_out, XS1_CT_END);
break;
}
}
}
#ifdef SPDIF
/* Notify S/PDIF thread of impending new freq... */
outct(c_spdif_out, XS1_CT_END);
#endif
}
}
}
}

View File

@@ -0,0 +1,17 @@
#ifndef _AUDIOSTREAM_H_
#define _AUDIOSTREAM_H_
/* Functions that handle functions that must occur on stream start/stop e.g. DAC mute/un-mute
*
* THESE NEED IMPLEMENTING FOR A SPECIFIC DESIGN
*
* */
/* Any actions required for stream start e.g. DAC un-mute - run every stream start */
void AudioStreamStart(void);
/* Any actions required on stream stop e.g. DAC mute - run every steam stop */
void AudioStreamStop(void);
#endif

View File

@@ -0,0 +1,30 @@
#include "devicedefines.h"
/* Clocking commands - c_clk_ctl */
#define GET_SEL 0 /* Get value of clock selector */
#define SET_SEL 1 /* Set value of clock selector */
#define GET_FREQ 2 /* Get current freq */
#define GET_VALID 3 /* Get current validity */
#define CLOCK_INTERNAL 1
#define CLOCK_SPDIF 2
#ifdef SPDIF_RX
#define CLOCK_ADAT 3
#else
#define CLOCK_ADAT 2
#endif
#define CLOCK_INTERNAL_INDEX (CLOCK_INTERNAL - 1)
#define CLOCK_ADAT_INDEX (CLOCK_ADAT - 1)
#define CLOCK_SPDIF_INDEX (CLOCK_SPDIF - 1)
#define SET_SMUX 7
/* c_audioControl */
#define SET_SAMPLE_FREQ 4
#define SET_CHAN_COUNT_IN 5
#define SET_CHAN_COUNT_OUT 6

View File

@@ -0,0 +1,794 @@
#include <xs1.h>
#include <assert.h>
#include <print.h>
#include "devicedefines.h"
#include "clockcmds.h"
#include "SpdifReceive.h"
#define LOCAL_CLOCK_INCREMENT 166667
#define LOCAL_CLOCK_MARGIN 1666
#define MAX_SAMPLES 64 /* Must be power of 2 */
#define MAX_SPDIF_SAMPLES (2 * MAX_SAMPLES) /* Must be power of 2 */
#define MAX_ADAT_SAMPLES (8 * MAX_SAMPLES) /* Must be power of 2 */
#define SPDIF_FRAME_ERRORS_THRESH 40
unsigned g_digData[10];
typedef struct
{
int receivedSamples;
int samples;
int savedSamples;
int lastDiff;
unsigned identicaldiffs;
int samplesPerTick;
} Counter;
static int clockFreq[NUM_CLOCKS]; /* Store current clock freq for each clock unit */
static int clockValid[NUM_CLOCKS]; /* Store current validity of each clock unit */
static int clockInt[NUM_CLOCKS]; /* Interupt flag for clocks */
static int clockId[NUM_CLOCKS];
int abs(int x)
{
if (x < 0) return -x;
return x;
}
int channelContainsControlToken(chanend x)
{
unsigned char tmpc;
select
{
case inct_byref(x, tmpc):
return 1;
default:
return 0;
}
}
void outInterrupt(chanend c_interruptControl, int value)
{
/* Non-blocking check for control token */
//if (channelContainsControlToken(c_interruptControl))
{
outuint(c_interruptControl, value);
outct(c_interruptControl, XS1_CT_END);
}
}
#ifdef CLOCK_VALIDITY_CALL
void VendorClockValidity(int valid);
#endif
#if defined(SPDIF_RX) || defined(ADAT_RX)
static inline void setClockValidity(chanend c_interruptControl, int clkIndex, int valid, int currentClkMode)
{
if (clockValid[clkIndex] != valid)
{
clockValid[clkIndex] = valid;
outInterrupt(c_interruptControl, clockId[clkIndex]);
#ifdef CLOCK_VALIDITY_CALL
#ifdef ADAT_RX
if (currentClkMode == CLOCK_ADAT && clkIndex == CLOCK_ADAT_INDEX)
{
VendorClockValidity(valid);
}
#endif
#ifdef SPDIF_RX
if (currentClkMode == CLOCK_SPDIF && clkIndex == CLOCK_SPDIF_INDEX)
{
VendorClockValidity(valid);
}
#endif
#endif
}
}
/* Returns 1 for valid clock found else 0 */
static inline int validSamples(Counter &counter, int clockIndex)
{
int diff = counter.samples - counter.savedSamples;
counter.savedSamples = counter.samples;
/* Check for stable sample rate (with some small margin) */
if (diff != 0 && abs( diff - counter.lastDiff ) < 5 )
{
counter.identicaldiffs++;
if (counter.identicaldiffs > 10)
{
/* Detect current sample rate (round to nearest) */
int s = -1;
if (diff > 137 && diff < 157)
{
s = 147;
}
else if (diff > 150 && diff < 170)
{
s = 160;
}
else if(diff > 284 && diff < 304)
{
s = 294;
}
else if (diff > 310 && diff < 330)
{
s = 320;
}
else if (diff > 578 && diff < 598)
{
s = 588;
}
else if (diff > 630 && diff < 650)
{
s = 640;
}
/* Check if we found a valid freq */
if (s != -1)
{
/* Update expected samples per tick */
counter.samplesPerTick = s;
/* Update record of external clock source sample frequency */
s *= 300;
if (clockFreq[clockIndex] != s)
{
clockFreq[clockIndex] = s;
}
return 1;
}
else
{
/* Not a valid frequency - Reset counter and find another run of samples */
counter.identicaldiffs = 0;
}
}
}
else
{
counter.identicaldiffs = 0;
counter.lastDiff = diff;
}
return 0;
}
#endif
#ifdef SPDIF_RX
//:badParity
/* Returns 1 for bad parity, else 0 */
static inline int badParity(unsigned x)
{
unsigned X = (x>>4);
crc32(X, 0, 1);
return X & 1;
}
//:
#endif
#ifdef LEVEL_METER_LEDS
void VendorLedRefresh(unsigned levelData[]);
unsigned g_inputLevelData[NUM_USB_CHAN_IN];
extern int samples_to_host_inputs[NUM_USB_CHAN_IN];
extern int samples_to_host_inputs_buff[NUM_USB_CHAN_IN]; /* Audio transmitted to host i.e. dev inputs */
#endif
int VendorAudCoreReqs(unsigned cmd, chanend c);
#pragma unsafe arrays
void clockGen (streaming chanend c_spdif_rx, chanend c_adat_rx, out port p, chanend c_dig_rx, chanend c_clk_ctl, chanend c_clk_int)
{
timer t_local;
unsigned timeNextEdge, timeLastEdge, timeNextClockDetection;
unsigned pinVal = 0;
unsigned short pinTime;
unsigned clkMode = CLOCK_INTERNAL; /* Current clocking mode in operation */
unsigned tmp;
/* start in no-SMUX (8-channel) mode */
int smux = 0;
#ifdef LEVEL_METER_LEDS
timer t_level;
unsigned levelTime;
#endif
#if defined(SPDIF_RX) || defined(ADAT_RX)
timer t_external;
#endif
#ifdef SPDIF_RX
/* S/PDIF buffer state */
int spdifSamples[MAX_SPDIF_SAMPLES]; /* S/PDIF sample buffer */
int spdifWr = 0; /* Write index */
int spdifRd = 0; /* Read index */ //(spdifWriteIndex ^ (MAX_SPDIF_SAMPLES >> 1)) & ~1; // Start in middle
int spdifOverflow = 0; /* Overflow/undeflow flags */
int spdifUnderflow = 1;
int spdifSamps = 0; /* Number of samples in buffer */
Counter spdifCounters;
int spdifReceivedTime;
unsigned tmp2;
unsigned spdifLeft = 0;
#endif
#ifdef ADAT_RX
/* ADAT buffer state */
int adatSamples[MAX_ADAT_SAMPLES];
int adatWr = 0;
int adatRd = 0;
int adatOverflow = 0;
int adatUnderflow = 1;
//int adatFrameErrors = 0;
int adatSamps = 0;
Counter adatCounters;
int adatReceivedTime;
unsigned adatFrame[8];
int adatChannel = 0;
int adatSamplesEver = 0;
#endif
for(int i = 0; i < 10; i++)
{
g_digData[i] = 0;
}
/* Init clock unit state */
#ifdef SPDIF_RX
clockFreq[CLOCK_SPDIF_INDEX] = 0;
clockValid[CLOCK_SPDIF_INDEX] = 0;
clockInt[CLOCK_SPDIF_INDEX] = 0;
clockId[CLOCK_SPDIF_INDEX] = ID_CLKSRC_EXT;
#endif
clockFreq[CLOCK_INTERNAL_INDEX] = 0;
clockId[CLOCK_INTERNAL_INDEX] = ID_CLKSRC_INT;
clockValid[CLOCK_INTERNAL_INDEX] = 0;
clockInt[CLOCK_INTERNAL_INDEX] = 0;
#ifdef ADAT_RX
clockFreq[CLOCK_ADAT_INDEX] = 0;
clockInt[CLOCK_ADAT_INDEX] = 0;
clockValid[CLOCK_ADAT_INDEX] = 0;
clockId[CLOCK_ADAT_INDEX] = ID_CLKSRC_ADAT;
#endif
#ifdef SPDIF_RX
spdifCounters.receivedSamples = 0;
spdifCounters.samples = 0;
spdifCounters.savedSamples = 0;
spdifCounters.lastDiff = 0;
spdifCounters.identicaldiffs = 0;
spdifCounters.samplesPerTick = 0;
#endif
#ifdef ADAT_RX
adatCounters.receivedSamples = 0;
adatCounters.samples = 0;
adatCounters.savedSamples = 0;
adatCounters.lastDiff = 0;
adatCounters.identicaldiffs = 0;
adatCounters.samplesPerTick = 0;
#endif
t_local :> timeNextEdge;
timeLastEdge = timeNextEdge;
timeNextClockDetection = timeNextEdge + (LOCAL_CLOCK_INCREMENT / 2);
timeNextEdge += LOCAL_CLOCK_INCREMENT;
#ifdef LEVEL_METER_LEDS
t_level :> levelTime;
levelTime+= LEVEL_UPDATE_RATE;
#endif
#if defined(SPDIF_RX) || defined(ADAT_RX)
/* Fill channel */
outuint(c_dig_rx, 1);
#endif
/* Initial ref clock output and get timestamp */
p <: pinVal @ pinTime;
pinTime += (unsigned short)(LOCAL_CLOCK_INCREMENT - (LOCAL_CLOCK_INCREMENT/2));
p @ pinTime <: pinVal;
while(1)
{
select
{
#ifdef LEVEL_METER_LEDS
#warning Level metering enabled
case t_level when timerafter(levelTime) :> void:
levelTime += LEVEL_UPDATE_RATE;
/* Copy over level data and reset */
for(int i = 0; i< NUM_USB_CHAN_IN; i++)
{
int tmp;
//g_inputLevelData[i] = samples_to_host_inputs[i];
asm("ldw %0, %1[%2]":"=r"(tmp):"r"(samples_to_host_inputs),"r"(i));
g_inputLevelData[i] = tmp;
//samples_to_host_inputs[i] = 0;
asm("stw %0, %1[%2]"::"r"(0),"r"(samples_to_host_inputs),"r"(i));
/* Guard against host polling slower than timer and missing peaks */
if(g_inputLevelData[i] > samples_to_host_inputs_buff[i])
{
samples_to_host_inputs_buff[i] = g_inputLevelData[i];
}
}
/* Call user LED refresh */
VendorLedRefresh(g_inputLevelData);
break;
#endif
/* Updates to clock settings from endpoint 0 */
case inuint_byref(c_clk_ctl, tmp):
switch(tmp)
{
case GET_SEL:
chkct(c_clk_ctl, XS1_CT_END);
/* Send back current clock mode */
outuint(c_clk_ctl, clkMode);
outct(c_clk_ctl, XS1_CT_END);
break;
case SET_SEL:
/* Update clock mode */
tmp = inuint(c_clk_ctl);
chkct(c_clk_ctl, XS1_CT_END);
if(tmp!=0)
{
clkMode = tmp;
}
#ifdef CLOCK_VALIDITY_CALL
switch(clkMode)
{
case CLOCK_INTERNAL:
VendorClockValidity(1);
break;
#ifdef ADAT_RX
case CLOCK_ADAT:
VendorClockValidity(clockValid[CLOCK_ADAT_INDEX]);
break;
#endif
#ifdef SPDIF_RX
case CLOCK_SPDIF:
VendorClockValidity(clockValid[CLOCK_SPDIF_INDEX]);
break;
#endif
}
#endif
break;
case GET_VALID:
/* Clock Unit Index */
tmp = inuint(c_clk_ctl);
chkct(c_clk_ctl, XS1_CT_END);
outuint(c_clk_ctl, clockValid[tmp]);
outct(c_clk_ctl, XS1_CT_END);
break;
case GET_FREQ:
tmp = inuint(c_clk_ctl);
chkct(c_clk_ctl, XS1_CT_END);
outuint(c_clk_ctl, clockFreq[tmp]);
outct(c_clk_ctl, XS1_CT_END);
break;
case SET_SMUX:
smux = inuint(c_clk_ctl);
#ifdef ADAT_RX
adatRd = 0; /* Reset adat FIFO */
adatWr = 0;
adatSamps = 0;
#endif
chkct(c_clk_ctl, XS1_CT_END);
break;
default:
#ifdef VENDOR_AUDCORE_REQS
if(VendorAudCoreReqs(tmp, c_clk_ctl))
#endif
printstrln("ERR: Bad req in clockgen\n");
break;
}
break;
/* Generate local clock from timer */
case t_local when timerafter(timeNextEdge) :> void:
/* Setup next local clock edge */
pinTime += (short) LOCAL_CLOCK_INCREMENT;
pinVal = !pinVal;
p @ pinTime <: pinVal;
/* Record time of edge */
timeLastEdge = timeNextEdge;
/* Setup for next edge */
timeNextClockDetection = timeNextEdge + (LOCAL_CLOCK_INCREMENT/2);
timeNextEdge += LOCAL_CLOCK_INCREMENT;
/* If we are in an external clock mode and this fire, then clock invalid */
#ifdef SPDIF_RX
// if(clkMode == CLOCK_SPDIF)
{
/* We must have lost valid S/PDIF stream, reset counters, so we dont produce a double edge */
spdifCounters.receivedSamples = 0;
}
#endif
#ifdef ADAT_RX
//if(clkMode == CLOCK_ADAT)
{
adatCounters.receivedSamples = 0;
}
#endif
#ifdef CLOCK_VALIDITY_CALL
if(clkMode == CLOCK_INTERNAL)
{
/* Internal clock always valid */
VendorClockValidity(1);
}
#endif
break;
#if defined(SPDIF_RX) || defined(ADAT_RX)
case t_external when timerafter(timeNextClockDetection) :> void:
timeNextClockDetection += (LOCAL_CLOCK_INCREMENT);
#ifdef SPDIF_RX
tmp = spdifCounters.samplesPerTick;
/* Returns 1 if valid clock found */
tmp = validSamples(spdifCounters, CLOCK_SPDIF_INDEX);
setClockValidity(c_clk_int, CLOCK_SPDIF_INDEX, tmp, clkMode);
#endif
#ifdef ADAT_RX
tmp = validSamples(adatCounters, CLOCK_ADAT_INDEX);
setClockValidity(c_clk_int, CLOCK_ADAT_INDEX, tmp, clkMode);
#endif
break;
#endif
#ifdef SPDIF_RX
/* Receive sample from S/PDIF RX thread (steaming chan) */
case c_spdif_rx :> tmp:
/* Record time of sample */
t_local :> spdifReceivedTime;
/* Check parity and ignore if bad */
if(badParity(tmp))
continue;
/* Get pre-amble */
tmp2 = tmp & 0xF;
switch(tmp2)
{
/* LEFT */
case FRAME_X:
case FRAME_Z:
spdifLeft = tmp << 4;
break;
/* RIGHT */
case FRAME_Y:
/* Only store sample if not in overflow and stream is reasonably valid */
if(!spdifOverflow && clockValid[CLOCK_SPDIF_INDEX])
{
/* Store left and right sample pair to buffer */
spdifSamples[spdifWr] = spdifLeft;
spdifSamples[spdifWr+1] = tmp << 4;
spdifWr = (spdifWr + 2) & (MAX_SPDIF_SAMPLES - 1);
spdifSamps += 2;
/* Check for over flow */
if(spdifSamps > MAX_SPDIF_SAMPLES-1)
{
spdifOverflow = 1;
}
/* Check for coming out of under flow */
if(spdifUnderflow && (spdifSamps >= (MAX_SPDIF_SAMPLES >> 1)))
{
spdifUnderflow = 0;
}
}
break;
default:
/* Bad sample, skip */
continue;
break;
}
spdifCounters.samples += 1;
if(clkMode == CLOCK_SPDIF && clockValid[CLOCK_SPDIF_INDEX])
{
spdifCounters.receivedSamples+=1;
/* Inspect for if we need to produce an edge */
if((spdifCounters.receivedSamples >= spdifCounters.samplesPerTick))
{
/* Check edge is about right... S/PDIF may have changed freq... */
if(timeafter(spdifReceivedTime, (timeLastEdge + LOCAL_CLOCK_INCREMENT - LOCAL_CLOCK_MARGIN)))
{
/* Record edge time */
timeLastEdge = spdifReceivedTime;
/* Setup for next edge */
timeNextEdge = spdifReceivedTime + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN;
/* Toggle edge */
p <: pinVal @ pinTime;
pinTime += (short) LOCAL_CLOCK_INCREMENT;
pinVal = !pinVal;
p @ pinTime <: pinVal;
/* Reset counters */
spdifCounters.receivedSamples = 0;
}
}
}
break;
#endif
#ifdef ADAT_RX
/* receive sample from ADAT rx thread (streaming channel with CT_END) */
case inuint_byref(c_adat_rx, tmp):
/* record time of sample */
t_local :> adatReceivedTime;
/* Sync is: 1 | (user_byte << 4) */
if(tmp&1)
{
/* user bits - start of frame */
adatChannel = 0;
continue;
}
else
{
/* audio sample */
adatSamplesEver++;
adatFrame[adatChannel] = tmp;
adatChannel++;
if (adatChannel == 8)
{
/* only store left samples if not in overflow and stream is reasonably valid */
if (!adatOverflow && clockValid[CLOCK_ADAT_INDEX])
{
if(smux)
{
adatSamples[adatWr + 0] = adatFrame[0];
adatSamples[adatWr + 4] = adatFrame[1];
adatSamples[adatWr + 1] = adatFrame[2];
adatSamples[adatWr + 5] = adatFrame[3];
adatSamples[adatWr + 2] = adatFrame[4];
adatSamples[adatWr + 6] = adatFrame[5];
adatSamples[adatWr + 3] = adatFrame[6];
adatSamples[adatWr + 7] = adatFrame[7];
}
else
{
adatSamples[adatWr + 0] = adatFrame[0];
adatSamples[adatWr + 1] = adatFrame[1];
adatSamples[adatWr + 2] = adatFrame[2];
adatSamples[adatWr + 3] = adatFrame[3];
adatSamples[adatWr + 4] = adatFrame[4];
adatSamples[adatWr + 5] = adatFrame[5];
adatSamples[adatWr + 6] = adatFrame[6];
adatSamples[adatWr + 7] = adatFrame[7];
}
adatWr = (adatWr + 8) & (MAX_ADAT_SAMPLES - 1);
adatSamps += 8;
/* check for overflow */
if (adatSamps > MAX_ADAT_SAMPLES - 1)
{
adatOverflow = 1;
}
/* check for coming out of underflow */
if (adatUnderflow && (adatSamps >= (MAX_ADAT_SAMPLES >> 1)))
{
adatUnderflow = 0;
}
}
}
if(adatChannel == 4 || adatChannel == 8)
{
adatCounters.samples += 1;
if (clkMode == CLOCK_ADAT && clockValid[CLOCK_ADAT_INDEX])
{
adatCounters.receivedSamples += 1;
/* Inspect for if we need to produce an edge */
if ((adatCounters.receivedSamples >= adatCounters.samplesPerTick))
{
/* Check edge is about right... S/PDIF may have changed freq... */
if (timeafter(adatReceivedTime, (timeLastEdge + LOCAL_CLOCK_INCREMENT - LOCAL_CLOCK_MARGIN)))
{
/* Record edge time */
timeLastEdge = adatReceivedTime;
/* Setup for next edge */
timeNextEdge = adatReceivedTime + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN;
/* Toggle edge */
p <: pinVal @ pinTime;
pinTime += LOCAL_CLOCK_INCREMENT;
pinVal = !pinVal;
p @ pinTime <: pinVal;
/* Reset counters */
adatCounters.receivedSamples = 0;
}
}
}
}
if (adatChannel == 8)
adatChannel = 0;
}
break;
#endif
#if defined(SPDIF_RX) || defined(ADAT_RX)
/* Mixer requests data */
case inuint_byref(c_dig_rx, tmp):
#ifdef SPDIF_RX
if(spdifUnderflow)
{
/* S/PDIF underflowing, send out zero samples */
g_digData[0] = 0;
g_digData[1] = 0;
}
else
{
/* Read out samples from S/PDIF buffer and send... */
tmp = spdifSamples[spdifRd];
tmp2 = spdifSamples[spdifRd + 1];
spdifRd += 2;
spdifRd &= (MAX_SPDIF_SAMPLES - 1);
g_digData[0] = tmp;
g_digData[1] = tmp2;
spdifSamps -= 2;
/* spdifSamps could go to -1 */
if(spdifSamps < 0)
{
/* We're out of S/PDIF samples, mark underflow condition */
spdifUnderflow = 1;
spdifLeft = 0;
}
/* If we are in over flow condition and we have a sensible number of samples
* come out of overflow condition */
if(spdifOverflow && (spdifSamps < (MAX_SPDIF_SAMPLES>>1)))
{
spdifOverflow = 0;
}
}
#endif
#ifdef ADAT_RX
if (adatUnderflow)
{
/* ADAT underflowing, send out zero samples */
g_digData[2] = 0;
g_digData[3] = 0;
g_digData[4] = 0;
g_digData[5] = 0;
g_digData[6] = 0;
g_digData[7] = 0;
g_digData[8] = 0;
g_digData[9] = 0;
}
else
{
/* TODO SMUX II mode */
/* read out samples from the ADAT buffer and send */
/* always return 8 samples */
if (smux)
{
/* SMUX mode - 4 samples from fifo and 4 zero samples */
g_digData[2] = adatSamples[adatRd + 0];
g_digData[3] = adatSamples[adatRd + 1];
g_digData[4] = adatSamples[adatRd + 2];
g_digData[5] = adatSamples[adatRd + 3];
g_digData[6] = 0;
g_digData[7] = 0;
g_digData[8] = 0;
g_digData[9] = 0;
adatRd = (adatRd + 4) & (MAX_ADAT_SAMPLES - 1);
adatSamps -= 4;
}
else
{
/* no SMUX mode - 8 samples from fifo */
g_digData[2] = adatSamples[adatRd + 0];
g_digData[3] = adatSamples[adatRd + 1];
g_digData[4] = adatSamples[adatRd + 2];
g_digData[5] = adatSamples[adatRd + 3];
g_digData[6] = adatSamples[adatRd + 4];
g_digData[7] = adatSamples[adatRd + 5];
g_digData[8] = adatSamples[adatRd + 6];
g_digData[9] = adatSamples[adatRd + 7];
adatRd = (adatRd + 8) & (MAX_ADAT_SAMPLES - 1);
adatSamps -= 8;
}
/* adatSamps could go to -1 */
if (adatSamps < 0)
{
/* we're out of ADAT samples, mark underflow condition */
adatUnderflow = 1;
}
/* if we are in overflow condition and have a sensible number of samples
come out of overflow condition */
if (adatOverflow && adatSamps < (MAX_ADAT_SAMPLES >> 1))
{
adatOverflow = 0;
}
}
#endif
outuint(c_dig_rx, 1);
break;
#endif
}
}
}

View File

@@ -0,0 +1,27 @@
#ifndef _CLOCKING_H_
#define _CLOCKING_H_
/* Functions that handle master clock generation. These need modifying for an existing design */
/* Any initialisation required for master clock generation - run once at start up */
void ClockingInit(chanend ?c);
/* Configuration for a specific master clock frequency - run every sample frequency change */
void ClockingConfig(unsigned mClkFreq, chanend ?c);
/** Clock generation and digital audio I/O handling.
*
* \param c_spdif_rx channel connected to S/PDIF receive thread
* \param c_adat_rx channel connect to ADAT receive thread
* \param p port to output clock signal to drive external frequency synthesizer
* \param c_audio channel connected to the audio() thread
* \param c_clk_ctl channel connected to Endpoint0() for configuration of the
* clock
* \param c_clk_int channel connected to the decouple() thread for clock
interrupts
*/
void clockGen (streaming chanend c_spdif_rx, chanend c_adat_rx, out port p, chanend c_audio, chanend c_clk_ctl, chanend c_clk_int);
#endif

View File

@@ -0,0 +1,14 @@
#ifndef _CODEC_H_
#define _CODEC_H_
/* These functions must be implemented for the CODEC/ADC/DAC arrangement of a specific design */
/* TODO Are the channel args required? */
/* Any required CODEC initialisation - run once at start up */
void CodecInit(chanend ?c_codec);
/* Configure condec for a specific mClk/Sample frquency - run on every sample frequency change */
void CodecConfig(unsigned samFreq, unsigned mClk, chanend ?c_codec);
#endif

144
module_usb_audio/dbtable.h Normal file
View File

@@ -0,0 +1,144 @@
/**
* @file dbtable.h
* @brief 128 entry + neg inf db table from -inf to 0xffffffff
* @author Ross Owen, XMOS Semiconductor
* @version 1.0
*/
#ifndef _DBTABLE_
#define _DBTABLE_
static unsigned dbTable[129] =
{
4294967295, /* 0 -> 0xffffffff */
3827893631,
3411613790,
3040603991,
2709941159,
2415237600,
2152582777,
1918491420,
1709857277,
1523911903,
1358187913,
1210486251,
1078847007,
961523407,
856958639,
763765191,
680706443,
606680256,
540704347,
481903257,
429496729,
382789363,
341161379,
304060399,
270994116,
241523760,
215258278,
191849142,
170985728,
152391190,
135818791,
121048625,
107884701,
96152341,
85695864,
76376519,
68070644,
60668026,
54070435,
48190326,
42949673,
38278936,
34116138,
30406040,
27099412,
24152376,
21525828,
19184914,
17098573,
15239119,
13581879,
12104863,
10788470,
9615234,
8569586,
7637652,
6807064,
6066803,
5407043,
4819033,
4294967,
3827894,
3411614,
3040604,
2709941,
2415238,
2152583,
1918491,
1709857,
1523912,
1358188,
1210486,
1078847,
961523,
856959,
763765,
680706,
606680,
540704,
481903,
429497,
382789,
341161,
304060,
270994,
241524,
215258,
191849,
170986,
152391,
135819,
121049,
107885,
96152,
85696,
76377,
68071,
60668,
54070,
48190,
42950,
38279,
34116,
30406,
27099,
24152,
21526,
19185,
17099,
15239,
13582,
12105,
10788,
9615,
8570,
7638,
6807,
6067,
5407,
4819,
4295,
3828,
3412,
3041,
2710,
2415,
2153,
1918,
0 // -0x8000
};
#endif

View File

@@ -0,0 +1,414 @@
/**
* @file internaldefines.h
* @brief Defines relating to device configuration and customisation.
* @author Ross Owen, XMOS Limited
*/
#ifndef _DEVICEDEFINES_H_
#define _DEVICEDEFINES_H_
#include "customdefines.h"
/* Tidy up historical INPUT/OUTPUT defines. INPUT/OUTPUT now enabled based on channel count defines */
#if !defined(NUM_USB_CHAN_IN)
#error NUM_USB_CHAN_IN must be defined!
#else
#if (NUM_USB_CHAN_IN == 0)
#undef INPUT
#else
#define INPUT 1
#endif
#endif
#if !defined(NUM_USB_CHAN_OUT)
#error NUM_USB_CHAN_OUT must be defined!
#else
#if (NUM_USB_CHAN_OUT == 0)
#undef OUTPUT
#else
#define OUTPUT 1
#endif
#endif
#if defined(IO_EXPANSION) && (IO_EXPANSION == 0)
#undef IO_EXPANSION
#endif
#if defined(IAP) && (IAP == 0)
#undef IAP
#endif
#if defined(HID_CONTROLS) && (HID_CONTROLS == 0)
#undef HID_CONTROLS
#endif
#if defined(MIDI) && (MIDI == 0)
#undef MIDI
#endif
#if defined(SPDIF) && (SPDIF == 0)
#undef SPDIF
#endif
#if defined(INPUT) && (INPUT == 0)
#undef INPUT
#endif
#if defined(OUTPUT) && (OUTPUT == 0)
#undef OUTPUT
#endif
#if defined(SPDIF_RX) && (SPDIF_RX == 0)
#undef SPDIF_RX
#endif
#if defined(ADAT_RX) && (ADAT_RX == 0)
#undef ADAT_RX
#endif
#if defined(DFU) && (DFU == 0)
#undef DFU
#endif
#if defined(CODEC_SLAVE) && (CODEC_SLAVE == 0)
#undef CODEC_SLAVE
#endif
#if defined(LEVEL_METER_LEDS) && !defined(LEVEL_UPDATE_RATE)
#define LEVEL_UPDATE_RATE 400000
#endif
#if(AUDIO_CLASS != 1) && (AUDIO_CLASS != 2)
#warning AUDIO_CLASS not defined, using 2
#define AUDIO_CLASS 2
#endif
/* Number of IS2 chans to DAC */
#ifndef I2S_CHANS_DAC
#error I2S_CHANS_DAC not defined
#else
#define I2S_WIRES_DAC (I2S_CHANS_DAC >> 1)
#endif
/* Number of I2S chans from ADC */
#ifndef I2S_CHANS_ADC
#error I2S_CHANS_ADC not defined
#else
#define I2S_WIRES_ADC (I2S_CHANS_ADC >> 1)
#endif
/* SPDIF and ADAT first input chan indices */
#ifdef SPDIF_RX
#ifndef SPDIF_RX_INDEX
#error SPDIF_RX_INDEX not defined and SPDIF_RX defined
#endif
#endif
#ifdef ADAT_RX
#ifndef ADAT_RX_INDEX
#error ADAT_RX_INDEX not defined and ADAT_RX defined
#endif
#endif
#ifndef SPDIF_TX_INDEX
#warning SPDIF_TX_INDEX not defined! Using 0
#define SPDIF_TX_INDEX (0)
#endif
/* Max supported freq for device */
#ifndef MAX_FREQ
#warning MAX_FREQ not defined! Using 48000
#define MAX_FREQ (48000)
#endif
/* Default device freq */
#ifndef DEFAULT_FREQ
#warning DEFAULT not defined! Using MAX_FREQ
#define DEFAULT_FREQ (MAX_FREQ)
#endif
/* Master clock defines (in Hz) */
#ifndef MCLK_441
#error MCLK_441 not defined
#endif
#ifndef MCLK_48
#error MCLK_441 not defined
#endif
/* The number of clock ticks to wait for the audio PLL to lock */
#define AUDIO_PLL_LOCK_DELAY (40000000)
/* Vendor/Product strings */
#ifndef VENDOR_STR
#define VENDOR_STR "XMOS "
//#warning VENDOR_STR not defined. Using XMOS
#endif
#ifndef VENDOR_ID
#warning VENDOR_ID not defined. Using XMOS vendor ID
#define VENDOR_ID (0x20B1) /* XMOS VID */
#endif
#ifndef PID_AUDIO_1
#define PID_AUDIO_1 (0x0001)
#warning PRODUCT_ID not defined. Using 0x0001
#endif
#ifndef PID_AUDIO_2
#define PID_AUDIO_2 (0x0001)
#warning PRODUCT_ID not defined. Using 0x0001
#endif
/* Device release number in BCD: 0xJJMNi */
#ifndef BCD_DEVICE
#define BCD_DEVICE (0x0000)
#warning BCD_DEVICE not defined. Using 0x0000
#endif
/* Addition interfaces based on defines */
#if defined(DFU) && DFU != 0
#define DFU_INTERFACES (1) /* DFU interface count */
#else
#define DFU_INTERFACES (0)
#endif
#ifdef INPUT
#define INPUT_INTERFACES (1)
#else
#define INPUT_INTERFACES (0)
#endif
#if defined(OUTPUT) && OUTPUT != 0
#define OUTPUT_INTERFACES (1)
#else
#define OUTPUT_INTERFACES (0)
#endif
#define NUM_EP_OUT_AUD (OUTPUT_INTERFACES)
#define NUM_EP_IN_AUD (OUTPUT_INTERFACES + INPUT_INTERFACES)
#if defined(MIDI)
#define MIDI_INTERFACES (2)
#define NUM_EP_OUT_MIDI (1)
#define NUM_EP_IN_MIDI (1)
#else
#define MIDI_INTERFACES (0)
#define NUM_EP_OUT_MIDI (0)
#define NUM_EP_IN_MIDI (0)
#endif
#if defined(IAP)
#define IAP_INTERFACES (1)
#else
#define IAP_INTERFACES (0)
#endif
#if defined(HID_CONTROLS)
#define HID_INTERFACES (1)
#else
#define HID_INTERFACES (0)
#endif
#define NUM_EP_OUT_IAP (IAP_INTERFACES)
#define NUM_EP_IN_IAP (IAP_INTERFACES * 2)
#define NUM_EP_OUT_HID (0)
#define NUM_EP_IN_HID (HID_INTERFACES)
/* Define for number of audio interfaces (+1 for mandatory control interface) */
#define AUDIO_INTERFACES (INPUT_INTERFACES + OUTPUT_INTERFACES + 1)
/* Interface number defines */
#define INTERFACE_NUM_IAP (INPUT_INTERFACES+OUTPUT_INTERFACES+MIDI_INTERFACES+DFU_INTERFACES+1)
#define INTERFACE_NUM_HID (INPUT_INTERFACES+OUTPUT_INTERFACES+MIDI_INTERFACES+DFU_INTERFACES+IAP_INTERFACES+1)
/* Endpoint Number Defines */
#define EP_NUM_IN_FB (1) /* Always 1 */
#define EP_NUM_IN_AUD (2) /* Always 2 */
#define EP_NUM_IN_AUD_INT (3) /* Audio interrupt/status EP */
#define EP_NUM_IN_MIDI ((EP_NUM_IN_AUD_INT + 1))
#define EP_NUM_IN_HID ((EP_NUM_IN_AUD_INT + NUM_EP_IN_MIDI + 1))
#define EP_NUM_IN_IAP ((EP_NUM_IN_AUD_INT + NUM_EP_IN_MIDI + NUM_EP_IN_HID + 1)) /* iAP Bulk */
#define EP_NUM_IN_IAP_INT ((EP_NUM_IN_AUD_INT + NUM_EP_IN_MIDI + NUM_EP_IN_HID + 2)) /* iAP interrupt */
#define EP_NUM_OUT_AUD 1 /* Always 1 */
#define EP_NUM_OUT_MIDI 2 /* Always 2 */
#define EP_NUM_OUT_IAP 3 /* Always 3 */
/* Endpoint Address Defines */
#define EP_ADR_IN_FB (EP_NUM_IN_FB | 0x80)
#define EP_ADR_IN_AUD (EP_NUM_IN_AUD | 0x80)
#define EP_ADR_IN_AUD_INT (EP_NUM_IN_AUD_INT | 0x80)
#define EP_ADR_IN_MIDI (EP_NUM_IN_MIDI | 0x80)
#define EP_ADR_IN_HID (EP_NUM_IN_HID | 0x80)
#define EP_ADR_IN_IAP (EP_NUM_IN_IAP | 0x80)
#define EP_ADR_IN_IAP_INT (EP_NUM_IAP_INT | 0x80)
#define EP_ADR_OUT_AUD EP_NUM_OUT_AUD
#define EP_ADR_OUT_MIDI EP_NUM_OUT_MIDI
#define EP_ADR_OUT_IAP EP_NUM_OUT_IAP
/* Endpoint count totals */
#define NUM_EP_OUT (1 + 1 /*NUM_EP_OUT_AUD*/ + NUM_EP_OUT_MIDI + NUM_EP_OUT_IAP) /* +1 due to EP0 */
#define NUM_EP_IN (2 + 2 /*NUM_EP_IN_AUD*/ + NUM_EP_IN_MIDI + NUM_EP_IN_IAP + NUM_EP_IN_HID) /* +1 due to EP0 and Int EP */
#define AUDIO_STOP_FOR_DFU (0x12345678)
#define AUDIO_START_FROM_DFU (0x87654321)
#define AUDIO_REBOOT_FROM_DFU (0xa5a5a5a5)
#define MAX_VOL (0x20000000)
/* Length of clock unit/clock-selector units */
#if defined(SPDIF_RX) && defined(ADAT_RX)
#define NUM_CLOCKS 3
#elif defined(SPDIF_RX) || defined(ADAT_RX)
#define NUM_CLOCKS 2
#else
#define NUM_CLOCKS 1
#endif
/* Total number of USB interfaces this device implements (+1 for required control interface) */
#define NUM_INTERFACES INPUT_INTERFACES + OUTPUT_INTERFACES + DFU_INTERFACES + MIDI_INTERFACES + IAP_INTERFACES + 1 + HID_INTERFACES
/* Number of interfaces for Audio 1.0 */
#define NUM_INTERFACES_A1 (1+INPUT_INTERFACES+OUTPUT_INTERFACES)
/* Audio Unit ID defines */
#define FU_USBIN 11 /* Feature Unit: USB Audio device -> host */
#define FU_USBOUT 10 /* Feature Unit: USB Audio host -> device*/
#define ID_IT_USB 2 /* Input terminal: USB streaming */
#define ID_IT_AUD 1 /* Input terminal: Analogue input */
#define ID_OT_USB 22 /* Output terminal: USB streaming */
#define ID_OT_AUD 20 /* Output terminal: Analogue output */
#define ID_CLKSEL 40 /* Clock selector ID */
#define ID_CLKSRC_INT 41 /* Clock source ID (internal) */
#define ID_CLKSRC_EXT 42 /* Clock source ID (external) */
#define ID_CLKSRC_ADAT 43 /* Clock source ID (external) */
#define ID_XU_MIXSEL 50
#define ID_XU_OUT 51
#define ID_XU_IN 52
#define ID_MIXER_1 60
#define MANUFACTURER_STR_INDEX 0x01
#define PRODUCT_STR_INDEX 0x02
/* Mixer defines */
#ifndef MIX_INPUTS
#define MIX_INPUTS 18
#endif
#ifndef MAX_MIX_COUNT
#define MAX_MIX_COUNT 8
#endif
/* Volume defines */
#ifndef MIN_VOLUME
/* The minimum volume setting above -inf. This is a signed 8.8 fixed point
number that must be strictly greater than -128 (0x8000) */
/* Default min volume is -127db */
#define MIN_VOLUME (0x8100)
#endif
#ifndef MAX_VOLUME
/* The maximum volume setting. This is a signed 8.8 fixed point number. */
/* Default max volume is 0db */
#define MAX_VOLUME (0x0000)
#endif
#ifndef VOLUME_RES
/* The resolution of the volume control in db as a 8.8 fixed point number */
/* Default volume resolution 1db */
#define VOLUME_RES (0x100)
#endif
#ifndef MIN_MIXER_VOLUME
/* The minimum volume setting for the mixer unit above -inf.
This is a signed 8.8 fixed point
number that must be strictly greater than -128 (0x8000) */
/* Default min volume is -127db */
#define MIN_MIXER_VOLUME (0x8100)
#endif
#ifndef MAX_MIXER_VOLUME
/* The maximum volume setting for the mixer.
This is a signed 8.8 fixed point number. */
/* Default max volume is 0db */
#define MAX_MIXER_VOLUME (0x0000)
#endif
#ifndef VOLUME_RES_MIXER
/* The resolution of the volume control in db as a 8.8 fixed point number */
/* Default volume resolution 1db */
#define VOLUME_RES_MIXER (0x100)
#endif
/* Handle out volume control in the mixer */
#if defined(OUT_VOLUME_IN_MIXER) && (OUT_VOLUME_IN_MIXER==0)
#undef OUT_VOLUME_IN_MIXER
#else
#if defined(MIXER)
// Enabled by default
#define OUT_VOLUME_IN_MIXER
#endif
#endif
/* Apply out volume controls after the mix */
#if defined(OUT_VOLUME_AFTER_MIX) && (OUT_VOLUME_AFTER_MIX==0)
#undef OUT_VOLUME_AFTER_MIX
#else
#if defined(MIXER) && defined(OUT_VOLUME_IN_MIXER)
// Enabled by default
#define OUT_VOLUME_AFTER_MIX
#endif
#endif
/* Define for reporting as self or bus-powered to the host */
#if defined(SELF_POWERED) && (SELF_POWERED==0)
#undef SELF_POWERED
#endif
/* Handle in volume control in the mixer */
#if defined(IN_VOLUME_IN_MIXER) && (IN_VOLUME_IN_MIXER==0)
#undef IN_VOLUME_IN_MIXER
#else
#if defined(MIXER)
/* Enabled by default */
#define IN_VOLUME_IN_MIXER
#endif
#endif
/* Apply in volume controls after the mix */
#if defined(IN_VOLUME_AFTER_MIX) && (IN_VOLUME_AFTER_MIX==0)
#undef IN_VOLUME_AFTER_MIX
#else
#if defined(MIXER) && defined(IN_VOLUME_IN_MIXER)
// Enabled by default
#define IN_VOLUME_AFTER_MIX
#endif
#endif
#if defined(AUDIO_CLASS_FALLBACK) && (AUDIO_CLASS_FALLBACK==0)
#undef AUDIO_CLASS_FALLBACK
#endif
/* Defines for DFU */
#ifndef PID_DFU
#define PID_DFU PID_AUDIO_2
#endif
#define DFU_VENDOR_ID VENDOR_ID
#define DFU_BCD_DEVICE BCD_DEVICE
#define DFU_MANUFACTURER_INDEX MANUFACTURER_STR_INDEX
#define DFU_PRODUCT_INDEX PRODUCT_STR_INDEX
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,20 @@
#ifndef __dbcalc_h__
#define __dbcalc_h__
/* Function: db_to_mult
This function converts decibels into a volume multiplier. It uses a fixed-point polynomial approximation
to 10^(db/10).
Parameters:
db - The db value to convert.
db_frac_bits - The number of binary fractional bits in the supplied decibel value
result_frac_bits - The number of required fractional bits in the result.
Returns:
The multiplier value as a fixed point value with the number of fractional bits as specified by
the result_frac_bits parameter.
*/
unsigned db_to_mult(int db, int db_frac_bits, int result_frac_bits);
#endif // __dbcalc_h__

View File

@@ -0,0 +1,127 @@
#include <xs1.h>
#include <print.h>
/* The coefficients of the chebychev polynomial to approximate 10^x in the interval [-1,1].
This polynomial was calculated using the mpmath library in Python:
from mpmath import *
mp.dps = 15
mp.pretty = True
poly, err = chebyfit(lambda x: pow(10,x), [-1,1], 14, error=True)
nprint([int(x * pow(2,28)) for x in poly])
*/
#define COEF_PREC 28
static unsigned coef[14] = {2407, 13778, 64588, 308051, 1346110, 5261991, 18277531, 55564576, 144789513, 314406484, 546179875, 711608713, 618095479, 268435456};
#define DB_CALC_PREC 28
/* Function: db_to_mult
This function converts decibels into a volume multiplier. It uses a fixed-point polynomial approximation
to 10^(db/10).
Parameters:
db - The db value to convert.
db_frac_bits - The number of binary fractional bits in the supplied decibel value
result_frac_bits - The number of required fractional bits in the result.
Returns:
The multiplier value as a fixed point value with the number of fractional bits as specified by
the result_frac_bits parameter.
*/
unsigned db_to_mult(int db, int db_frac_bits, int result_frac_bits)
{
int intpart;
int val = 0;
int val0=0;
unsigned ret;
unsigned mask = ~((1<<DB_CALC_PREC)-1);
/* Make sure we get 0db bang on */
if (db == 0)
return (1 << result_frac_bits);
/* First scale the decibal value to the required precision and divide by 10
We scale to DB_CALC_PREC - 4 before the division with to make sure we don't overflow */
db = db << (DB_CALC_PREC - 4 - 1 - db_frac_bits);
db = db / 10;
db = db << 4;
/* Extract the integer part of the exponent and calculate the integer power */
/* This could have been done a bit more efficiently by extracting the largest multiple log_10(2)
and then calculating a power of 2 (with the polynomial calc in the range [-log_10(2),log_10(2)].
But we have something that works here and ultra-fast performance is not a requirement */
if (db < 0) {
intpart = ((-db) & mask);
db = db + intpart;
intpart = intpart >> DB_CALC_PREC;
if (intpart) {
val0 = 1 << DB_CALC_PREC;
for (int i=0;i<intpart;i++)
val0 = val0/10;
}
}
else {
intpart = (db & mask);
db = db - intpart;
intpart = intpart >> DB_CALC_PREC;
if (intpart) {
val0 = 1 << DB_CALC_PREC;
for (int i=0;i<intpart;i++)
val0 = val0*10;
}
}
/* db is now just the fractional part in the interval [-1,1] so we can approximate using
the chebychev polynomial */
for (int i=0;i<14;i++)
{
int hi=0;
unsigned lo=0;
{hi, lo} = macs(db,val,hi,lo);
val = (hi << (32-DB_CALC_PREC)) | (lo >> DB_CALC_PREC);
val += coef[i] >> (COEF_PREC - DB_CALC_PREC);
}
/* Finally multiply by the integer power (if there was an integer part) */
if (val0) {
int hi=0;
unsigned lo=0;
{hi, lo} = macs(val0,val,hi,lo);
val = (hi << (32-DB_CALC_PREC)) | (lo >> DB_CALC_PREC);
}
/* We now have the result, just need to scale it to the required precision */
ret = val;
if (result_frac_bits > DB_CALC_PREC) {
return ret<<(result_frac_bits-DB_CALC_PREC);
}
else {
return ret>>(DB_CALC_PREC-result_frac_bits);
}
}
#ifdef TEST_DBCALC
#include <print.h>
int main() {
/* Check that we don't overflow up to 9db
Should give a value just under 0x80000 */
printhexln(db_to_mult(9,0,16));
/* This test recreates the old db lookup table */
printuintln(0xffffffff);
for (int i=1;i<128;i++) {
printuintln(db_to_mult(-i,0,32));
}
return 0;
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,58 @@
Descriptors in XMOS USB Audio
=============================
ADAT Alternate Settings
-----------------------
6 interfaces
endpoint can only be shared between alternate settings of the same interface (not between interfaces)
change alternate settings via SET_INTERFACE request
an alternate setting has its' own interface and endpoint descriptors
+-----------+------+-------+----------------+--------------------------------------------------+
| interface | type | alt. | endps. | note |
+-----------+------+-------+----------------+--------------------------------------------------+
| 0 | AC | irq_in | |
+-----------+------+-------+----------------+--------------------------------------------------+
| 1 | AS | 0 | | null interface |
| | +-------+----------------+--------------------------------------------------+
| | | 1 | iso_out iso_in | 18 channels, host -> device, iso_in is feedback |
| | +-------+----------------+--------------------------------------------------+
| | | 2 | shared | 18 channels |
+-----------+------+-------+----------------+--------------------------------------------------+
| 2 | AS | 0 | | null interface |
| | +-------+----------------+--------------------------------------------------+
| | | 1 | iso_in | 18 channels, device -> host |
| | +-------+----------------+--------------------------------------------------+
| | | 2 | shared | 14 channels |
+-----------+------+-------+----------------+--------------------------------------------------+
| 3 | AC | | MIDI |
+-----------+------+-------+----------------+--------------------------------------------------+
| 4 | MS | bul_out bul_in | |
+-----------+------+-------+----------------+--------------------------------------------------+
| 5 | DFU | | |
+-----------+------+-------+----------------+--------------------------------------------------+
Interface 1: host -> device
Interface 2: device -> host
Structure of defines::
alt 0
alt 1
18 ch
#ifdef ADAT
alt 2
14 ch
#endif
Inconsistencies
---------------
- is MIDI AC interface is required? spec says 1 AC interface per function
- interface association descriptor specifies 3 interfaces, but there are 6

View File

@@ -0,0 +1,25 @@
#ifndef _ENDPOINT0_H_
#define _ENDPOINT0_H_
/** Function implementing Endpoint 0 for enumeration, control and configuration
* of USB audio devices. It uses the descriptors defined in ``descriptors_2.h``.
*
* \param c_ep0_out Chanend connected to the XUD_Manager() out endpoint array
* \param c_ep0_in Chanend connected to the XUD_Manager() in endpoint array
* \param c_audioCtrl Chanend connected to the decouple thread for control
* audio (sample rate changes etc.)
* \param c_mix_ctl Optional chanend to be connected to the mixer thread if
* present
* \param c_clk_ctl Optional chanend to be connected to the clockgen thread if
* present.
* \param c_usb_test Optional chanend to be connected to XUD if test modes required.
*/
void Endpoint0( chanend c_ep0_out, chanend c_ep0_in, chanend c_audioCtrl,
chanend ?c_mix_ctl,chanend ?c_clk_ctl, chanend ?c_usb_test
#ifdef EP0_THREAD_COMBINED_WITH_SPI
, chanend c_spi, chanend c_spi_ss
#endif
);
#endif

View File

@@ -0,0 +1,902 @@
/**
* @file endpoint0.xc
* @brief Implements endpoint zero for an USB Audio 1.0/2.0 device
* @author Ross Owen, XMOS Semiconductor
*/
#include <xs1.h>
#include <print.h>
#include <safestring.h>
#include "xud.h" /* XUD user defines and functions */
#include "usb.h" /* Defines from USB 2.0 Spec */
#include "usbaudio20.h" /* Defines from USB Audio 2.0 spec */
#include "devicedefines.h"
#include "DescriptorRequests.h" /* Standard descriptor requests */
#include "descriptors_2.h" /* This devices descriptors */
#include "clockcmds.h"
#include "audiostream.h"
#include "vendorrequests.h"
#include "dfu_types.h"
#include "xc_ptr.h"
#ifdef HID_CONTROLS
#include "hid.h"
#endif
/* Some warnings.... */
/* Windows does not have a built in DFU driver (windows will prompt), so warn that DFU will not be functional in Audio 1.0 mode */
#if ((AUDIO_CLASS==1) || defined(AUDIO_CLASS_FALLBACK)) && defined(DFU)
#warning DFU will not be enabled in AUDIO 1.0 mode due to Windows requesting driver
#endif
/* MIDI not supported in Audio 1.0 mode */
#if ((AUDIO_CLASS==1) || defined(AUDIO_CLASS_FALLBACK)) && defined(MIDI)
#warning MIDI is currently not supported and will not be enabled in AUDIO 1.0 mode
#endif
/* If PID_DFU not defined, standard PID used.. this is probably what we want.. */
#ifndef PID_DFU
#warning PID_DFU not defined, Using PID_AUDIO_2. This is probably fine!
#endif
#ifdef DFU
#include "dfu.h"
#define DFU_IF_NUM INPUT_INTERFACES + OUTPUT_INTERFACES + MIDI_INTERFACES + 1
unsigned int DFU_mode_active = 0; // 0 - App active, 1 - DFU active
extern void device_reboot(chanend);
#endif
/* Handles Audio Class requests */
int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, SetupPacket &sp, chanend c_audioControl, chanend ?c_mix_ctl, chanend ?c_clk_ctl);
int AudioClassRequests_1(XUD_ep ep0_out, XUD_ep ep0_in, SetupPacket &sp, chanend c_audioControl, chanend ?c_mix_ctl, chanend ?c_clk_ctl);
/* Global var for current frequency, set to default freq */
unsigned int g_curSamFreq = DEFAULT_FREQ;
unsigned int g_curSamFreq48000Family = DEFAULT_FREQ % 48000 == 0;
unsigned int g_curSamFreqMultiplier = DEFAULT_FREQ / 48000;
/* Global volume and mute tables */
int volsOut[NUM_USB_CHAN_OUT + 1];
unsigned int mutesOut[NUM_USB_CHAN_OUT + 1];
//unsigned int multOut[NUM_USB_CHAN_OUT + 1];
int volsIn[NUM_USB_CHAN_IN + 1];
unsigned int mutesIn[NUM_USB_CHAN_IN + 1];
//unsigned int multIn[NUM_USB_CHAN_IN + 1];
#ifdef MIXER
unsigned char mixer1Crossbar[18];
short mixer1Weights[18*8];
//#define MAX_MIX_COUNT 8
unsigned char channelMap[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT];
#if NUM_USB_CHAN_OUT > 0
unsigned char channelMapAud[NUM_USB_CHAN_OUT];
#endif
#if NUM_USB_CHAN_IN > 0
unsigned char channelMapUsb[NUM_USB_CHAN_IN];
#endif
unsigned char mixSel[MIX_INPUTS];
#endif
int min(int x, int y);
/* Records alt setting for each interface */
int interfaceAlt[NUM_INTERFACES] = {0, 0, 0, 0};
/* Global current device config var*/
unsigned g_config = 0;
/* Global endpoint status arrays */
unsigned g_epStatusOut[NUM_EP_OUT];
unsigned g_epStatusIn[NUM_EP_IN];
/* Global variable for current USB bus speed (i.e. FS/HS) */
unsigned g_curUsbSpeed = 0;
#ifdef HOST_ACTIVE_CALL
void VendorHostActive(int active);
#endif
/* Global used for signalling reset to decouple */
#ifdef IAP
extern unsigned g_iap_reset;
#endif
/* Used when setting/clearing EP halt */
void SetEndpointStatus(unsigned epNum, unsigned status)
{
/* Inspect for IN bit */
if( epNum & 0x80 )
{
epNum &= 0x7f;
/* Range check */
if(epNum < NUM_EP_IN)
{
g_epStatusIn[ epNum & 0x7F ] = status;
}
}
else
{
if(epNum < NUM_EP_OUT)
{
g_epStatusOut[ epNum ] = status;
}
}
}
#define STR_USENG 0x0409
#define DESC_STR_LANGIDS \
{ \
STR_USENG & 0xff, /* 2 wLangID[0] */ \
STR_USENG>>8, /* 3 wLangID[0] */ \
'\0' \
}
/* String descriptors */
static unsigned char strDesc_langIDs[] = DESC_STR_LANGIDS;
void VendorAudioRequestsInit(chanend c_audioControl, chanend ?c_mix_ctl, chanend ?c_clk_ctl);
/* Endpoint 0 function. Handles all requests to the device */
void Endpoint0( chanend c_ep0_out, chanend c_ep0_in, chanend c_audioControl,
chanend ?c_mix_ctl, chanend ?c_clk_ctl, chanend ?c_usb_test)
{
unsigned char buffer[2];
SetupPacket sp;
XUD_ep ep0_out = XUD_Init_Ep(c_ep0_out);
XUD_ep ep0_in = XUD_Init_Ep(c_ep0_in);
/* Init endpoint status tables */
for (int i = 0; i++; i < NUM_EP_OUT)
g_epStatusOut[i] = 0;
for (int i = 0; i++; i < NUM_EP_IN)
g_epStatusIn[i] = 0;
/* Init tables for volumes (+ 1 for master) */
for(int i = 0; i < NUM_USB_CHAN_OUT + 1; i++)
{
volsOut[i] = 0;
mutesOut[i] = 0;
}
for(int i = 0; i < NUM_USB_CHAN_IN + 1; i++)
{
volsIn[i] = 0;
mutesIn[i] = 0;
}
#ifdef MIXER
/* Set up mixer default state */
for (int i = 0; i < 18*8; i++) {
mixer1Weights[i] = 0x8001; //-inf
}
/* Configure default connections */
mixer1Weights[0] = 0;
mixer1Weights[9] = 0;
mixer1Weights[18] = 0;
mixer1Weights[27] = 0;
mixer1Weights[36] = 0;
mixer1Weights[45] = 0;
mixer1Weights[54] = 0;
mixer1Weights[63] = 0;
#if NUM_USB_CHAN_OUT > 0
/* Setup up audio output channel mapping */
for(int i = 0; i < NUM_USB_CHAN_OUT; i++)
{
channelMapAud[i] = i;
}
#endif
#if NUM_USB_CHAN_IN > 0
for(int i = 0; i < NUM_USB_CHAN_IN; i++)
{
channelMapUsb[i] = i + NUM_USB_CHAN_OUT;
}
#endif
/* Set up channel mapping default */
for (int i = 0; i < NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN; i++)
{
channelMap[i] = i;
}
#if MAX_MIX_COUNT > 0
/* Mixer outputs mapping defaults */
for (int i = 0; i < MAX_MIX_COUNT; i++)
{
channelMap[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + i] = i;
}
#endif
/* Init mixer inputs */
for(int i = 0; i < MIX_INPUTS; i++)
{
mixSel[i] = i;
}
#endif
/* Copy langIDs string desc into string[0] */
/* TODO: Macro? */
#if defined(AUDIO_CLASS_FALLBACK) || (AUDIO_CLASS == 1)
safememcpy(strDescs_Audio1[0], strDesc_langIDs, sizeof(strDesc_langIDs));
#endif
safememcpy(strDescs_Audio2[0], strDesc_langIDs, sizeof(strDesc_langIDs));
/* Build up channel string table - By default all channels are marked as analogue
* TODO We really want to do this an build time... */
#if defined(SPDIF_RX) && (SPDIF_RX_INDEX != 0)
safestrcpy(strDescs_Audio2[SPDIF_RX_INDEX + STR_INDEX_IN_CHAN], "S/PDIF 1");
safestrcpy(strDescs_Audio2[SPDIF_RX_INDEX + STR_INDEX_IN_CHAN + 1], "S/PDIF 2");
#endif
#if defined(ADAT_RX) && (ADAT_RX_INDEX != 0)
safestrcpy(strDescs_Audio2[ADAT_RX_INDEX + STR_INDEX_IN_CHAN], "ADAT 1");
safestrcpy(strDescs_Audio2[ADAT_RX_INDEX + STR_INDEX_IN_CHAN + 1], "ADAT 2");
safestrcpy(strDescs_Audio2[ADAT_RX_INDEX + STR_INDEX_IN_CHAN + 2], "ADAT 3");
safestrcpy(strDescs_Audio2[ADAT_RX_INDEX + STR_INDEX_IN_CHAN + 3], "ADAT 4");
safestrcpy(strDescs_Audio2[ADAT_RX_INDEX + STR_INDEX_IN_CHAN + 4], "ADAT 5");
safestrcpy(strDescs_Audio2[ADAT_RX_INDEX + STR_INDEX_IN_CHAN + 5], "ADAT 6");
safestrcpy(strDescs_Audio2[ADAT_RX_INDEX + STR_INDEX_IN_CHAN + 6], "ADAT 7");
safestrcpy(strDescs_Audio2[ADAT_RX_INDEX + STR_INDEX_IN_CHAN + 7], "ADAT 8");
#endif
#if defined(SPDIF) && (SPDIF_TX_INDEX != 0) /* "Analogue naming gets priority */
safestrcpy(strDescs_Audio2[SPDIF_TX_INDEX + STR_INDEX_OUT_CHAN], "S/PDIF 1");
safestrcpy(strDescs_Audio2[SPDIF_TX_INDEX + STR_INDEX_OUT_CHAN + 1], "S/PDIF 2");
#endif
#if defined(ADAT_TX) && (ADAT_TX_INDEX != 0)
safestrcpy(strDescs_Audio2[ADAT_TX_INDEX + STR_INDEX_OUT_CHAN], "ADAT 1");
safestrcpy(strDescs_Audio2[ADAT_TX_INDEX + STR_INDEX_OUT_CHAN + 1], "ADAT 2");
safestrcpy(strDescs_Audio2[ADAT_TX_INDEX + STR_INDEX_OUT_CHAN + 2], "ADAT 3");
safestrcpy(strDescs_Audio2[ADAT_TX_INDEX + STR_INDEX_OUT_CHAN + 3], "ADAT 4");
safestrcpy(strDescs_Audio2[ADAT_TX_INDEX + STR_INDEX_OUT_CHAN + 4], "ADAT 5");
safestrcpy(strDescs_Audio2[ADAT_TX_INDEX + STR_INDEX_OUT_CHAN + 5], "ADAT 6");
safestrcpy(strDescs_Audio2[ADAT_TX_INDEX + STR_INDEX_OUT_CHAN + 6], "ADAT 7");
safestrcpy(strDescs_Audio2[ADAT_TX_INDEX + STR_INDEX_OUT_CHAN + 7], "ADAT 8");
#endif
#ifdef VENDOR_AUDIO_REQS
VendorAudioRequestsInit(c_audioControl, c_mix_ctl, c_clk_ctl);
#endif
#if 0
{
char rdata[1];
char wdata[1];
//wdata[0] = 77;
//write_glx_periph_reg(GLXID, XS1_GLX_PERIPH_SCTH_ID, 0x0, 0, 1, wdata);
read_glx_periph_reg(GLXID, XS1_GLX_PERIPH_SCTH_ID, 0x1, 0, 1, rdata);
if(rdata[0] != 0)
{
while(1);
}
// printintln(rdata[0]);
#endif
#ifdef DFU
/* Check if device has started in DFU mode */
if (DFUReportResetState(null))
{
/* Stop audio */
outuint(c_audioControl, SET_SAMPLE_FREQ);
outuint(c_audioControl, AUDIO_STOP_FOR_DFU);
// No Handshake
//chkct(c_audioControl, XS1_CT_END);
DFU_mode_active = 1;
}
#endif
while(1)
{
int retVal = 1;
/* Do standard enumeration requests */
#ifndef DFU
if(g_curUsbSpeed == XUD_SPEED_HS)
{
#ifdef AUDIO_CLASS_FALLBACK
/* Return Audio 2.0 Descriptors with Audio 1.0 as fallback */
cfgDesc_Audio2[1] = CONFIGURATION;
cfgDesc_Audio1[1] = OTHER_SPEED_CONFIGURATION;
retVal = DescriptorRequests(ep0_out, ep0_in,
devDesc_Audio2, sizeof(devDesc_Audio2),
cfgDesc_Audio2, sizeof(cfgDesc_Audio2),
devQualDesc_Audio1, sizeof(devQualDesc_Audio1),
cfgDesc_Audio1, sizeof(cfgDesc_Audio1),
strDescs_Audio2, sp, c_usb_test);
#else
/* Return Audio 2.0 Descriptors */
cfgDesc_Audio2[1] = CONFIGURATION;
cfgDesc_Null[1] = OTHER_SPEED_CONFIGURATION;
retVal = DescriptorRequests(ep0_out, ep0_in,
devDesc_Audio2, sizeof(devDesc_Audio2),
cfgDesc_Audio2, sizeof(cfgDesc_Audio2),
devQualDesc_Null, sizeof(devQualDesc_Null),
cfgDesc_Null, sizeof(cfgDesc_Null),
strDescs_Audio2, sp, c_usb_test);
#endif
}
else
{
/* Return descriptors for full-speed - Audio 1.0? */
#ifdef AUDIO_CLASS_FALLBACK
cfgDesc_Audio1[1] = CONFIGURATION;
cfgDesc_Audio2[1] = OTHER_SPEED_CONFIGURATION;
retVal = DescriptorRequests(ep0_out, ep0_in,
devDesc_Audio1, sizeof(devDesc_Audio1),
cfgDesc_Audio1, sizeof(cfgDesc_Audio1),
devQualDesc_Audio2, sizeof(devQualDesc_Audio2),
cfgDesc_Audio2, sizeof(cfgDesc_Audio2),
strDescs_Audio1, sp, c_usb_test);
#else
cfgDesc_Null[1] = CONFIGURATION;
cfgDesc_Audio2[1] = OTHER_SPEED_CONFIGURATION;
retVal = DescriptorRequests(ep0_out, ep0_in,
devDesc_Null, sizeof(devDesc_Null),
cfgDesc_Null, sizeof(cfgDesc_Null),
devQualDesc_Audio2, sizeof(devQualDesc_Audio2),
cfgDesc_Audio2, sizeof(cfgDesc_Audio2),
strDescs_Audio2, sp, c_usb_test);
#endif
}
#else /* ifndef DFU */
if (!DFU_mode_active)
{
if(g_curUsbSpeed == XUD_SPEED_HS)
{
#ifdef AUDIO_CLASS_FALLBACK
/* Return Audio 2.0 Descriptors with Audio 1.0 as fallback */
cfgDesc_Audio2[1] = CONFIGURATION;
cfgDesc_Audio1[1] = OTHER_SPEED_CONFIGURATION;
retVal = DescriptorRequests(ep0_out, ep0_in,
devDesc_Audio2, sizeof(devDesc_Audio2),
cfgDesc_Audio2, sizeof(cfgDesc_Audio2),
devQualDesc_Audio1, sizeof(devQualDesc_Audio1),
cfgDesc_Audio1, sizeof(cfgDesc_Audio1),
strDescs_Audio2, sp, c_usb_test);
#else
/* Return Audio 2.0 Descriptors with Null device as fallback */
cfgDesc_Audio2[1] = CONFIGURATION;
cfgDesc_Null[1] = OTHER_SPEED_CONFIGURATION;
retVal = DescriptorRequests(ep0_out, ep0_in,
devDesc_Audio2, sizeof(devDesc_Audio2),
cfgDesc_Audio2, sizeof(cfgDesc_Audio2),
devQualDesc_Null, sizeof(devQualDesc_Null),
cfgDesc_Null, sizeof(cfgDesc_Null),
strDescs_Audio2, sp, c_usb_test);
#endif
}
else
{
/* Return descriptors for full-speed - Audio 1.0? */
#ifdef AUDIO_CLASS_FALLBACK
cfgDesc_Audio1[1] = CONFIGURATION;
cfgDesc_Audio2[1] = OTHER_SPEED_CONFIGURATION;
retVal = DescriptorRequests(ep0_out, ep0_in,
devDesc_Audio1, sizeof(devDesc_Audio1),
cfgDesc_Audio1, sizeof(cfgDesc_Audio1),
devQualDesc_Audio2, sizeof(devQualDesc_Audio2),
cfgDesc_Audio2, sizeof(cfgDesc_Audio2),
strDescs_Audio1, sp, c_usb_test);
#else
cfgDesc_Null[1] = CONFIGURATION;
cfgDesc_Audio2[1] = OTHER_SPEED_CONFIGURATION;
retVal = DescriptorRequests(ep0_out, ep0_in,
devDesc_Null, sizeof(devDesc_Null),
cfgDesc_Null, sizeof(cfgDesc_Null),
devQualDesc_Audio2, sizeof(devQualDesc_Audio2),
cfgDesc_Audio2, sizeof(cfgDesc_Audio2),
strDescs_Audio2, sp, c_usb_test);
#endif
}
}
else
{
/* Running in DFU mode - always return same descs for DFU whether HS or FS */
retVal = DescriptorRequests(ep0_out, ep0_in,
DFUdevDesc, sizeof(DFUdevDesc),
DFUcfgDesc, sizeof(DFUcfgDesc),
DFUdevQualDesc, sizeof(DFUdevQualDesc),
DFUoSpeedCfgDesc, sizeof(DFUoSpeedCfgDesc),
strDescs_Audio2, sp, c_usb_test);
}
#endif
if (retVal == 1)
{
/* Request not covered by XUD_DoEnumReqs() so decode ourselves */
/* Inspect Request type and Receipient */
switch( (sp.bmRequestType.Recipient ) | (sp.bmRequestType.Type << 5) )
{
case STANDARD_INTERFACE_REQUEST:
switch(sp.bRequest)
{
/* Set Interface */
case SET_INTERFACE:
#if defined(OUTPUT) && defined(INPUT)
/* Check for stream start stop on output and input audio interfaces */
if(sp.wValue && !interfaceAlt[1] && !interfaceAlt[2])
{
/* If start and input AND output not currently running */
AudioStreamStart();
}
else if(((sp.wIndex == 1)&& (!sp.wValue)) && interfaceAlt[1] && (!interfaceAlt[2]))
{
/* if output stop and output running and input not running */
AudioStreamStop();
}
else if(((sp.wIndex == 2) && (!sp.wValue)) && interfaceAlt[2] && (!interfaceAlt[1]))
{
/* if input stop and input running and output not running */
AudioStreamStop();
}
#elif defined(OUTPUT) || defined(INPUT)
if(sp.wValue && (!interfaceAlt[1]))
{
/* if start and not currently running */
AudioStreamStart();
}
else if (!sp.wValue && interfaceAlt[1])
{
/* if stop and currently running */
AudioStreamStop();
}
#endif
/* Record interface change */
if( sp.wIndex < NUM_INTERFACES )
interfaceAlt[sp.wIndex] = sp.wValue;
#if 1
/* Check for audio stream from host start/stop */
if(sp.wIndex == 2) // Input interface
{
switch(sp.wValue)
{
case 0:
break;
case 1:
/* Stream active + 0 chans */
outuint(c_audioControl, SET_CHAN_COUNT_IN);
outuint(c_audioControl, NUM_USB_CHAN_IN);
#ifdef ADAT_RX
outuint(c_clk_ctl, SET_SMUX);
outuint(c_clk_ctl, 0);
outct(c_clk_ctl, XS1_CT_END);
#endif
break;
#ifdef ADAT_RX
case 2:
/* Stream active + 8 chans */
outuint(c_audioControl, SET_CHAN_COUNT_IN);
outuint(c_audioControl, NUM_USB_CHAN_IN-4);
outuint(c_clk_ctl, SET_SMUX);
outuint(c_clk_ctl, 1);
outct(c_clk_ctl, XS1_CT_END);
break;
case 3:
outuint(c_audioControl, SET_CHAN_COUNT_IN);
outuint(c_audioControl, NUM_USB_CHAN_IN-6);
outuint(c_clk_ctl, SET_SMUX);
outuint(c_clk_ctl, 1);
outct(c_clk_ctl, XS1_CT_END);
/* Stream active + 8 chans */
//outuint(c_audioControl, 8);
// Handshake
//chkct(c_audioControl, XS1_CT_END);
break;
#endif
}
}
#endif
/* No data stage for this request, just do data stage */
retVal = XUD_DoSetRequestStatus(ep0_in, 0);
break;
/* A device must support the GetInterface request if it has alternate setting for that interface */
case GET_INTERFACE:
buffer[0] = 0;
/* Bounds check */
if( sp.wIndex < NUM_INTERFACES )
buffer[0] = interfaceAlt[sp.wIndex];
retVal = XUD_DoGetRequest(ep0_out, ep0_in, buffer, 1, sp.wLength);
break;
#ifdef HID_CONTROLS
case GET_DESCRIPTOR:
if(sp.wIndex == INTERFACE_NUM_HID)
{
switch (sp.wValue>>8)
{
case REPORT:
/* Return HID report descriptor */
retVal = XUD_DoGetRequest(ep0_out, ep0_in, hidReportDescriptor,
min(sizeof(hidReportDescriptor),sp.wLength), sp.wLength);
break;
}
}
break;
#endif
default:
//printstr("Unknown Standard Interface Request: ");
//printhexln(sp.bRequest);
//printhexln(sp.bmRequestType.Type);
//printhexln(sp.bmRequestType.Recipient);
//printhexln(sp.bmRequestType.Recipient | (sp.bmRequestType.Type << 5));
break;
}
break;
/* Recipient: Device */
case STANDARD_DEVICE_REQUEST:
/* Standard Device requests (8) */
switch( sp.bRequest )
{
/* Set Device Address: This is a unique set request. */
case SET_ADDRESS:
/* Status stage: Send a zero length packet */
retVal = XUD_SetBuffer(ep0_in, buffer, 0);
/* TODO We should wait until ACK is received for status stage before changing address */
//XUD_Sup_Delay(50000);
{
timer t;
unsigned time;
t :> time;
t when timerafter(time+50000) :> void;
}
/* Set device address in XUD */
XUD_SetDevAddr(sp.wValue);
break;
/* TODO Check direction */
/* Standard request: SetConfiguration */
case SET_CONFIGURATION:
g_config = sp.wValue;
#ifdef HOST_ACTIVE_CALL
if(g_config == 1)
{
/* Consider host active with valid driver at this point */
VendorHostActive(1);
}
#endif
#ifdef IAP
{
int iap_reset = 1;
SET_SHARED_GLOBAL(g_iap_reset, iap_reset);
}
#endif
/* No data stage for this request, just do status stage */
retVal = XUD_DoSetRequestStatus(ep0_in, 0);
break;
case GET_CONFIGURATION:
buffer[0] = g_config;
retVal = XUD_DoGetRequest(ep0_out, ep0_in, buffer, 1, sp.wLength);
break;
/* Get Status request */
case GET_STATUS:
#ifdef SELF_POWERED
buffer[0] = 1; // self powered
#else
buffer[0] = 0; // bus powered
#endif
buffer[1] = 0; // remote wakeup not supported
retVal = XUD_DoGetRequest(ep0_out, ep0_in, buffer, 2, sp.wLength);
break;
default:
XUD_Error("Unknown device request");
break;
}
break;
/* Receipient: Endpoint */
case STANDARD_ENDPOINT_REQUEST:
/* Standard endpoint requests */
switch ( sp.bRequest )
{
/* ClearFeature */
case CLEAR_FEATURE:
switch ( sp.wValue )
{
case ENDPOINT_HALT:
/* Mark the endpoint status */
SetEndpointStatus(sp.wIndex, 0);
/* No data stage for this request, just do status stage */
retVal = XUD_DoSetRequestStatus(ep0_in, 0);
break;
default:
XUD_Error( "Unknown request in Endpoint ClearFeature" );
break;
}
break; /* B_REQ_CLRFEAR */
/* SetFeature */
case SET_FEATURE:
switch( sp.wValue )
{
case ENDPOINT_HALT:
/* Check request is in range */
SetEndpointStatus(sp.wIndex, 1);
break;
default:
XUD_Error("Unknown feature in SetFeature Request");
break;
}
retVal = XUD_DoSetRequestStatus(ep0_in, 0);
break;
/* Endpoint GetStatus Request */
case GET_STATUS:
buffer[0] = 0;
buffer[1] = 0;
if( sp.wIndex & 0x80 )
{
/* IN Endpoint */
if((sp.wIndex&0x7f) < NUM_EP_IN)
{
buffer[0] = ( g_epStatusIn[ sp.wIndex & 0x7F ] & 0xff );
buffer[1] = ( g_epStatusIn[ sp.wIndex & 0x7F ] >> 8 );
}
}
else
{
/* OUT Endpoint */
if(sp.wIndex < NUM_EP_OUT)
{
buffer[0] = ( g_epStatusOut[ sp.wIndex ] & 0xff );
buffer[1] = ( g_epStatusOut[ sp.wIndex ] >> 8 );
}
}
retVal = XUD_DoGetRequest(ep0_out, ep0_in, buffer, 2, sp.wLength);
break;
default:
//printstrln("Unknown Standard Endpoint Request");
break;
}
break;
case CLASS_INTERFACE_REQUEST:
case CLASS_ENDPOINT_REQUEST:
{
unsigned interfaceNum = sp.wIndex & 0xff;
unsigned request = (sp.bmRequestType.Recipient ) | (sp.bmRequestType.Type << 5);
/* TODO Check interface number */
/* TODO Check on return value retval = */
#ifdef DFU
unsigned DFU_IF = DFU_IF_NUM;
/* DFU interface number changes based on which mode we are currently running in */
if (DFU_mode_active)
{
DFU_IF = 0;
}
if (interfaceNum == DFU_IF)
{
/* If running in application mode stop audio */
/* Don't interupt audio for save and restore cmds */
if ((DFU_IF == DFU_IF_NUM) && (sp.bRequest != XMOS_DFU_SAVESTATE) && (sp.bRequest != XMOS_DFU_RESTORESTATE))
{
// Stop audio
outuint(c_audioControl, SET_SAMPLE_FREQ);
outuint(c_audioControl, AUDIO_STOP_FOR_DFU);
// Handshake
chkct(c_audioControl, XS1_CT_END);
}
/* This will return 1 if reset requested */
if (DFUDeviceRequests(ep0_out, ep0_in, sp, null, interfaceAlt[sp.wIndex], 1))
{
timer tmr;
unsigned s;
/* Send reboot command */
//outuint(c_audioControl, SET_SAMPLE_FREQ);
//outuint(c_audioControl, AUDIO_REBOOT_FROM_DFU);
tmr :> s;
tmr when timerafter(s + 50000000) :> s;
device_reboot(c_audioControl);
}
/* TODO we should not make the assumption that all DFU requests are handled */
retVal = 0;
}
/* Check for: - Audio CONTROL interface request - always 0, note we check for DFU first
* - Audio STREAMING interface request
* - Audio endpoint request
*/
else if(((request == CLASS_INTERFACE_REQUEST) && (interfaceNum == 0))
|| ((request == CLASS_INTERFACE_REQUEST) && (interfaceNum == 1 || interfaceNum == 2))
|| (request == CLASS_ENDPOINT_REQUEST && ((interfaceNum == 0x82) || (interfaceNum == 0x01))))
{
#endif
#if (AUDIO_CLASS == 2) && defined(AUDIO_CLASS_FALLBACK)
if(g_curUsbSpeed == XUD_SPEED_HS)
{
retVal = AudioClassRequests_2(ep0_out, ep0_in, sp, c_audioControl, c_mix_ctl, c_clk_ctl);
}
else
{
retVal = AudioClassRequests_1(ep0_out, ep0_in, sp, c_audioControl, c_mix_ctl, c_clk_ctl);
}
#elif (AUDIO_CLASS==2)
retVal = AudioClassRequests_2(ep0_out, ep0_in, sp, c_audioControl, c_mix_ctl, c_clk_ctl);
#else
retVal = AudioClassRequests_1(ep0_out, ep0_in, sp, c_audioControl, c_mix_ctl, c_clk_ctl);
#endif
#ifdef VENDOR_AUDIO_REQS
/* If retVal is 1 at this point, then request to audio interface not handled - handle vendor audio reqs */
if(retVal == 1)
{
retVal = VendorAudioRequests(ep0_out, ep0_in, sp.bRequest,
sp.wValue >> 8, sp.wValue & 0xff,
sp.wIndex >> 8, sp.bmRequestType.Direction,
c_audioControl, c_mix_ctl, c_clk_ctl);
}
#endif
#ifdef DFU
}
#endif
}
break;
default:
//printstr("unrecognised request\n");
//printhexln(sp.bRequest);
//printhexln(sp.bmRequestType.Type);
//printhexln(sp.bmRequestType.Recipient);
//printhexln(sp.bmRequestType.Recipient | (sp.bmRequestType.Type << 5));
break;
}
} /* if(retVal == 0) */
if(retVal == 1)
{
/* Did not handle request - Protocol Stall Secion 8.4.5 of USB 2.0 spec
* Detailed in Section 8.5.3. Protocol stall is unique to control pipes.
Protocol stall differs from functional stall in meaning and duration.
A protocol STALL is returned during the Data or Status stage of a control
transfer, and the STALL condition terminates at the beginning of the
next control transfer (Setup). The remainder of this section refers to
the general case of a functional stall */
XUD_SetStall_Out(0);
XUD_SetStall_In(0);
}
if (retVal < 0)
{
g_curUsbSpeed = XUD_ResetEndpoint(ep0_out, ep0_in);
//printintln(g_curUsbSpeed);
g_config = 0;
#ifdef DFU
if (DFUReportResetState(null))
{
if (!DFU_mode_active)
{
timer tmr;
unsigned s;
DFU_mode_active = 1;
//tmr :> s;
//tmr when timerafter(s + 500000) :> s;
}
}
else
{
if (DFU_mode_active)
{
timer tmr;
unsigned s;
// Restart audio
//outuint(c_audioControl, AUDIO_START_FROM_DFU);
DFU_mode_active = 0;
// Send reboot command
//outuint(c_audioControl, SET_SAMPLE_FREQ);
//outuint(c_audioControl, AUDIO_REBOOT_FROM_DFU);
// No handshake on reboot
tmr :> s;
tmr when timerafter(s + 5000000) :> s;
device_reboot(c_audioControl);
}
}
#endif
}
}
}

View File

@@ -0,0 +1,16 @@
#ifndef _VENDORREQUESTS_H_
#define _VENDORREQUESTS_H_
/* Functions that handle vustomer vendor requests.
*
* THESE NEED IMPLEMENTING FOR A SPECIFIC DESIGN
*
* Should return 0 if handled sucessfully, else return 0 (-1 for passing up reset/suspend)
*
* */
int VendorAudioRequests(XUD_ep ep0_out, XUD_ep ep0_in, unsigned char bRequest, unsigned char cs, unsigned char cn,
unsigned short unitId, unsigned char direction, chanend c_audioControl, chanend ?c_mix_ctl, chanend ?c_clk_ctL);
#endif

View File

@@ -0,0 +1,55 @@
/** lock.S
* @brief Functions for using hardware locks
* @author Ross Owen, XMOS Limited
*/
#include <xs1_user.h>
/////////////////////////////////////////////////////////////////////////
// void GetLockResource()
.extern GetLockResource
.globl GetLockResource.nstackwords
.linkset GetLockResource.nstackwords, 0
.globl GetLockResource
.text
GetLockResource:
getr r0, XS1_RES_TYPE_LOCK
retsp 0
/////////////////////////////////////////////////////////////////////////
// void ClaimLock(lock l)
.extern ClaimLock
.globl ClaimLock.nstackwords
.linkset ClaimLock.nstackwords, 0
.globl ClaimLock
.text
ClaimLock:
in r0, res[r0]
retsp 0
/////////////////////////////////////////////////////////////////////////
// void FreeLock(lock l)
.extern FreeLock
.globl FreeLock.nstackwords
.linkset FreeLock.nstackwords, 0
.globl FreeLock
.text
FreeLock:
out res[r0], r0
retsp 0
/////////////////////////////////////////////////////////////////////////
// void FreeLockResource(lock l)
.extern FreeLockResource
.globl FreeLockResource.nstackwords
.linkset FreeLockResource.nstackwords, 0
.globl FreeLockResource
.text
FreeLockResource:
freer res[r0]
retsp 0

View File

@@ -0,0 +1,25 @@
/** @file lock.h
* @brief Functions for using hardware locks
* @author Ross Owen, XMOS Limited
*/
#ifndef _LOCK_H_
#define _LOCK_H_ 1
typedef unsigned lock;
/* Allocates and returns a lock resource - returns 0 if out of lock */
lock GetLockResource() ;
/* Claims the passed lock, this is a blocking call */
void ClaimLock(lock l);
/* Frees the passed lock */
void FreeLock(lock l);
/* De-allocated the passed lock resource */
void FreeLockResource(lock l);
#endif

271
module_usb_audio/main.xc Executable file
View File

@@ -0,0 +1,271 @@
/**
* @file main.xc
* @brief Top level for XMOS USB 2.0 Audio 2.0 Reference Designs.
* @author Ross Owen, XMOS Semiconductor Ltd
*/
#include <syscall.h>
#include <platform.h>
#include <xs1.h>
#include <xclib.h>
#include <print.h>
#include <xscope.h>
#include "xud.h" /* XMOS USB Device Layer defines and functions */
#include "usb.h" /* Defines from the USB 2.0 Specification */
#include "devicedefines.h" /* Device specific defines */
#include "endpoint0.h"
#include "usb_buffer.h"
#include "decouple.h"
#include "usb_midi.h"
#include "audio.h"
#include "ports.h" /* Portmap defines and ports for current app instance */
/* Audio I/O */
#if I2S_WIRES_DAC > 0
on stdcore[0] : buffered out port:32 p_i2s_dac[I2S_WIRES_DAC] =
{PORT_I2S_DAC0,
#endif
#if I2S_WIRES_DAC > 1
PORT_I2S_DAC1,
#endif
#if I2S_WIRES_DAC > 2
PORT_I2S_DAC2,
#endif
#if I2S_WIRES_DAC > 3
PORT_I2S_DAC3,
#endif
#if I2S_WIRES_DAC > 4
PORT_I2S_DAC4,
#endif
#if I2S_WIRES_DAC > 5
PORT_I2S_DAC5,
#endif
#if I2S_WIRES_DAC > 6
PORT_I2S_DAC6,
#endif
#if I2S_WIRES_DAC > 7
#error Not supported
#endif
#if I2S_WIRES_DAC > 0
};
#endif
#if I2S_WIRES_ADC > 0
on stdcore[0] : buffered in port:32 p_i2s_adc[I2S_WIRES_ADC] =
{PORT_I2S_ADC0,
#endif
#if I2S_WIRES_ADC > 1
PORT_I2S_ADC1,
#endif
#if I2S_WIRES_ADC > 2
PORT_I2S_ADC2,
#endif
#if I2S_WIRES_ADC > 3
PORT_I2S_ADC3,
#endif
#if I2S_WIRES_ADC > 4
PORT_I2S_ADC4,
#endif
#if I2S_WIRES_ADC > 5
PORT_I2S_ADC5,
#endif
#if I2S_WIRES_ADC > 6
PORT_I2S_ADC6,
#endif
#if I2S_WIRES_ADC > 7
#error Not supported
#endif
#if I2S_WIRES_ADC > 0
};
#endif
on stdcore[0] : buffered out port:32 p_lrclk = PORT_I2S_LRCLK;
on stdcore[0] : buffered out port:32 p_bclk = PORT_I2S_BCLK;
on stdcore[0] : port p_mclk = PORT_MCLK_IN;
on stdcore[0] : in port p_for_mclk_count = PORT_MCLK_COUNT;
#ifdef SPDIF
on stdcore[0] : buffered out port:32 p_spdif_tx = PORT_SPDIF_OUT;
#endif
#ifdef MIDI
on stdcore[0] : port p_midi_tx = PORT_MIDI_OUT;
on stdcore[0] : port p_midi_rx = PORT_MIDI_IN;
#endif
/* Clock blocks */
#ifdef MIDI
on stdcore[0] : clock clk_midi = XS1_CLKBLK_REF;
#endif
on stdcore[0] : clock clk_audio_mclk = XS1_CLKBLK_2; /* Master clock */
on stdcore[0] : clock clk_audio_bclk = XS1_CLKBLK_3; /* Bit clock */
#ifdef SPDIF
on stdcore[0] : clock clk_mst_spd = XS1_CLKBLK_1;
#endif
/* L Series needs a port to use for USB reset */
#ifdef ARCH_L
#ifdef PORT_USB_RESET
on stdcore[0] : out port p_usb_rst = PORT_USB_RESET;
#else
#error Need a USB Reset port for L series
#endif
/* L Series also needs a clock for this port */
clock clk = XS1_CLKBLK_4;
#else
/* Reset port not required for SU1 due to built in Phy */
#define p_usb_rst null
#define clk null
#endif
/* Endpoint type tables for XUD */
XUD_EpType epTypeTableOut[NUM_EP_OUT] = { XUD_EPTYPE_CTL | XUD_STATUS_ENABLE,
XUD_EPTYPE_ISO, /* Audio */
#ifdef MIDI
XUD_EPTYPE_BUL /* MIDI */
#endif
};
XUD_EpType epTypeTableIn[NUM_EP_IN] = { XUD_EPTYPE_CTL | XUD_STATUS_ENABLE,
XUD_EPTYPE_ISO,
XUD_EPTYPE_ISO,
XUD_EPTYPE_BUL,
#ifdef MIDI
XUD_EPTYPE_BUL,
#endif
#ifdef HID_CONTROLS
XUD_EPTYPE_BUL,
#endif
};
#define FAST_MODE 0
void thread_speed()
{
#if (FAST_MODE)
set_thread_fast_mode_on();
#else
set_thread_fast_mode_off();
#endif
}
#ifdef XSCOPE
void xscope_user_init()
{
xscope_register(0, 0, "", 0, "");
xscope_config_io(XSCOPE_IO_BASIC);
}
#endif
int main()
{
chan c_sof;
chan c_xud_out[NUM_EP_OUT]; /* Endpoint channels for XUD */
chan c_xud_in[NUM_EP_IN];
chan c_aud_ctl;
chan c_mix_out;
#ifdef MIDI
chan c_midi;
#endif
#ifdef TEST_MODE_SUPPORT
#warning Building with test mode support
chan c_usb_test;
#else
#define c_usb_test null
#endif
#ifdef SU1_ADC
chan c_adc;
#else
#define c_adc null
#endif
//SPI_SS Mux - disable access to SPI_SS
//p_gpio <: 1;
par
{
/* USB Interface */
#if (AUDIO_CLASS==2)
on stdcore[0]: XUD_Manager(c_xud_out, NUM_EP_OUT, c_xud_in, NUM_EP_IN,
c_sof, epTypeTableOut, epTypeTableIn, p_usb_rst,
clk, 1, XUD_SPEED_HS, c_usb_test);
#else
on stdcore[0]:XUD_Manager(c_xud_out, NUM_EP_OUT, c_xud_in, NUM_EP_IN,
c_sof, epTypeTableOut, epTypeTableIn, p_usb_rst,
clk, 1, XUD_SPEED_FS, c_usb_test);
#endif
/* Endpoint 0 */
on stdcore[0]:{
thread_speed();
Endpoint0( c_xud_out[0], c_xud_in[0], c_aud_ctl, null,null, c_usb_test);
}
on stdcore[0]:
{
thread_speed();
/* Attach mclk count port to mclk clock-block (for feedback) */
//set_port_clock(p_for_mclk_count, clk_audio_mclk);
{
unsigned x;
asm("ldw %0, dp[clk_audio_mclk]":"=r"(x));
asm("setclk res[%0], %1"::"r"(p_for_mclk_count), "r"(x));
}
buffer(c_xud_out[EP_NUM_OUT_AUD],/* Audio Out*/
c_xud_in[EP_NUM_IN_AUD], /* Audio In */
c_xud_in[EP_NUM_IN_FB], /* Audio FB */
#ifdef MIDI
c_xud_out[EP_NUM_OUT_MIDI], /* MIDI Out */ // 2
c_xud_in[EP_NUM_IN_MIDI], /* MIDI In */ // 4
c_midi,
#endif
c_xud_in[EP_NUM_IN_AUD_INT], /* Int */
c_sof, c_aud_ctl, p_for_mclk_count
#ifdef HID_CONTROLS
,c_xud_in[EP_NUM_IN_HID]
#endif
);
}
on stdcore[0]:
{
thread_speed();
decouple(c_mix_out, null);
}
on stdcore[0]:
{
thread_speed();
/* Audio I/O (pars additional S/PDIF TX thread) */
audio(c_mix_out, null, null, c_adc);
}
//on stdcore[0]: test(c_adc2);
#ifdef MIDI
on stdcore[0]:
{
thread_speed();
usb_midi(p_midi_rx, p_midi_tx, clk_midi, c_midi, 0, null, null, null, null);
}
#endif
#ifdef SU1_ADC
xs1_su_adc_service(c_adc);
#endif
}
return 0;
}

View File

@@ -0,0 +1,167 @@
//#include "devicedefines.h"
#define MAX_MIX_COUNT 8
#define MIX_INPUTS 18
#define DOMIX_TOP(i) \
.cc_top doMix##i.function,doMix##i; \
.align 4 ;\
.globl doMix##i ;\
.globl doMix##i##.nstackwords ;\
.globl doMix##i##.maxthreads ; \
.globl doMix##i##.maxtimers ; \
.globl doMix##i##.maxchanends ; \
.globl doMix##i##.maxsync ;\
.linkset doMix##i##.locnoside, 1; \
.linkset doMix##i##.locnochandec, 1;\
.linkset doMix##i##.nstackwords, 0 ;\
.linkset doMix##i##.maxchanends, 0 ;\
.linkset doMix##i##.maxtimers, 0 ;\
.linkset doMix##i##.maxthreads, 1; \
doMix##i##: ;\
set cp, r0; \
set dp, r1; \
lsub r0, r1, r0, r0, r0;\
.label_##i##:
#define DOMIX_BOT(i) \
ldap r11, _dp; \
set dp, r11;\
ldap r11, _cp;\
set cp, r11;\
\
mov r0, r1;\
ldc r2, 0x19;\
sext r0, r2;\
eq r0, r0, r1;\
bf r0, .L20; \
\
shl r0, r1, 0x7;\
retsp 0x0;\
\
\
.cc_bottom doMix##i##.function;
#define N MIX_INPUTS
#define BODY(i) \
ldw r2,cp[i]; \
ldw r11, dp[i]; \
maccs r1, r0, r2, r11;
.text
.L20:\
lss r0, r1, r3;\
bt r0, .L16; \
ldw r0, cp[.LC0];\
retsp 0x0; \
.L16:\
ldw r0, cp[.LC1];\
retsp 0x0; \
#if(MAX_MIX_COUNT > 0)
DOMIX_TOP(0)
#include "repeat.h"
DOMIX_BOT(0)
#endif
#if(MAX_MIX_COUNT > 1)
DOMIX_TOP(1)
#include "repeat.h"
DOMIX_BOT(1)
#endif
#if(MAX_MIX_COUNT > 2)
DOMIX_TOP(2)
#include "repeat.h"
DOMIX_BOT(2)
#endif
#if(MAX_MIX_COUNT > 3)
DOMIX_TOP(3)
#include "repeat.h"
DOMIX_BOT(3)
#endif
#if(MAX_MIX_COUNT > 4)
DOMIX_TOP(4)
#include "repeat.h"
DOMIX_BOT(4)
#endif
#if(MAX_MIX_COUNT > 5)
DOMIX_TOP(5)
#include "repeat.h"
DOMIX_BOT(5)
#endif
#if(MAX_MIX_COUNT > 6)
DOMIX_TOP(6)
#include "repeat.h"
DOMIX_BOT(6)
#endif
#if(MAX_MIX_COUNT > 7)
DOMIX_TOP(7)
#include "repeat.h"
DOMIX_BOT(7)
#endif
#if(MAX_MIX_COUNT>8)
#error MAX_MIX_COUNT>7
#endif
/* We need MIX_OUTPUT x setPtr functions */
#undef N
#undef BODY
#define N MAX_MIX_COUNT
.cc_top setPtr.function,setPtr;
.align 4 ;
.globl setPtr.nstackwords;
.globl setPtr;
.globl setPtr.maxthreads;
.globl setPtr.maxtimers;
.globl setPtr.maxchanends;
.globl setPtr.maxsync;
.linkset setPtr.locnoside, 1;
.linkset setPtr.locnochandec, 1;
.linkset setPtr.nstackwords, 0;
.linkset setPtr.maxchanends, 0;
.linkset setPtr.maxtimers, 0;
.linkset setPtr.maxthreads, 1;
setPtr:
shl r2, r2, 1
.xtabranch .label_0
bru r2
#define BODY(i) \
ldap r11, .label_##i; \
bu setPtr_go
#include "repeat.h"
setPtr_go:
shl r0, r0, 3;
ldc r2, 0x80;
add r1, r1, r2;
st8 r1, r11[r0];
retsp 0;
.cc_bottom setPtr.function
.section .cp.const4, "acM", @progbits, 4
.LC0:
.align 4
.int 0x7fffff00
.LC1:
.int 0x80000000
#undef N
#undef BODY

View File

@@ -0,0 +1,32 @@
#ifndef __mixer_h__
#define __mixer_h__
enum mix_ctl_cmd {
SET_SAMPLES_TO_HOST_MAP,
SET_SAMPLES_TO_DEVICE_MAP,
SET_MIX_MULT,
SET_MIX_MAP,
SET_MIX_IN_VOL,
SET_MIX_OUT_VOL,
GET_INPUT_LEVELS,
GET_STREAM_LEVELS,
GET_OUTPUT_LEVELS
};
/** Digital sample mixer.
*
* This thread mixes audio streams between the decouple() thread and
* the audio() thread.
*
* \param c_to_host a chanend connected to the decouple() thread for
* receiving/transmitting samples
* \param c_to_audio a chanend connected to the audio() thread for
* receiving/transmitting samples
* \param c_mix_ctl a chanend connected to the Endpoint0() thread for
* receiving control commands
*
*/
void mixer(chanend c_to_host, chanend c_to_audio, chanend c_mix_ctl);
#endif

View File

@@ -0,0 +1,704 @@
#include <xs1.h>
#include <print.h>
#include "mixer.h"
#include "devicedefines.h"
#include "xc_ptr.h"
#ifdef MIXER
#define FAST_MIXER 1
#warning USING FAST MIXER
#ifdef OUT_VOLUME_IN_MIXER
static unsigned int multOut_array[NUM_USB_CHAN_OUT + 1];
static xc_ptr multOut;
#endif
#ifdef IN_VOLUME_IN_MIXER
unsigned int multIn_array[NUM_USB_CHAN_IN + 1];
static xc_ptr multIn;
#endif
#if defined (LEVEL_METER_LEDS) || defined (LEVEL_METER_HOST)
static unsigned abs(int x)
{
#if 0
if (x < 0)
return x*-1;
return x;
#else
int const mask = x >> sizeof(int) * 8 - 1;
return (x + mask) ^ mask;
#endif
}
#endif
int samples_array[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT + 1]; /* One larger for an "off" channel for mixer sources" */
xc_ptr samples;
int savedsamples2[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT];
int samples_to_host_map_array[NUM_USB_CHAN_IN];
xc_ptr samples_to_host_map;
int samples_to_device_map_array[NUM_USB_CHAN_OUT];
xc_ptr samples_to_device_map;
#if MAX_MIX_COUNT > 0
int mix_mult_array[MAX_MIX_COUNT][MIX_INPUTS];
xc_ptr mix_mult;
#define write_word_to_mix_mult(x,y,val) write_via_xc_ptr_indexed(mix_mult,((x)*MIX_INPUTS)+(y), val)
#define mix_mult_slice(x) (mix_mult + x * MIX_INPUTS * sizeof(int))
#ifndef FAST_MIXER
int mix_map_array[MAX_MIX_COUNT][MIX_INPUTS];
xc_ptr mix_map;
#define write_word_to_mix_map(x,y,val) write_via_xc_ptr_indexed(mix_map,((x)*MIX_INPUTS)+(y), val)
#define mix_map_slice(x) (mix_map + x * MIX_INPUTS * sizeof(int))
#endif
#endif
/* Arrays for level data */
int samples_to_host_inputs[NUM_USB_CHAN_IN]; /* Audio transmitted to host i.e. dev inputs */
#ifdef LEVEL_METER_LEDS
int samples_to_host_inputs_buff[NUM_USB_CHAN_IN]; /* Audio transmitted to host i.e. dev inputs */
#endif
static int samples_to_host_streams[NUM_USB_CHAN_OUT]; /* Audio stream to host from host */
static int samples_to_host_outputs[NUM_USB_CHAN_OUT]; /* Device outputs */
#if 0
#pragma xta command "add exclusion mixer1_rate_change"
#pragma xta command "analyse path mixer1_req mixer1_req"
#pragma xta command "set required - 10400 ns" /* 96kHz */
#endif
#if 0
#pragma xta command "add exclusion mixer2_rate_change"
#pragma xta command "analyse path mixer2_req mixer2_req"
#pragma xta command "set required - 10400 ns" /* 96kHz */
#endif
#if defined (LEVEL_METER_LEDS) || defined (LEVEL_METER_HOST)
static inline void ComputeMixerLevel(int sample, int i)
{
int x;
int y;
xc_ptr ptr;
x = abs(sample);
/* y = samples_to_host_outputs[i] */
asm("ldaw %0, dp[samples_to_host_outputs]":"=r"(ptr):); /* Might want to hoist this */
asm("ldw %0, %1[%2]":"=r"(y):"r"(ptr),"r"(i));
if(x > y)
{
/* samples_to_host_outputs[i] = x; */
write_via_xc_ptr_indexed(ptr,i,y);
//asm("stw %0, %1[%2]"::"r"(y),"r"(ptr),"r"(i));
}
}
#endif
#ifdef FAST_MIXER
void setPtr(int src, int dst, int mix);
int doMix0(xc_ptr samples, xc_ptr mult);
int doMix1(xc_ptr samples, xc_ptr mult);
int doMix2(xc_ptr samples, xc_ptr mult);
int doMix3(xc_ptr samples, xc_ptr mult);
int doMix4(xc_ptr samples, xc_ptr mult);
int doMix5(xc_ptr samples, xc_ptr mult);
int doMix6(xc_ptr samples, xc_ptr mult);
int doMix7(xc_ptr samples, xc_ptr mult);
int doMix8(xc_ptr samples, xc_ptr mult);
#else
/* DO NOT inline, causes 10.4.2 tools to add extra loads in loop */
/* At 18 x 12dB we could get 64 x bigger */
#pragma unsafe arrays
int doMix(xc_ptr samples, xc_ptr ptr, xc_ptr mult)
{
int h=0;
int l=0;
/* By breaking up the loop we keep things in the encoding for ldw (0-11) */
#pragma loop unroll
for (int i=0; i<MIX_INPUTS; i++)
{
int sample;
int index;
int m;
read_via_xc_ptr_indexed(index, ptr, i);
read_via_xc_ptr_indexed(sample,samples,index);
read_via_xc_ptr_indexed(m, mult, i);
{h,l} = macs(sample, m, h, l);
}
#if 1
/* Perform saturation */
l = sext(h, 25);
if(l != h)
{
//if(h < 0)
if(h>>32)
h = (0x80000000>>7);
else
h = (0x7fffff00>>7);
}
#endif
return h<<7;
}
#endif
#pragma unsafe arrays
void giveSamplesToHost(chanend c, xc_ptr samples, xc_ptr ptr, xc_ptr multIn)
{
#if defined(IN_VOLUME_IN_MIXER) && defined(IN_VOLUME_AFTER_MIX)
int mult;
int h;
unsigned l;
#endif
#pragma loop unroll
for (int i=0;i<NUM_USB_CHAN_IN;i++)
{
int sample;
int index;
read_via_xc_ptr_indexed(index,ptr,i);
read_via_xc_ptr_indexed(sample,samples,index);
#if defined(IN_VOLUME_IN_MIXER) && defined(IN_VOLUME_AFTER_MIX)
#warning IN Vols in mixer, AFTER mix & map
//asm("ldw %0, %1[%2]":"=r"(mult):"r"(multIn),"r"(i));
read_via_xc_ptr_indexed(mult, multIn, i);
{h, l} = macs(mult, sample, 0, 0);
//h <<= 3 done on other side */
outuint(c, h);
#else
outuint(c,sample);
#endif
}
}
#pragma unsafe arrays
static void getSamplesFromHost(chanend c, xc_ptr samples, int base)
{
#pragma loop unroll
for (int i=0;i<NUM_USB_CHAN_OUT;i++)
{
int sample, x;
#if defined(OUT_VOLUME_IN_MIXER) && !defined(OUT_VOLUME_AFTER_MIX)
int mult;
int h;
unsigned l;
#endif
/* Receive sample from decouple */
sample = inuint(c);
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
/* Compute peak level data */
x = abs(sample);
if(x > samples_to_host_streams[i])
samples_to_host_streams[i] = x;
#endif
#if defined(OUT_VOLUME_IN_MIXER) && !defined(OUT_VOLUME_AFTER_MIX)
#warning OUT Vols in mixer, BEFORE mix & map
read_via_xc_ptr_indexed(mult, multOut, i);
{h, l} = macs(mult, sample, 0, 0);
h<<=3;
write_via_xc_ptr_indexed(multOut, index, val);
write_via_xc_ptr_indexed(samples,base+i,h);
#else
write_via_xc_ptr_indexed(samples,base+i,sample);
#endif
}
}
#pragma unsafe arrays
void giveSamplesToDevice(chanend c, xc_ptr samples, xc_ptr ptr, xc_ptr multOut)
{
#pragma loop unroll
for (int i=0;i<NUM_USB_CHAN_OUT;i++)
{
int sample,x;
#if defined(OUT_VOLUME_IN_MIXER) && defined(OUT_VOLUME_AFTER_MIX)
int mult;
int h;
unsigned l;
#endif
int index;
read_via_xc_ptr_indexed(index, ptr, i);
read_via_xc_ptr_indexed(sample, samples, index)
#if defined(OUT_VOLUME_IN_MIXER) && defined(OUT_VOLUME_AFTER_MIX)
#warning OUT Vols in mixer, AFTER mix & map
read_via_xc_ptr_indexed(mult, multOut, i);
{h, l} = macs(mult, sample, 0, 0);
//h <<= 3 done in audio thread
outuint(c, h);
#else
outuint(c, sample);
#endif
}
}
#pragma unsafe arrays
void getSamplesFromDevice(chanend c, xc_ptr samples, int base)
{
#if defined(IN_VOLUME_IN_MIXER) && !defined(IN_VOLUME_AFTER_MIX)
int mult;
int h;
unsigned l;
#endif
#pragma loop unroll
for (int i=0;i<NUM_USB_CHAN_IN;i++)
{
int sample;
int x;
sample = inuint(c);
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
/* Compute peak level data */
x = abs(sample);
if(x > samples_to_host_inputs[i])
samples_to_host_inputs[i] = x;
#endif
#if defined(IN_VOLUME_IN_MIXER) && !defined(IN_VOLUME_AFTER_MIX)
read_via_xc_ptr_indexed(mult, multIn, i);
{h, l} = macs(mult, sample, 0, 0);
h <<=3;
write_via_xc_ptr_indexed(samples,base+i,h);
#else
write_via_xc_ptr_indexed(samples,base+i,sample);
#endif
}
}
int mixer1_mix2_flag = (DEFAULT_FREQ > 96000);
#pragma unsafe arrays
void mixer1(chanend c_host, chanend c_mix_ctl, chanend c_mixer2)
{
int mixed;
unsigned cmd;
while (1)
{
#pragma xta endpoint "mixer1_req"
inuint(c_mixer2);
/* Request data from decouple thread */
outuint(c_host, 0);
/* Between request to decouple and respose ~ 400nS latency for interrupt to fire */
select
{
case inuint_byref(c_mix_ctl, cmd):
{
int mix, index, val;
switch (cmd)
{
#if MAX_MIX_COUNT > 0
case SET_SAMPLES_TO_HOST_MAP:
index = inuint(c_mix_ctl);
val = inuint(c_mix_ctl);
inct(c_mix_ctl);
write_via_xc_ptr_indexed(samples_to_host_map,
index,
val);
break;
case SET_SAMPLES_TO_DEVICE_MAP:
index = inuint(c_mix_ctl);
val = inuint(c_mix_ctl);
inct(c_mix_ctl);
write_via_xc_ptr_indexed(samples_to_device_map,index,val);
break;
case SET_MIX_MULT:
mix = inuint(c_mix_ctl);
index = inuint(c_mix_ctl);
val = inuint(c_mix_ctl);
inct(c_mix_ctl);
write_word_to_mix_mult(mix, index, val);
break;
case SET_MIX_MAP:
mix = inuint(c_mix_ctl);
index = inuint(c_mix_ctl); /* mixer input */
val = inuint(c_mix_ctl); /* source */
inct(c_mix_ctl);
#ifdef FAST_MIXER
setPtr(index, val, mix);
#else
write_word_to_mix_map(mix, index, val);
#endif
break;
#endif /* if MAX_MIX_COUNT > 0 */
#ifdef IN_VOLUME_IN_MIXER
case SET_MIX_IN_VOL:
index = inuint(c_mix_ctl);
val = inuint(c_mix_ctl);
inct(c_mix_ctl);
write_via_xc_ptr_indexed(multIn, index, val);
break;
#endif
#ifdef OUT_VOLUME_IN_MIXER
case SET_MIX_OUT_VOL:
index = inuint(c_mix_ctl);
val = inuint(c_mix_ctl);
inct(c_mix_ctl);
write_via_xc_ptr_indexed(multOut, index, val);
break;
#endif
case GET_STREAM_LEVELS:
index = inuint(c_mix_ctl);
chkct(c_mix_ctl, XS1_CT_END);
outuint(c_mix_ctl, samples_to_host_streams[index]);
outct(c_mix_ctl, XS1_CT_END);
samples_to_host_streams[index] = 0;
break;
case GET_INPUT_LEVELS:
index = inuint(c_mix_ctl);
chkct(c_mix_ctl, XS1_CT_END);
#ifdef LEVEL_METER_LEDS
/* Level LEDS process reseting samples_to_host_inputs
* Other side makes sure we don't miss a peak */
read_via_xc_ptr_indexed(val, samples_to_host_inputs_buff, index);
write_via_xc_ptr_indexed(samples_to_host_inputs_buff, index, 0);
#else
/* We dont have a level LEDs process, so reset ourselves */
read_via_xc_ptr_indexed(val, samples_to_host_inputs, index);
write_via_xc_ptr_indexed(samples_to_host_inputs, index, 0);
#endif
outuint(c_mix_ctl, val);
outct(c_mix_ctl, XS1_CT_END);
break;
#if MAX_MIX_COUNT > 0
case GET_OUTPUT_LEVELS:
index = inuint(c_mix_ctl);
chkct(c_mix_ctl, XS1_CT_END);
read_via_xc_ptr_indexed(val, samples_to_host_outputs, index);
write_via_xc_ptr_indexed(samples_to_host_outputs, index, mix);
outuint(c_mix_ctl, val);
outct(c_mix_ctl, XS1_CT_END);
break;
#endif
}
break;
}
default:
/* Select default */
break;
}
/* Get response from decouple */
if(testct(c_host))
{
int sampFreq;
#pragma xta endpoint "mixer1_rate_change"
inct(c_host);
sampFreq = inuint(c_host);
mixer1_mix2_flag = sampFreq > 96000;
#pragma loop unroll
for (int i=0;i<MAX_MIX_COUNT;i++)
{
write_via_xc_ptr_indexed(samples,
(NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + i),
0);
}
/* Inform mixer 2 about freq change */
outct(c_mixer2, XS1_CT_END);
outuint(c_mixer2, sampFreq);
/* Wait for handshake and pass on */
chkct(c_mixer2, XS1_CT_END);
outct(c_host, XS1_CT_END);
}
else
{
inuint(c_host);
#if MAX_MIX_COUNT > 0
outuint(c_mixer2, 0);
giveSamplesToHost(c_host, samples, samples_to_host_map, multIn);
outuint(c_mixer2, 0);
inuint(c_mixer2);
getSamplesFromHost(c_host, samples, 0);
outuint(c_mixer2, 0);
inuint(c_mixer2);
#ifdef FAST_MIXER
mixed = doMix0(samples, mix_mult_slice(0));
#else
mixed = doMix(samples,mix_map_slice(0),mix_mult_slice(0));
#endif
write_via_xc_ptr_indexed(samples, (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 0), mixed);
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
ComputeMixerLevel(mixed, 0);
#endif
#if (MAX_FREQ > 96000)
if (!mixer1_mix2_flag)
#endif
{
#if MAX_MIX_COUNT > 2
#ifdef FAST_MIXER
mixed = doMix2(samples, mix_mult_slice(2));
#else
mixed = doMix(samples,mix_map_slice(2),mix_mult_slice(2));
#endif
write_via_xc_ptr_indexed(samples, (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 2), mixed);
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
ComputeMixerLevel(mixed, 2);
#endif
#endif
#if MAX_MIX_COUNT > 4
#ifdef FAST_MIXER
mixed = doMix4(samples, mix_mult_slice(4));
#else
mixed = doMix(samples,mix_map_slice(4),mix_mult_slice(4));
#endif
write_via_xc_ptr_indexed(samples, (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 4), mixed);
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
ComputeMixerLevel(mixed, 4);
#endif
#endif
#if MAX_MIX_COUNT > 6
#ifdef FAST_MIXER
mixed = doMix6(samples, mix_mult_slice(6));
#else
mixed = doMix(samples,mix_map_slice(6),mix_mult_slice(6));
#endif
write_via_xc_ptr_indexed(samples, (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 6), mixed);
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
ComputeMixerLevel(mixed, 6);
#endif
#endif
}
#else /* IF MAX_MIX_COUNT > 0 */
/* No mixes, this thread runs on its own doing just volume */
giveSamplesToDevice(c_mixer2, samples, samples_to_device_map, multOut);
getSamplesFromDevice(c_mixer2, samples, NUM_USB_CHAN_OUT);
giveSamplesToHost(c_host, samples, samples_to_host_map, multIn);
getSamplesFromHost(c_host, samples, 0);
#endif
}
}
}
int mixer2_mix2_flag = (DEFAULT_FREQ > 96000);
#pragma unsafe arrays
void mixer2(chanend c_mixer1, chanend c_audio)
{
int mixed;
while (1) {
outuint(c_mixer1, 0);
#pragma xta endpoint "mixer2_req"
inuint(c_audio);
if(testct(c_mixer1))
{
int sampFreq;
#pragma xta endpoint "mixer2_rate_change"
inct(c_mixer1);
sampFreq = inuint(c_mixer1);
mixer2_mix2_flag = sampFreq > 96000;
for (int i=0;i<MAX_MIX_COUNT;i++)
{
write_via_xc_ptr_indexed(samples, (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + i), 0);
}
/* Inform audio thread about freq change */
outct(c_audio, XS1_CT_END);
outuint(c_audio, sampFreq);
/* Wait for handshake and pass on */
chkct(c_audio, XS1_CT_END);
outct(c_mixer1, XS1_CT_END);
}
else {
(void) inuint(c_mixer1);
giveSamplesToDevice(c_audio, samples, samples_to_device_map, multOut);
inuint(c_mixer1);
outuint(c_mixer1, 0);
getSamplesFromDevice(c_audio, samples, NUM_USB_CHAN_OUT);
inuint(c_mixer1);
outuint(c_mixer1, 0);
#if MAX_MIX_COUNT > 1
#ifdef FAST_MIXER
mixed = doMix1(samples, mix_mult_slice(1));
#else
mixed = doMix(samples,mix_map_slice(1),mix_mult_slice(1));
#endif
write_via_xc_ptr_indexed(samples, (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 1), mixed);
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
ComputeMixerLevel(mixed, 1);
#endif
#endif
#if (MAX_FREQ > 96000)
if (!mixer2_mix2_flag)
#endif
{
#if MAX_MIX_COUNT > 3
#ifdef FAST_MIXER
mixed = doMix3(samples, mix_mult_slice(3));
#else
mixed = doMix(samples,mix_map_slice(3),mix_mult_slice(3));
#endif
write_via_xc_ptr_indexed(samples, (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 3), mixed);
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
ComputeMixerLevel(mixed, 3);
#endif
#endif
#if MAX_MIX_COUNT > 5
#ifdef FAST_MIXER
mixed = doMix5(samples, mix_mult_slice(5));
#else
mixed = doMix(samples,mix_map_slice(5),mix_mult_slice(5));
#endif
write_via_xc_ptr_indexed(samples, NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 5, mixed);
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
ComputeMixerLevel(mixed, 5);
#endif
#endif
#if MAX_MIX_COUNT > 7
#ifdef FAST_MIXER
mixed = doMix7(samples, mix_mult_slice(7));
#else
mixed = doMix(samples,mix_map_slice(7),mix_mult_slice(7));
#endif
write_via_xc_ptr_indexed(samples, NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 7, mixed);
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
ComputeMixerLevel(mixed, 7);
#endif
#endif
}
}
}
}
void mixer(chanend c_mix_in, chanend c_mix_out, chanend c_mix_ctl)
{
chan c;
multOut = array_to_xc_ptr((multOut_array,unsigned[]));
multIn = array_to_xc_ptr((multIn_array,unsigned[]));
samples = array_to_xc_ptr((samples_array,unsigned[]));
samples_to_host_map =
array_to_xc_ptr((samples_to_host_map_array,unsigned[]));
samples_to_device_map =
array_to_xc_ptr((samples_to_device_map_array,unsigned[]));
#if MAX_MIX_COUNT >0
mix_mult = array_to_xc_ptr((mix_mult_array,unsigned[]));
#ifndef FAST_MIXER
mix_map = array_to_xc_ptr((mix_map_array,unsigned[]));
#endif
#endif
for (int i=0;i<NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT;i++)
{
write_via_xc_ptr_indexed(samples,i,0);
}
{
int num_mixes = DEFAULT_FREQ > 96000 ? 2 : MAX_MIX_COUNT;
for (int i=0;i<NUM_USB_CHAN_OUT;i++)
{
asm("stw %0, %1[%2]"::
"r"(i),
"r"(samples_to_device_map),
"r"(i));
}
}
#ifdef OUT_VOLUME_IN_MIXER
for (int i=0;i<NUM_USB_CHAN_OUT;i++)
{
write_via_xc_ptr_indexed(multOut, i, MAX_VOL);
}
#endif
#ifdef IN_VOLUME_IN_MIXER
for (int i=0;i<NUM_USB_CHAN_IN;i++)
{
write_via_xc_ptr_indexed(multIn, i, MAX_VOL);
}
#endif
for (int i=0;i<NUM_USB_CHAN_IN;i++)
{
write_via_xc_ptr_indexed(samples_to_host_map, i, NUM_USB_CHAN_OUT + i);
}
#if MAX_MIX_COUNT> 0
for (int i=0;i<MAX_MIX_COUNT;i++)
for (int j=0;j<MIX_INPUTS;j++)
{
#ifndef FAST_MIXER
write_word_to_mix_map(i,j, j < 16 ? j : j + 2);
#endif
write_word_to_mix_mult(i,j, i==j ? MAX_VOL >> 3 : 0);
}
#endif
par
{
#if (MAX_MIX_COUNT > 0)
mixer1(c_mix_in, c_mix_ctl, c);
mixer2(c, c_mix_out);
#else
mixer1(c_mix_in, c_mix_ctl, c_mix_out);
#endif
}
}
#endif

View File

@@ -0,0 +1,777 @@
#ifndef N
#error "N must be defined before including repeat.h"
#endif
#if N > 256
#error "N cannot be larger than 256"
#endif
#ifndef BODY
#error "BODY must be defined before including repeat.h"
#endif
#if N > 0
BODY(0)
#endif
#if N > 1
BODY(1)
#endif
#if N > 2
BODY(2)
#endif
#if N > 3
BODY(3)
#endif
#if N > 4
BODY(4)
#endif
#if N > 5
BODY(5)
#endif
#if N > 6
BODY(6)
#endif
#if N > 7
BODY(7)
#endif
#if N > 8
BODY(8)
#endif
#if N > 9
BODY(9)
#endif
#if N > 10
BODY(10)
#endif
#if N > 11
BODY(11)
#endif
#if N > 12
BODY(12)
#endif
#if N > 13
BODY(13)
#endif
#if N > 14
BODY(14)
#endif
#if N > 15
BODY(15)
#endif
#if N > 16
BODY(16)
#endif
#if N > 17
BODY(17)
#endif
#if N > 18
BODY(18)
#endif
#if N > 19
BODY(19)
#endif
#if N > 20
BODY(20)
#endif
#if N > 21
BODY(21)
#endif
#if N > 22
BODY(22)
#endif
#if N > 23
BODY(23)
#endif
#if N > 24
BODY(24)
#endif
#if N > 25
BODY(25)
#endif
#if N > 26
BODY(26)
#endif
#if N > 27
BODY(27)
#endif
#if N > 28
BODY(28)
#endif
#if N > 29
BODY(29)
#endif
#if N > 30
BODY(30)
#endif
#if N > 31
BODY(31)
#endif
#if N > 32
BODY(32)
#endif
#if N > 33
BODY(33)
#endif
#if N > 34
BODY(34)
#endif
#if N > 35
BODY(35)
#endif
#if N > 36
BODY(36)
#endif
#if N > 37
BODY(37)
#endif
#if N > 38
BODY(38)
#endif
#if N > 39
BODY(39)
#endif
#if N > 40
BODY(40)
#endif
#if N > 41
BODY(41)
#endif
#if N > 42
BODY(42)
#endif
#if N > 43
BODY(43)
#endif
#if N > 44
BODY(44)
#endif
#if N > 45
BODY(45)
#endif
#if N > 46
BODY(46)
#endif
#if N > 47
BODY(47)
#endif
#if N > 48
BODY(48)
#endif
#if N > 49
BODY(49)
#endif
#if N > 50
BODY(50)
#endif
#if N > 51
BODY(51)
#endif
#if N > 52
BODY(52)
#endif
#if N > 53
BODY(53)
#endif
#if N > 54
BODY(54)
#endif
#if N > 55
BODY(55)
#endif
#if N > 56
BODY(56)
#endif
#if N > 57
BODY(57)
#endif
#if N > 58
BODY(58)
#endif
#if N > 59
BODY(59)
#endif
#if N > 60
BODY(60)
#endif
#if N > 61
BODY(61)
#endif
#if N > 62
BODY(62)
#endif
#if N > 63
BODY(63)
#endif
#if N > 64
BODY(64)
#endif
#if N > 65
BODY(65)
#endif
#if N > 66
BODY(66)
#endif
#if N > 67
BODY(67)
#endif
#if N > 68
BODY(68)
#endif
#if N > 69
BODY(69)
#endif
#if N > 70
BODY(70)
#endif
#if N > 71
BODY(71)
#endif
#if N > 72
BODY(72)
#endif
#if N > 73
BODY(73)
#endif
#if N > 74
BODY(74)
#endif
#if N > 75
BODY(75)
#endif
#if N > 76
BODY(76)
#endif
#if N > 77
BODY(77)
#endif
#if N > 78
BODY(78)
#endif
#if N > 79
BODY(79)
#endif
#if N > 80
BODY(80)
#endif
#if N > 81
BODY(81)
#endif
#if N > 82
BODY(82)
#endif
#if N > 83
BODY(83)
#endif
#if N > 84
BODY(84)
#endif
#if N > 85
BODY(85)
#endif
#if N > 86
BODY(86)
#endif
#if N > 87
BODY(87)
#endif
#if N > 88
BODY(88)
#endif
#if N > 89
BODY(89)
#endif
#if N > 90
BODY(90)
#endif
#if N > 91
BODY(91)
#endif
#if N > 92
BODY(92)
#endif
#if N > 93
BODY(93)
#endif
#if N > 94
BODY(94)
#endif
#if N > 95
BODY(95)
#endif
#if N > 96
BODY(96)
#endif
#if N > 97
BODY(97)
#endif
#if N > 98
BODY(98)
#endif
#if N > 99
BODY(99)
#endif
#if N > 100
BODY(100)
#endif
#if N > 101
BODY(101)
#endif
#if N > 102
BODY(102)
#endif
#if N > 103
BODY(103)
#endif
#if N > 104
BODY(104)
#endif
#if N > 105
BODY(105)
#endif
#if N > 106
BODY(106)
#endif
#if N > 107
BODY(107)
#endif
#if N > 108
BODY(108)
#endif
#if N > 109
BODY(109)
#endif
#if N > 110
BODY(110)
#endif
#if N > 111
BODY(111)
#endif
#if N > 112
BODY(112)
#endif
#if N > 113
BODY(113)
#endif
#if N > 114
BODY(114)
#endif
#if N > 115
BODY(115)
#endif
#if N > 116
BODY(116)
#endif
#if N > 117
BODY(117)
#endif
#if N > 118
BODY(118)
#endif
#if N > 119
BODY(119)
#endif
#if N > 120
BODY(120)
#endif
#if N > 121
BODY(121)
#endif
#if N > 122
BODY(122)
#endif
#if N > 123
BODY(123)
#endif
#if N > 124
BODY(124)
#endif
#if N > 125
BODY(125)
#endif
#if N > 126
BODY(126)
#endif
#if N > 127
BODY(127)
#endif
#if N > 128
BODY(128)
#endif
#if N > 129
BODY(129)
#endif
#if N > 130
BODY(130)
#endif
#if N > 131
BODY(131)
#endif
#if N > 132
BODY(132)
#endif
#if N > 133
BODY(133)
#endif
#if N > 134
BODY(134)
#endif
#if N > 135
BODY(135)
#endif
#if N > 136
BODY(136)
#endif
#if N > 137
BODY(137)
#endif
#if N > 138
BODY(138)
#endif
#if N > 139
BODY(139)
#endif
#if N > 140
BODY(140)
#endif
#if N > 141
BODY(141)
#endif
#if N > 142
BODY(142)
#endif
#if N > 143
BODY(143)
#endif
#if N > 144
BODY(144)
#endif
#if N > 145
BODY(145)
#endif
#if N > 146
BODY(146)
#endif
#if N > 147
BODY(147)
#endif
#if N > 148
BODY(148)
#endif
#if N > 149
BODY(149)
#endif
#if N > 150
BODY(150)
#endif
#if N > 151
BODY(151)
#endif
#if N > 152
BODY(152)
#endif
#if N > 153
BODY(153)
#endif
#if N > 154
BODY(154)
#endif
#if N > 155
BODY(155)
#endif
#if N > 156
BODY(156)
#endif
#if N > 157
BODY(157)
#endif
#if N > 158
BODY(158)
#endif
#if N > 159
BODY(159)
#endif
#if N > 160
BODY(160)
#endif
#if N > 161
BODY(161)
#endif
#if N > 162
BODY(162)
#endif
#if N > 163
BODY(163)
#endif
#if N > 164
BODY(164)
#endif
#if N > 165
BODY(165)
#endif
#if N > 166
BODY(166)
#endif
#if N > 167
BODY(167)
#endif
#if N > 168
BODY(168)
#endif
#if N > 169
BODY(169)
#endif
#if N > 170
BODY(170)
#endif
#if N > 171
BODY(171)
#endif
#if N > 172
BODY(172)
#endif
#if N > 173
BODY(173)
#endif
#if N > 174
BODY(174)
#endif
#if N > 175
BODY(175)
#endif
#if N > 176
BODY(176)
#endif
#if N > 177
BODY(177)
#endif
#if N > 178
BODY(178)
#endif
#if N > 179
BODY(179)
#endif
#if N > 180
BODY(180)
#endif
#if N > 181
BODY(181)
#endif
#if N > 182
BODY(182)
#endif
#if N > 183
BODY(183)
#endif
#if N > 184
BODY(184)
#endif
#if N > 185
BODY(185)
#endif
#if N > 186
BODY(186)
#endif
#if N > 187
BODY(187)
#endif
#if N > 188
BODY(188)
#endif
#if N > 189
BODY(189)
#endif
#if N > 190
BODY(190)
#endif
#if N > 191
BODY(191)
#endif
#if N > 192
BODY(192)
#endif
#if N > 193
BODY(193)
#endif
#if N > 194
BODY(194)
#endif
#if N > 195
BODY(195)
#endif
#if N > 196
BODY(196)
#endif
#if N > 197
BODY(197)
#endif
#if N > 198
BODY(198)
#endif
#if N > 199
BODY(199)
#endif
#if N > 200
BODY(200)
#endif
#if N > 201
BODY(201)
#endif
#if N > 202
BODY(202)
#endif
#if N > 203
BODY(203)
#endif
#if N > 204
BODY(204)
#endif
#if N > 205
BODY(205)
#endif
#if N > 206
BODY(206)
#endif
#if N > 207
BODY(207)
#endif
#if N > 208
BODY(208)
#endif
#if N > 209
BODY(209)
#endif
#if N > 210
BODY(210)
#endif
#if N > 211
BODY(211)
#endif
#if N > 212
BODY(212)
#endif
#if N > 213
BODY(213)
#endif
#if N > 214
BODY(214)
#endif
#if N > 215
BODY(215)
#endif
#if N > 216
BODY(216)
#endif
#if N > 217
BODY(217)
#endif
#if N > 218
BODY(218)
#endif
#if N > 219
BODY(219)
#endif
#if N > 220
BODY(220)
#endif
#if N > 221
BODY(221)
#endif
#if N > 222
BODY(222)
#endif
#if N > 223
BODY(223)
#endif
#if N > 224
BODY(224)
#endif
#if N > 225
BODY(225)
#endif
#if N > 226
BODY(226)
#endif
#if N > 227
BODY(227)
#endif
#if N > 228
BODY(228)
#endif
#if N > 229
BODY(229)
#endif
#if N > 230
BODY(230)
#endif
#if N > 231
BODY(231)
#endif
#if N > 232
BODY(232)
#endif
#if N > 233
BODY(233)
#endif
#if N > 234
BODY(234)
#endif
#if N > 235
BODY(235)
#endif
#if N > 236
BODY(236)
#endif
#if N > 237
BODY(237)
#endif
#if N > 238
BODY(238)
#endif
#if N > 239
BODY(239)
#endif
#if N > 240
BODY(240)
#endif
#if N > 241
BODY(241)
#endif
#if N > 242
BODY(242)
#endif
#if N > 243
BODY(243)
#endif
#if N > 244
BODY(244)
#endif
#if N > 245
BODY(245)
#endif
#if N > 246
BODY(246)
#endif
#if N > 247
BODY(247)
#endif
#if N > 248
BODY(248)
#endif
#if N > 249
BODY(249)
#endif
#if N > 250
BODY(250)
#endif
#if N > 251
BODY(251)
#endif
#if N > 252
BODY(252)
#endif
#if N > 253
BODY(253)
#endif
#if N > 254
BODY(254)
#endif
#if N > 255
BODY(255)
#endif

View File

@@ -0,0 +1,32 @@
# This file describes how this module will affect the application
# using it. The file is included in the Makefile of the main application.
#
# The module contributes to the build of the application by extending
# the following variables:
#
# SOURCE_DIRS - directories with source files to be included in the build
# INCLUDE_DIRS - directories to be added to the include path during the build
# LIB_DIRS - directories containing libraries to be linked into the build
#
# Note that all the source files in each directory in SOURCE_DIRS
# will be compiled (you do not need to name the files individually).
#
# You can change the flags of a set of files using the SET_XCC_[lang]_FLAGS
# functions. The first argument is a list of directories and the
# second argument is the value to set the compile flags to. e.g.
#
# $(call SET_XCC_C_FLAGS, mydir1 mydir2, $(XCC_FLAGS) -g -O3)
# You can change the flags of an individual file by setting the
# XCC_FLAGS_[filename] variable. e.g.
#
# XCC_FLAGS_myfile.xc = $(XCC_FLAGS) -fsubword-select
# You can exclude particular files from the build even if they occur
# within SOURCE_DIRS by adding the file name (with no path) to the
# EXCLUDE_FILES variable e..g
#
EXCLUDE_FILES += descriptors_2.rst

View File

@@ -0,0 +1 @@
Common USB Audio source files/headers.

View File

@@ -0,0 +1,4 @@
void PllInit(chanend ?c);
void PllMult(unsigned mult, chanend ?c);

View File

@@ -0,0 +1,6 @@
#ifndef _AUDIOPORTS_H_
#define _AUDIOPORTS_H_
void ConfigAudioPorts(unsigned int divide);
#endif

View File

@@ -0,0 +1,138 @@
#include <xs1.h>
#include "devicedefines.h"
#include "audioports.h"
/* Audio IOs */
#if (I2S_CHANS_DAC != 0)
extern buffered out port:32 p_i2s_dac[I2S_WIRES_DAC];
#endif
#if (I2S_CHANS_ADC != 0)
extern buffered in port:32 p_i2s_adc[I2S_WIRES_ADC];
#endif
#if (I2S_CHANS_DAC != 0) || (I2S_CHANS_ADC != 0)
#ifdef CODEC_SLAVE
extern buffered out port:32 p_lrclk;
extern buffered out port:32 p_bclk;
#else
extern in port p_lrclk;
extern in port p_bclk;
#endif
#endif
extern port p_mclk;
extern clock clk_audio_mclk;
extern clock clk_audio_bclk;
void ConfigAudioPorts(unsigned int divide)
{
#ifdef CODEC_SLAVE
/* Output 0 on BCLK to ensure clock is low
* Required as stop_clock will only complete when the clock is low
*/
configure_out_port_no_ready(p_bclk, clk_audio_mclk, 0);
p_bclk <: 0;
/* Stop bit and master clock blocks and clear port buffers */
stop_clock(clk_audio_bclk);
stop_clock(clk_audio_mclk);
clearbuf(p_lrclk);
clearbuf(p_bclk);
#if (I2S_CHANS_ADC != 0)
for(int i = 0; i < I2S_WIRES_ADC; i++)
{
clearbuf(p_i2s_adc[i]);
}
#endif
#if (I2S_CHANS_DAC != 0)
for(int i = 0; i < I2S_WIRES_DAC; i++)
{
clearbuf(p_i2s_dac[i]);
}
#endif
/* Clock master clock-block from master-clock port */
configure_clock_src(clk_audio_mclk, p_mclk);
/* For a divide of one (i.e. bitclock == master-clock) BClk is set to clock_output mode.
* In this mode it outputs an edge clock on every tick of itsassociated clock_block.
*
* For all other divides, BClk is clocked by the master clock and data
* will be output to p_bclk to generate the bit clock.
*/
if (divide == 1) /* e.g. 176.4KHz from 11.2896 */
{
configure_port_clock_output(p_bclk, clk_audio_mclk);
}
else
{
/* bit clock port from master clock clock-clock block */
configure_out_port_no_ready(p_bclk, clk_audio_mclk, 0);
}
/* Generate bit clock block from pin */
configure_clock_src(clk_audio_bclk, p_bclk);
#if (I2S_CHANS_DAC != 0)
/* Clock I2S output data ports from clock block */
for(int i = 0; i < I2S_WIRES_DAC; i++)
{
configure_out_port_no_ready(p_i2s_dac[i], clk_audio_bclk, 0);
}
#endif
#if (I2S_CHANS_ADC != 0)
/* Clock I2S input data ports from clock block */
for(int i = 0; i < I2S_WIRES_ADC; i++)
{
configure_in_port_no_ready(p_i2s_adc[i], clk_audio_bclk);
}
#endif
/* Clock LR clock from bit clock-block */
configure_out_port_no_ready(p_lrclk, clk_audio_bclk, 0);
/* Start clock blocks ticking */
start_clock(clk_audio_mclk);
start_clock(clk_audio_bclk);
/* bclk initial state needs to be high */
p_bclk <: 0xFFFFFFFF;
/* Pause until output completes */
sync(p_bclk);
#else
/* Stop bit and master clock blocks and clear port buffers */
stop_clock(clk_audio_bclk);
stop_clock(clk_audio_mclk);
/* Clock bclk clock-block from bclk pin */
configure_clock_src(clk_audio_bclk, p_bclk);
/* Clock I2S output data ports from b-clock clock block */
for(int i = 0; i < I2S_WIRES_DAC; i++)
{
configure_out_port_no_ready(p_i2s_dac[i], clk_audio_bclk, 0);
}
/* Clock I2S input data ports from clock block */
for(int i = 0; i < I2S_WIRES_ADC; i++)
{
configure_in_port_no_ready(p_i2s_adc[i], clk_audio_bclk);
}
configure_in_port_no_ready(p_lrclk, clk_audio_bclk);
start_clock(clk_audio_bclk);
#endif
}

View File

@@ -0,0 +1,43 @@
#include <xs1.h>
#include <print.h>
#define GLXID 0x0001
#define XS1_GLX_PERIPH_USB_ID 0x1
#define XS1_GLX_CFG_RST_MISC_ADRS 0x50
#define XS1_UIFM_PHY_CONTROL_REG 0x3c
#define XS1_UIFM_PHY_CONTROL_FORCERESET 0x0
#define XS1_GLX_CFG_USB_CLK_EN_BASE 0x3
#define XS1_GLX_CFG_USB_EN_BASE 0x2
#define XS1_GLX_PERIPH_SCTH_ID 0x3
#define XS1_UIFM_FUNC_CONTROL_REG 0xc
#define XS1_UIFM_FUNC_CONTROL_XCVRSELECT 0x0
#define XS1_UIFM_FUNC_CONTROL_TERMSELECT 0x1
int write_sswitch_reg_blind(unsigned coreid, unsigned reg, unsigned data);
void write_sswitch_reg_verify(unsigned coreid, unsigned reg, unsigned data, unsigned failval);
int write_glx_periph_word(unsigned destId, unsigned periphAddress, unsigned destRegAddr, unsigned data);
int write_glx_periph_reg(unsigned dest_id, unsigned periph_addr, unsigned dest_reg_addr, unsigned bad_packet, unsigned data_size, char buf[]);
void read_sswitch_reg_verify(unsigned coreid, unsigned reg, unsigned &data, unsigned failval);
/* Reboots XMOS device by writing to the PLL config register */
void device_reboot_implementation(chanend spare)
{
#ifdef ARCH_S
/* Disconnect from bus */
write_glx_periph_word(GLXID, XS1_GLX_PERIPH_USB_ID, XS1_UIFM_FUNC_CONTROL_REG, 4);
#endif
outct(spare, XS1_CT_END); // have to do this before freeing the chanend
inct(spare); // Receive end ct from usb_buffer to close down in both directions
// Need a spare chanend so we can talk to the pll register
asm("freer res[%0]"::"r"(spare));
{
unsigned int pllVal;
unsigned int core_id = get_core_id();
read_sswitch_reg(core_id, 6, pllVal);
write_sswitch_reg_blind(core_id^0x8000, 6, pllVal);
write_sswitch_reg_blind(core_id, 6, pllVal);
}
}

View File

@@ -0,0 +1,36 @@
# Wrapper generated from this xc code
# Then modified to fix maxchanends to 0
# Then simplified
#void device_reboot_implementation(chanend spare);
#
#// This version just exists so generate an assembly wrapper function for me.
#void device_reboot(chanend spare)
#{
# device_reboot_implementation(spare);
#}
.extern device_reboot_implementation, "f{0}(chd)"
.text
.align 2
.cc_top device_reboot.function,device_reboot
.align 4
.call device_reboot, device_reboot_implementation
.globl device_reboot, "f{0}(chd)"
.globl device_reboot.nstackwords
.globl device_reboot.maxthreads
.globl device_reboot.maxtimers
.globl device_reboot.maxchanends
.globl device_reboot.maxsync
.type device_reboot, @function
.linkset device_reboot.locnoside, 1
.linkset device_reboot.locnochandec, 1
.linkset device_reboot.nstackwords, device_reboot_implementation.nstackwords + 1
device_reboot:
entsp 0x1
bl device_reboot_implementation
retsp 0x1
.size device_reboot, .-device_reboot
.cc_bottom device_reboot.function
.linkset device_reboot.maxchanends, 0#device_reboot_implementation.maxchanends
.linkset device_reboot.maxtimers, device_reboot_implementation.maxtimers
.linkset device_reboot.maxthreads, device_reboot_implementation.maxthreads

View File

@@ -0,0 +1,22 @@
#ifndef __decouple_h__
#define __decouple_h__
/** Manage the data transfer between the USB audio buffer and the
* Audio I/O driver.
*
* \param c_audio_out Channel connected to the audio() or mixer() threads
* \param c_led Optional chanend connected to an led driver thread for
* debugging purposes
* \param c_midi Optional chanend connect to usb_midi() thread if present
* \param c_clk_int Optional chanend connected to the clockGen() thread if present
*/
void decouple(chanend c_audio_out,
// chanend ?c_midi,
chanend ?c_clk_int
#ifdef IAP
, chanend ?c_iap
#endif
);
#endif // __decouple_h__

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,49 @@
extern unsigned int g_curUsbSpeed;
#define XUD_SPEED_HS 2
/* Returns the max and min packet sizes to send back to host for a given sample frequency
* See page 13 of USB Audio Device Class Definitions for Audio Data Formats Spec (v2.0)
*
* Audio samples per frame = INT(sampFreq/frametime); Variation allowed is + 1;
*
* For HS frame time = 8 * 1000
*
* so n = INT(SampFreq/8000) | INT (SampFreq/8000) + 1
*
* In the case where INT(SampFreq/8000) == SampFreq/8000) n may vary between
*
* INT(SamFreq/8000) - 1 | INT(SampFreq/8000) | INT (SampFreq/8000) + 1
*
* Note: Assumes HS (i.e. 8 frames per 1ms)
*
* Examples:
* 44100: min: 5 max: 6
* 48000: min: 5 max: 7
* 96000: min: 11 max: 13
* 88200: min: 11 max: 12
* 176400: min: 22 max: 23
* 192000: min: 23 max: 25
*
*/
void GetADCCounts(unsigned samFreq, int *min, int *mid, int *max)
{
unsigned frameTime;
int usb_speed;
usb_speed = g_curUsbSpeed;
if (usb_speed == XUD_SPEED_HS)
frameTime = 8000;
else
frameTime = 1000;
*min = samFreq / frameTime;
*max = *min + 1;
*mid = *min;
/* Check for INT(SampFreq/8000) == SampFreq/8000 */
if((samFreq % frameTime) == 0)
{
*min -= 1;
}
}

View File

@@ -0,0 +1,198 @@
#ifndef __interrupt_h__
#define __interrupt_h__
#define store_args0(c) \
asm("kentsp 19; stw %0, sp[1]; krestsp 19"::"r"(c));
#define store_args1(c,x) \
asm("kentsp 20; stw %0, sp[1]; stw %1, sp[2]; krestsp 20"::"r"(c),"r"(x));
#define store_args2(c,x0,x1) \
asm("kentsp 21; stw %0, sp[1];" \
"stw %1, sp[2];" \
"stw %2, sp[3];" \
" krestsp 21"::"r"(c),"r"(x0),"r"(x1));
#define store_args3(c,x0,x1,x2) \
asm("kentsp 22; stw %0, sp[1];" \
"stw %1, sp[2];" \
"stw %2, sp[3];" \
"stw %3, sp[4];" \
" krestsp 22"::"r"(c),"r"(x0),"r"(x1),"r"(x2));
#define store_args4(c,x0,x1,x2,x3) \
asm("kentsp 23; stw %4, sp[1];" \
"stw %0, sp[2];" \
"stw %1, sp[3];" \
"stw %2, sp[4];" \
"stw %3, sp[5];" \
" krestsp 23"::"r"(c),"r"(x0),"r"(x1),"r"(x2),"r"(x3));
#define store_args5(c,x0,x1,x2,x3,x4) \
asm("kentsp 24;" \
"stw %4, sp[1];" \
"stw %5, sp[2];" \
"stw %0, sp[3];" \
"stw %1, sp[4];" \
"stw %2, sp[5];" \
"stw %3, sp[6];" \
" krestsp 24"::"r"(c),"r"(x0),"r"(x1),"r"(x2),"r"(x3),"r"(x4));
#define store_args6(c,x0,x1,x2,x3,x4,x5) \
asm("kentsp 25;" \
"stw %4, sp[1];" \
"stw %5, sp[2];" \
"stw %6, sp[3];" \
"stw %0, sp[4];" \
"stw %1, sp[5];" \
"stw %2, sp[6];" \
"stw %3, sp[7];" \
" krestsp 25"::"r"(c),"r"(x0),"r"(x1),"r"(x2),"r"(x3),"r"(x4),"r"(x5));
#define store_args7(c,x0,x1,x2,x3,x4,x5,x6) \
asm("kentsp 26;" \
"stw %4, sp[1];" \
"stw %5, sp[2];" \
"stw %6, sp[3];" \
"stw %7, sp[4];" \
"stw %0, sp[5];" \
"stw %1, sp[6];" \
"stw %2, sp[7];" \
"stw %3, sp[8];" \
" krestsp 26"::"r"(c),"r"(x0),"r"(x1),"r"(x2),"r"(x3),"r"(x4),"r"(x5),"r"(x6));
#define store_args8(c,x0,x1,x2,x3,x4,x5,x6,x7) \
asm("kentsp 27;" \
"stw %4, sp[1];" \
"stw %5, sp[2];" \
"stw %6, sp[3];" \
"stw %7, sp[4];" \
"stw %8, sp[5];" \
"stw %0, sp[6];" \
"stw %1, sp[7];" \
"stw %2, sp[8];" \
"stw %3, sp[9];" \
" krestsp 27"::"r"(c),"r"(x0),"r"(x1),"r"(x2),"r"(x3),"r"(x4),"r"(x5),"r"(x6),"r"(x7));
#define load_args0(f) \
"ldw r0, sp[1]\n"
#define load_args1(f)\
"ldw r0, sp[1]\n" \
"ldw r1, sp[2]\n"
#define load_args2(f)\
"ldw r0, sp[1]\n" \
"ldw r1, sp[2]\n" \
"ldw r2, sp[3]\n"
#define load_args3(f)\
"ldw r0, sp[1]\n" \
"ldw r1, sp[2]\n" \
"ldw r2, sp[3]\n" \
"ldw r3, sp[4]\n"
#define load_argsn(f, args) \
".linkset __"#f"_handler_arg0, "#args"-2\n"\
"ldw r0, sp[" "__"#f"_handler_arg0" "]\n" \
".linkset __"#f"_handler_arg1, "#args"-1\n"\
"ldw r1, sp[" "__"#f"_handler_arg1" "]\n" \
".linkset __"#f"_handler_arg2, "#args"-0\n"\
"ldw r2, sp[" "__"#f"_handler_arg2" "]\n" \
".linkset __"#f"_handler_arg3, "#args"+1\n"\
"ldw r3, sp[" "__"#f"_handler_arg3" "]\n"
#define load_args4(f) load_argsn(f,4)
#define load_args5(f) load_argsn(f,5)
#define load_args6(f) load_argsn(f,6)
#define load_args7(f) load_argsn(f,7)
#define load_args8(f) load_argsn(f,8)
#define save_state(f,args) \
".linkset __"#f"_handler_r0_save, "#args"+12\n" \
"stw r0, sp[" "__"#f"_handler_r0_save" "]\n" \
".linkset __"#f"_handler_r1_save, "#args"+13\n" \
"stw r1, sp[" "__"#f"_handler_r1_save" "]\n" \
".linkset __"#f"_handler_r2_save, "#args"+2\n" \
"stw r2, sp[" "__"#f"_handler_r2_save" "]\n" \
".linkset __"#f"_handler_r3_save, "#args"+3\n" \
"stw r3, sp[" "__"#f"_handler_r3_save" "]\n" \
".linkset __"#f"_handler_r4_save, "#args"+4\n" \
"stw r4, sp[" "__"#f"_handler_r4_save" "]\n" \
".linkset __"#f"_handler_r5_save, "#args"+5\n" \
"stw r5, sp[" "__"#f"_handler_r5_save" "]\n" \
".linkset __"#f"_handler_r6_save, "#args"+6\n" \
"stw r6, sp[" "__"#f"_handler_r6_save" "]\n" \
".linkset __"#f"_handler_r7_save, "#args"+7\n" \
"stw r7, sp[" "__"#f"_handler_r7_save" "]\n" \
".linkset __"#f"_handler_r8_save, "#args"+8\n" \
"stw r8, sp[" "__"#f"_handler_r8_save" "]\n" \
".linkset __"#f"_handler_r9_save, "#args"+9\n" \
"stw r9, sp[" "__"#f"_handler_r9_save" "]\n" \
".linkset __"#f"_handler_r10_save, "#args"+10\n" \
"stw r10, sp[" "__"#f"_handler_r10_save" "]\n" \
".linkset __"#f"_handler_r11_save, "#args"+11\n" \
"stw r11, sp[" "__"#f"_handler_r11_save" "]\n" \
".linkset __"#f"_handler_lr_save, "#args"+14\n" \
"stw lr, sp[" "__"#f"_handler_lr_save" "]\n"
#define restore_state(f,args) \
"ldw r0, sp[" "__"#f"_handler_r0_save" "]\n" \
"ldw r1, sp[" "__"#f"_handler_r1_save" "]\n" \
"ldw r2, sp[" "__"#f"_handler_r2_save" "]\n" \
"ldw r3, sp[" "__"#f"_handler_r3_save" "]\n" \
"ldw r4, sp[" "__"#f"_handler_r4_save" "]\n" \
"ldw r5, sp[" "__"#f"_handler_r5_save" "]\n" \
"ldw r6, sp[" "__"#f"_handler_r6_save" "]\n" \
"ldw r7, sp[" "__"#f"_handler_r7_save" "]\n" \
"ldw r8, sp[" "__"#f"_handler_r8_save" "]\n" \
"ldw r9, sp[" "__"#f"_handler_r9_save" "]\n" \
"ldw r10, sp[" "__"#f"_handler_r10_save" "]\n" \
"ldw r11, sp[" "__"#f"_handler_r11_save" "]\n" \
"ldw lr, sp[" "__"#f"_handler_lr_save" "]\n"
#define STRINGIFY0(x) #x
#define STRINGIFY(x) STRINGIFY0(x)
#define ENABLE_INTERRUPTS() asm("setsr " STRINGIFY(XS1_SR_IEBLE_SET(0, 1)))
#define DISABLE_INTERRUPTS() asm("clrsr " STRINGIFY(XS1_SR_IEBLE_SET(0, 1)))
//int ksp_enter, ksp_exit, r11_store;
#define do_interrupt_handler(f,args) \
asm("bu .L__" #f "_handler_skip;\n" \
"__" #f "_handler:\n" \
"kentsp " #args " + 19\n" \
"__kent:" \
save_state(f,args) \
load_args ## args (f) \
"bl " #f "\n" \
restore_state(f,args) \
"krestsp " #args " + 19 \n" \
"__kret:\n" \
"kret\n" \
".L__" #f "_handler_skip:\n");
#define set_interrupt_handler(f, nstackwords, args, c, ...) \
asm (" .section .dp.data, \"adw\", @progbits\n" \
" .align 4\n" \
"__" #f "_kernel_stack:\n" \
" .space " #nstackwords ", 0\n" \
" .text\n"); \
asm("mov r10, %0; ldaw r11, dp[__" #f "_kernel_stack];add r11, r11, r10;ldaw r10, sp[0]; "\
"set sp,r11;stw r10, sp[0]; krestsp 0"::"r"(nstackwords-8):"r10","r11"); \
store_args ## args(c, __VA_ARGS__) \
do_interrupt_handler(f, args) \
asm("ldap r11, __" #f "_handler; setv res[%0],r11"::"r"(c):"r11"); \
asm("setc res[%0], 0xa; eeu res[%0]"::"r"(c)); \
asm("setsr (((0) & ~(((1 << 0x1) - 1) << 0x1)) | (((1) << 0x1) & (((1 << 0x1) - 1) << 0x1)))");
#endif

View File

@@ -0,0 +1,17 @@
//#pragma select handler
#include <xs1.h>
/* TODO Currently complier does not support inline select functions, hense this is in a seperate file to ensure this is not the case */
#pragma select handler
static inline void testct_byref(chanend c, unsigned &isCt)
{
if (testct(c))
{
isCt = 1;
}
else
{
isCt = 0;
}
}
//void testct_byref(chanend c, unsigned &isCt) ;

View File

@@ -0,0 +1,15 @@
#include <xs1.h>
/* TODO Currently complier does not support inline select functions, hense this is in a seperate file to ensure this is not the case */
#pragma select handler
void testct_byrefnot(chanend c, unsigned &isCt)
{
if (testct(c))
{
isCt = 1;
}
else
{
isCt = 0;
}
}

View File

@@ -0,0 +1,41 @@
#ifndef __usb_buffer_h__
#define __usb_buffer_h__
/** USB Audio Buffering Thread.
*
* This function buffers USB audio data between the XUD layer and the decouple
* thread. Most of the chanend parameters to the function should be connected to
* XUD_Manager()
*
* \param c_aud_out Audio OUT endpoint channel connected to the XUD
* \param c_aud_in Audio IN endpoint channel connected to the XUD
* \param c_aud_fb Audio feedback endpoint channel connected to the XUD
* \param c_midi_from_host MIDI OUT endpoint channel connected to the XUD
* \param c_midi_to_host MIDI IN endpoint channel connected to the XUD
* \param c_int Audio clocking interrupt endpoint channel connected to the XUD
* \param c_sof Start of frame channel connected to the XUD
* \param c_aud_ctl Audio control channel connected to Endpoint0()
* \param p_off_mclk A port that is clocked of the MCLK input (not the MCLK input itself)
*/
void buffer(chanend c_aud_out,
chanend c_aud_in,
chanend c_aud_fb,
#ifdef MIDI
chanend c_midi_from_host,
chanend c_midi_to_host,
chanend c_midi,
#endif
#ifdef IAP
#error IAP
chanend c_iap_from_host,
chanend c_iap_to_host,
chanend c_iap_to_host_int,
#endif
chanend? c_int,
chanend c_sof,
chanend c_aud_ctl,
in port p_off_mclk
#ifdef HID_CONTROLS
,chanend c_hid
#endif
);
#endif

View File

@@ -0,0 +1,738 @@
#include <xs1.h>
#include <print.h>
//In this file xud.h is not included since we are interpreting the
//assembly functions GetData/SetData as taking xc_ptrs
//#include "xud.h"
#define XUD_SPEED_HS 2
#include "usb.h"
#include "devicedefines.h"
#include "usb_midi.h"
#ifdef IAP
#include "iAP.h"
#endif
#include "xc_ptr.h"
#include "clockcmds.h"
#include "xud.h"
#include "testct_byref.h"
int pktCount = 0;
XUD_ep XUD_Init_Ep(chanend c_ep);
//
//static inline void XUD_SetNotReady(XUD_ep e)
///{
// int chan_array_ptr;
// asm ("ldw %0, %1[0]":"=r"(chan_array_ptr):"r"(e));
// asm ("stw %0, %1[0]"::"r"(0),"r"(chan_array_ptr));
//}
void GetADCCounts(unsigned samFreq, int &min, int &mid, int &max);
#define BUFFER_SIZE_OUT (1028 >> 2)
#define BUFFER_SIZE_IN (1028 >> 2)
/* Packet buffers for audio data */
extern unsigned int g_curSamFreqMultiplier;
/* Global var for speed. Related to feedback. Used by input stream to determine IN packet size */
unsigned g_speed;
unsigned g_freqChange = 0;
/* Interrupt EP data */
unsigned char g_intData[8];
#if defined (MIDI) || defined(IAP)
static inline void swap(xc_ptr &a, xc_ptr &b)
{
xc_ptr tmp;
tmp = a;
a = b;
b = tmp;
return;
}
#endif
#ifdef MIDI
unsigned int g_midi_to_host_buffer_A[MAX_USB_MIDI_PACKET_SIZE/4+4];
unsigned int g_midi_to_host_buffer_B[MAX_USB_MIDI_PACKET_SIZE/4+4];
int g_midi_from_host_buffer[MAX_USB_MIDI_PACKET_SIZE/4+4];
#endif
unsigned char fb_clocks[16];
//#define FB_TOLERANCE_TEST
#define FB_TOLERANCE 0x100
extern unsigned inZeroBuff[];
extern unsigned g_numUsbChanIn;
/**
* Buffers data from audio endpoints
* @param c_aud_out chanend for audio from xud
* @param c_aud_in chanend for audio to xud
* @param c_aud_fb chanend for feeback to xud
* @return void
*/
void buffer(register chanend c_aud_out, register chanend c_aud_in, chanend c_aud_fb,
#ifdef MIDI
chanend c_midi_from_host,
chanend c_midi_to_host,
chanend c_midi,
#endif
#ifdef IAP
chanend c_iap_from_host,
chanend c_iap_to_host,
chanend c_iap_to_host_int,
#endif
chanend ?c_int, chanend c_sof,
chanend c_aud_ctl,
in port p_off_mclk
#ifdef HID_CONTROLS
,chanend c_hid
#endif
)
{
XUD_ep ep_aud_out = XUD_Init_Ep(c_aud_out);
XUD_ep ep_aud_in = XUD_Init_Ep(c_aud_in);
XUD_ep ep_aud_fb = XUD_Init_Ep(c_aud_fb);
#ifdef MIDI
XUD_ep ep_midi_from_host = XUD_Init_Ep(c_midi_from_host);
XUD_ep ep_midi_to_host = XUD_Init_Ep(c_midi_to_host);
#endif
#ifdef IAP
XUD_ep ep_iap_from_host = XUD_Init_Ep(c_iap_from_host);
XUD_ep ep_iap_to_host = XUD_Init_Ep(c_iap_to_host);
XUD_ep ep_iap_to_host_int = XUD_Init_Ep(c_iap_to_host_int);
#endif
#if defined(SPDIF_RX) || defined(ADAT_RX)
XUD_ep ep_int = XUD_Init_Ep(c_int);
#endif
#ifdef HID_CONTROLS
XUD_ep ep_hid = XUD_Init_Ep(c_hid);
#endif
unsigned tmp;
unsigned sampleFreq = 0;
unsigned lastClock;
unsigned clocks = 0;
unsigned bufferIn = 1;
unsigned remnant = 0, cycles;
unsigned sofCount = 0;
unsigned freqChange = 0;
//unsigned expected = (DEFAULT_FREQ/8000)<<16;
#ifdef FB_TOLERANCE_TEST
unsigned expected_fb = 0;
#endif
xc_ptr aud_from_host_buffer = 0;
#ifdef MIDI
xc_ptr midi_from_host_buffer = 0;
xc_ptr midi_to_host_buffer = 0;
xc_ptr midi_to_host_waiting_buffer = 0;
xc_ptr midi_from_host_rdptr;
xc_ptr midi_to_host_buffer_being_sent = array_to_xc_ptr(g_midi_to_host_buffer_A);
xc_ptr midi_to_host_buffer_being_collected = array_to_xc_ptr(g_midi_to_host_buffer_B);
int is_ack;
unsigned int datum;
int midi_data_remaining_to_device = 0;
int midi_data_collected_from_device = 0;
int midi_waiting_on_send_to_host = 0;
int midi_to_host_flag = 0;
int midi_from_host_flag = 0;
#endif
xc_ptr p_inZeroBuff = array_to_xc_ptr(inZeroBuff);
set_thread_fast_mode_on();
#ifdef IAP
/* Note the order here is important */
XUD_ResetDrain(c_iap_to_host);
XUD_ResetDrain(c_iap_to_host_int);
XUD_GetBusSpeed(c_iap_to_host);
XUD_GetBusSpeed(c_iap_to_host_int);
#endif
#if defined(SPDIF_RX) || defined(ADAT_RX)
asm("stw %0, dp[int_usb_ep]"::"r"(ep_int));
#endif
asm("stw %0, dp[aud_from_host_usb_ep]"::"r"(ep_aud_out));
asm("stw %0, dp[aud_to_host_usb_ep]"::"r"(ep_aud_in));
#ifdef HID_CONTROLS
asm("stw %0, dp[g_ep_hid]"::"r"(ep_hid));
#endif
asm("stw %0, dp[buffer_aud_ctl_chan]"::"r"(c_aud_ctl));
/* Wait for USB connect then setup our first packet */
{
int min, mid, max;
int usb_speed = 0;
int frameTime;
while(usb_speed == 0)
{
GET_SHARED_GLOBAL(usb_speed, g_curUsbSpeed);
}
GetADCCounts(DEFAULT_FREQ, min, mid, max);
asm("stw %0, dp[g_speed]"::"r"(mid << 16));
if (usb_speed == XUD_SPEED_HS)
mid*=NUM_USB_CHAN_IN*4;
else
mid*=NUM_USB_CHAN_IN*3;
asm("stw %0, %1[0]"::"r"(mid),"r"(p_inZeroBuff));
#ifdef FB_TOLERANCE_TEST
expected_fb = ((DEFAULT_FREQ * 0x2000) / 1000);
#endif
}
#ifdef MIDI
// get the two buffers to use for midi device->host
asm("ldaw %0, dp[g_midi_to_host_buffer_A]":"=r"(midi_to_host_buffer));
asm("ldaw %0, dp[g_midi_to_host_buffer_B]":"=r"(midi_to_host_waiting_buffer));
asm("ldaw %0, dp[g_midi_from_host_buffer]":"=r"(midi_from_host_buffer));
// pass the midi->XUD chanends to decouple so that thread can
// initialize comm with XUD
//asm("stw %0, dp[midi_to_host_usb_ep]"::"r"(ep_midi_to_host));
//asm("stw %0, dp[midi_from_host_usb_ep]"::"r"(ep_midi_from_host));
swap(midi_to_host_buffer, midi_to_host_waiting_buffer);
//SET_SHARED_GLOBAL(g_midi_from_host_flag, 1);
#endif
#ifdef IAP
// get the two buffers to use for iap device->host
asm("ldaw %0, dp[g_iap_to_host_buffer_A]":"=r"(iap_to_host_buffer));
asm("ldaw %0, dp[g_iap_to_host_buffer_B]":"=r"(iap_to_host_waiting_buffer));
asm("ldaw %0, dp[g_iap_from_host_buffer]":"=r"(iap_from_host_buffer));
// pass the iap->XUD chanends to decouple so that thread can
// initialize comm with XUD
asm("stw %0, dp[iap_to_host_usb_ep]"::"r"(ep_iap_to_host));
asm("stw %0, dp[iap_to_host_int_usb_ep]"::"r"(ep_iap_to_host_int));
asm("stw %0, dp[iap_from_host_usb_ep]"::"r"(ep_iap_from_host));
swap(iap_to_host_buffer, iap_to_host_waiting_buffer);
SET_SHARED_GLOBAL(g_iap_from_host_flag, 1);
#endif
#ifdef OUTPUT
SET_SHARED_GLOBAL(g_aud_from_host_flag, 1);
#endif
#ifdef INPUT
SET_SHARED_GLOBAL(g_aud_to_host_flag, 1);
#endif
(fb_clocks, unsigned[])[0] = 0;
{
int usb_speed;
int x;
asm("ldaw %0, dp[fb_clocks]":"=r"(x));
GET_SHARED_GLOBAL(usb_speed, g_curUsbSpeed);
if (usb_speed == XUD_SPEED_HS)
{
XUD_SetReady_In(ep_aud_fb, fb_clocks, 4);
}
else
{
XUD_SetReady_In(ep_aud_fb, fb_clocks, 3);
}
}
#ifdef MIDI
XUD_SetReady_OutPtr(ep_midi_from_host, midi_from_host_buffer);
#endif
while(1)
{
/* Wait for response from XUD and service relevant EP */
select
{
#if defined(SPDIF_RX) || defined(ADAT_RX)
/* Interrupt EP, send back interrupt data. Note, request made from decouple */
case inuint_byref(c_int, tmp):
{
int sent_ok = 0;
/* Start XUD_SetData */
//XUD_SetData_Inline(ep_int, c_int);
#if 0
while (!sent_ok)
{
outct(c_int, 64);
asm("ldw %0, dp[g_intData]":"=r"(tmp));
outuint(c_int, tmp);
asm("ldw %0, dp[g_intData+4]":"=r"(tmp));
outct(c_int, 64);
outuint(c_int, tmp);
sent_ok = inuint(c_int);
/* End XUD_SetData */
}
#endif
asm("stw %0, dp[g_intFlag]" :: "r" (0) );
//XUD_SetNotReady(ep_int);
break;
}
#endif
/* Sample Freq our chan count update from ep 0 */
case testct_byref(c_aud_ctl, tmp):
{
if (tmp) {
// is a control token sent by reboot_device
inct(c_aud_ctl);
outct(c_aud_ctl, XS1_CT_END);
while(1) {};
} else {
int min, mid, max;
int usb_speed;
int frameTime;
tmp = inuint(c_aud_ctl);
GET_SHARED_GLOBAL(usb_speed, g_curUsbSpeed);
if(tmp == SET_SAMPLE_FREQ)
{
sampleFreq = inuint(c_aud_ctl);
/* Tidy up double buffer, note we can do better than this for 44.1 etc but better
* than sending two packets at old speed! */
if (usb_speed == XUD_SPEED_HS)
frameTime = 8000;
else
frameTime = 1000;
min = sampleFreq / frameTime;
max = min + 1;
mid = min;
/* Check for INT(SampFreq/8000) == SampFreq/8000 */
if((sampleFreq % frameTime) == 0)
{
min -= 1;
}
#ifdef FB_TOLERANCE_TEST
expected_fb = ((sampleFreq * 0x2000) / frametime);
#endif
asm("stw %0, dp[g_speed]"::"r"(mid << 16));
if (usb_speed == XUD_SPEED_HS)
mid *= NUM_USB_CHAN_IN*4;
else
mid *= NUM_USB_CHAN_IN*3;
asm("stw %0, %1[0]"::"r"(mid),"r"(p_inZeroBuff));
/* Reset FB */
/* Note, Endpoint 0 will hold off host for a sufficient period to allow out feedback
* to stabilise (i.e. sofCount == 128 to fire) */
sofCount = 0;
clocks = 0;
remnant = 0;
/* Ideally we want to wait for handshake (and pass back up) here. But we cannot keep this
* thread locked, it must stay responsive to packets/SOFs. So, set a flag and check for
* handshake elsewhere */
/* Pass on sample freq change to decouple */
SET_SHARED_GLOBAL(g_freqChange, SET_SAMPLE_FREQ);
SET_SHARED_GLOBAL(g_freqChange_sampFreq, sampleFreq);
SET_SHARED_GLOBAL(g_freqChange_flag, SET_SAMPLE_FREQ);
}
else
{
sampleFreq = inuint(c_aud_ctl);
SET_SHARED_GLOBAL(g_freqChange, tmp); /* Set command */
SET_SHARED_GLOBAL(g_freqChange_sampFreq, sampleFreq); /* Set flag */
SET_SHARED_GLOBAL(g_freqChange_flag, tmp);
}
}
break;
}
#define MASK_16_13 (7) // Bits that should not be transmitted as part of feedback.
#define MASK_16_10 (127) //(63) /* For Audio 1.0 we use a mask 1 bit longer than expected to avoid Windows LSB isses */
case inuint_byref(c_sof, tmp):
/* NOTE our feedback will be wrong for a couple of SOF's after a SF change due to
* lastClock being incorrect */
asm("#sof");
/* Get MCLK count */
asm (" getts %0, res[%1]" : "=r" (tmp) : "r" (p_off_mclk));
GET_SHARED_GLOBAL(freqChange, g_freqChange);
if(freqChange == SET_SAMPLE_FREQ)
{
/* Keep getting MCLK counts */
lastClock = tmp;
}
else
{
unsigned mask = MASK_16_13, usb_speed;
GET_SHARED_GLOBAL(usb_speed, g_curUsbSpeed);
if(usb_speed != XUD_SPEED_HS)
mask = MASK_16_10;
/* Number of MCLKS this SOF, approx 125 * 24 (3000), sample by sample rate */
GET_SHARED_GLOBAL(cycles, g_curSamFreqMultiplier);
cycles = ((int)((short)(tmp - lastClock))) * cycles;
/* Any odd bits (lower than 16.23) have to be kept seperate */
remnant += cycles & mask;
/* Add 16.13 bits into clock count */
clocks += (cycles & ~mask) + (remnant & ~mask);
/* and overflow from odd bits. Remove overflow from odd bits. */
remnant &= mask;
/* Store MCLK for next time around... */
lastClock = tmp;
/* Reset counts based on SOF counting. Expect 16ms (128 HS SOFs/16 FS SOFS) per feedback poll
* We always could 128 sofs, so 16ms @ HS, 128ms @ FS */
if(sofCount == 128)
{
sofCount = 0;
#ifdef FB_TOLERANCE_TEST
if (clocks > (expected_fb - FB_TOLERANCE) &&
clocks < (expected_fb + FB_TOLERANCE))
#endif
{
int usb_speed;
asm("stw %0, dp[g_speed]"::"r"(clocks)); // g_speed = clocks
//fb_clocks = clocks;
GET_SHARED_GLOBAL(usb_speed, g_curUsbSpeed);
if (usb_speed == XUD_SPEED_HS)
{
(fb_clocks, unsigned[])[0] = clocks;
}
else
{
(fb_clocks, unsigned[])[0] = clocks>>2;
}
}
#ifdef FB_TOLERANCE_TEST
else {
}
#endif
clocks = 0;
}
sofCount++;
}
break;
#ifdef INPUT
/* DEVICE -> HOST */
case XUD_SetData_Select(c_aud_in, ep_aud_in, tmp):
{
/* Inform stream that buffer sent */
SET_SHARED_GLOBAL(g_aud_to_host_flag, bufferIn+1);
}
break;
#endif
#ifdef OUTPUT
/* Feedback Pipe */
case XUD_SetData_Select(c_aud_fb, ep_aud_fb, tmp):
{
int usb_speed;
int x;
asm("#aud fb");
asm("ldaw %0, dp[fb_clocks]":"=r"(x));
GET_SHARED_GLOBAL(usb_speed, g_curUsbSpeed);
if (usb_speed == XUD_SPEED_HS)
{
XUD_SetReady_In(ep_aud_fb, fb_clocks, 4);
}
else
{
XUD_SetReady_In(ep_aud_fb, fb_clocks, 3);
}
}
break;
/* Audio HOST -> DEVICE */
case XUD_GetData_Select(c_aud_out, ep_aud_out, tmp):
{
asm("#h->d aud data");
GET_SHARED_GLOBAL(aud_from_host_buffer, g_aud_from_host_buffer);
write_via_xc_ptr(aud_from_host_buffer, tmp);
/* Sync with audio thread */
SET_SHARED_GLOBAL(g_aud_from_host_flag, 1);
}
break;
#endif
#ifdef MIDI
case XUD_GetData_Select(c_midi_from_host, ep_midi_from_host, tmp):
asm("#midi h->d");
/* Get buffer data from host - MIDI OUT from host always into a single buffer */
/* Write datalength (tmp) into buffer[0], data stored in buffer[4] onwards */
midi_data_remaining_to_device = tmp;
/* Increment read pointer - buffer[0] is length */
midi_from_host_rdptr = midi_from_host_buffer + 4;
if (midi_data_remaining_to_device)
{
read_via_xc_ptr(datum, midi_from_host_rdptr);
outuint(c_midi, datum);
midi_from_host_rdptr += 4;
midi_data_remaining_to_device -= 4;
}
break;
/* MIDI IN to host */
case XUD_SetData_Select(c_midi_to_host, ep_midi_to_host, tmp):
asm("#midi d->h");
swap(midi_to_host_buffer, midi_to_host_waiting_buffer);
/* The buffer has been sent to the host, so we can ack the midi thread */
if (midi_data_collected_from_device != 0)
{
/* We have some more data to send set the amount of data to send */
write_via_xc_ptr(midi_to_host_buffer_being_collected, midi_data_collected_from_device);
/* Swap the collecting and sending buffer */
swap(midi_to_host_buffer_being_collected, midi_to_host_buffer_being_sent);
/* Request to send packet */
XUD_SetReady_InPtr(ep_midi_to_host, midi_to_host_buffer_being_sent+4, midi_data_collected_from_device);
/* Mark as waiting for host to poll us */
midi_waiting_on_send_to_host = 1;
/* Reset the collected data count */
midi_data_collected_from_device = 0;
}
else
{
midi_waiting_on_send_to_host = 0;
}
break;
#endif
#ifdef IAP
case testct_byref(c_iap_from_host, tmp):
asm("#iap h->d");
if (tmp) {
// Is a control token, not expected
} else {
// Not a control token so is data
/* Get buffer data from host - IAP OUT from host always into a single buffer */
xc_ptr p = iap_from_host_buffer + 4;
xc_ptr p0 = p;
int tail;
inuint(c_iap_from_host); // And discard
while (!testct(c_iap_from_host))
{
unsigned int datum = inuint(c_iap_from_host);
write_via_xc_ptr(p, datum);
p += 4;
}
tail = inct(c_iap_from_host);
datalength = p - p0;// - 4;
switch (tail)
{
case 10:
// the tail is 0 which means
datalength -= 6;
break;
case 11:
// the tail is 1 which means
datalength -= 5;
break;
case 12:
// the tail is 2 which means
datalength -= 4;
break;
case 13:
// the tail is 3 which means
datalength -= 3;
break;
default:
break;
}
// XUD_SetNotReady(ep_iap_from_host);
write_via_xc_ptr(iap_from_host_buffer, datalength);
/* release the buffer */
SET_SHARED_GLOBAL(g_iap_from_host_flag, 1);
}
break;
/* IAP IN to host */
case testct_byref(c_iap_to_host, tmp):
asm("#iap d->h");
if (tmp)
{
// Is a control token so reset
XUD_ResetDrain(c_iap_to_host);
XUD_ResetDrain(c_iap_to_host_int);
XUD_GetBusSpeed(c_iap_to_host);
XUD_GetBusSpeed(c_iap_to_host_int);
XUD_SetNotReady(ep_iap_to_host);
XUD_SetNotReady(ep_iap_to_host_int);
} else {
inuint(c_iap_to_host); // And discard
// fill in the data
//XUD_SetData_Inline(ep_iap_to_host, c_iap_to_host);
//XUD_SetNotReady(ep_iap_to_host);
// ack the decouple thread to say it has been sent to host
SET_SHARED_GLOBAL(g_iap_to_host_flag, 1);
swap(iap_to_host_buffer, iap_to_host_waiting_buffer);
}
break;
/* IAP interrupt IN to host */
case testct_byref(c_iap_to_host_int, tmp):
asm("#iap interrupt d->h");
if (tmp)
{
// Is a control token so reset
XUD_ResetDrain(c_iap_to_host);
XUD_ResetDrain(c_iap_to_host_int);
XUD_GetBusSpeed(c_iap_to_host);
XUD_GetBusSpeed(c_iap_to_host_int);
XUD_SetNotReady(ep_iap_to_host);
XUD_SetNotReady(ep_iap_to_host_int);
}
else
{
inuint(c_iap_to_host_int); // And discard
// fill in the data
// XUD_SetData_Inline(ep_iap_to_host_int, c_iap_to_host_int);
//XUD_SetNotReady(ep_iap_to_host_int);
// Don't need to handle data here as always ZLP
}
break;
#endif
#ifdef HID_CONTROLS
/* HID Report Data */
case XUD_SetData_Select(c_hid, ep_hid, tmp):
{
asm("stw %0, dp[g_hidFlag]" :: "r" (0) );
}
break;
#endif
#ifdef MIDI
/* Received word from MIDI thread - Check for ACK or Data */
case midi_get_ack_or_data(c_midi, is_ack, datum):
if (is_ack)
{
/* An ack from the midi/uart thread means it has accepted some data we sent it
* we are okay to send another word */
if (midi_data_remaining_to_device == 0)
{
/* We have read an entire packet - Mark ready to receive another */
XUD_SetReady_OutPtr(ep_midi_from_host, midi_from_host_buffer+4);
}
else
{
/* Read another word from the fifo and output it to MIDI thread */
read_via_xc_ptr(datum, midi_from_host_rdptr);
outuint(c_midi, datum);
midi_from_host_rdptr += 4;
midi_data_remaining_to_device -= 4;
}
}
else
{
/* The midi/uart thread has sent us some data - handshake back */
midi_send_ack(c_midi);
if (midi_data_collected_from_device < MIDI_USB_BUFFER_TO_HOST_SIZE)
{
/* There is room in the collecting buffer for the data */
xc_ptr p = (midi_to_host_buffer_being_collected + 4) + midi_data_collected_from_device;
// Add data to the buffer
write_via_xc_ptr(p, datum);
midi_data_collected_from_device += 4;
}
else
{
// Too many events from device - drop
}
// If we are not sending data to the host then initiate it
if (!midi_waiting_on_send_to_host)
{
write_via_xc_ptr(midi_to_host_buffer_being_collected, midi_data_collected_from_device);
swap(midi_to_host_buffer_being_collected, midi_to_host_buffer_being_sent);
// Signal other side to swap
XUD_SetReady_InPtr(ep_midi_to_host, midi_to_host_buffer_being_sent+4, midi_data_collected_from_device);
midi_data_collected_from_device = 0;
midi_waiting_on_send_to_host = 1;
}
}
break;
#endif /* ifdef MIDI */
}
}
set_thread_fast_mode_off();
}

View File

@@ -0,0 +1,34 @@
#ifndef __xc_ptr__
#define __xc_ptr__
typedef unsigned int xc_ptr;
// Note that this function is marked as const to avoid the XC
// parallel usage checks, this is only really going to work if this
// is the *only* way the array a is accessed (and everything else uses
// the xc_ptr)
inline xc_ptr array_to_xc_ptr(const unsigned a[])
{
xc_ptr x;
asm("mov %0, %1":"=r"(x):"r"(a));
return x;
}
#define write_via_xc_ptr(p,x) asm("stw %0, %1[0]"::"r"(x),"r"(p))
#define write_via_xc_ptr_indexed(p,i,x) asm("stw %0, %1[%2]"::"r"(x),"r"(p),"r"(i))
#define write_byte_via_xc_ptr_indexed(p,i,x) asm("st8 %0, %1[%2]"::"r"(x),"r"(p),"r"(i))
// No immediate st8 format
#define write_byte_via_xc_ptr(p,x) write_byte_via_xc_ptr_indexed(p, 0, x)
#define read_via_xc_ptr(x,p) asm("ldw %0, %1[0]":"=r"(x):"r"(p));
#define read_via_xc_ptr_indexed(x,p,i) asm("ldw %0, %1[%2]":"=r"(x):"r"(p),"r"(i));
#define read_byte_via_xc_ptr_indexed(x,p,i) asm("ld8u %0, %1[%2]":"=r"(x):"r"(p),"r"(i));
// No immediate ld8u format
#define read_byte_via_xc_ptr(x,p) read_byte_via_xc_ptr_indexed(x, p, 0)
#define GET_SHARED_GLOBAL(x, g) asm("ldw %0, dp[" #g "]":"=r"(x))
#define SET_SHARED_GLOBAL(g, v) asm("stw %0, dp[" #g "]"::"r"(v))
#endif

View File

@@ -0,0 +1,3 @@
#include "xc_ptr.h"
extern inline xc_ptr array_to_xc_ptr(const unsigned a[]);

View File

@@ -0,0 +1,59 @@
#include "xs1_kernel.h"
#include "xs1_user.h"
.global write_sswitch_reg_blind, "f{si}(ui,ui,ui)"
.global write_sswitch_reg_blind.nstackwords
.linkset write_sswitch_reg_blind.nstackwords, 0
//.type read_sswitch_reg, @function
// r0 - coreid
// r1 - reg
// r2 - data
.cc_top write_sswitch_reg_blind.function, write_sswitch_reg_blind
.align 2
write_sswitch_reg_blind:
// Check range of coreid
shr r3, r0, XS1_CHAN_ID_PROCESSOR_SIZE + XS1_CHAN_ID_NODE_SIZE
bt r3, write_switch_reg_fail
// Check range of reg
shr r3, r1, 16
bt r3, write_switch_reg_fail
// Allocate channel end
getr r3, XS1_RES_TYPE_CHANEND
// Set destination
ldc r11, XS1_RES_TYPE_CONFIG | (XS1_CT_SSCTRL << XS1_CHAN_ID_CHANNUM_SHIFT)
// r0 - l
// r1 - reg
// r2 - data
// r3 - chanend
// r11 - low half of dest
write_switch_reg:
shl r0, r0, XS1_CHAN_ID_PROCESSOR_SHIFT
or r0, r0, r11
setd res[r3], r0
// Send packet
ldc r11, XS1_CT_WRITEC // Too big for outct immediate
outct res[r3], r11
mkmsk r0, 32
shl r0, r0, 8
shr r11, r1, 8
or r0, r0, r11
out res[r3], r0 // (0xffffff00) | (reg >> 8)
outt res[r3], r1 // reg & 0xff
out res[r3], r2
outct res[r3], XS1_CT_END
// Receive response
freer res[r3]
retsp 0
write_switch_reg_fail:
ldc r0, 0
retsp 0
.cc_bottom write_sswitch_reg_blind.function

View File

@@ -0,0 +1,23 @@
#include "devicedefines.h"
#ifdef HOST_ACTIVE_CALL
void VendorHostActive(int valid);
void XUD_UserSuspend(void)
{
VendorHostActive(0);
}
void XUD_UserResume(void)
{
unsigned config;
asm("ldw %0, dp[g_config]" : "=r" (config):);
if(config == 1)
{
VendorHostActive(1);
}
}
#endif