/** * @file AudioRequests.xc * @brief Implements relevant requests from the USB Audio 2.0 Specification * @author Ross Owen, XMOS Semiconductor * @version 1.4 */ #include //#include #include "xud.h" #include "usb.h" #include "usbaudio20.h" #include "dbcalc.h" #include "devicedefines.h" #include "common.h" #include "clockcmds.h" #include "xc_ptr.h" #ifdef MIXER #include "mixer.h" #endif #include #define CS_XU_MIXSEL (0x06) extern unsigned int multOut[NUM_USB_CHAN_OUT + 1]; extern unsigned int multIn[NUM_USB_CHAN_IN + 1]; extern int interfaceAlt[]; /* Global volume and mute tables */ extern int volsOut[]; extern unsigned int mutesOut[]; extern int volsIn[]; extern unsigned int mutesIn[]; /* Mixer settings */ #ifdef MIXER extern unsigned char mixer1Crossbar[]; extern short mixer1Weights[]; /* Device channel mapping */ #if NUM_USB_CHAN_OUT > 0 extern unsigned char channelMapAud[NUM_USB_CHAN_OUT]; #endif #if NUM_USB_CHAN_IN > 0 extern unsigned char channelMapUsb[NUM_USB_CHAN_IN]; #endif /* Mixer input mapping */ extern unsigned char mixSel[MIX_INPUTS]; #endif /* Global var for current frequency */ extern unsigned int g_curSamFreq; extern unsigned int g_curSamFreq48000Family; extern unsigned int g_curSamFreqMultiplier; /* Store an int into a char array: Note this allows non-word aligned access unlike reinerpret cast */ void storeInt(unsigned char buffer[], int index, int val) { buffer[index+3] = val>>24; buffer[index+2] = val>>16; buffer[index+1] = val>>8; buffer[index] = val; } /* Store an short into a char array: Note this allows non-word aligned access unlike reinerpret cast */ void storeShort(unsigned char buffer[], int index, short val) { buffer[index+1] = val>>8; buffer[index] = val; } void storeFreq(unsigned char buffer[], int &i, int freq) { storeInt(buffer, i, freq); i+=4; storeInt(buffer, i, freq); i+=4; storeInt(buffer, i, 0); i+=4; return; } unsigned longMul(unsigned a, unsigned b, int prec) { unsigned x,y; unsigned ret; // {x, y} = lmul(a, b, 0, 0); asm("lmul %0, %1, %2, %3, %4, %5":"=r"(x),"=r"(y):"r"(a),"r"(b),"r"(0),"r"(0)); ret = (x << (32-prec) | (y >> prec)); return ret; } void setG_curSamFreqMultiplier(int x) { asm(" stw %0, dp[g_curSamFreqMultiplier]" :: "r"(x)); } /* Update master volume i.e. i.e update weights for all channels */ void updateMasterVol( int unitID, chanend ?c_mix_ctl) { int x; #ifndef OUT_VOLUME_IN_MIXER xc_ptr p_multOut = array_to_xc_ptr(multOut); #endif #ifndef IN_VOLUME_IN_MIXER xc_ptr p_multIn = array_to_xc_ptr(multIn); #endif switch( unitID) { case FU_USBOUT: for (int i = 1; i < (NUM_USB_CHAN_OUT + 1); i++) { /* Calc multipliers with 29 fractional bits from a db value with 8 fractional bits */ /* 0x8000 is a special value representing -inf (i.e. mute) */ unsigned master_vol = volsOut[0] == 0x8000 ? 0 : db_to_mult(volsOut[0], 8, 29); unsigned vol = volsOut[i] == 0x8000 ? 0 : db_to_mult(volsOut[i], 8, 29); x = longMul(master_vol, vol, 29) * !mutesOut[0] * !mutesOut[i]; #ifdef OUT_VOLUME_IN_MIXER if (!isnull(c_mix_ctl)) { outuint(c_mix_ctl, SET_MIX_OUT_VOL); outuint(c_mix_ctl, i-1); outuint(c_mix_ctl, x); outct(c_mix_ctl, XS1_CT_END); } #else asm("stw %0, %1[%2]"::"r"(x),"r"(p_multOut),"r"(i-1)); #endif } break; case FU_USBIN: for (int i = 1; i < (NUM_USB_CHAN_IN + 1); i++) { /* Calc multipliers with 29 fractional bits from a db value with 8 fractional bits */ /* 0x8000 is a special value representing -inf (i.e. mute) */ unsigned master_vol = volsIn[0] == 0x8000 ? 0 : db_to_mult(volsIn[0], 8, 29); unsigned vol = volsIn[i] == 0x8000 ? 0 : db_to_mult(volsIn[i], 8, 29); x = longMul(master_vol, vol, 29) * !mutesIn[0] * !mutesIn[i]; #ifdef IN_VOLUME_IN_MIXER if (!isnull(c_mix_ctl)) { //master //{ //c_mix_ctl <: SET_MIX_IN_VOL; //c_mix_ctl <: i-1; //c_mix_ctl <: x; //} outuint(c_mix_ctl, SET_MIX_IN_VOL); outuint(c_mix_ctl, i-1); outuint(c_mix_ctl, x); outct(c_mix_ctl, XS1_CT_END); } #else asm("stw %0, %1[%2]"::"r"(x),"r"(p_multIn),"r"(i-1)); #endif } break; default: XUD_Error_hex("MVol: No such unit: ", unitID); break; } } void updateVol(int unitID, int channel, chanend ?c_mix_ctl) { int x; #ifndef OUT_VOLUME_IN_MIXER xc_ptr p_multOut = array_to_xc_ptr(multOut); #endif #ifndef IN_VOLUME_IN_MIXER xc_ptr p_multIn = array_to_xc_ptr(multIn); #endif /* Check for master volume update */ if (channel == 0) { updateMasterVol( unitID , c_mix_ctl); } else { switch( unitID ) { case FU_USBOUT: { /* Calc multipliers with 29 fractional bits from a db value with 8 fractional bits */ /* 0x8000 is a special value representing -inf (i.e. mute) */ unsigned master_vol = volsOut[0] == 0x8000 ? 0 : db_to_mult(volsOut[0], 8, 29); unsigned vol = volsOut[channel] == 0x8000 ? 0 : db_to_mult(volsOut[channel], 8, 29); x = longMul(master_vol, vol, 29) * !mutesOut[0] * !mutesOut[channel]; #ifdef OUT_VOLUME_IN_MIXER if (!isnull(c_mix_ctl)) { //master { // c_mix_ctl <: SET_MIX_OUT_VOL; // c_mix_ctl <: channel-1; // /c_mix_ctl <: x; //} outuint(c_mix_ctl, SET_MIX_OUT_VOL); outuint(c_mix_ctl, channel-1); outuint(c_mix_ctl, x); outct(c_mix_ctl, XS1_CT_END); } #else asm("stw %0, %1[%2]"::"r"(x),"r"(p_multOut),"r"(channel-1)); #endif break; } case FU_USBIN: { /* Calc multipliers with 29 fractional bits from a db value with 8 fractional bits */ /* 0x8000 is a special value representing -inf (i.e. mute) */ unsigned master_vol = volsIn[0] == 0x8000 ? 0 : db_to_mult(volsIn[0], 8, 29); unsigned vol = volsIn[channel] == 0x8000 ? 0 : db_to_mult(volsIn[channel], 8, 29); x = longMul(master_vol, vol, 29) * !mutesIn[0] * !mutesIn[channel]; #ifdef IN_VOLUME_IN_MIXER if (!isnull(c_mix_ctl)) { //master { // c_mix_ctl <: SET_MIX_IN_VOL; // c_mix_ctl <: channel-1; // c_mix_ctl <: x; //} outuint(c_mix_ctl, SET_MIX_IN_VOL); outuint(c_mix_ctl, channel-1); outuint(c_mix_ctl, x); outct(c_mix_ctl, XS1_CT_END); } #else asm("stw %0, %1[%2]"::"r"(x),"r"(p_multIn),"r"(channel-1)); #endif break; } default: /* Don't hit */ XUD_Error_hex("Vol: No such unit: ", unitID); break; } } } #ifdef EP0_THREAD_COMBINED_WITH_SPI void spi(chanend c_spi, chanend c_spi_ss); #endif /* Handles the audio class specific requests * returns: 0 if request delt with successfully without error, * <0 for device reset suspend * else 1 */ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, SetupPacket &sp, chanend c_audioControl, chanend ?c_mix_ctl, chanend ?c_clk_ctl #ifdef EP0_THREAD_COMBINED_WITH_SPI , chanend c_spi, chanend c_spi_ss #endif ) { unsigned char buffer[128]; int i_tmp; int unitID; int loop = 1; int datalength; /* Inspect request, NOTE: these are class specific requests */ switch( sp.bRequest ) { /* CUR Request*/ case CUR: { /* Extract unitID from wIndex */ unitID = sp.wIndex >> 8; switch( unitID ) { /* Clock Unit(s) */ case ID_CLKSRC_INT: case ID_CLKSRC_EXT: case ID_CLKSRC_ADAT: { /* Check Control selector (CS) */ switch( sp.wValue >> 8 ) { /* Sample Frequency control */ case CS_SAM_FREQ_CONTROL: { /* Direction: Host-to-device */ if( sp.bmRequestType.Direction == 0 ) { /* Get OUT data with Sample Rate into buffer*/ datalength = XUD_GetBuffer(ep0_out, buffer); /* Check for reset/suspend */ if(datalength < 0) { return datalength; } if(datalength == 4) { /* Re-construct Sample Freq */ i_tmp = buffer[0] | (buffer[1] << 8) | buffer[2] << 16 | buffer[3] << 24; /* Instruct audio thread to change sample freq */ if(i_tmp != g_curSamFreq) { g_curSamFreq = i_tmp; g_curSamFreq48000Family = g_curSamFreq % 48000 == 0; if(g_curSamFreq48000Family) { i_tmp = MCLK_48; } else { i_tmp = MCLK_441; } setG_curSamFreqMultiplier(g_curSamFreq/(i_tmp/512)); outuint(c_audioControl, SET_SAMPLE_FREQ); outuint(c_audioControl, g_curSamFreq); #ifdef EP0_THREAD_COMBINED_WITH_SPI spi(c_spi, c_spi_ss); /* CodecConfig */ #endif /* Wait for handshake back - i.e. pll locked and clocks okay */ chkct(c_audioControl, XS1_CT_END); } /* Allow time for our feedback to stabalise*/ { timer t; unsigned time; t :> time; t when timerafter(time+5000000):> void; } } /* Send 0 Length as status stage */ return XUD_SetBuffer(ep0_in, buffer, 0); } /* Direction: Device-to-host: Send Current Sample Freq */ else { switch(unitID) { case ID_CLKSRC_EXT: case ID_CLKSRC_ADAT: #ifdef REPORT_SPDIF_FREQ /* Interogate clockgen thread for SPDIF freq */ if (!isnull(c_clk_ctl)) { outuint(c_clk_ctl, GET_FREQ); outuint(c_clk_ctl, CLOCK_SPDIF_INDEX); outct(c_clk_ctl, XS1_CT_END); (buffer, unsigned[])[0] = inuint(c_clk_ctl); chkct(c_clk_ctl, XS1_CT_END); } else { (buffer, unsigned[])[0] = g_curSamFreq; } break; #endif case ID_CLKSRC_INT: /* Always report our current operating frequency */ (buffer, unsigned[])[0] = g_curSamFreq; break; default: XUD_Error_hex("Unknown Unit ID in Sample Frequency Control Request", unitID); break; } return XUD_DoGetRequest(ep0_out, ep0_in, buffer, sp.wLength, sp.wLength ); } break; } /* Clock Valid Control */ case CS_CLOCK_VALID_CONTROL: { switch(unitID) { case ID_CLKSRC_INT: /* Internal clock always valid */ buffer[0] = 1; break; case ID_CLKSRC_EXT: /* Interogate clockgen thread for validity */ if (!isnull(c_clk_ctl)) { outuint(c_clk_ctl, GET_VALID); outuint(c_clk_ctl, CLOCK_SPDIF_INDEX); outct(c_clk_ctl, XS1_CT_END); buffer[0] = inuint(c_clk_ctl); chkct(c_clk_ctl, XS1_CT_END); } break; case ID_CLKSRC_ADAT: if (!isnull(c_clk_ctl)) { outuint(c_clk_ctl, GET_VALID); outuint(c_clk_ctl, CLOCK_ADAT_INDEX); outct(c_clk_ctl, XS1_CT_END); buffer[0] = inuint(c_clk_ctl); chkct(c_clk_ctl, XS1_CT_END); } break; default: XUD_Error_hex("Unknown Unit ID in Clock Valid Control Request: ", unitID); break; } return XUD_DoGetRequest( ep0_out, ep0_in, buffer, sp.wLength, sp.wLength ); break; } default: XUD_Error_hex("Unknown Control Selector for Clock Unit: ", sp.wValue >> 8 ); break; } break; /* Clock Unit IDs */ } /* Clock Selector Unit(s) */ case ID_CLKSEL: { if ((sp.wValue >> 8) == CX_CLOCK_SELECTOR_CONTROL) { if( sp.bmRequestType.Direction == 0 ) { /* Direction: Host-to-device */ datalength = XUD_GetBuffer(ep0_out, buffer); if(datalength < 0) return datalength; /* Check for correct datalength for clock sel */ if(datalength == 1) { if (!isnull(c_clk_ctl)) { outuint(c_clk_ctl, SET_SEL); outuint(c_clk_ctl, buffer[0]); outct(c_clk_ctl, XS1_CT_END); } } /* Send 0 Length as status stage */ return XUD_DoSetRequestStatus(ep0_in, 0); } else { buffer[0] = 1; /* Direction: Device-to-host: Send Current Selection */ if (!isnull(c_clk_ctl)) { outuint(c_clk_ctl, GET_SEL); outct(c_clk_ctl, XS1_CT_END); buffer[0] = inuint(c_clk_ctl); chkct(c_clk_ctl, XS1_CT_END); } return XUD_DoGetRequest( ep0_out, ep0_in, buffer, 1, sp.wLength ); } } else { XUD_Error_hex("Unknown control on clock selector", sp.wValue); } break; } /* Feature Units */ case FU_USBOUT: case FU_USBIN: /* Inspect Control Selector (CS) */ switch(sp.wValue >> 8) { case FU_VOLUME_CONTROL: if(sp.bmRequestType.Direction == BM_REQTYPE_DIRECTION_OUT) /* Direction: Host-to-device */ { /* Expect OUT here (with v2yyolume) */ loop = XUD_GetBuffer(ep0_out, buffer); /* Check for rst/suspend */ if(loop < 0) { printintln(loop); return loop; } #if 1 if(unitID == FU_USBOUT) { if ((sp.wValue & 0xff) <= NUM_USB_CHAN_OUT) { volsOut[ sp.wValue&0xff ] = buffer[0] | (((int) (signed char) buffer[1]) << 8); updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl ); } } else { if ((sp.wValue & 0xff) <= NUM_USB_CHAN_IN) { volsIn[ sp.wValue&0xff ] = buffer[0] | (((int) (signed char) buffer[1]) << 8); updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl ); } } #endif /* Send 0 Length as status stage */ return XUD_DoSetRequestStatus(ep0_in, 0); } else /* Direction: Device-to-host */ { if(unitID == FU_USBOUT) { if ((sp.wValue & 0xff) <= NUM_USB_CHAN_OUT) { buffer[0] = volsOut[ sp.wValue&0xff ]; buffer[1] = volsOut[ sp.wValue&0xff ] >> 8; } else { buffer[0] = buffer[1] = 0; } } else { if ((sp.wValue & 0xff) <= NUM_USB_CHAN_IN) { buffer[0] = volsIn[ sp.wValue&0xff ]; buffer[1] = volsIn[ sp.wValue&0xff ] >> 8; } else { buffer[0] = buffer[1] = 0; } } return XUD_DoGetRequest(ep0_out, ep0_in, buffer, sp.wLength, sp.wLength); } break; /* FU_VOLUME_CONTROL */ case FU_MUTE_CONTROL: if(sp.bmRequestType.Direction == BM_REQTYPE_DIRECTION_OUT) // Direction: Host-to-device { { unsigned time; timer t; t :> time; t when timerafter(time+10000000):> void; } /* Expect OUT here with mute */ loop = XUD_GetBuffer(ep0_out, buffer); if(loop < 0) return loop; if (unitID == FU_USBOUT) { if ((sp.wValue & 0xff) <= NUM_USB_CHAN_OUT) { mutesOut[sp.wValue & 0xff] = buffer[0]; updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl); } } else { mutesIn[ sp.wValue&0xff ] = buffer[0]; updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl); } /* Send 0 Length as status stage */ return XUD_DoSetRequestStatus(ep0_in, 0); } else // Direction: Device-to-host { if(unitID == FU_USBOUT) { if ((sp.wValue & 0xff) <= NUM_USB_CHAN_OUT) { buffer[0] = mutesOut[sp.wValue&0xff]; } else { buffer[0] = 0; } } else { buffer[0] = mutesIn[ sp.wValue&0xff ]; } return XUD_DoGetRequest(ep0_out, ep0_in, buffer, sp.wLength, sp.wLength); } break; //default: // XUD_Error("Unknown Control Selector for FU"); //break; } break; /* FU_USBIN */ #ifdef MIXER case ID_XU_OUT: { if(sp.bmRequestType.Direction == BM_REQTYPE_DIRECTION_OUT) /* Direction: Host-to-device */ { unsigned volume = 0; int c = sp.wValue & 0xff; loop = XUD_GetBuffer(ep0_out, buffer); if(loop < 0) return loop; channelMapAud[c] = buffer[0] | buffer[1] << 8; if (!isnull(c_mix_ctl)) { if (c < NUM_USB_CHAN_OUT) { //master { // c_mix_ctl <: SET_SAMPLES_TO_DEVICE_MAP; // c_mix_ctl <: c; // c_mix_ctl <: (int) channelMapAud[c]; //} outuint(c_mix_ctl, SET_SAMPLES_TO_DEVICE_MAP); outuint(c_mix_ctl, c); outuint(c_mix_ctl, channelMapAud[c]); outct(c_mix_ctl, XS1_CT_END); } } /* Send 0 Length as status stage */ return XUD_DoSetRequestStatus(ep0_in, 0); } else { buffer[0] = channelMapAud[sp.wValue & 0xff]; buffer[1] = 0; return XUD_DoGetRequest(ep0_out, ep0_in, buffer, sp.wLength, sp.wLength); } } break; case ID_XU_IN: { if(sp.bmRequestType.Direction == BM_REQTYPE_DIRECTION_OUT) /* Direction: Host-to-device */ { unsigned volume = 0; int c = sp.wValue & 0xff; loop = XUD_GetBuffer(ep0_out, buffer); if(loop < 0) return loop; channelMapUsb[c] = buffer[0] | buffer[1] << 8; if (!isnull(c_mix_ctl)) { if (c < NUM_USB_CHAN_IN) { //master { // c_mix_ctl <: SET_SAMPLES_TO_HOST_MAP; // c_mix_ctl <: c; // c_mix_ctl <: (int) channelMapUsb[c]; //} outuint(c_mix_ctl, SET_SAMPLES_TO_HOST_MAP); outuint(c_mix_ctl, c); outuint(c_mix_ctl, channelMapUsb[c]); outct(c_mix_ctl, XS1_CT_END); } } /* Send 0 Length as status stage */ return XUD_DoSetRequestStatus(ep0_in, 0); } else { buffer[0] = channelMapUsb[sp.wValue & 0xff]; buffer[1] = 0; return XUD_DoGetRequest(ep0_out, ep0_in, buffer, sp.wLength, sp.wLength); } } break; case ID_XU_MIXSEL: { int cs = sp.wValue >> 8; /* Control Selector */ int cn = sp.wValue & 0xff; /* Channel number */ /* Check for Get or Set */ if(sp.bmRequestType.Direction == BM_REQTYPE_DIRECTION_OUT) { /* Direction: Host-to-device */ /* Host-to-device */ datalength = XUD_GetBuffer(ep0_out, buffer); /* Check for reset/suspend */ if(datalength < 0) { return datalength; } if(datalength > 0) { /* cn bounds check for safety..*/ if(cn < MIX_INPUTS) { if(cs == CS_XU_MIXSEL) { /* Check for "off" - update local state */ if(buffer[0] == 0xFF) mixSel[cn] = (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT); else mixSel[cn] = buffer[0]; /* Update all mix maps */ for (int i = 0; i < MAX_MIX_COUNT; i++) { outuint(c_mix_ctl, SET_MIX_MAP); outuint(c_mix_ctl, i); /* Mix bus */ outuint(c_mix_ctl, cn); /* Mixer input */ outuint(c_mix_ctl, (int) mixSel[cn]); /* Source */ outct(c_mix_ctl, XS1_CT_END); } } } } /* Send 0 Length as status stage */ return XUD_DoSetRequestStatus(ep0_in, 0); } else { /* Direction: Device-to-Host (GET) */ buffer[0] = 0; /* Channel Number bounds check for safety */ if(cn < MIX_INPUTS) { /* Inspect control selector */ if(cs == CS_XU_MIXSEL) { buffer[0] = mixSel[cn]; } } return XUD_DoGetRequest(ep0_out, ep0_in, buffer, 1, 1 ); } break; } case ID_MIXER_1: if(sp.bmRequestType.Direction == BM_REQTYPE_DIRECTION_OUT) /* Direction: Host-to-device */ { unsigned volume = 0; /* Expect OUT here with mute */ loop = XUD_GetBuffer(ep0_out, buffer); if(loop < 0) return loop; mixer1Weights[sp.wValue & 0xff] = buffer[0] | buffer[1] << 8; if (mixer1Weights[sp.wValue & 0xff] == 0x8000) { volume = 0; } else { volume = db_to_mult(mixer1Weights[sp.wValue & 0xff], 8, 25); } if (!isnull(c_mix_ctl)) { //master { // c_mix_ctl <: SET_MIX_MULT; //c_mix_ctl <: (sp.wValue & 0xff) % 8; //c_mix_ctl <: (sp.wValue & 0xff) / 8; ///c_mix_ctl <: volume; //}/ outuint(c_mix_ctl, SET_MIX_MULT); outuint(c_mix_ctl, (sp.wValue & 0xff) % 8); outuint(c_mix_ctl, (sp.wValue & 0xff) / 8); outuint(c_mix_ctl, volume); outct(c_mix_ctl, XS1_CT_END); } /* Send 0 Length as status stage */ return XUD_DoSetRequestStatus(ep0_in, 0); } else { short weight = mixer1Weights[sp.wValue & 0xff]; buffer[0] = weight & 0xff; buffer[1] = (weight >> 8) & 0xff; return XUD_DoGetRequest(ep0_out, ep0_in, buffer, sp.wLength, sp.wLength); } break; #endif //default: ///* We dont have a unit with this ID! */ //XUD_Error_hex("ERR: Unknown control unit: ", sp.wIndex); //break; } /* switch(sp.wIndex >> 8) i.e Unit ID */ break; } case RANGE: { unitID = sp.wIndex >> 8; switch( unitID ) { /* Clock Source Units */ case ID_CLKSRC_EXT: case ID_CLKSRC_ADAT: case ID_CLKSRC_INT: /* Control Selector (CS) */ switch( sp.wValue >> 8 ) { case CS_SAM_FREQ_CONTROL: /* Currently always return all freqs for all clocks */ //switch(unitID) //{ // case ID_CLKSRC_INT: // case ID_CLKSRC_EXT: { int num_freqs = 0; int i = 2; #if MAX_FREQ >= 44100 storeFreq(buffer, i, 44100); num_freqs++; #endif #if MAX_FREQ >= 48000 storeFreq(buffer, i, 48000); num_freqs++; #endif #if MAX_FREQ >= 88200 storeFreq(buffer, i, 88200); num_freqs++; #endif #if MAX_FREQ >= 96000 storeFreq(buffer, i, 96000); num_freqs++; #endif #if MAX_FREQ >= 176400 storeFreq(buffer, i, 176400); num_freqs++; #endif #if MAX_FREQ >= 192000 storeFreq(buffer, i, 192000); num_freqs++; #endif #if MAX_FREQ >= 352800 storeFreq(buffer, i, 352800); num_freqs++; #endif #if MAX_FREQ >= 384000 storeFreq(buffer, i, 384000); num_freqs++; #endif storeShort(buffer, 0, num_freqs); // break; //} return XUD_DoGetRequest(ep0_out, ep0_in, buffer, i, sp.wLength); } break; //default: // XUD_Error_hex("Unknown Control Selector in Clock Source Range Request: ", sp.wValue); // break; } break; /* Feature Units */ case FU_USBIN: /* USB Data into Device */ case FU_USBOUT: /* USB Data from Device */ /* Control Selector (CS) */ switch( sp.wValue >> 8 ) { /* Volume control, send back same range for all channels (i.e. ignore CN) */ case FU_VOLUME_CONTROL: storeShort(buffer, 0, 1); storeShort(buffer, 2, MIN_VOLUME); storeShort(buffer, 4, MAX_VOLUME); storeShort(buffer, 6, VOLUME_RES); return XUD_DoGetRequest(ep0_out, ep0_in, buffer, sp.wLength, sp.wLength); break; //default: //XUD_Error_hex("Unknown control selector for FU: ", sp.wValue); // break; } break; #ifdef MIXER /* Mixer Unit */ case ID_MIXER_1: storeShort(buffer, 0, 1); storeShort(buffer, 2, MIN_MIXER_VOLUME); storeShort(buffer, 4, MAX_MIXER_VOLUME); storeShort(buffer, 6, VOLUME_RES_MIXER); return XUD_DoGetRequest(ep0_out, ep0_in, buffer, sp.wLength, sp.wLength); break; #endif //default: //XUD_Error_hex("Unknown Unit ID in Range Request selector for FU: ", sp.wIndex >> 8); //break; } break; } #ifdef MIXER case MEM: /* Memory Requests (5.2.7.1) */ unitID = sp.wIndex >> 8; switch( unitID ) { case ID_MIXER_1: if(sp.bmRequestType.Direction == BM_REQTYPE_DIRECTION_IN) { int length = 0; /* Device-to-Host (GET) */ switch(sp.wValue) /* offset */ { case 0: /* Input levels */ length = (NUM_USB_CHAN_IN + NUM_USB_CHAN_OUT) * 2; /* 2 bytes per chan */ for(int i = 0; i < (NUM_USB_CHAN_IN + NUM_USB_CHAN_OUT); i++) { /* Get the level and truncate to 16-bit */ if(i < NUM_USB_CHAN_IN) { if (!isnull(c_mix_ctl)) { outuint(c_mix_ctl, GET_INPUT_LEVELS); outuint(c_mix_ctl, (i - NUM_USB_CHAN_IN)); outct(c_mix_ctl, XS1_CT_END); storeShort(buffer, i*2, (inuint(c_mix_ctl)>>15)); chkct(c_mix_ctl, XS1_CT_END); } else { storeShort(buffer, i*2, 0); } } else { if (!isnull(c_mix_ctl)) { outuint(c_mix_ctl, GET_STREAM_LEVELS); outuint(c_mix_ctl, (i - NUM_USB_CHAN_IN)); outct(c_mix_ctl, XS1_CT_END); storeShort(buffer, i*2, (inuint(c_mix_ctl) >> 15)); chkct(c_mix_ctl, XS1_CT_END); } else { storeShort(buffer, i*2, 0); } } } break; case 1: /* Mixer Output levels */ length = MAX_MIX_COUNT * 2; /* 2 bytes per chan */ for(int i = 0; i < MAX_MIX_COUNT; i++) { if (!isnull(c_mix_ctl)) { outuint(c_mix_ctl, GET_OUTPUT_LEVELS); outuint(c_mix_ctl, i); outct(c_mix_ctl, XS1_CT_END); storeShort(buffer, i*2, (inuint(c_mix_ctl) >> 15)); chkct(c_mix_ctl, XS1_CT_END); } else { storeShort(buffer, i*2, 0); } } break; } return XUD_DoGetRequest(ep0_out, ep0_in, buffer, length, sp.wLength); } else { /* Host-to-device (SET) */ /* Currently no action for set mem request for any offset */ datalength = XUD_GetBuffer(ep0_out, buffer); /* Check for reset/suspend */ if(datalength < 0) { return datalength; } /* Send 0 Length as status stage */ return XUD_DoSetRequestStatus(ep0_in, 0); } break; } break; #endif } /* Didn't deal with request, return 1 */ return 1; } #if defined (AUDIO_CLASS_FALLBACK) || (AUDIO_CLASS==1) /* Handles the Audio Class 1.0 specific requests */ int AudioClassRequests_1(XUD_ep c_ep0_out, XUD_ep c_ep0_in, SetupPacket &sp, chanend c_audioControl, chanend ?c_mix_ctl, chanend ?c_clk_ctl #ifdef EP0_THREAD_COMBINED_WITH_SPI , chanend c_spi, chanend c_spi_ss #endif ) { unsigned char buffer[1024]; int unitID; int loop = 1; int i_tmp; /* Inspect request, NOTE: these are class specific requests */ switch( sp.bRequest ) { case SET_INTERFACE: { return XUD_SetBuffer(c_ep0_in, buffer, 0); break; } case B_REQ_SET_CUR: { loop = XUD_GetBuffer(c_ep0_out, buffer); /* Inspect for rst/suspend */ if(loop < 0) return loop; unitID = sp.wIndex >> 8; if (unitID == FU_USBOUT) { switch ((sp.wValue>>8) & 0xff) { case FU_VOLUME_CONTROL: { volsOut[ sp.wValue & 0xff ] = buffer[0] | (((int) (signed char) buffer[1]) << 8); updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl ); break; } case FU_MUTE_CONTROL: { mutesOut[ sp.wValue & 0xff ] = buffer[0]; updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl ); break; } } } else if (unitID == FU_USBIN) { switch ((sp.wValue>>8) & 0xff) { case FU_VOLUME_CONTROL: { volsIn[ sp.wValue & 0xff ] = buffer[0] | (((int) (signed char) buffer[1]) << 8); updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl ); break; } case FU_MUTE_CONTROL: { mutesIn[ sp.wValue & 0xff ] = buffer[0]; updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl ); break; } } } else if (unitID == 0) // sample freq { i_tmp = buffer[0] | (buffer [1] << 8) | (buffer[2] << 16); if(i_tmp != g_curSamFreq) { int curSamFreq44100Family; /* Windows Audio Class driver has a nice habbit of sending invalid SF's (e.g. 48001Hz) * when under stress. Lets double check it here and ignore if not valid. */ g_curSamFreq48000Family = i_tmp % 48000 == 0; curSamFreq44100Family = i_tmp % 44100 == 0; if(g_curSamFreq48000Family || curSamFreq44100Family) { g_curSamFreq = i_tmp; if(g_curSamFreq48000Family) { i_tmp = MCLK_48; } else { i_tmp = MCLK_441; } setG_curSamFreqMultiplier(g_curSamFreq/(i_tmp/512)); /* Instruct audio thread to change sample freq */ outuint(c_audioControl, SET_SAMPLE_FREQ); outuint(c_audioControl, g_curSamFreq); #ifdef EP0_THREAD_COMBINED_WITH_SPI spi(c_spi, c_spi_ss); /* CodecConfig */ #endif /* Wait for handshake back - i.e. pll locked and clocks okay */ chkct(c_audioControl, XS1_CT_END); /* Allow time for the change - feedback to stabalise */ { timer t; unsigned time; t :> time; t when timerafter(time+50000000):> void; } } } } return XUD_SetBuffer(c_ep0_in, buffer, 0); break; } case B_REQ_GET_CUR: { unitID = sp.wIndex >> 8; if (unitID == FU_USBOUT) { switch ((sp.wValue>>8) & 0xff) { case FU_VOLUME_CONTROL: { buffer[0] = volsOut[ sp.wValue&0xff ]; buffer[1] = volsOut[ sp.wValue&0xff ] >> 8; break; } case FU_MUTE_CONTROL: { buffer[0] = mutesOut[ sp.wValue & 0xff ]; break; } } } else if (unitID == FU_USBIN) { switch ((sp.wValue>>8) & 0xff) { case FU_VOLUME_CONTROL: { buffer[0] = volsIn[ sp.wValue&0xff ]; buffer[1] = volsIn[ sp.wValue&0xff ] >> 8; break; } case FU_MUTE_CONTROL: { buffer[0] = mutesIn[ sp.wValue & 0xff ]; break; } } } else if(unitID == 0) { //printintln(unitID); } loop = XUD_SetBuffer(c_ep0_in, buffer, sp.wLength); if(loop < 0) return loop; /* Status stage (0 length OUT) */ return XUD_GetBuffer(c_ep0_out,buffer); break; } case B_REQ_GET_MIN: { buffer[0] = (MIN_MIXER_VOLUME & 0xff); buffer[1] = (MIN_MIXER_VOLUME >> 8); loop = XUD_SetBuffer(c_ep0_in, buffer, sp.wLength); if(loop < 0) return loop; // Status stage (0 length OUT) return XUD_GetBuffer(c_ep0_out, buffer); break; } case B_REQ_GET_MAX: { buffer[0] = (MAX_MIXER_VOLUME & 0xff); buffer[1] = (MAX_MIXER_VOLUME >> 8); loop = XUD_SetBuffer(c_ep0_in, buffer, sp.wLength); if(loop < 0) return 0; // Status stage (0 length OUT) return XUD_GetBuffer(c_ep0_out, buffer); break; } case B_REQ_GET_RES: { buffer[0] = (VOLUME_RES_MIXER & 0xff); buffer[1] = (VOLUME_RES_MIXER >> 8); loop = XUD_SetBuffer(c_ep0_in, buffer, sp.wLength); if(loop < 0) return loop; // Status stage (0 length OUT) return XUD_GetBuffer(c_ep0_out, buffer); break; } } return 1; } #endif