From 75508503676cf3e02c98a005d46d9ea104ab28a8 Mon Sep 17 00:00:00 2001 From: Dave Solt Date: Wed, 4 Feb 2026 10:05:20 -0500 Subject: [PATCH 1/8] Restore and update Config.h for FT8 integration --- code/src/PhoenixSketch/Config.h | Bin 3218 -> 7162 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/code/src/PhoenixSketch/Config.h b/code/src/PhoenixSketch/Config.h index 380087a9822fc60619d77a358ecc5ad8c6d0e72a..2c2194b5ea4ad28467fd332684846bd80e5415a5 100644 GIT binary patch literal 7162 zcmcJU+fo}x5QgVERe6Uk=f=b?8_6cNb0;J)Dgz>tfH*fQ&QJl10%7bte3Jj0)-bD8 zO9q^jO6=@vyQhzTcMt#k{Zlv$N8vb}g-^N`VIzDDgU}3(u&z(>-Jd7ov@CwDuo9k# z+e>{4-LdChxC!TBr27l4zvy}vuJyMT7WMZaoa*V7p8Obo2)>;C(3Zq2;g8S>o4P(; z-Y25&g?{)|JdpG8lA>W%+O{}v%dTDR@?J6{?PF!Fm84sd1+T&|T*{uFmHx|pk+)vj zdt3Hu#9jYyjbD{^Z73ej6c;aL!vkr(r?}_JVKytqhYvN+jcOZ*h=yN1mqHm-~ zv&b9Dp7vy@=O=oDyIR!yR8&}-_s=3Vd-C;T`m>}u7sXMG>0?ntyQlB5Ry;k``btj@ z#c$|p&{WiPW7KRaf(BtVu7A_2A=)>RYw_-5*=n-v=*>=i%d^XDuFH|@Vt6%|>#{2= z8NMpHwuKx0J4xG$^k$TrJVgu3Z{$w?m_!3I@M*+==UOf4J+5Gat?HF$<==T{E<0 z?jhkIY{^EulBz39t?Rm@FIHUAHzPn#{sxCe^5vzTU&>dv@*mzj*Y&f$=k9)3156p{ zyB+aqpxpQEk@uUlhD8hUO6=@wZ}#J<#ZyLZx?;s^WiMGk4*Ce+lcej>f@_kv z6*HkB4GcX8lC>xP78f~|)}nXt%y2BhNUI`d8-6Q9b*W$X#`#y4V^WW1$yL%m-_ zNkIB=?nQ z@Zg0TAv?;2b=jCKwK_#MbY-oT7$?{PEc2pkRX!o-Et9Bm_ov;EhslX@jy#M5yHoq; zbD-Nem(6?)K&GZF0EU4E*s&>%I=U;A7cYdgOL}VAk-T*i`Q_h__51DE%GSS7pTG>F zJjV__4xZT4-y&Wur(f%i=o-cMN8%3JShSHpN4k3+J&@#h?A5A7!jNH<#`c+()Ntlp zdhJJvz`jDUfGx?t7qZWB1p``oJLLwJpmLIRWCXfX^~n2+Xf0w2iK={J*Q;7PO{^t*id>)S`7kc1U_m_qQemyx{&Oywit1jyyhzHb6G83Ar+-szvCz zh;-1K4!xspaxkVUqgR!V_@)oXc3|m>*4txRBEB8D+MpjjX)|_X9a)r|TGf-cvMK(iJEm4qhxufiX6e3X1yUHI zk==!AOgCDwt39cHpcwl0GzTF!dQf{zBCMaSMUO=kWsAJfT_NhUNBR9{;@^%sJ`=a? zxI%)&#jKF@t$MJps#}xqh=Ao70hxnM1d~=t9`rmkxue-Q?OVt>9h#D})Nqj0(Aj1+vY6kjeosoB?yRp(I7{bE<*wHBTS-Mt%iR!J zUPiM&mDl9-WkjRvn9cioX2=JA9ck`_N9tEh`zk_F(dh!gU{8M{fSO^w?;w28{YQPN zCA+dg>Z_7|-@2lI(!Sb%=D8y4ZF(Al-%4A*#*<%IepWB_Evc%%KNT*f`NOKfOW`3M zyWRa9Gh@D87H0J7WefIY%Pm>6y8lD&Kg3ArD;lV%Z6PnwX?`ZgPqj`Kw~FUc*fZ?x zML}8S)rfvu-z9VIStcbb_hLD3d(sOXQkULVg`$ozUmo7YzfGecip502babc;@odtQt(P=UE=BaB>hxrJ=8sg#T8a`-)IN*K zN22Q6yb>x8)NQ6|*W(2`8XETHWpduPOu2WXf2ZR?TjOqh8hU~{^fsP^W=XKEDNEMl z99>hrr(#dambIm`^mv&s9?I*r%L{MbpA#MJm)PHUa_z{Ib#VjPsdVdeIS*o=WBh!t zK9pZwJ~4i^sH^$bEMJbtYOlq4;+gzvwT;ss`mx-vkF;h|WWJ|T(C^xGsN+B|K4tzy~9kkRgm%BFxTarr8FZX!3ED9KyowDVD> zY(EyuohIiFph1p9vcjzR){U6?)Y%8`A~kVh+O0}gA163jLvrTiV`;=hlzIvuJM6@W zbJLEZEl>U+2c0Z)0eR%dRh}#}A5K3K(xj_JQMIE^eDZtAS&Z9f`sQRy5 zY95qH`r{J#Dv$t@;~kn@gDf^La*9a?YDP&An3yyf%II z>Brl=Jh^LuW#}kpXW!HJ5NXX`dB2`|jCNCIBgd|QWlEaG?_v^a=B|uJW zx8=l bUL&e;n-Wqh5N9A@9BWBWe*+jDMD9G9HKXap`_v%ctH$9_QGf@#2N%bND= zlZH&Z8TlRtw#`f9& z-S%3pwA-AG!WCy_&eZ0D3ToXvic_wt-Hw+XbX(-D>hVhj8{UCw!M5rf=<*#u!_5`N z&#dTq3+t)@2J42mTWDC8R|?%pqlg3vokrvjYQvpnhzI3Fkf&)#?YBKAaA#b<_M9kB zmrU){PXweFI1!oQ2B@-OoCx&=E==PJ#`uSpE3gjVhTn$q`1j?{cyt~vNJM?|M0^;d zh!|qrv8L;5sH;71py1EEP|K!voX{i<2tAELE<0x6x~X=+men(t6v>3{`cf+&Cw7#i zb2_mD7sLm>F=oToiO>N5bz2VN<&u?|{k-r zOP?%n*|OmB7>Y4q#inZVZo5-JPobY;B1Xu56cZP^rq?u0JwHKd-Zsu`FBo)q+dlPB zBB_(IuUXM*bP_nBi||w1Nx}$>&(?SR9c&q@djlEo zuyR_ck+8Po_0^ex_~=1+Q8yI|+-OXEQ{>AQ_FQgmem2%a#YqvcLi@Vl8Pdbbc9(Z2 zoxt@X;w01w)45Mvn*1r(g|$$Q6<;JA!*A6F;za}~XCjj31#H)WIxkzWZsB6Oa4%s5 z)*W0RU6-Fvs~y>B(fjWH-o&KXiR>Vrc?pf@wu3TZGYZp0B<#t9O+$zwvh|p{dzN=Y+bIVE|KcxDV*_eo$&pqL z{72r2I&l_?EEI-yTK7XApKUh-}J@w9@k{3Us*xtGxxvm@VZJ3$)elQ!Tt-YEJ9 z;HmG;--~m2FRSH%CCgIeM2urWv?kATfMuCr9+RV5%a{IqdIb~l5#gWc8rTVOuGFmL z1uQGpWGBta8()u2I!R+}gCc!gHxe5gKW+^bYdko)sqIraH?)%}LQ7tUGL&mu7nmjJ zjz!vJd)8>}F+w_X=GL`g-l-ksrq)<(iYIu_=|(w4Z~I{Q)}3;CGA%l{1LD8Nqo#1v zr+m-N5^koT9pfJOJJ#W9o7ZA)Bl)sLb^8jZRomvG>1tszUh^^&^?-F><4}XL-z_m2 zBzME4!Aim@jz+e_X4b$<)kBHRUW^L7L6sX#-_QgoiZ98N$M{{H_9v^1{$93%>1i7D zU5CM}`%(@Uoe((BQyR|Y4hQAc~wX#Me zCUT;C!Gm0gA6jRL?7qI-=~PL=hX*UY_OkX54nL+4b zQAET|9l?lPey@;&FmWE~FdvV|rY1|zJ`HVZwqCCr)|;9xmkfh(INZUk`EsZFtZMHQ z!iXK6FeyDu%@{WP=;!sOrtC4q+dz!BJEAE!HD`zG!X!-+b>TKOX@?8LYqMre&?0XH RCl04&h!@k-GmFqI^B;0uccB0P From 2020809d9e10f24ef1677a93f5f504d02b8e3de2 Mon Sep 17 00:00:00 2001 From: Dave Solt Date: Wed, 4 Feb 2026 10:56:53 -0500 Subject: [PATCH 2/8] FT8 integration: USB bridge, DSP routing, CAT control, audio fixes --- code/src/PhoenixSketch/CAT.cpp | 26 ++- code/src/PhoenixSketch/DSP.cpp | 98 ++++++-- code/src/PhoenixSketch/Ft8UsbBridge.cpp | 221 ++++++++++++++++++ code/src/PhoenixSketch/Ft8UsbBridge.h | 33 +++ code/src/PhoenixSketch/Globals.cpp | 2 +- code/src/PhoenixSketch/Loop.cpp | 25 +- code/src/PhoenixSketch/MainBoard_AudioIO.cpp | 158 +++++++++++-- code/src/PhoenixSketch/MainBoard_AudioIO.h | 4 + .../PhoenixSketch/MainBoard_DisplayHome.cpp | 29 ++- code/src/PhoenixSketch/SDT.h | 10 +- 10 files changed, 548 insertions(+), 58 deletions(-) create mode 100644 code/src/PhoenixSketch/Ft8UsbBridge.cpp create mode 100644 code/src/PhoenixSketch/Ft8UsbBridge.h diff --git a/code/src/PhoenixSketch/CAT.cpp b/code/src/PhoenixSketch/CAT.cpp index 376951a..1691df0 100644 --- a/code/src/PhoenixSketch/CAT.cpp +++ b/code/src/PhoenixSketch/CAT.cpp @@ -1,4 +1,5 @@ #include "CAT.h" +#include "MainBoard_AudioIO.h" // GetFt8Mode() // Kenwood TS-480 CAT Interface (partial) // @@ -693,10 +694,22 @@ char *RX_write( char* cmd ){ char *TX_write( char* cmd ){ switch (modeSM.state_id){ case (ModeSm_StateId_SSB_RECEIVE):{ +#ifdef T41_USB_AUDIO + // Only allow CAT PTT (WSJT-X TX) when FT8 mode is enabled + if (GetFt8Mode()) { + ModeSm_dispatch_event(&modeSM, ModeSm_EventId_PTT_PRESSED); + } else { + // Ignore TX request if not in FT8 mode + // (optional) Serial.println("CAT TX ignored: FT8 mode is OFF"); + } +#else + // No USB audio build: allow normal CAT PTT behavior ModeSm_dispatch_event(&modeSM, ModeSm_EventId_PTT_PRESSED); +#endif break; } case (ModeSm_StateId_CW_RECEIVE):{ + // CW keying via CAT still allowed ModeSm_dispatch_event(&modeSM, ModeSm_EventId_KEY_PRESSED); break; } @@ -706,6 +719,7 @@ char *TX_write( char* cmd ){ return empty_string_p; } + char *VX_write( char* cmd ){ Debug("Got VX write"); return empty_string_p; @@ -751,8 +765,8 @@ char *PR_read( char* cmd ){ void CheckForCATSerialEvents(void){ int i; char c; - while( ( i = SerialUSB1.available() ) > 0 ){ - c = ( char )SerialUSB1.read(); + while( ( i = Serial.available() ) > 0 ){ + c = ( char )Serial.read(); i--; catCommand[ catCommandIndex ] = c; #ifdef DEBUG_CAT @@ -777,17 +791,17 @@ void CheckForCATSerialEvents(void){ #endif // DEBUG_CAT int i = 0; while( parser_output[i] != '\0' ){ - if( SerialUSB1.availableForWrite() > 0 ){ - SerialUSB1.print( parser_output[i] ); + if( Serial.availableForWrite() > 0 ){ + Serial.print( parser_output[i] ); #ifdef DEBUG_CAT Serial.print( parser_output[i] ); #endif i++; }else{ - SerialUSB1.flush(); + Serial.flush(); } } - SerialUSB1.flush(); + Serial.flush(); #ifdef DEBUG_CAT Serial.println(); #endif // DEBUG_CAT diff --git a/code/src/PhoenixSketch/DSP.cpp b/code/src/PhoenixSketch/DSP.cpp index 47f7169..87a0867 100644 --- a/code/src/PhoenixSketch/DSP.cpp +++ b/code/src/PhoenixSketch/DSP.cpp @@ -1,4 +1,6 @@ #include "SDT.h" +#include "Ft8UsbBridge.h" +#include "MainBoard_AudioIO.h" // for GetFt8Mode() float32_t DMAMEM float_buffer_L[READ_BUFFER_SIZE]; float32_t DMAMEM float_buffer_R[READ_BUFFER_SIZE]; @@ -14,6 +16,11 @@ static char *filename = nullptr; void SaveData(DataBlock *data, uint32_t suffix); // used by the unit tests static uint32_t swrTimer_ms = 0; +#ifdef T41_USB_AUDIO +extern AudioPlayQueue Q_usbOut_L; +extern AudioPlayQueue Q_usbOut_R; +#endif + #define RXTXZoom 3 #define TXIQZOOM 3 @@ -718,6 +725,36 @@ void PlayBuffer(DataBlock *data){ } } +#ifdef T41_USB_AUDIO +static float32_t usbTmp[BUFFER_SIZE]; +static float32_t g_usbRxGain = 1.5f; // GAIN SET FOR WJST, DEFAULT IS 2 + +void PlayUsbBufferPreVol(DataBlock *data){ + for (unsigned i = 0; i < N_BLOCKS; i++) { + int16_t *pL = Q_usbOut_L.getBuffer(); + int16_t *pR = Q_usbOut_R.getBuffer(); + + // Copy one block then apply gain + arm_copy_f32(&data->I[BUFFER_SIZE * i], usbTmp, BUFFER_SIZE); + arm_scale_f32(usbTmp, g_usbRxGain, usbTmp, BUFFER_SIZE); + + // Optional: clip to [-1, +1] to avoid wrap distortion + for (size_t k = 0; k < BUFFER_SIZE; k++) { + if (usbTmp[k] > 1.0f) usbTmp[k] = 1.0f; + else if (usbTmp[k] < -1.0f) usbTmp[k] = -1.0f; + } + + arm_float_to_q15(usbTmp, pL, BUFFER_SIZE); + arm_float_to_q15(usbTmp, pR, BUFFER_SIZE); + + Q_usbOut_L.playBuffer(); + Q_usbOut_R.playBuffer(); + } +} +#endif + + + /** * Initialize the global variables to their default startup values * 1) Configure the RXfilters @@ -921,23 +958,21 @@ DataBlock * ReceiveProcessing(const char *fname){ CWAudioFilter(&data, &RXfilters); } - // Interpolate - InterpolateReceiveData(&data, &RXfilters); - - // Volume adjust for audio volume setting. I and Q contain duplicate data, don't - // need to scale both - AdjustVolume(&data, &RXfilters); - - SaveData(&data, 6); // used by the unit tests - - // Play sound on the speaker - PlayBuffer(&data); - - elapsed_micros_sum = elapsed_micros_sum + usec; - elapsed_micros_idx_t++; - //Flag(0); - - return &data; +// Interpolate +InterpolateReceiveData(&data, &RXfilters); +// Volume knob only affects the speaker path +PlayUsbBufferPreVol(&data); +// Volume knob only affects the speaker path +AdjustVolume(&data, &RXfilters); +SaveData(&data, 6); // used by the unit tests +// Play sound on the speaker (post-volume) +PlayBuffer(&data); + +elapsed_micros_sum = elapsed_micros_sum + usec; +elapsed_micros_idx_t++; +//Flag(0); + +return &data; } @@ -965,7 +1000,34 @@ float32_t GetMicRRMS(void){ * @param data The data block to put the samples in * @return ESUCCESS if samples were read, EFAIL if insufficient samples are available */ -errno_t ReadMicrophoneBuffer(DataBlock *data){ +errno_t ReadMicrophoneBuffer(DataBlock *data) +{ + if (!data) return EFAIL; + +#ifdef T41_USB_AUDIO +if (GetFt8Mode()) { + const uint32_t outCount = N_BLOCKS_EX * BUFFER_SIZE; + + bool ok = Ft8UsbBridge_GetSamples(data->I, outCount); + if (!ok) { + memset(data->I, 0, outCount * sizeof(float32_t)); + } + + // Dual-mono + attenuation (prevents harshness/clipping/pulsing) + for (uint32_t i = 0; i < outCount; i++) { + float s = data->I[i] * 0.20f; // try 0.10–0.30 + data->I[i] = s; + data->Q[i] = s; + } + + data->N = outCount; + data->sampleRate_Hz = SR[SampleRate].rate; + return ESUCCESS; +} +#endif + + // ----- existing microphone code continues below ----- + // are there at least N_BLOCKS buffers in each channel available ? if ((uint32_t)Q_in_L_Ex.available() > N_BLOCKS_EX+0 && (uint32_t)Q_in_R_Ex.available() > N_BLOCKS_EX+0) { //counter++; diff --git a/code/src/PhoenixSketch/Ft8UsbBridge.cpp b/code/src/PhoenixSketch/Ft8UsbBridge.cpp new file mode 100644 index 0000000..ab4883e --- /dev/null +++ b/code/src/PhoenixSketch/Ft8UsbBridge.cpp @@ -0,0 +1,221 @@ +// Ft8UsbBridge.cpp +// +// USB (WSJT-X, ~44.1 kHz) -> Phoenix SDR TX path (sampleRate, e.g. 192 kHz) +// Linear resampler with a float FIFO. +// +// This version adds a simple "warm-up" phase: +// - The first few calls to Ft8UsbBridge_GetSamples() return pure silence. +// - After that, we only send fully interpolated data plus a small +// "hold last sample" tail if we ever run short. +// Goal: avoid the ugly "slow motor" pulsing on the very first TUNE. + +#include +#include +#include "Ft8UsbBridge.h" + +// ----------------------------------------------------------------------------- +// USB audio objects +// We use LEFT channel only from WSJT-X (WSJT usually sends same audio on L/R). +// ----------------------------------------------------------------------------- +AudioInputUSB g_usbIn; +AudioRecordQueue g_usbQueueL; +AudioRecordQueue g_usbQueueR; +AudioConnection g_pcUsbToQueueL(g_usbIn, 0, g_usbQueueL, 0); +AudioConnection g_pcUsbToQueueR(g_usbIn, 1, g_usbQueueR, 0); + +// ----------------------------------------------------------------------------- +// Internal SRC state +// ----------------------------------------------------------------------------- + +// SDR sample rate (e.g., 192000.0f). Set in Ft8UsbBridge_Init(). +static float g_sdrSampleRate = 192000.0f; + +// Teensy USB audio nominal sample rate. +// AUDIO_SAMPLE_RATE_EXACT ≈ 44117.64706 Hz. +static const float kUsbSampleRate = AUDIO_SAMPLE_RATE_EXACT; + +// FIFO for buffering USB-rate samples (float, mono). +// 4096 samples is ~90 ms at 44.1 kHz – plenty of cushion. +static float g_usbFifo[4096]; +static uint32_t g_fifoCount = 0; // number of valid samples in g_usbFifo[] +static float g_srcPhase = 0.0f; // fractional index in FIFO (in USB samples) + +// Track the last valid audio sample so we can avoid sudden zeros mid-stream. +static float g_lastSample = 0.0f; +static bool g_haveSample = false; + +// Simple "warm-up" counter: how many complete SDR blocks we've produced +// since Ft8UsbBridge_Init() was called. +static uint32_t g_blocksSinceInit = 0; + +// ----------------------------------------------------------------------------- +// FIFO helpers +// ----------------------------------------------------------------------------- + +// Compact FIFO after "consumed" samples have been used. +static void fifo_consume(uint32_t consumed) +{ + if (consumed == 0 || g_fifoCount == 0) return; + + if (consumed >= g_fifoCount) { + g_fifoCount = 0; + return; + } + + memmove(g_usbFifo, + g_usbFifo + consumed, + (g_fifoCount - consumed) * sizeof(float)); + + g_fifoCount -= consumed; +} + +// Pull as many 128-sample USB blocks as we can and append them to FIFO. +static void pump_usb_to_fifo() +{ + const uint32_t blockSamples = 128; + const uint32_t fifoCapacity = (uint32_t)(sizeof(g_usbFifo) / sizeof(g_usbFifo[0])); + + while (g_usbQueueL.available() > 0 && + g_usbQueueR.available() > 0 && + (g_fifoCount + blockSamples) <= fifoCapacity) + { + int16_t *pL = g_usbQueueL.readBuffer(); + int16_t *pR = g_usbQueueR.readBuffer(); + if (!pL || !pR) break; + + for (uint32_t i = 0; i < blockSamples; ++i) { + float l = (float)pL[i] / 32768.0f; + float r = (float)pR[i] / 32768.0f; + g_usbFifo[g_fifoCount + i] = 0.5f * (l + r); + } + + g_fifoCount += blockSamples; + g_usbQueueL.freeBuffer(); + g_usbQueueR.freeBuffer(); + } +} + + +// ----------------------------------------------------------------------------- +// Public API +// ----------------------------------------------------------------------------- + +void Ft8UsbBridge_Init(float sdrSampleRateHz) +{ + g_sdrSampleRate = (sdrSampleRateHz > 0.0f) ? sdrSampleRateHz : 192000.0f; + + g_fifoCount = 0; + g_srcPhase = 0.0f; + g_lastSample = 0.0f; + g_haveSample = false; + g_blocksSinceInit = 0; + + // Optional but helpful: reset queues so we start clean + g_usbQueueL.end(); + g_usbQueueR.end(); + g_usbQueueL.begin(); + g_usbQueueR.begin(); +} + + +// Get "outCount" SDR-rate samples for the TX path. +// Caller (DSP.cpp) will: +// - Put this into data->I, +// - Zero data->Q, +// - Run the TX chain (or direct-to-IQ, depending on your variant). +// +// We always fill the whole buffer. On startup we deliberately output +// a few blocks of silence so the USB FIFO can "stabilize" before the +// rig ever sees actual RF drive. +bool Ft8UsbBridge_GetSamples(float *out, uint32_t outCount) +{ + if (!out || outCount == 0) { + return false; + } + + // -------------------------------------------------------------- + // 0) WARM-UP PHASE + // + // On the very first TUNE, USB audio may only have just started to + // arrive from WSJT-X. To avoid any chance of block-by-block + // "motorboating", we deliberately send a few blocks of *pure + // silence* before we ever send any real tone. + // + // At 192 kHz with 2048-sample blocks, each block is ~10.7 ms. + // 4 blocks ≈ 43 ms of silence – inaudible in an FT8 TX but enough + // to get a stable FIFO. + // -------------------------------------------------------------- + const uint32_t WARMUP_BLOCKS = 4; + + if (g_blocksSinceInit < WARMUP_BLOCKS) + { + // Keep draining USB queues so they don’t overflow while we “warm up” + pump_usb_to_fifo(); + + memset(out, 0, outCount * sizeof(float)); + g_blocksSinceInit++; + return true; + } + + + // Bring in fresh USB data. + pump_usb_to_fifo(); + + // How many input (USB) samples to advance per output (SDR) sample. + // E.g. for 44.1k -> 192k: step ≈ 0.2299 + const float step = kUsbSampleRate / g_sdrSampleRate; + + float phase = g_srcPhase; + uint32_t produced = 0; + + // -------------------------------------------------------------- + // 1) Normal interpolation from FIFO + // -------------------------------------------------------------- + while (produced < outCount && g_fifoCount > 1) { + uint32_t i0 = (uint32_t)phase; + + // Need two samples for interpolation. + if (i0 + 1 >= g_fifoCount) { + break; + } + + float frac = phase - (float)i0; + float s0 = g_usbFifo[i0]; + float s1 = g_usbFifo[i0 + 1]; + + float s = s0 + frac * (s1 - s0); // linear interpolation + + out[produced++] = s; + + // Remember last valid sample so we can avoid sharp transitions + // if the FIFO ever runs slightly short mid-block. + g_lastSample = s; + g_haveSample = true; + + phase += step; + } + + // How many whole input samples did we consume? + uint32_t consumed = (uint32_t)phase; + g_srcPhase = phase - (float)consumed; + + if (consumed > 0) { + fifo_consume(consumed); + } + + // -------------------------------------------------------------- + // 2) Tail fill: if we ever underrun after warm-up, fill the rest + // of the block with the last valid sample. For a continuous + // WSJT tone this should be rare and effectively inaudible. + // -------------------------------------------------------------- + if (produced < outCount) { + float fill = (g_haveSample ? g_lastSample : 0.0f); + + for (uint32_t i = produced; i < outCount; ++i) { + out[i] = fill; + } + } + + g_blocksSinceInit++; + return true; +} diff --git a/code/src/PhoenixSketch/Ft8UsbBridge.h b/code/src/PhoenixSketch/Ft8UsbBridge.h new file mode 100644 index 0000000..92a85a4 --- /dev/null +++ b/code/src/PhoenixSketch/Ft8UsbBridge.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +// Simple USB→SDR sample-rate converter for FT8 transmit. +// - Captures mono audio from WSJT via USB (AudioInputUSB) +// - Stores it in a FIFO at ~44.1 kHz +// - Resamples to the Phoenix SDR sample rate (sampleRate, e.g., 192 kHz) +// - Provides blocks of float samples for the FT8 TX path. + +/** + * Initialize the FT8 USB bridge. + * + * Must be called after AudioMemory() and after the audio system is + * initialized (e.g., from InitializeAudio()). + * + * @param sdrSampleRateHz Phoenix SDR's internal sample rate (e.g. 192000.0f) + */ +void Ft8UsbBridge_Init(float sdrSampleRateHz); + +/** + * Get a block of SDR-rate audio samples for FT8 transmit. + * + * @param out Pointer to output buffer (length = outCount floats) + * @param outCount Number of SDR-rate samples requested + * + * @return true if we produced a reasonably complete block, + * false if there was not enough USB audio yet. + * + * On failure, the caller should typically zero-fill the TX buffer + * (or just accept that this block will be silent). + */ +bool Ft8UsbBridge_GetSamples(float *out, uint32_t outCount); diff --git a/code/src/PhoenixSketch/Globals.cpp b/code/src/PhoenixSketch/Globals.cpp index 8a81a23..c7ea452 100644 --- a/code/src/PhoenixSketch/Globals.cpp +++ b/code/src/PhoenixSketch/Globals.cpp @@ -489,7 +489,7 @@ time_t getTeensy3Time() { void setup(void){ Serial.begin(115200); - SerialUSB1.begin(38400); // For CAT control + Serial.begin(38400); // For CAT control Serial.println("T41 SDT Setup"); // get TIME from real time clock with 3V backup battery diff --git a/code/src/PhoenixSketch/Loop.cpp b/code/src/PhoenixSketch/Loop.cpp index 6bd3b12..a263421 100644 --- a/code/src/PhoenixSketch/Loop.cpp +++ b/code/src/PhoenixSketch/Loop.cpp @@ -85,6 +85,9 @@ */ #include "SDT.h" +#include "MainBoard_AudioIO.h" +#include "FrontPanel.h" + // FIFO buffer for interrupt events #define INTERRUPT_BUFFER_SIZE 16 @@ -1275,11 +1278,23 @@ void ConsumeInterrupt(void){ // Handle all the other non-encoder interrupts switch (interrupt){ - case (iBUTTON_PRESSED):{ - int32_t button = GetButton(); - HandleButtonPress(button); - break; - } + +case (iBUTTON_PRESSED):{ + int32_t button = GetButton(); + SetButton(-1); // clear latched button + + if (button == 16) { // SET FT8 MODE + bool newState = !GetFt8Mode(); + SetFt8Mode(newState); + break; + } + + HandleButtonPress(button); + break; +} + + + case (iVFO_CHANGE):{ // The VFO has been updated. We might have selected a different active VFO, // we might have changed frequency. diff --git a/code/src/PhoenixSketch/MainBoard_AudioIO.cpp b/code/src/PhoenixSketch/MainBoard_AudioIO.cpp index cfbc434..6b75b0a 100644 --- a/code/src/PhoenixSketch/MainBoard_AudioIO.cpp +++ b/code/src/PhoenixSketch/MainBoard_AudioIO.cpp @@ -52,6 +52,7 @@ */ #include "MainBoard_AudioIO.h" +#include "Ft8UsbBridge.h" /** * The transition from analog to digital and digital to analog are handled using a fork @@ -98,6 +99,7 @@ * to see what it is what you're trying to transmit. Probably don't need this anymore. */ + // Generated using this tool: https://www.pjrc.com/teensy/gui/index.html // GUItool: begin automatically generated code AudioInputI2SQuad i2s_quadIn; //xy=576.75,225 @@ -120,6 +122,16 @@ AudioMixer4 modeSelectOutL; //xy=1725.75,184 AudioMixer4 modeSelectOutExR; //xy=1727.75,103 AudioMixer4 modeSelectOutR; //xy=1732.75,291 AudioOutputI2SQuad i2s_quadOut; //xy=1969.75,138 + +#ifdef T41_USB_AUDIO +AudioPlayQueue Q_usbOut_L; +AudioPlayQueue Q_usbOut_R; +AudioOutputUSB usbOut; +// stereo gain blocks for the PC feed +AudioAmplifier usbRxGainL; +AudioAmplifier usbRxGainR; +#endif + AudioConnection patchCord1(i2s_quadIn, 0, modeSelectInExL, 0); AudioConnection patchCord2(i2s_quadIn, 1, modeSelectInExR, 0); AudioConnection patchCord3(i2s_quadIn, 2, modeSelectInL, 0); @@ -141,7 +153,17 @@ AudioConnection patchCord18(Q_out_L, 0, modeSelectOutL, 0); AudioConnection patchCord19(modeSelectOutExL, 0, i2s_quadOut, 0); AudioConnection patchCord20(modeSelectOutL, 0, i2s_quadOut, 2); AudioConnection patchCord21(modeSelectOutExR, 0, i2s_quadOut, 1); -AudioConnection patchCord22(modeSelectOutR, 0, i2s_quadOut, 3); +AudioConnection patchCord22(modeSelectOutR, 0, i2s_quadOut, 3); + +#ifdef T41_USB_AUDIO +AudioConnection patchUsbGainL(Q_usbOut_L, 0, usbRxGainL, 0); +AudioConnection patchUsbGainR(Q_usbOut_R, 0, usbRxGainR, 0); + +AudioConnection patchUsbL(usbRxGainL, 0, usbOut, 0); +AudioConnection patchUsbR(usbRxGainR, 0, usbOut, 1); +#endif + + AudioControlSGTL5000 pcm5102_mainBoard; //xy=874.75,449 // GUItool: end automatically generated code @@ -151,18 +173,20 @@ AudioControlSGTL5000_Extended sgtl5000_teensy; static ModeSm_StateId previousAudioIOState = ModeSm_StateId_ROOT; -/** - * Get the previous audio I/O state. - * - * Returns the ModeSm state that the audio routing was last configured for. - * Used to detect state changes and avoid unnecessary reconfiguration of the - * audio graph when the mode hasn't changed. - * - * @return The previous ModeSm_StateId that audio routing was configured for - */ -ModeSm_StateId GetAudioPreviousState(void){ - return previousAudioIOState; -} +// Always exist so other modules can link +static bool g_ft8Mode = false; + +#ifdef T41_USB_AUDIO +enum class TxAudioSource : uint8_t { MIC = 0, USB = 2 }; +static void SelectTxInputSource(void); + +// remember the user’s modulation so FT8 can restore it +static ModulationType g_savedModulation = USB; +static bool g_haveSavedModulation = false; + +static TxAudioSource g_prevTxAudioSource = TxAudioSource::MIC; +static TxAudioSource g_txAudioSource = TxAudioSource::MIC; // <-- IMPORTANT: MIC at boot +#endif /** * Select a single active channel on a 4-channel audio mixer. @@ -177,6 +201,7 @@ ModeSm_StateId GetAudioPreviousState(void){ * @param mixer Pointer to the AudioMixer4 object to configure * @param channel Channel number to enable (0-3), all others will be muted */ + void SelectMixerChannel(AudioMixer4 *mixer, uint8_t channel){ for (uint8_t k = 0; k < 4; k++){ if (k == channel) mixer->gain(k,1); @@ -184,6 +209,58 @@ void SelectMixerChannel(AudioMixer4 *mixer, uint8_t channel){ } } +#ifdef T41_USB_AUDIO +static void SelectTxInputSource(void) +{ + const uint8_t ch = static_cast(g_txAudioSource); + SelectMixerChannel(&modeSelectInExL, ch); + SelectMixerChannel(&modeSelectInExR, ch); +} +#endif + +void SetFt8Mode(bool enabled) +{ + g_ft8Mode = enabled; + +#ifdef T41_USB_AUDIO + // Select TX audio source + g_txAudioSource = enabled ? TxAudioSource::USB : TxAudioSource::MIC; + + if (enabled) { + // Save current modulation once, then force USB + if (!g_haveSavedModulation) { + g_savedModulation = ED.modulation[ED.activeVFO]; + g_haveSavedModulation = true; + } + ED.modulation[ED.activeVFO] = USB; + UpdateRFHardwareState(); + } else { + // Restore modulation when leaving FT8 + if (g_haveSavedModulation) { + ED.modulation[ED.activeVFO] = g_savedModulation; + g_haveSavedModulation = false; + UpdateRFHardwareState(); + } + } + + // If we're already in TX, apply immediately + if (modeSM.state_id == ModeSm_StateId_SSB_TRANSMIT) { + SelectTxInputSource(); + g_prevTxAudioSource = g_txAudioSource; + } +#else + (void)enabled; +#endif +} + + + +bool GetFt8Mode(void) +{ + return g_ft8Mode; +} + + /** * Mute all channels on a 4-channel audio mixer. * @@ -259,11 +336,21 @@ void UpdateTransmitAudioGain(void){ * @see ModeSm state machine for state transition logic * @see UpdateRFHardwareState() in RFBoard.cpp */ + void UpdateAudioIOState(void){ - if (modeSM.state_id == previousAudioIOState){ - // Already in this state, no need to change - return; +if (modeSM.state_id == previousAudioIOState){ + +#ifdef T41_USB_AUDIO + // If still in TX and the selected TX source changed, re-apply it. + if (modeSM.state_id == ModeSm_StateId_SSB_TRANSMIT && g_txAudioSource != g_prevTxAudioSource) { + SelectTxInputSource(); + g_prevTxAudioSource = g_txAudioSource; } +#endif + + return; +} + switch (modeSM.state_id){ case (ModeSm_StateId_CALIBRATE_OFFSET_SPACE): case (ModeSm_StateId_CALIBRATE_TX_IQ_SPACE): @@ -297,13 +384,36 @@ void UpdateAudioIOState(void){ Q_in_L.begin(); Q_in_R.begin(); // Microphone input starts - Q_in_L_Ex.begin(); + #ifdef T41_USB_AUDIO + if (GetFt8Mode()) { + // FT8 TX uses Ft8UsbBridge (not the mic record queues) + Q_in_L_Ex.end(); + Q_in_R_Ex.end(); + } else { + // Normal voice SSB TX uses microphone queues + Q_in_L_Ex.begin(); + Q_in_R_Ex.begin(); + } + #else + Q_in_L_Ex.begin(); Q_in_R_Ex.begin(); + #endif + sgtl5000_teensy.micGain(ED.currentMicGain); // Input is microphone - SelectMixerChannel(&modeSelectInExL,0); + #ifdef T41_USB_AUDIO + SelectTxInputSource(); // chooses MIC(0) or USB(2) + #else + SelectMixerChannel(&modeSelectInExL,0); // mic SelectMixerChannel(&modeSelectInExR,0); + #endif + + #ifdef T41_USB_AUDIO + g_prevTxAudioSource = g_txAudioSource; + #endif + + // Output is samples to RF transmit SelectMixerChannel(&modeSelectOutExL,0); SelectMixerChannel(&modeSelectOutExR,0); @@ -425,7 +535,10 @@ void UpdateAudioIOState(void){ * @see SR[] array in Config.h for supported sample rates */ void InitializeAudio(void){ + SetI2SFreq(SR[SampleRate].rate); + + // The sgtl5000_teensy is the controller for the Teensy Audio board. We use it to get // the microphone input for SSB, and the I/Q output for the exciter board. In other // words, it is used for the transmit path. @@ -433,6 +546,15 @@ void InitializeAudio(void){ sgtl5000_teensy.enable(); AudioMemory(500); AudioMemory_F32(10); + + +#ifdef T41_USB_AUDIO +Ft8UsbBridge_Init((float)SR[SampleRate].rate); +usbRxGainL.gain(2.0f); // try 1.0 to 6.0 FOR FT8 GAIN TO WINDOWS +usbRxGainR.gain(2.0f); +SetFt8Mode(false); +#endif + sgtl5000_teensy.inputSelect(AUDIO_INPUT_MIC); // set mic pre-amp gain to 40dB & audio gain to 12dB sgtl5000_teensy.micGain(10); // sets pre-amp and input gain to achieve 10dB of total gain sgtl5000_teensy.lineInLevel(0); // set ADC right and left channel volumes to 0dB diff --git a/code/src/PhoenixSketch/MainBoard_AudioIO.h b/code/src/PhoenixSketch/MainBoard_AudioIO.h index 95088ae..750c904 100644 --- a/code/src/PhoenixSketch/MainBoard_AudioIO.h +++ b/code/src/PhoenixSketch/MainBoard_AudioIO.h @@ -24,6 +24,10 @@ extern AudioSynthWaveformSine transmitIQcal_oscillator; */ int SetI2SFreq(int freq); +// FT8 / USB TX audio select +void SetFt8Mode(bool enabled); +bool GetFt8Mode(void); + /** * @brief Initialize dual-codec audio subsystem for Phoenix SDR * @note Configures SGTL5000 codecs for transmit path (Teensy Audio Board) and receive path (main board) diff --git a/code/src/PhoenixSketch/MainBoard_DisplayHome.cpp b/code/src/PhoenixSketch/MainBoard_DisplayHome.cpp index 24994ab..8bbb06d 100644 --- a/code/src/PhoenixSketch/MainBoard_DisplayHome.cpp +++ b/code/src/PhoenixSketch/MainBoard_DisplayHome.cpp @@ -21,6 +21,8 @@ #include #include "FreeSansBold24pt7b.h" #include "FreeSansBold18pt7b.h" +#include "MainBoard_AudioIO.h" + // External references to objects and variables defined in MainBoard_Display.cpp extern RA8875 tft; @@ -285,23 +287,32 @@ static int64_t oldCenterFreq = 0; static int32_t oldBand = -1; static ModeSm_StateId oldState = ModeSm_StateId_ROOT; static ModulationType oldModulation = DCF77; +static bool oldFt8Mode = false; /** * Render the frequency, band name, and modulation mode pane. */ void DrawFreqBandModPane(void) { - if ((oldCenterFreq != ED.centerFreq_Hz[ED.activeVFO]) || - (oldBand != ED.currentBand[ED.activeVFO]) || - (oldState != modeSM.state_id) || - (oldModulation != ED.modulation[ED.activeVFO])){ - PaneFreqBandMod.stale = true; - } + bool ft8 = false; + #ifdef T41_USB_AUDIO + ft8 = GetFt8Mode(); + #endif + +if ((oldCenterFreq != ED.centerFreq_Hz[ED.activeVFO]) || + (oldBand != ED.currentBand[ED.activeVFO]) || + (oldState != modeSM.state_id) || + (oldModulation != ED.modulation[ED.activeVFO]) || + (oldFt8Mode != ft8)){ + PaneFreqBandMod.stale = true; +} + if (!PaneFreqBandMod.stale) return; oldCenterFreq = ED.centerFreq_Hz[ED.activeVFO]; oldBand = ED.currentBand[ED.activeVFO]; oldState = modeSM.state_id; oldModulation = ED.modulation[ED.activeVFO]; + oldFt8Mode = ft8; tft.setFontDefault(); tft.fillRect(PaneFreqBandMod.x0, PaneFreqBandMod.y0, PaneFreqBandMod.width, PaneFreqBandMod.height, RA8875_BLACK); @@ -336,8 +347,10 @@ void DrawFreqBandModPane(void) { tft.print("(LSB)"); break; case USB: - tft.print("(USB)"); - break; + // Only label FT8 when FT8 mode is enabled + if (ft8) tft.print("(FT8)"); + else tft.print("(USB)"); + break; case AM: tft.print("(AM)"); break; diff --git a/code/src/PhoenixSketch/SDT.h b/code/src/PhoenixSketch/SDT.h index 9d4198a..648240c 100644 --- a/code/src/PhoenixSketch/SDT.h +++ b/code/src/PhoenixSketch/SDT.h @@ -6,11 +6,17 @@ #include "Config.h" #define RIGNAME "T41-EP SDT" -#define VERSION "Phx V1.2" +#define VERSION "Phx V1.3" // WSJT VERSION #include "BuildInfo.h" -#define Debug(x) Serial.println(x) +// Debug logging: enable only when you are NOT using USB Serial for CAT (e.g., FT8) +#ifdef ENABLE_DEBUG_SERIAL + #define Debug(x) Serial.println(x) +#else + #define Debug(x) do {} while(0) +#endif + #include // Installed via Arduino library manager #include // https://github.com/chipaudette/OpenAudio_ArduinoLibrary From bf2f9533bbaebb1c8f1aa22908103e6858149f2f Mon Sep 17 00:00:00 2001 From: Dave Solt Date: Fri, 6 Feb 2026 10:03:51 -0500 Subject: [PATCH 3/8] FT8 integration: USB bridge, FT8 mode toggle, CAT gating, RX audio tap pre-volume --- code/src/PhoenixSketch/CAT.cpp | 56 +++++++++--------- code/src/PhoenixSketch/Config.h | Bin 7162 -> 3537 bytes code/src/PhoenixSketch/DSP.cpp | 34 ++++++----- code/src/PhoenixSketch/Ft8UsbBridge.cpp | 5 ++ code/src/PhoenixSketch/Globals.cpp | 8 ++- code/src/PhoenixSketch/Loop.cpp | 24 ++++---- code/src/PhoenixSketch/MainBoard_AudioIO.cpp | 12 ++-- .../PhoenixSketch/MainBoard_DisplayHome.cpp | 22 +++---- 8 files changed, 87 insertions(+), 74 deletions(-) diff --git a/code/src/PhoenixSketch/CAT.cpp b/code/src/PhoenixSketch/CAT.cpp index 1691df0..d09f23a 100644 --- a/code/src/PhoenixSketch/CAT.cpp +++ b/code/src/PhoenixSketch/CAT.cpp @@ -754,7 +754,6 @@ char *PR_read( char* cmd ){ return obuf; } - /** * Poll SerialUSB1 for incoming CAT commands and process them * @@ -765,55 +764,56 @@ char *PR_read( char* cmd ){ void CheckForCATSerialEvents(void){ int i; char c; - while( ( i = Serial.available() ) > 0 ){ - c = ( char )Serial.read(); + +#ifdef T41_USB_AUDIO + while ( (i = Serial.available()) > 0 ) { + c = (char)Serial.read(); +#else + while ( (i = SerialUSB1.available()) > 0 ) { + c = (char)SerialUSB1.read(); +#endif i--; catCommand[ catCommandIndex ] = c; - #ifdef DEBUG_CAT + +#ifdef DEBUG_CAT Serial.print( catCommand[ catCommandIndex ] ); - #endif +#endif + if( c == ';' ){ - // Finished reading CAT command - #ifdef DEBUG_CAT +#ifdef DEBUG_CAT Serial.println(); - #endif // DEBUG_CAT - - // Check to see if the command is a good one BEFORE sending it - // to the command executor - //Serial.println( String("catCommand is ")+String(catCommand)+String(" catCommandIndex is ")+String(catCommandIndex)); +#endif char *parser_output = command_parser( catCommand ); catCommandIndex = 0; - // We executed it, now erase it memset( catCommand, 0, sizeof( catCommand )); + if( parser_output[0] != '\0' ){ - #ifdef DEBUG_CAT1 - Serial.println( parser_output ); - #endif // DEBUG_CAT int i = 0; while( parser_output[i] != '\0' ){ +#ifdef T41_USB_AUDIO if( Serial.availableForWrite() > 0 ){ Serial.print( parser_output[i] ); - #ifdef DEBUG_CAT - Serial.print( parser_output[i] ); - #endif +#else + if( SerialUSB1.availableForWrite() > 0 ){ + SerialUSB1.print( parser_output[i] ); +#endif i++; - }else{ - Serial.flush(); } } +#ifdef T41_USB_AUDIO Serial.flush(); - #ifdef DEBUG_CAT - Serial.println(); - #endif // DEBUG_CAT +#else + SerialUSB1.flush(); +#endif } - }else{ + } else { catCommandIndex++; if( catCommandIndex >= 128 ){ catCommandIndex = 0; - memset( catCommand, 0, sizeof( catCommand )); //clear out that overflowed buffer! - #ifdef DEBUG_CAT + memset( catCommand, 0, sizeof( catCommand )); +#ifdef DEBUG_CAT Serial.println( "CAT command buffer overflow" ); - #endif +#endif } } } diff --git a/code/src/PhoenixSketch/Config.h b/code/src/PhoenixSketch/Config.h index 2c2194b5ea4ad28467fd332684846bd80e5415a5..7d67707e145867052c5df1ce09b5647c61417b96 100644 GIT binary patch literal 3537 zcmb7HTXUOA41UkA(4;TPX(l%0*7Oa-k}$>K1513;mz_;m+swhhaH*UA`lDTJ)^3xu z$CEfV(n{J-(kJCZDi&EGVEV{x%lF}TuVZUsJLDKfBXGomcX$NCaXjh0^+2My0mZAm2gjLzq&}M6Kgw6+u zzwx{i4J^tUI4o+>tf1y;Ru0zfB#KBtnH!P6nFTlISiUfwXym~Snf1En1a8I`hnkZ? z(;=>v_*9zo0y`p8bbwhNkM|Pw8BUDx8C>BnO3uLCe(JxE$M-)@f40YF;haRwClADj zD@-DW7`MD`y9%muBWlRSH<1s=Tq~xGCIMmlQK)0P3S89X8u(NBO(=*|LAPC@Gapkc zqREU+tiS>J#xBRx-Zhmuz@K&{Kt6opMXIo;**Yth%3!k%uX(P8D0GF#GvA8unUh2o z#e!thc);hr+(2I9c}#7y!Y^@^b4*)rLOHO@qOHpuMDbjvcvGq_{HY`k@&(Tuogos~p@ZgK%cfxj<8$-Mm2qKlbd6Bo7qG#rAjt?`?Ld=x z<1MT>rv4IA(PCRQP$8O4E2^`jcdyo)mrYe-_G@hupXAw70~?{78|ULi-;OFn*uGtr zixjcrMYGP@J(_U5h}e|bVKVcH!{|TiTv*Ap^8B+h$MI9Sgm@kS=9jol#n2P1SRF@AJML=a|@HkuAHL%XAI3ZO<8vtRS9xl*Kd4#zICe4-+c;eeZ>wE6Y_j z`>yjhJZpQ_F8UJtSG+)l{N2Elz`_$CzGtnZ8GAmI6P7M-b!Tj8n0+CS*dOzm6lUNcT0olpPc}E9Ioc1+-Xd z_+7^RZbcLGDx*(7m6Bf?Y;E#!qw2#Fw7*_*P zsU$<)8iQ1=AX=ab*c_x^!|<0TzZj$uiHTZ>yx>k9#5bL1W!X90jdiKe@a_)ze~P1a zcNDG%%ML+7;;UU|C)UNGN4(y{G?+o?VN*oJNobnlU_W&bSw1=e9Ff?LF_|ebj4Gr0Sh|5%r%Q4ZA q8yd6iwlzs89sXZ2G-%tE>QJmvld!0rX-{Fl4RNpEzq1V8H2wn^c*OSr literal 7162 zcmcJU+fo}x5QgVERe6Uk=f=b?8_6cNb0;J)Dgz>tfH*fQ&QJl10%7bte3Jj0)-bD8 zO9q^jO6=@vyQhzTcMt#k{Zlv$N8vb}g-^N`VIzDDgU}3(u&z(>-Jd7ov@CwDuo9k# z+e>{4-LdChxC!TBr27l4zvy}vuJyMT7WMZaoa*V7p8Obo2)>;C(3Zq2;g8S>o4P(; z-Y25&g?{)|JdpG8lA>W%+O{}v%dTDR@?J6{?PF!Fm84sd1+T&|T*{uFmHx|pk+)vj zdt3Hu#9jYyjbD{^Z73ej6c;aL!vkr(r?}_JVKytqhYvN+jcOZ*h=yN1mqHm-~ zv&b9Dp7vy@=O=oDyIR!yR8&}-_s=3Vd-C;T`m>}u7sXMG>0?ntyQlB5Ry;k``btj@ z#c$|p&{WiPW7KRaf(BtVu7A_2A=)>RYw_-5*=n-v=*>=i%d^XDuFH|@Vt6%|>#{2= z8NMpHwuKx0J4xG$^k$TrJVgu3Z{$w?m_!3I@M*+==UOf4J+5Gat?HF$<==T{E<0 z?jhkIY{^EulBz39t?Rm@FIHUAHzPn#{sxCe^5vzTU&>dv@*mzj*Y&f$=k9)3156p{ zyB+aqpxpQEk@uUlhD8hUO6=@wZ}#J<#ZyLZx?;s^WiMGk4*Ce+lcej>f@_kv z6*HkB4GcX8lC>xP78f~|)}nXt%y2BhNUI`d8-6Q9b*W$X#`#y4V^WW1$yL%m-_ zNkIB=?nQ z@Zg0TAv?;2b=jCKwK_#MbY-oT7$?{PEc2pkRX!o-Et9Bm_ov;EhslX@jy#M5yHoq; zbD-Nem(6?)K&GZF0EU4E*s&>%I=U;A7cYdgOL}VAk-T*i`Q_h__51DE%GSS7pTG>F zJjV__4xZT4-y&Wur(f%i=o-cMN8%3JShSHpN4k3+J&@#h?A5A7!jNH<#`c+()Ntlp zdhJJvz`jDUfGx?t7qZWB1p``oJLLwJpmLIRWCXfX^~n2+Xf0w2iK={J*Q;7PO{^t*id>)S`7kc1U_m_qQemyx{&Oywit1jyyhzHb6G83Ar+-szvCz zh;-1K4!xspaxkVUqgR!V_@)oXc3|m>*4txRBEB8D+MpjjX)|_X9a)r|TGf-cvMK(iJEm4qhxufiX6e3X1yUHI zk==!AOgCDwt39cHpcwl0GzTF!dQf{zBCMaSMUO=kWsAJfT_NhUNBR9{;@^%sJ`=a? zxI%)&#jKF@t$MJps#}xqh=Ao70hxnM1d~=t9`rmkxue-Q?OVt>9h#D})Nqj0(Aj1+vY6kjeosoB?yRp(I7{bE<*wHBTS-Mt%iR!J zUPiM&mDl9-WkjRvn9cioX2=JA9ck`_N9tEh`zk_F(dh!gU{8M{fSO^w?;w28{YQPN zCA+dg>Z_7|-@2lI(!Sb%=D8y4ZF(Al-%4A*#*<%IepWB_Evc%%KNT*f`NOKfOW`3M zyWRa9Gh@D87H0J7WefIY%Pm>6y8lD&Kg3ArD;lV%Z6PnwX?`ZgPqj`Kw~FUc*fZ?x zML}8S)rfvu-z9VIStcbb_hLD3d(sOXQkULVg`$ozUmo7YzfGecip502babc;@odtQt(P=UE=BaB>hxrJ=8sg#T8a`-)IN*K zN22Q6yb>x8)NQ6|*W(2`8XETHWpduPOu2WXf2ZR?TjOqh8hU~{^fsP^W=XKEDNEMl z99>hrr(#dambIm`^mv&s9?I*r%L{MbpA#MJm)PHUa_z{Ib#VjPsdVdeIS*o=WBh!t zK9pZwJ~4i^sH^$bEMJbtYOlq4;+gzvwT;ss`mx-vkF;h|WWJ|T(C^xGsN+B|K4tzy~9kkRgm%BFxTarr8FZX!3ED9KyowDVD> zY(EyuohIiFph1p9vcjzR){U6?)Y%8`A~kVh+O0}gA163jLvrTiV`;=hlzIvuJM6@W zbJLEZEl>U+2c0Z)0eR%dRh}#}A5K3K(xj_JQMIE^eDZtAS&Z9f`sQRy5 zY95qH`r{J#Dv$t@;~kn@gDf^La*9a?YDP&An3yyf%II z>Brl=Jh #include #include "Ft8UsbBridge.h" +#include "Config.h" + +#if defined(T41_USB_AUDIO) && (defined(USB_AUDIO) || defined(USB_MIDI_AUDIO_SERIAL)) // ----------------------------------------------------------------------------- // USB audio objects @@ -219,3 +222,5 @@ bool Ft8UsbBridge_GetSamples(float *out, uint32_t outCount) g_blocksSinceInit++; return true; } + +#endif \ No newline at end of file diff --git a/code/src/PhoenixSketch/Globals.cpp b/code/src/PhoenixSketch/Globals.cpp index c7ea452..6a5f6be 100644 --- a/code/src/PhoenixSketch/Globals.cpp +++ b/code/src/PhoenixSketch/Globals.cpp @@ -489,7 +489,13 @@ time_t getTeensy3Time() { void setup(void){ Serial.begin(115200); - Serial.begin(38400); // For CAT control + +#ifndef T41_USB_AUDIO + // Non-USB-audio build: + // CAT on SerialUSB1 + SerialUSB1.begin(38400); +#endif + Serial.println("T41 SDT Setup"); // get TIME from real time clock with 3V backup battery diff --git a/code/src/PhoenixSketch/Loop.cpp b/code/src/PhoenixSketch/Loop.cpp index a263421..607e24d 100644 --- a/code/src/PhoenixSketch/Loop.cpp +++ b/code/src/PhoenixSketch/Loop.cpp @@ -1279,21 +1279,19 @@ void ConsumeInterrupt(void){ // Handle all the other non-encoder interrupts switch (interrupt){ -case (iBUTTON_PRESSED):{ - int32_t button = GetButton(); - SetButton(-1); // clear latched button - - if (button == 16) { // SET FT8 MODE - bool newState = !GetFt8Mode(); - SetFt8Mode(newState); - break; - } - - HandleButtonPress(button); - break; -} + case (iBUTTON_PRESSED):{ + int32_t button = GetButton(); + SetButton(-1); // clear latched button + if (button == 16) { // SET FT8 MODE + bool newState = !GetFt8Mode(); + SetFt8Mode(newState); + break; + } + HandleButtonPress(button); + break; + } case (iVFO_CHANGE):{ // The VFO has been updated. We might have selected a different active VFO, diff --git a/code/src/PhoenixSketch/MainBoard_AudioIO.cpp b/code/src/PhoenixSketch/MainBoard_AudioIO.cpp index 6b75b0a..8b23df1 100644 --- a/code/src/PhoenixSketch/MainBoard_AudioIO.cpp +++ b/code/src/PhoenixSketch/MainBoard_AudioIO.cpp @@ -548,12 +548,12 @@ void InitializeAudio(void){ AudioMemory_F32(10); -#ifdef T41_USB_AUDIO -Ft8UsbBridge_Init((float)SR[SampleRate].rate); -usbRxGainL.gain(2.0f); // try 1.0 to 6.0 FOR FT8 GAIN TO WINDOWS -usbRxGainR.gain(2.0f); -SetFt8Mode(false); -#endif + #ifdef T41_USB_AUDIO + Ft8UsbBridge_Init((float)SR[SampleRate].rate); + usbRxGainL.gain(2.0f); // try 1.0 to 6.0 FOR FT8 GAIN TO WINDOWS + usbRxGainR.gain(2.0f); + SetFt8Mode(false); + #endif sgtl5000_teensy.inputSelect(AUDIO_INPUT_MIC); // set mic pre-amp gain to 40dB & audio gain to 12dB sgtl5000_teensy.micGain(10); // sets pre-amp and input gain to achieve 10dB of total gain diff --git a/code/src/PhoenixSketch/MainBoard_DisplayHome.cpp b/code/src/PhoenixSketch/MainBoard_DisplayHome.cpp index 8bbb06d..5424d18 100644 --- a/code/src/PhoenixSketch/MainBoard_DisplayHome.cpp +++ b/code/src/PhoenixSketch/MainBoard_DisplayHome.cpp @@ -298,13 +298,13 @@ void DrawFreqBandModPane(void) { ft8 = GetFt8Mode(); #endif -if ((oldCenterFreq != ED.centerFreq_Hz[ED.activeVFO]) || - (oldBand != ED.currentBand[ED.activeVFO]) || - (oldState != modeSM.state_id) || - (oldModulation != ED.modulation[ED.activeVFO]) || - (oldFt8Mode != ft8)){ - PaneFreqBandMod.stale = true; -} + if ((oldCenterFreq != ED.centerFreq_Hz[ED.activeVFO]) || + (oldBand != ED.currentBand[ED.activeVFO]) || + (oldState != modeSM.state_id) || + (oldModulation != ED.modulation[ED.activeVFO]) || + (oldFt8Mode != ft8)){ + PaneFreqBandMod.stale = true; + } if (!PaneFreqBandMod.stale) return; @@ -347,10 +347,10 @@ if ((oldCenterFreq != ED.centerFreq_Hz[ED.activeVFO]) || tft.print("(LSB)"); break; case USB: - // Only label FT8 when FT8 mode is enabled - if (ft8) tft.print("(FT8)"); - else tft.print("(USB)"); - break; + // Only label FT8 when FT8 mode is enabled + if (ft8) tft.print("(FT8)"); + else tft.print("(USB)"); + break; case AM: tft.print("(AM)"); break; From 8c12f4acc65c5e0b9e3e5d24dd1cf2d86cf8e846 Mon Sep 17 00:00:00 2001 From: Dave Solt Date: Fri, 3 Jul 2026 15:12:44 -0400 Subject: [PATCH 4/8] FT8 working: RX clean (no comb), TX clean, contacts made. Requires Teensy core mods to usb_audio.cpp/h --- code/src/PhoenixSketch/Config.h | 14 ++-- code/src/PhoenixSketch/DSP.cpp | 68 ++++++++++-------- code/src/PhoenixSketch/Ft8UsbBridge.cpp | 75 ++++++++++++++++++++ code/src/PhoenixSketch/Ft8UsbBridge.h | 37 +++------- code/src/PhoenixSketch/Globals.cpp | 6 ++ code/src/PhoenixSketch/Loop.cpp | 5 +- code/src/PhoenixSketch/MainBoard_AudioIO.cpp | 30 ++++---- code/src/PhoenixSketch/SDT.h | 7 ++ 8 files changed, 159 insertions(+), 83 deletions(-) diff --git a/code/src/PhoenixSketch/Config.h b/code/src/PhoenixSketch/Config.h index 7d67707..c1b5788 100644 --- a/code/src/PhoenixSketch/Config.h +++ b/code/src/PhoenixSketch/Config.h @@ -18,22 +18,22 @@ // Control encoder direction and speed #define FAST_TUNE // comment this out to disable the FAST_TUNE algorithm -#define VOLUME_REVERSED true -#define FILTER_REVERSED true -#define MAIN_TUNE_REVERSED true -#define FINE_TUNE_REVERSED false +#define VOLUME_REVERSED false +#define FILTER_REVERSED false +#define MAIN_TUNE_REVERSED false +#define FINE_TUNE_REVERSED true #define ENCODER_FACTOR 0.25F // use 0.25f with cheap encoders that have 4 detents per step, // for other encoders or libs we use 1.0f // Direct coupled transmit -//#define DIRECT_COUPLED_TX +#define DIRECT_COUPLED_TX // Optional: use analog SWR on Teensy ADC pins 26 (FWD) / 27 (REV). // Default uses AD7991 digital SWR. -//#define USE_ANALOG_SWR +#define USE_ANALOG_SWR //#define ENABLE_DEBUG_SERIAL // TO ENABLE DEBUG UNCOMMENT THIS LINE AND SWITCH OFF FT8/DIGITAL NEED TO PROGRAM TEENSY AS DUAL SERIAL -//#define T41_USB_AUDIO 1 // TO ENABLE FT8 UNCOMMENT THIS LINE AND SWITCH ON FT8/DIGITAL NEED TO PROGRAM TEENSY AS SERIAL + MIDI + AUDIO. +#define T41_USB_AUDIO 1 // TO ENABLE FT8 UNCOMMENT THIS LINE AND SWITCH ON FT8/DIGITAL NEED TO PROGRAM TEENSY AS SERIAL + MIDI + AUDIO. // CW configuration #define CW_TRANSMIT_SPACE_TIMEOUT_MS 200 // how long to wait for another key press before exiting CW transmit state diff --git a/code/src/PhoenixSketch/DSP.cpp b/code/src/PhoenixSketch/DSP.cpp index ca8aa25..8151a2b 100644 --- a/code/src/PhoenixSketch/DSP.cpp +++ b/code/src/PhoenixSketch/DSP.cpp @@ -727,21 +727,35 @@ void PlayBuffer(DataBlock *data){ #ifdef T41_USB_AUDIO static float32_t usbTmp[BUFFER_SIZE]; -static float32_t g_usbRxGain = 1.5f; // GAIN SET FOR WJST, DEFAULT IS 2 +static float usbPhase = 0.0f; void PlayUsbBufferPreVol(DataBlock *data){ - for (unsigned i = 0; i < N_BLOCKS; i++) { + (void)data; // unused for this test + + static uint32_t usbCallCount = 0; + static elapsedMillis usbCallTimer = 0; + + usbCallCount++; + if (usbCallTimer > 1000) { + usbCallTimer = 0; + Serial.print("PlayUsbBufferPreVol calls/sec = "); + Serial.println(usbCallCount); + usbCallCount = 0; + } + + + const float usbFs = 44100.0f; + const float f0 = 1000.0f; + const float dphi = 2.0f * PI * f0 / usbFs; + + for (unsigned i = 0; i < 1; i++) { int16_t *pL = Q_usbOut_L.getBuffer(); int16_t *pR = Q_usbOut_R.getBuffer(); - // Copy one block then apply gain - arm_copy_f32(&data->I[BUFFER_SIZE * i], usbTmp, BUFFER_SIZE); - arm_scale_f32(usbTmp, g_usbRxGain, usbTmp, BUFFER_SIZE); - - // Optional: clip to [-1, +1] to avoid wrap distortion for (size_t k = 0; k < BUFFER_SIZE; k++) { - if (usbTmp[k] > 1.0f) usbTmp[k] = 1.0f; - else if (usbTmp[k] < -1.0f) usbTmp[k] = -1.0f; + usbTmp[k] = 0.2f * sinf(usbPhase); + usbPhase += dphi; + if (usbPhase >= 2.0f * PI) usbPhase -= 2.0f * PI; } arm_float_to_q15(usbTmp, pL, BUFFER_SIZE); @@ -960,15 +974,12 @@ DataBlock * ReceiveProcessing(const char *fname){ // Interpolate InterpolateReceiveData(&data, &RXfilters); - #if defined(T41_USB_AUDIO) && (defined(USB_AUDIO) || defined(USB_MIDI_AUDIO_SERIAL)) - // Send audio to PC *before* volume knob affects it - PlayUsbBufferPreVol(&data); + Ft8UsbBridge_PutRxSamples(data.I, data.N); #endif // Speaker path volume knob AdjustVolume(&data, &RXfilters); - SaveData(&data, 6); // used by the unit tests // Always feed the speaker output (works in both builds) @@ -1004,30 +1015,25 @@ float32_t GetMicRRMS(void){ * @param data The data block to put the samples in * @return ESUCCESS if samples were read, EFAIL if insufficient samples are available */ + errno_t ReadMicrophoneBuffer(DataBlock *data) { if (!data) return EFAIL; #ifdef T41_USB_AUDIO -if (GetFt8Mode()) { - const uint32_t outCount = N_BLOCKS_EX * BUFFER_SIZE; - - bool ok = Ft8UsbBridge_GetSamples(data->I, outCount); - if (!ok) { - memset(data->I, 0, outCount * sizeof(float32_t)); - } - - // Dual-mono + attenuation (prevents harshness/clipping/pulsing) - for (uint32_t i = 0; i < outCount; i++) { - float s = data->I[i] * 0.20f; // try 0.10–0.30 - data->I[i] = s; - data->Q[i] = s; + if (GetFt8Mode()) { + const uint32_t outCount = N_BLOCKS_EX * BUFFER_SIZE; + bool ok = Ft8UsbBridge_GetSamples(data->I, outCount); + if (!ok) { + memset(data->I, 0, outCount * sizeof(float32_t)); + } + for (uint32_t i = 0; i < outCount; i++) { + data->Q[i] = data->I[i]; + } + data->N = outCount; + data->sampleRate_Hz = SR[SampleRate].rate; + return ESUCCESS; } - - data->N = outCount; - data->sampleRate_Hz = SR[SampleRate].rate; - return ESUCCESS; -} #endif // ----- existing microphone code continues below ----- diff --git a/code/src/PhoenixSketch/Ft8UsbBridge.cpp b/code/src/PhoenixSketch/Ft8UsbBridge.cpp index dd8f753..d2ea03c 100644 --- a/code/src/PhoenixSketch/Ft8UsbBridge.cpp +++ b/code/src/PhoenixSketch/Ft8UsbBridge.cpp @@ -13,6 +13,7 @@ #include #include "Ft8UsbBridge.h" #include "Config.h" +static volatile bool g_ft8TxActive = false; #if defined(T41_USB_AUDIO) && (defined(USB_AUDIO) || defined(USB_MIDI_AUDIO_SERIAL)) @@ -223,4 +224,78 @@ bool Ft8UsbBridge_GetSamples(float *out, uint32_t outCount) return true; } +// ───────────────────────────────────────────────────────────── +// RX PATH: 192kHz DSP audio → resample → FIFO → USB to PC +// ───────────────────────────────────────────────────────────── +static const uint32_t RX_FIFO_SIZE = 16384; +DMAMEM static float g_rxFifo[RX_FIFO_SIZE]; +static volatile uint32_t g_rxWriteIdx = 0; +static volatile uint32_t g_rxReadIdx = 0; + +static inline uint32_t RxNextIdx(uint32_t i) { return (i + 1u) % RX_FIFO_SIZE; } + +static inline bool RxFifoPush(float x) { + uint32_t next = RxNextIdx(g_rxWriteIdx); + if (next == g_rxReadIdx) return false; + g_rxFifo[g_rxWriteIdx] = x; + g_rxWriteIdx = next; + return true; +} + +static inline bool RxFifoPop(float *x) { + if (g_rxReadIdx == g_rxWriteIdx) return false; + *x = g_rxFifo[g_rxReadIdx]; + g_rxReadIdx = RxNextIdx(g_rxReadIdx); + return true; +} + +static inline uint32_t RxFifoCount(void) { + if (g_rxWriteIdx >= g_rxReadIdx) return g_rxWriteIdx - g_rxReadIdx; + return RX_FIFO_SIZE - g_rxReadIdx + g_rxWriteIdx; +} + +void Ft8UsbBridge_PutRxSamples(const float *in, uint32_t count) +{ + if (!in || count < 2) return; + static float srcPos = 0.0f; + const float step = 192000.0f / 44100.0f; + while (srcPos < (float)(count - 1)) { + uint32_t i0 = (uint32_t)srcPos; + uint32_t i1 = i0 + 1; + float frac = srcPos - (float)i0; + float x = in[i0] + frac * (in[i1] - in[i0]); + if (x > 1.0f) x = 1.0f; + if (x < -1.0f) x = -1.0f; + RxFifoPush(x); + srcPos += step; + } + srcPos -= (float)(count - 1); +} + +void Ft8UsbBridge_DrainToUSB(void) +{ + + if (g_ft8TxActive) return; + if (RxFifoCount() < AUDIO_BLOCK_SAMPLES) return; + + int16_t left_buf[AUDIO_BLOCK_SAMPLES]; + int16_t right_buf[AUDIO_BLOCK_SAMPLES]; + + for (int k = 0; k < AUDIO_BLOCK_SAMPLES; k++) { + float x = 0.0f; + RxFifoPop(&x); + if (x > 1.0f) x = 1.0f; + if (x < -1.0f) x = -1.0f; + int16_t s = (int16_t)(x * 32767.0f); + left_buf[k] = s; + right_buf[k] = s; + } + usb_audio_push_block(left_buf, right_buf); +} + +void Ft8UsbBridge_SetTxActive(bool on) +{ + g_ft8TxActive = on; +} + #endif \ No newline at end of file diff --git a/code/src/PhoenixSketch/Ft8UsbBridge.h b/code/src/PhoenixSketch/Ft8UsbBridge.h index 92a85a4..37242f7 100644 --- a/code/src/PhoenixSketch/Ft8UsbBridge.h +++ b/code/src/PhoenixSketch/Ft8UsbBridge.h @@ -1,33 +1,12 @@ -#pragma once +#ifndef FT8_USB_BRIDGE_H_ +#define FT8_USB_BRIDGE_H_ #include -// Simple USB→SDR sample-rate converter for FT8 transmit. -// - Captures mono audio from WSJT via USB (AudioInputUSB) -// - Stores it in a FIFO at ~44.1 kHz -// - Resamples to the Phoenix SDR sample rate (sampleRate, e.g., 192 kHz) -// - Provides blocks of float samples for the FT8 TX path. - -/** - * Initialize the FT8 USB bridge. - * - * Must be called after AudioMemory() and after the audio system is - * initialized (e.g., from InitializeAudio()). - * - * @param sdrSampleRateHz Phoenix SDR's internal sample rate (e.g. 192000.0f) - */ -void Ft8UsbBridge_Init(float sdrSampleRateHz); - -/** - * Get a block of SDR-rate audio samples for FT8 transmit. - * - * @param out Pointer to output buffer (length = outCount floats) - * @param outCount Number of SDR-rate samples requested - * - * @return true if we produced a reasonably complete block, - * false if there was not enough USB audio yet. - * - * On failure, the caller should typically zero-fill the TX buffer - * (or just accept that this block will be silent). - */ +void Ft8UsbBridge_Init(float dspSampleRate); bool Ft8UsbBridge_GetSamples(float *out, uint32_t outCount); +void Ft8UsbBridge_PutRxSamples(const float *in, uint32_t count); +void Ft8UsbBridge_DrainToUSB(void); +void Ft8UsbBridge_SetTxActive(bool on); + +#endif \ No newline at end of file diff --git a/code/src/PhoenixSketch/Globals.cpp b/code/src/PhoenixSketch/Globals.cpp index 6a5f6be..a9b2eb8 100644 --- a/code/src/PhoenixSketch/Globals.cpp +++ b/code/src/PhoenixSketch/Globals.cpp @@ -7,6 +7,8 @@ */ #include "SDT.h" +#include "Ft8UsbBridge.h" + struct config_t ED; bool psdupdated = false; @@ -498,6 +500,10 @@ void setup(void){ Serial.println("T41 SDT Setup"); + Ft8UsbBridge_Init(192000.0f); + Serial.println("After Ft8UsbBridge init/enable:"); + //Ft8UsbBridge_PrintStats(Serial); + // get TIME from real time clock with 3V backup battery setSyncProvider(getTeensy3Time); diff --git a/code/src/PhoenixSketch/Loop.cpp b/code/src/PhoenixSketch/Loop.cpp index 607e24d..d966c9f 100644 --- a/code/src/PhoenixSketch/Loop.cpp +++ b/code/src/PhoenixSketch/Loop.cpp @@ -87,7 +87,7 @@ #include "SDT.h" #include "MainBoard_AudioIO.h" #include "FrontPanel.h" - +#include "Ft8UsbBridge.h" // FIFO buffer for interrupt events #define INTERRUPT_BUFFER_SIZE 16 @@ -1453,7 +1453,8 @@ FASTRUN void loop(void){ CheckForFrontPanelInterrupts(); CheckForCATSerialEvents(); ConsumeInterrupt(); - + + // Step 2: Perform signal processing PerformSignalProcessing(); diff --git a/code/src/PhoenixSketch/MainBoard_AudioIO.cpp b/code/src/PhoenixSketch/MainBoard_AudioIO.cpp index 8b23df1..e297294 100644 --- a/code/src/PhoenixSketch/MainBoard_AudioIO.cpp +++ b/code/src/PhoenixSketch/MainBoard_AudioIO.cpp @@ -126,8 +126,8 @@ AudioOutputI2SQuad i2s_quadOut; //xy=1969.75,138 #ifdef T41_USB_AUDIO AudioPlayQueue Q_usbOut_L; AudioPlayQueue Q_usbOut_R; -AudioOutputUSB usbOut; -// stereo gain blocks for the PC feed +// usbOut removed from audio graph — tx_event owns USB transmit directly +// AudioOutputUSB usbOut; AudioAmplifier usbRxGainL; AudioAmplifier usbRxGainR; #endif @@ -156,14 +156,14 @@ AudioConnection patchCord21(modeSelectOutExR, 0, i2s_quadOut, 1); AudioConnection patchCord22(modeSelectOutR, 0, i2s_quadOut, 3); #ifdef T41_USB_AUDIO -AudioConnection patchUsbGainL(Q_usbOut_L, 0, usbRxGainL, 0); -AudioConnection patchUsbGainR(Q_usbOut_R, 0, usbRxGainR, 0); - -AudioConnection patchUsbL(usbRxGainL, 0, usbOut, 0); -AudioConnection patchUsbR(usbRxGainR, 0, usbOut, 1); +// Q_usbOut_L/R disconnected — Ft8UsbBridge_DrainToUSB() sends directly +// AudioConnection patchUsbGainL(Q_usbOut_L, 0, usbRxGainL, 0); +// AudioConnection patchUsbGainR(Q_usbOut_R, 0, usbRxGainR, 0); +// AudioConnection patchUsbL(usbRxGainL, 0, usbOut, 0); +// AudioConnection patchUsbR(usbRxGainR, 0, usbOut, 1); +// USB audio input (from WSJT-X) connected to channel 2 of TX input mixer #endif - AudioControlSGTL5000 pcm5102_mainBoard; //xy=874.75,449 // GUItool: end automatically generated code @@ -358,6 +358,7 @@ if (modeSM.state_id == previousAudioIOState){ case (ModeSm_StateId_CALIBRATE_RX_IQ): case (ModeSm_StateId_CW_RECEIVE): case (ModeSm_StateId_SSB_RECEIVE):{ + Ft8UsbBridge_SetTxActive(false); // Microphone input stops Q_in_L_Ex.end(); Q_in_R_Ex.end(); @@ -380,17 +381,17 @@ if (modeSM.state_id == previousAudioIOState){ break; } case (ModeSm_StateId_SSB_TRANSMIT):{ + Ft8UsbBridge_SetTxActive(true); // IQ from receive continues Q_in_L.begin(); Q_in_R.begin(); // Microphone input starts #ifdef T41_USB_AUDIO if (GetFt8Mode()) { - // FT8 TX uses Ft8UsbBridge (not the mic record queues) + // FT8 TX audio comes from g_usbQueueL/R in Ft8UsbBridge.cpp directly Q_in_L_Ex.end(); Q_in_R_Ex.end(); } else { - // Normal voice SSB TX uses microphone queues Q_in_L_Ex.begin(); Q_in_R_Ex.begin(); } @@ -547,12 +548,13 @@ void InitializeAudio(void){ AudioMemory(500); AudioMemory_F32(10); - - #ifdef T41_USB_AUDIO +#ifdef T41_USB_AUDIO Ft8UsbBridge_Init((float)SR[SampleRate].rate); - usbRxGainL.gain(2.0f); // try 1.0 to 6.0 FOR FT8 GAIN TO WINDOWS - usbRxGainR.gain(2.0f); + usbRxGainL.gain(0.1f); + usbRxGainR.gain(0.1f); SetFt8Mode(false); + static IntervalTimer usbAudioTimer; + usbAudioTimer.begin([]() { Ft8UsbBridge_DrainToUSB(); }, 2902); #endif sgtl5000_teensy.inputSelect(AUDIO_INPUT_MIC); // set mic pre-amp gain to 40dB & audio gain to 12dB diff --git a/code/src/PhoenixSketch/SDT.h b/code/src/PhoenixSketch/SDT.h index 648240c..9ab7742 100644 --- a/code/src/PhoenixSketch/SDT.h +++ b/code/src/PhoenixSketch/SDT.h @@ -4,6 +4,8 @@ #include #include #include "Config.h" +#include +extern AudioSynthWaveformSine sidetone_oscillator; #define RIGNAME "T41-EP SDT" #define VERSION "Phx V1.3" // WSJT VERSION @@ -17,6 +19,11 @@ #define Debug(x) do {} while(0) #endif +#ifdef T41_USB_AUDIO +extern AudioPlayQueue Q_usbOut_L; +extern AudioPlayQueue Q_usbOut_R; +#endif + #include // Installed via Arduino library manager #include // https://github.com/chipaudette/OpenAudio_ArduinoLibrary From 9a1f452c94dc35468894fd400842dd79b4448d42 Mon Sep 17 00:00:00 2001 From: Dave Solt Date: Fri, 3 Jul 2026 15:17:20 -0400 Subject: [PATCH 5/8] Backup of modified Teensy core files (1.62) required for FT8 --- code/core_mods/usb_audio.cpp | 575 +++++++ code/core_mods/usb_audio.h | 119 ++ code/core_mods/usb_desc.c | 2852 ++++++++++++++++++++++++++++++++++ 3 files changed, 3546 insertions(+) create mode 100644 code/core_mods/usb_audio.cpp create mode 100644 code/core_mods/usb_audio.h create mode 100644 code/core_mods/usb_desc.c diff --git a/code/core_mods/usb_audio.cpp b/code/core_mods/usb_audio.cpp new file mode 100644 index 0000000..0085022 --- /dev/null +++ b/code/core_mods/usb_audio.cpp @@ -0,0 +1,575 @@ +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2017 PJRC.COM, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "usb_dev.h" +#include "usb_audio.h" +#include "debug/printf.h" + +volatile uint8_t usb_audio_tx_ready = 0; + +#ifdef AUDIO_INTERFACE +bool AudioInputUSB::update_responsibility; +audio_block_t * AudioInputUSB::incoming_left; +audio_block_t * AudioInputUSB::incoming_right; +audio_block_t * AudioInputUSB::ready_left; +audio_block_t * AudioInputUSB::ready_right; +uint16_t AudioInputUSB::incoming_count; +uint8_t AudioInputUSB::receive_flag; + +struct usb_audio_features_struct AudioInputUSB::features = {0,0,FEATURE_MAX_VOLUME/2}; + +extern volatile uint8_t usb_high_speed; +static void rx_event(transfer_t *t); +static void tx_event(transfer_t *t); + +/*static*/ transfer_t rx_transfer __attribute__ ((used, aligned(32))); +/*static*/ transfer_t sync_transfer __attribute__ ((used, aligned(32))); +/*static*/ transfer_t tx_transfer __attribute__ ((used, aligned(32))); +DMAMEM static uint8_t rx_buffer[AUDIO_RX_SIZE] __attribute__ ((aligned(32))); +DMAMEM static uint8_t tx_buffer[AUDIO_RX_SIZE] __attribute__ ((aligned(32))); +DMAMEM uint32_t usb_audio_sync_feedback __attribute__ ((aligned(32))); + +uint8_t usb_audio_receive_setting=0; +uint8_t usb_audio_transmit_setting=0; +uint8_t usb_audio_sync_nbytes; +uint8_t usb_audio_sync_rshift; + +uint32_t feedback_accumulator; + +volatile uint32_t usb_audio_underrun_count; +volatile uint32_t usb_audio_overrun_count; +volatile uint32_t usb_audio_sync_count = 0; + +static void rx_event(transfer_t *t) +{ + if (t) { + int len = AUDIO_RX_SIZE - ((rx_transfer.status >> 16) & 0x7FFF); + printf("rx %u\n", len); + usb_audio_receive_callback(len); + } + usb_prepare_transfer(&rx_transfer, rx_buffer, AUDIO_RX_SIZE, 0); + arm_dcache_delete(&rx_buffer, AUDIO_RX_SIZE); + usb_receive(AUDIO_RX_ENDPOINT, &rx_transfer); +} + +static void sync_event(transfer_t *t) +{ + usb_audio_sync_count++; + // USB 2.0 Specification, 5.12.4.2 Feedback, pages 73-75 + //printf("sync %x\n", sync_transfer.status); // too slow, can't print this much + usb_audio_sync_feedback = feedback_accumulator >> usb_audio_sync_rshift; + usb_prepare_transfer(&sync_transfer, &usb_audio_sync_feedback, usb_audio_sync_nbytes, 0); + arm_dcache_flush(&usb_audio_sync_feedback, usb_audio_sync_nbytes); + usb_transmit(AUDIO_SYNC_ENDPOINT, &sync_transfer); +} + +void usb_audio_configure(void) +{ + printf("usb_audio_configure\n"); + printf("usb_high_speed=%d\n", usb_high_speed); + usb_audio_underrun_count = 0; + usb_audio_overrun_count = 0; + feedback_accumulator = 739875226; // 44.1 * 2^24 + if (usb_high_speed) { + usb_audio_sync_nbytes = 4; + usb_audio_sync_rshift = 8; + } else { + usb_audio_sync_nbytes = 3; + usb_audio_sync_rshift = 10; + } + memset(&rx_transfer, 0, sizeof(rx_transfer)); + usb_config_rx_iso(AUDIO_RX_ENDPOINT, AUDIO_RX_SIZE, 1, rx_event); + rx_event(NULL); + memset(&sync_transfer, 0, sizeof(sync_transfer)); + usb_config_tx_iso(AUDIO_SYNC_ENDPOINT, usb_audio_sync_nbytes, 1, sync_event); + sync_event(NULL); + memset(&tx_transfer, 0, sizeof(tx_transfer)); + usb_config_tx_iso(AUDIO_TX_ENDPOINT, AUDIO_TX_SIZE, 1, tx_event); + tx_event(NULL); +} + +void AudioInputUSB::begin(void) +{ + incoming_count = 0; + incoming_left = NULL; + incoming_right = NULL; + ready_left = NULL; + ready_right = NULL; + receive_flag = 0; + // update_responsibility = update_setup(); + // TODO: update responsibility is tough, partly because the USB + // interrupts aren't sychronous to the audio library block size, + // but also because the PC may stop transmitting data, which + // means we no longer get receive callbacks from usb.c + update_responsibility = false; +} + +static void copy_to_buffers(const uint32_t *src, int16_t *left, int16_t *right, unsigned int len) +{ + uint32_t *target = (uint32_t*) src + len; + while ((src < target) && (((uintptr_t) left & 0x02) != 0)) { + uint32_t n = *src++; + *left++ = n & 0xFFFF; + *right++ = n >> 16; + } + + while ((src < target - 2)) { + uint32_t n1 = *src++; + uint32_t n = *src++; + *(uint32_t *)left = (n1 & 0xFFFF) | ((n & 0xFFFF) << 16); + left+=2; + *(uint32_t *)right = (n1 >> 16) | ((n & 0xFFFF0000)) ; + right+=2; + } + + while ((src < target)) { + uint32_t n = *src++; + *left++ = n & 0xFFFF; + *right++ = n >> 16; + } +} + +// Called from the USB interrupt when an isochronous packet arrives +// we must completely remove it from the receive buffer before returning +// +#if 1 +void usb_audio_receive_callback(unsigned int len) +{ + unsigned int count, avail; + audio_block_t *left, *right; + const uint32_t *data; + + AudioInputUSB::receive_flag = 1; + usb_audio_tx_ready++; // reuse as RX arrival counter + len >>= 2; // 1 sample = 4 bytes: 2 left, 2 right + data = (const uint32_t *)rx_buffer; + + count = AudioInputUSB::incoming_count; + left = AudioInputUSB::incoming_left; + right = AudioInputUSB::incoming_right; + if (left == NULL) { + left = AudioStream::allocate(); + if (left == NULL) return; + AudioInputUSB::incoming_left = left; + } + if (right == NULL) { + right = AudioStream::allocate(); + if (right == NULL) return; + AudioInputUSB::incoming_right = right; + } + while (len > 0) { + avail = AUDIO_BLOCK_SAMPLES - count; + if (len < avail) { + copy_to_buffers(data, left->data + count, right->data + count, len); + AudioInputUSB::incoming_count = count + len; + return; + } else if (avail > 0) { + copy_to_buffers(data, left->data + count, right->data + count, avail); + data += avail; + len -= avail; + if (AudioInputUSB::ready_left || AudioInputUSB::ready_right) { + // buffer overrun, PC sending too fast + AudioInputUSB::incoming_count = count + avail; + if (len > 0) { + usb_audio_overrun_count++; + printf("!"); + //serial_phex(len); + } + return; + } + send: + AudioInputUSB::ready_left = left; + AudioInputUSB::ready_right = right; + //if (AudioInputUSB::update_responsibility) AudioStream::update_all(); + left = AudioStream::allocate(); + if (left == NULL) { + AudioInputUSB::incoming_left = NULL; + AudioInputUSB::incoming_right = NULL; + AudioInputUSB::incoming_count = 0; + return; + } + right = AudioStream::allocate(); + if (right == NULL) { + AudioStream::release(left); + AudioInputUSB::incoming_left = NULL; + AudioInputUSB::incoming_right = NULL; + AudioInputUSB::incoming_count = 0; + return; + } + AudioInputUSB::incoming_left = left; + AudioInputUSB::incoming_right = right; + count = 0; + } else { + if (AudioInputUSB::ready_left || AudioInputUSB::ready_right) return; + goto send; // recover from buffer overrun + } + } + AudioInputUSB::incoming_count = count; +} +#endif + +void AudioInputUSB::update(void) +{ + audio_block_t *left, *right; + + __disable_irq(); + left = ready_left; + ready_left = NULL; + right = ready_right; + ready_right = NULL; + uint16_t c = incoming_count; + uint8_t f = receive_flag; + receive_flag = 0; + __enable_irq(); + if (f) { + int diff = AUDIO_BLOCK_SAMPLES/2 - (int)c; + feedback_accumulator += diff * 1; + //uint32_t feedback = (feedback_accumulator >> 8) + diff * 100; + //usb_audio_sync_feedback = feedback; + + //printf(diff >= 0 ? "." : "^"); + } + //serial_phex(c); + //serial_print("."); + if (!left || !right) { + usb_audio_underrun_count++; + //printf("#"); // buffer underrun - PC sending too slow + if (f) feedback_accumulator += 3500; + } + if (left) { + transmit(left, 0); + release(left); + } + if (right) { + transmit(right, 1); + release(right); + } +} + + +#if 1 +bool AudioOutputUSB::update_responsibility; +audio_block_t * AudioOutputUSB::left_1st; +audio_block_t * AudioOutputUSB::left_2nd; +audio_block_t * AudioOutputUSB::right_1st; +audio_block_t * AudioOutputUSB::right_2nd; +uint16_t AudioOutputUSB::offset_1st; + +/*DMAMEM*/ uint16_t usb_audio_transmit_buffer[AUDIO_TX_SIZE/2] __attribute__ ((used, aligned(32))); + +static void tx_event(transfer_t *t) +{ + int len = usb_audio_transmit_callback(); + usb_audio_sync_feedback = feedback_accumulator >> usb_audio_sync_rshift; + usb_prepare_transfer(&tx_transfer, usb_audio_transmit_buffer, len, 0); + arm_dcache_flush_delete(usb_audio_transmit_buffer, len); + usb_transmit(AUDIO_TX_ENDPOINT, &tx_transfer); +} + +void AudioOutputUSB::begin(void) +{ + update_responsibility = false; + left_1st = NULL; + right_1st = NULL; +} + +static void copy_from_buffers(uint32_t *dst, int16_t *left, int16_t *right, unsigned int len) +{ + // TODO: optimize... + while (len > 0) { + *dst++ = (*right++ << 16) | (*left++ & 0xFFFF); + len--; + } +} + +void AudioOutputUSB::update(void) +{ + audio_block_t *left, *right; + + // TODO: we shouldn't be writing to these...... + //left = receiveReadOnly(0); // input 0 = left channel + //right = receiveReadOnly(1); // input 1 = right channel + left = receiveWritable(0); // input 0 = left channel + right = receiveWritable(1); // input 1 = right channel + if (usb_audio_transmit_setting == 0) { + if (left) release(left); + if (right) release(right); + if (left_1st) { release(left_1st); left_1st = NULL; } + if (left_2nd) { release(left_2nd); left_2nd = NULL; } + if (right_1st) { release(right_1st); right_1st = NULL; } + if (right_2nd) { release(right_2nd); right_2nd = NULL; } + offset_1st = 0; + return; + } + if (left == NULL) { + left = allocate(); + if (left == NULL) { + if (right) release(right); + return; + } + memset(left->data, 0, sizeof(left->data)); + } + if (right == NULL) { + right = allocate(); + if (right == NULL) { + release(left); + return; + } + memset(right->data, 0, sizeof(right->data)); + } + __disable_irq(); + if (left_1st == NULL) { + left_1st = left; + right_1st = right; + offset_1st = 0; + } else if (left_2nd == NULL) { + left_2nd = left; + right_2nd = right; + } else { + // buffer overrun - PC is consuming too slowly + audio_block_t *discard1 = left_1st; + left_1st = left_2nd; + left_2nd = left; + audio_block_t *discard2 = right_1st; + right_1st = right_2nd; + right_2nd = right; + offset_1st = 0; // TODO: discard part of this data? + //serial_print("*"); + release(discard1); + release(discard2); + } + __enable_irq(); +} + + +// Called from the USB interrupt when ready to transmit another +// isochronous packet. If we place data into the transmit buffer, +// the return is the number of bytes. Otherwise, return 0 means +// no data to transmit +unsigned int usb_audio_transmit_callback(void) +{ + static uint32_t count=5; + uint32_t avail, num, target, offset, len=0; + audio_block_t *left, *right; + + if (++count < 10) { // TODO: dynamic adjust to match USB rate + target = 44; + } else { + count = 0; + target = 45; + } + while (len < target) { + num = target - len; + left = AudioOutputUSB::left_1st; + if (left == NULL) { + // buffer underrun - PC is consuming too quickly + memset(usb_audio_transmit_buffer + len, 0, num * 4); + //serial_print("%"); + break; + } + right = AudioOutputUSB::right_1st; + offset = AudioOutputUSB::offset_1st; + + avail = AUDIO_BLOCK_SAMPLES - offset; + if (num > avail) num = avail; + + copy_from_buffers((uint32_t *)usb_audio_transmit_buffer + len, + left->data + offset, right->data + offset, num); + len += num; + offset += num; + if (offset >= AUDIO_BLOCK_SAMPLES) { + AudioStream::release(left); + AudioStream::release(right); + AudioOutputUSB::left_1st = AudioOutputUSB::left_2nd; + AudioOutputUSB::left_2nd = NULL; + AudioOutputUSB::right_1st = AudioOutputUSB::right_2nd; + AudioOutputUSB::right_2nd = NULL; + AudioOutputUSB::offset_1st = 0; + } else { + AudioOutputUSB::offset_1st = offset; + } + } + return target * 4; +} +#endif + + + + +struct setup_struct { + union { + struct { + uint8_t bmRequestType; + uint8_t bRequest; + union { + struct { + uint8_t bChannel; // 0=main, 1=left, 2=right + uint8_t bCS; // Control Selector + }; + uint16_t wValue; + }; + union { + struct { + uint8_t bIfEp; // type of entity + uint8_t bEntityId; // UnitID, TerminalID, etc. + }; + uint16_t wIndex; + }; + uint16_t wLength; + }; + }; +}; + +int usb_audio_get_feature(void *stp, uint8_t *data, uint32_t *datalen) +{ + struct setup_struct setup = *((struct setup_struct *)stp); + if (setup.bmRequestType==0xA1) { // should check bRequest, bChannel, and UnitID + if (setup.bCS==0x01) { // mute + data[0] = AudioInputUSB::features.mute; // 1=mute, 0=unmute + *datalen = 1; + return 1; + } + else if (setup.bCS==0x02) { // volume + if (setup.bRequest==0x81) { // GET_CURR + data[0] = AudioInputUSB::features.volume & 0xFF; + data[1] = (AudioInputUSB::features.volume>>8) & 0xFF; + } + else if (setup.bRequest==0x82) { // GET_MIN + //serial_print("vol get_min\n"); + data[0] = 0; // min level is 0 + data[1] = 0; + } + else if (setup.bRequest==0x83) { // GET_MAX + data[0] = FEATURE_MAX_VOLUME; // max level, for range of 0 to MAX + data[1] = 0; + } + else if (setup.bRequest==0x84) { // GET_RES + data[0] = 1; // increment vol by by 1 + data[1] = 0; + } + else { // pass over SET_MEM, etc. + return 0; + } + *datalen = 2; + return 1; + } + } + return 0; +} + +int usb_audio_set_feature(void *stp, uint8_t *buf) +{ + struct setup_struct setup = *((struct setup_struct *)stp); + if (setup.bmRequestType==0x21) { // should check bRequest, bChannel and UnitID + if (setup.bCS==0x01) { // mute + if (setup.bRequest==0x01) { // SET_CUR + AudioInputUSB::features.mute = buf[0]; // 1=mute,0=unmute + AudioInputUSB::features.change = 1; + return 1; + } + } + else if (setup.bCS==0x02) { // volume + if (setup.bRequest==0x01) { // SET_CUR + AudioInputUSB::features.volume = buf[0]; + AudioInputUSB::features.change = 1; + return 1; + } + } + } + return 0; +} + + +void usb_audio_push_block(const int16_t *left_data, const int16_t *right_data) +{ + audio_block_t *left = AudioStream::allocate(); + audio_block_t *right = AudioStream::allocate(); + if (!left || !right) { + if (left) AudioStream::release(left); + if (right) AudioStream::release(right); + return; + } + memcpy(left->data, left_data, AUDIO_BLOCK_SAMPLES * sizeof(int16_t)); + memcpy(right->data, right_data, AUDIO_BLOCK_SAMPLES * sizeof(int16_t)); + __disable_irq(); + if (AudioOutputUSB::left_1st == NULL) { + AudioOutputUSB::left_1st = left; + AudioOutputUSB::right_1st = right; + AudioOutputUSB::offset_1st = 0; + } else if (AudioOutputUSB::left_2nd == NULL) { + AudioOutputUSB::left_2nd = left; + AudioOutputUSB::right_2nd = right; + } else { + AudioStream::release(left); + AudioStream::release(right); + } + __enable_irq(); +} + +bool usb_audio_pop_receive_block(int16_t *left_data, int16_t *right_data) +{ + __disable_irq(); + audio_block_t *left = AudioInputUSB::ready_left; + audio_block_t *right = AudioInputUSB::ready_right; + AudioInputUSB::ready_left = NULL; + AudioInputUSB::ready_right = NULL; + __enable_irq(); + + if (!left || !right) { + if (left) AudioStream::release(left); + if (right) AudioStream::release(right); + return false; + } + + memcpy(left_data, left->data, AUDIO_BLOCK_SAMPLES * sizeof(int16_t)); + memcpy(right_data, right->data, AUDIO_BLOCK_SAMPLES * sizeof(int16_t)); + AudioStream::release(left); + AudioStream::release(right); + return true; +} + +bool usb_audio_tx_has_room(void) +{ + return (AudioOutputUSB::left_2nd == NULL); +} + +void usb_audio_manual_transmit(const int16_t *left_data, const int16_t *right_data, unsigned int samples) +{ + if (!left_data || !right_data || samples == 0) return; + if (usb_audio_transmit_setting == 0) return; + + uint32_t *dst = (uint32_t *)usb_audio_transmit_buffer; + for (unsigned int i = 0; i < samples; i++) { + *dst++ = ((uint32_t)(uint16_t)right_data[i] << 16) | (uint16_t)left_data[i]; + } +} + +#endif // AUDIO_INTERFACE diff --git a/code/core_mods/usb_audio.h b/code/core_mods/usb_audio.h new file mode 100644 index 0000000..7d285c8 --- /dev/null +++ b/code/core_mods/usb_audio.h @@ -0,0 +1,119 @@ +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2017 PJRC.COM, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include "usb_desc.h" +#ifdef AUDIO_INTERFACE + +#define FEATURE_MAX_VOLUME 0xFF // volume accepted from 0 to 0xFF + +#ifdef __cplusplus +extern "C" { +#endif +extern void usb_audio_configure(); +extern uint16_t usb_audio_receive_buffer[]; +extern uint16_t usb_audio_transmit_buffer[]; +extern uint32_t usb_audio_sync_feedback; +extern uint8_t usb_audio_receive_setting; +extern uint8_t usb_audio_transmit_setting; +extern void usb_audio_receive_callback(unsigned int len); +extern unsigned int usb_audio_transmit_callback(void); +extern int usb_audio_set_feature(void *stp, uint8_t *buf); +extern int usb_audio_get_feature(void *stp, uint8_t *data, uint32_t *datalen); +extern volatile uint8_t usb_audio_tx_ready; +#ifdef __cplusplus +} +#endif + +// audio features supported +struct usb_audio_features_struct { + int change; // set to 1 when any value is changed + int mute; // 1=mute, 0=unmute + int volume; // volume from 0 to FEATURE_MAX_VOLUME, maybe should be float from 0.0 to 1.0 +}; + +#ifdef __cplusplus +#include "AudioStream.h" + +class AudioInputUSB : public AudioStream +{ +public: + AudioInputUSB(void) : AudioStream(0, NULL) { begin(); } + virtual void update(void); + void begin(void); + friend void usb_audio_receive_callback(unsigned int len); + friend int usb_audio_set_feature(void *stp, uint8_t *buf); + friend int usb_audio_get_feature(void *stp, uint8_t *data, uint32_t *datalen); + static struct usb_audio_features_struct features; + float volume(void) { + if (features.mute) return 0.0; + return (float)(features.volume) * (1.0 / (float)FEATURE_MAX_VOLUME); + } +private: + static bool update_responsibility; + static audio_block_t *incoming_left; + static audio_block_t *incoming_right; + static audio_block_t *ready_left; + static audio_block_t *ready_right; + static uint16_t incoming_count; + static uint8_t receive_flag; + +friend bool usb_audio_pop_receive_block(int16_t *left_data, int16_t *right_data); + +}; + +class AudioOutputUSB : public AudioStream +{ +public: + AudioOutputUSB(void) : AudioStream(2, inputQueueArray) { begin(); } + virtual void update(void); + void begin(void); + friend unsigned int usb_audio_transmit_callback(void); + friend void usb_audio_push_block(const int16_t *left_data, const int16_t *right_data); + friend bool usb_audio_tx_has_room(void); +private: + static bool update_responsibility; + static audio_block_t *left_1st; + static audio_block_t *left_2nd; + static audio_block_t *right_1st; + static audio_block_t *right_2nd; + static uint16_t offset_1st; + audio_block_t *inputQueueArray[2]; +}; + +void usb_audio_push_block(const int16_t *left_data, const int16_t *right_data); +bool usb_audio_tx_has_room(void); +void usb_audio_manual_transmit(const int16_t *left_data, const int16_t *right_data, unsigned int samples); +bool usb_audio_pop_receive_block(int16_t *left_data, int16_t *right_data); + +#endif // __cplusplus + +#endif // AUDIO_INTERFACE \ No newline at end of file diff --git a/code/core_mods/usb_desc.c b/code/core_mods/usb_desc.c new file mode 100644 index 0000000..653471f --- /dev/null +++ b/code/core_mods/usb_desc.c @@ -0,0 +1,2852 @@ +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2019 PJRC.COM, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +//#if F_CPU >= 20000000 + +#define USB_DESC_LIST_DEFINE +#include "usb_desc.h" +#ifdef NUM_ENDPOINTS +#include "usb_names.h" +#include "imxrt.h" +#include "avr_functions.h" +#include "avr/pgmspace.h" + +// At very slow CPU speeds, the OCRAM just isn't fast enough for +// USB to work reliably. But the precious/limited DTCM is. So +// as an ugly workaround, undefine DMAMEM so all buffers which +// would normally be allocated in OCRAM are placed in DTCM. +#if defined(F_CPU) && F_CPU < 30000000 +#undef DMAMEM +#endif + +// USB Descriptors are binary data which the USB host reads to +// automatically detect a USB device's capabilities. The format +// and meaning of every field is documented in numerous USB +// standards. When working with USB descriptors, despite the +// complexity of the standards and poor writing quality in many +// of those documents, remember descriptors are nothing more +// than constant binary data that tells the USB host what the +// device can do. Computers will load drivers based on this data. +// Those drivers then communicate on the endpoints specified by +// the descriptors. + +// To configure a new combination of interfaces or make minor +// changes to existing configuration (eg, change the name or ID +// numbers), usually you would edit "usb_desc.h". This file +// is meant to be configured by the header, so generally it is +// only edited to add completely new USB interfaces or features. + + + +// ************************************************************** +// USB Device +// ************************************************************** + +#define LSB(n) ((n) & 255) +#define MSB(n) (((n) >> 8) & 255) + +#ifdef CDC_IAD_DESCRIPTOR +#ifndef DEVICE_CLASS +#define DEVICE_CLASS 0xEF +#endif +#ifndef DEVICE_SUBCLASS +#define DEVICE_SUBCLASS 0x02 +#endif +#ifndef DEVICE_PROTOCOL +#define DEVICE_PROTOCOL 0x01 +#endif +#endif + + +// USB Device Descriptor. The USB host reads this first, to learn +// what type of device is connected. +static uint8_t device_descriptor[] = { + 18, // bLength + 1, // bDescriptorType + 0x00, 0x02, // bcdUSB +#ifdef DEVICE_CLASS + DEVICE_CLASS, // bDeviceClass +#else + 0, +#endif +#ifdef DEVICE_SUBCLASS + DEVICE_SUBCLASS, // bDeviceSubClass +#else + 0, +#endif +#ifdef DEVICE_PROTOCOL + DEVICE_PROTOCOL, // bDeviceProtocol +#else + 0, +#endif + EP0_SIZE, // bMaxPacketSize0 + LSB(VENDOR_ID), MSB(VENDOR_ID), // idVendor + LSB(PRODUCT_ID), MSB(PRODUCT_ID), // idProduct +#ifdef BCD_DEVICE + LSB(BCD_DEVICE), MSB(BCD_DEVICE), // bcdDevice +#else + // For USB types that don't explicitly define BCD_DEVICE, + // use the minor version number to help teensy_ports + // identify which Teensy model is used. + #if defined(__IMXRT1062__) && defined(ARDUINO_TEENSY40) + 0x79, 0x02, // Teensy 4.0 + #elif defined(__IMXRT1062__) && defined(ARDUINO_TEENSY41) + 0x80, 0x02, // Teensy 4.1 + #elif defined(__IMXRT1062__) && defined(ARDUINO_TEENSY_MICROMOD) + 0x81, 0x02, // Teensy MicroMod + #else + 0x00, 0x02, + #endif +#endif + 1, // iManufacturer + 2, // iProduct + 3, // iSerialNumber + 1 // bNumConfigurations +}; + +PROGMEM static const uint8_t qualifier_descriptor[] = { // 9.6.2 Device_Qualifier, page 264 + 10, // bLength + 6, // bDescriptorType + 0x00, 0x02, // bcdUSB +#ifdef DEVICE_CLASS + DEVICE_CLASS, // bDeviceClass +#else + 0, +#endif +#ifdef DEVICE_SUBCLASS + DEVICE_SUBCLASS, // bDeviceSubClass +#else + 0, +#endif +#ifdef DEVICE_PROTOCOL + DEVICE_PROTOCOL, // bDeviceProtocol +#else + 0, +#endif + EP0_SIZE, // bMaxPacketSize0 + 1, // bNumConfigurations + 0 // bReserved +}; + +// These descriptors must NOT be "const", because the USB DMA +// has trouble accessing flash memory with enough bandwidth +// while the processor is executing from flash. + + + +// ************************************************************** +// HID Report Descriptors +// ************************************************************** + +// Each HID interface needs a special report descriptor that tells +// the meaning and format of the data. + +#ifdef KEYBOARD_INTERFACE +// Keyboard Protocol 1, HID 1.11 spec, Appendix B, page 59-60 +static uint8_t keyboard_report_desc[] = { + 0x05, 0x01, // Usage Page (Generic Desktop), + 0x09, 0x06, // Usage (Keyboard), + 0xA1, 0x01, // Collection (Application), + 0x75, 0x01, // Report Size (1), + 0x95, 0x08, // Report Count (8), + 0x05, 0x07, // Usage Page (Key Codes), + 0x19, 0xE0, // Usage Minimum (224), + 0x29, 0xE7, // Usage Maximum (231), + 0x15, 0x00, // Logical Minimum (0), + 0x25, 0x01, // Logical Maximum (1), + 0x81, 0x02, // Input (Data, Variable, Absolute), ;Modifier keys +#if KEYBOARD_SIZE == 8 + 0x95, 0x01, // Report Count (1), + 0x75, 0x08, // Report Size (8), + 0x81, 0x03, // Input (Constant), ;Reserved byte +#endif + 0x95, 0x05, // Report Count (5), + 0x75, 0x01, // Report Size (1), + 0x05, 0x08, // Usage Page (LEDs), + 0x19, 0x01, // Usage Minimum (1), + 0x29, 0x05, // Usage Maximum (5), + 0x91, 0x02, // Output (Data, Variable, Absolute), ;LED report + 0x95, 0x01, // Report Count (1), + 0x75, 0x03, // Report Size (3), + 0x91, 0x03, // Output (Constant), ;LED report padding +#if KEYBOARD_SIZE == 8 + 0x95, 0x06, // Report Count (6), + 0x75, 0x08, // Report Size (8), + 0x15, 0x00, // Logical Minimum (0), + 0x25, 0x7F, // Logical Maximum(104), + 0x05, 0x07, // Usage Page (Key Codes), + 0x19, 0x00, // Usage Minimum (0), + 0x29, 0x7F, // Usage Maximum (104), + 0x81, 0x00, // Input (Data, Array), ;Normal keys +#elif KEYBOARD_SIZE == 16 + 0x95, 0x78, // Report Count (120), + 0x75, 0x01, // Report Size (1), + 0x19, 0x00, // Usage Minimum (0), + 0x29, 0xE7, // Usage Maximum (119), + 0x05, 0x07, // Usage Page (Key Codes), + 0x15, 0x00, // Logical Minimum (0), + 0x25, 0x01, // Logical Maximum (1), + 0x81, 0x02, // Input (Data, Variable, Absolute), ;Normal keys NKRO +#endif + 0xC0 // End Collection +}; +#endif + +#ifdef KEYMEDIA_INTERFACE +static uint8_t keymedia_report_desc[] = { + 0x05, 0x0C, // Usage Page (Consumer) + 0x09, 0x01, // Usage (Consumer Controls) + 0xA1, 0x01, // Collection (Application) + 0x75, 0x0A, // Report Size (10) + 0x95, 0x04, // Report Count (4) + 0x19, 0x00, // Usage Minimum (0) + 0x2A, 0x9C, 0x02, // Usage Maximum (0x29C) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0x9C, 0x02, // Logical Maximum (0x29C) + 0x81, 0x00, // Input (Data, Array) + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x75, 0x08, // Report Size (8) + 0x95, 0x03, // Report Count (3) + 0x19, 0x00, // Usage Minimum (0) + 0x29, 0xB7, // Usage Maximum (0xB7) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xB7, 0x00, // Logical Maximum (0xB7) + 0x81, 0x00, // Input (Data, Array) + 0xC0 // End Collection +}; +#endif + +#ifdef MOUSE_INTERFACE +// Mouse Protocol 1, HID 1.11 spec, Appendix B, page 59-60, with wheel extension +static uint8_t mouse_report_desc[] = { + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x02, // Usage (Mouse) + 0xA1, 0x01, // Collection (Application) + 0x85, 0x01, // REPORT_ID (1) + 0x05, 0x09, // Usage Page (Button) + 0x19, 0x01, // Usage Minimum (Button #1) + 0x29, 0x08, // Usage Maximum (Button #8) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x95, 0x08, // Report Count (8) + 0x75, 0x01, // Report Size (1) + 0x81, 0x02, // Input (Data, Variable, Absolute) + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x30, // Usage (X) + 0x09, 0x31, // Usage (Y) + 0x09, 0x38, // Usage (Wheel) + 0x15, 0x81, // Logical Minimum (-127) + 0x25, 0x7F, // Logical Maximum (127) + 0x75, 0x08, // Report Size (8), + 0x95, 0x03, // Report Count (3), + 0x81, 0x06, // Input (Data, Variable, Relative) + 0x05, 0x0C, // Usage Page (Consumer) + 0x0A, 0x38, 0x02, // Usage (AC Pan) + 0x15, 0x81, // Logical Minimum (-127) + 0x25, 0x7F, // Logical Maximum (127) + 0x75, 0x08, // Report Size (8), + 0x95, 0x01, // Report Count (1), + 0x81, 0x06, // Input (Data, Variable, Relative) + 0xC0, // End Collection + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x02, // Usage (Mouse) + 0xA1, 0x01, // Collection (Application) + 0x85, 0x02, // REPORT_ID (2) + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x30, // Usage (X) + 0x09, 0x31, // Usage (Y) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x7F, // Logical Maximum (32767) + 0x75, 0x10, // Report Size (16), + 0x95, 0x02, // Report Count (2), + 0x81, 0x02, // Input (Data, Variable, Absolute) + 0xC0 // End Collection +}; +#endif + +#ifdef JOYSTICK_INTERFACE +#if JOYSTICK_SIZE == 12 +static uint8_t joystick_report_desc[] = { + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x04, // Usage (Joystick) + 0xA1, 0x01, // Collection (Application) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x20, // Report Count (32) + 0x05, 0x09, // Usage Page (Button) + 0x19, 0x01, // Usage Minimum (Button #1) + 0x29, 0x20, // Usage Maximum (Button #32) + 0x81, 0x02, // Input (variable,absolute) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x07, // Logical Maximum (7) + 0x35, 0x00, // Physical Minimum (0) + 0x46, 0x3B, 0x01, // Physical Maximum (315) + 0x75, 0x04, // Report Size (4) + 0x95, 0x01, // Report Count (1) + 0x65, 0x14, // Unit (20) + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x39, // Usage (Hat switch) + 0x81, 0x42, // Input (variable,absolute,null_state) + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x01, // Usage (Pointer) + 0xA1, 0x00, // Collection () + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x03, // Logical Maximum (1023) + 0x75, 0x0A, // Report Size (10) + 0x95, 0x04, // Report Count (4) + 0x09, 0x30, // Usage (X) + 0x09, 0x31, // Usage (Y) + 0x09, 0x32, // Usage (Z) + 0x09, 0x35, // Usage (Rz) + 0x81, 0x02, // Input (variable,absolute) + 0xC0, // End Collection + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x03, // Logical Maximum (1023) + 0x75, 0x0A, // Report Size (10) + 0x95, 0x02, // Report Count (2) + 0x09, 0x36, // Usage (Slider) + 0x09, 0x36, // Usage (Slider) + 0x81, 0x02, // Input (variable,absolute) + 0xC0 // End Collection +}; +#elif JOYSTICK_SIZE == 64 +// extreme joystick (to use this, edit JOYSTICK_SIZE to 64 in usb_desc.h) +// 128 buttons 16 +// 6 axes 12 +// 17 sliders 34 +// 4 pov 2 +static uint8_t joystick_report_desc[] = { + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x04, // Usage (Joystick) + 0xA1, 0x01, // Collection (Application) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x80, // Report Count (128) + 0x05, 0x09, // Usage Page (Button) + 0x19, 0x01, // Usage Minimum (Button #1) + 0x29, 0x80, // Usage Maximum (Button #128) + 0x81, 0x02, // Input (variable,absolute) + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x01, // Usage (Pointer) + 0xA1, 0x00, // Collection () + 0x15, 0x00, // Logical Minimum (0) + 0x27, 0xFF, 0xFF, 0, 0, // Logical Maximum (65535) + 0x75, 0x10, // Report Size (16) + 0x95, 23, // Report Count (23) + 0x09, 0x30, // Usage (X) + 0x09, 0x31, // Usage (Y) + 0x09, 0x32, // Usage (Z) + 0x09, 0x33, // Usage (Rx) + 0x09, 0x34, // Usage (Ry) + 0x09, 0x35, // Usage (Rz) + 0x09, 0x36, // Usage (Slider) + 0x09, 0x36, // Usage (Slider) + 0x09, 0x36, // Usage (Slider) + 0x09, 0x36, // Usage (Slider) + 0x09, 0x36, // Usage (Slider) + 0x09, 0x36, // Usage (Slider) + 0x09, 0x36, // Usage (Slider) + 0x09, 0x36, // Usage (Slider) + 0x09, 0x36, // Usage (Slider) + 0x09, 0x36, // Usage (Slider) + 0x09, 0x36, // Usage (Slider) + 0x09, 0x36, // Usage (Slider) + 0x09, 0x36, // Usage (Slider) + 0x09, 0x36, // Usage (Slider) + 0x09, 0x36, // Usage (Slider) + 0x09, 0x36, // Usage (Slider) + 0x09, 0x36, // Usage (Slider) + 0x81, 0x02, // Input (variable,absolute) + 0xC0, // End Collection + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x07, // Logical Maximum (7) + 0x35, 0x00, // Physical Minimum (0) + 0x46, 0x3B, 0x01, // Physical Maximum (315) + 0x75, 0x04, // Report Size (4) + 0x95, 0x04, // Report Count (4) + 0x65, 0x14, // Unit (20) + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x39, // Usage (Hat switch) + 0x09, 0x39, // Usage (Hat switch) + 0x09, 0x39, // Usage (Hat switch) + 0x09, 0x39, // Usage (Hat switch) + 0x81, 0x42, // Input (variable,absolute,null_state) + 0xC0 // End Collection +}; +#endif // JOYSTICK_SIZE +#endif // JOYSTICK_INTERFACE + +#ifdef MULTITOUCH_INTERFACE +// https://forum.pjrc.com/threads/32331-USB-HID-Touchscreen-support-needed +// https://msdn.microsoft.com/en-us/library/windows/hardware/jj151563%28v=vs.85%29.aspx +// https://msdn.microsoft.com/en-us/library/windows/hardware/jj151565%28v=vs.85%29.aspx +// https://msdn.microsoft.com/en-us/library/windows/hardware/ff553734%28v=vs.85%29.aspx +// https://msdn.microsoft.com/en-us/library/windows/hardware/jj151564%28v=vs.85%29.aspx +// download.microsoft.com/download/a/d/f/adf1347d-08dc-41a4-9084-623b1194d4b2/digitizerdrvs_touch.docx +static uint8_t multitouch_report_desc[] = { + 0x05, 0x0D, // Usage Page (Digitizer) + 0x09, 0x04, // Usage (Touch Screen) + 0xa1, 0x01, // Collection (Application) + 0x09, 0x22, // Usage (Finger) + 0xA1, 0x02, // Collection (Logical) + 0x09, 0x42, // Usage (Tip Switch) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x01, // Report Count (1) + 0x81, 0x02, // Input (variable,absolute) + 0x09, 0x51, // Usage (Contact Identifier) + 0x25, 0x7F, // Logical Maximum (127) + 0x75, 0x07, // Report Size (7) + 0x95, 0x01, // Report Count (1) + 0x81, 0x02, // Input (variable,absolute) + 0x09, 0x30, // Usage (Pressure) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x75, 0x08, // Report Size (8) + 0x95, 0x01, // Report Count (1) + 0x81, 0x02, // Input (variable,absolute) + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x30, // Usage (X) + 0x09, 0x31, // Usage (Y) + 0x26, 0xFF, 0x7F, // Logical Maximum (32767) + 0x65, 0x00, // Unit (None) <-- probably needs real units? + 0x75, 0x10, // Report Size (16) + 0x95, 0x02, // Report Count (2) + 0x81, 0x02, // Input (variable,absolute) + 0xC0, // End Collection + 0x05, 0x0D, // Usage Page (Digitizer) + 0x27, 0xFF, 0xFF, 0, 0, // Logical Maximum (65535) + 0x75, 0x10, // Report Size (16) + 0x95, 0x01, // Report Count (1) + 0x09, 0x56, // Usage (Scan Time) + 0x81, 0x02, // Input (variable,absolute) + 0x09, 0x54, // USAGE (Contact count) + 0x25, 0x7f, // LOGICAL_MAXIMUM (127) + 0x95, 0x01, // REPORT_COUNT (1) + 0x75, 0x08, // REPORT_SIZE (8) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x05, 0x0D, // Usage Page (Digitizers) + 0x09, 0x55, // Usage (Contact Count Maximum) + 0x25, MULTITOUCH_FINGERS, // Logical Maximum (10) + 0x75, 0x08, // Report Size (8) + 0x95, 0x01, // Report Count (1) + 0xB1, 0x02, // Feature (variable,absolute) + 0xC0 // End Collection +}; +#endif + +#ifdef SEREMU_INTERFACE +static uint8_t seremu_report_desc[] = { + 0x06, 0xC9, 0xFF, // Usage Page 0xFFC9 (vendor defined) + 0x09, 0x04, // Usage 0x04 + 0xA1, 0x5C, // Collection 0x5C + 0x75, 0x08, // report size = 8 bits (global) + 0x15, 0x00, // logical minimum = 0 (global) + 0x26, 0xFF, 0x00, // logical maximum = 255 (global) + 0x95, SEREMU_TX_SIZE, // report count (global) + 0x09, 0x75, // usage (local) + 0x81, 0x02, // Input + 0x95, SEREMU_RX_SIZE, // report count (global) + 0x09, 0x76, // usage (local) + 0x91, 0x02, // Output + 0x95, 0x04, // report count (global) + 0x09, 0x76, // usage (local) + 0xB1, 0x02, // Feature + 0xC0 // end collection +}; +#endif + +#ifdef RAWHID_INTERFACE +static uint8_t rawhid_report_desc[] = { + 0x06, LSB(RAWHID_USAGE_PAGE), MSB(RAWHID_USAGE_PAGE), + 0x0A, LSB(RAWHID_USAGE), MSB(RAWHID_USAGE), + 0xA1, 0x01, // Collection 0x01 + 0x75, 0x08, // report size = 8 bits + 0x15, 0x00, // logical minimum = 0 + 0x26, 0xFF, 0x00, // logical maximum = 255 + 0x95, RAWHID_TX_SIZE, // report count + 0x09, 0x01, // usage + 0x81, 0x02, // Input (array) + 0x95, RAWHID_RX_SIZE, // report count + 0x09, 0x02, // usage + 0x91, 0x02, // Output (array) + 0xC0 // end collection +}; +#endif + +#ifdef FLIGHTSIM_INTERFACE +static uint8_t flightsim_report_desc[] = { + 0x06, 0x1C, 0xFF, // Usage page = 0xFF1C + 0x0A, 0x39, 0xA7, // Usage = 0xA739 + 0xA1, 0x01, // Collection 0x01 + 0x75, 0x08, // report size = 8 bits + 0x15, 0x00, // logical minimum = 0 + 0x26, 0xFF, 0x00, // logical maximum = 255 + 0x95, FLIGHTSIM_TX_SIZE, // report count + 0x09, 0x01, // usage + 0x81, 0x02, // Input (array) + 0x95, FLIGHTSIM_RX_SIZE, // report count + 0x09, 0x02, // usage + 0x91, 0x02, // Output (array) + 0xC0 // end collection +}; +#endif + +#ifdef EXPERIMENTAL_INTERFACE +static uint8_t microsoft_os_string_desc[] = { + 18, 3, + 'M', 0, 'S', 0, 'F', 0, 'T', 0, '1', 0, '0', 0, '0', 0, + 0xF8, 0 // GET_MS_DESCRIPTOR will use bRequest=0xF8 +}; +static uint8_t microsoft_os_compatible_id_desc[] = { + 40, 0, 0, 0, // total length, 16 header + 24 function * 1 + 0, 1, 4, 0, // version 1.00, wIndex=4 (Compat ID) + 1, 0, 0, 0, 0, 0, 0, 0, // 1 function + EXPERIMENTAL_INTERFACE, 1, + 'W','I','N','U','S','B',0,0, // compatibleID + 0,0,0,0,0,0,0,0, // subCompatibleID + 0,0,0,0,0,0 +}; +#endif + +// ************************************************************** +// USB Descriptor Sizes +// ************************************************************** + +// pre-compute the size and position of everything in the config descriptor +// +#define CONFIG_HEADER_DESCRIPTOR_SIZE 9 + +#define CDC_IAD_DESCRIPTOR_POS CONFIG_HEADER_DESCRIPTOR_SIZE +#ifdef CDC_IAD_DESCRIPTOR +#define CDC_IAD_DESCRIPTOR_SIZE 8 +#else +#define CDC_IAD_DESCRIPTOR_SIZE 0 +#endif + +#define CDC_DATA_INTERFACE_DESC_POS CDC_IAD_DESCRIPTOR_POS+CDC_IAD_DESCRIPTOR_SIZE +#ifdef CDC_DATA_INTERFACE +#define CDC_DATA_INTERFACE_DESC_SIZE 9+5+5+4+5+7+9+7+7 +#else +#define CDC_DATA_INTERFACE_DESC_SIZE 0 +#endif + +#define CDC2_DATA_INTERFACE_DESC_POS CDC_DATA_INTERFACE_DESC_POS+CDC_DATA_INTERFACE_DESC_SIZE +#ifdef CDC2_DATA_INTERFACE +#define CDC2_DATA_INTERFACE_DESC_SIZE 8 + 9+5+5+4+5+7+9+7+7 +#else +#define CDC2_DATA_INTERFACE_DESC_SIZE 0 +#endif + +#define CDC3_DATA_INTERFACE_DESC_POS CDC2_DATA_INTERFACE_DESC_POS+CDC2_DATA_INTERFACE_DESC_SIZE +#ifdef CDC3_DATA_INTERFACE +#define CDC3_DATA_INTERFACE_DESC_SIZE 8 + 9+5+5+4+5+7+9+7+7 +#else +#define CDC3_DATA_INTERFACE_DESC_SIZE 0 +#endif + +#define MIDI_INTERFACE_DESC_POS CDC3_DATA_INTERFACE_DESC_POS+CDC3_DATA_INTERFACE_DESC_SIZE +#ifdef MIDI_INTERFACE + #if !defined(MIDI_NUM_CABLES) || MIDI_NUM_CABLES < 1 || MIDI_NUM_CABLES > 16 + #error "MIDI_NUM_CABLES must be defined between 1 to 16" + #endif +#define MIDI_INTERFACE_DESC_SIZE 9+7+((6+6+9+9)*MIDI_NUM_CABLES)+(9+4+MIDI_NUM_CABLES)*2 +#else +#define MIDI_INTERFACE_DESC_SIZE 0 +#endif + +#define KEYBOARD_INTERFACE_DESC_POS MIDI_INTERFACE_DESC_POS+MIDI_INTERFACE_DESC_SIZE +#ifdef KEYBOARD_INTERFACE +#define KEYBOARD_INTERFACE_DESC_SIZE 9+9+7 +#define KEYBOARD_HID_DESC_OFFSET KEYBOARD_INTERFACE_DESC_POS+9 +#else +#define KEYBOARD_INTERFACE_DESC_SIZE 0 +#endif + +#define MOUSE_INTERFACE_DESC_POS KEYBOARD_INTERFACE_DESC_POS+KEYBOARD_INTERFACE_DESC_SIZE +#ifdef MOUSE_INTERFACE +#define MOUSE_INTERFACE_DESC_SIZE 9+9+7 +#define MOUSE_HID_DESC_OFFSET MOUSE_INTERFACE_DESC_POS+9 +#else +#define MOUSE_INTERFACE_DESC_SIZE 0 +#endif + +#define RAWHID_INTERFACE_DESC_POS MOUSE_INTERFACE_DESC_POS+MOUSE_INTERFACE_DESC_SIZE +#ifdef RAWHID_INTERFACE +#define RAWHID_INTERFACE_DESC_SIZE 9+9+7+7 +#define RAWHID_HID_DESC_OFFSET RAWHID_INTERFACE_DESC_POS+9 +#else +#define RAWHID_INTERFACE_DESC_SIZE 0 +#endif + +#define FLIGHTSIM_INTERFACE_DESC_POS RAWHID_INTERFACE_DESC_POS+RAWHID_INTERFACE_DESC_SIZE +#ifdef FLIGHTSIM_INTERFACE +#define FLIGHTSIM_INTERFACE_DESC_SIZE 9+9+7+7 +#define FLIGHTSIM_HID_DESC_OFFSET FLIGHTSIM_INTERFACE_DESC_POS+9 +#else +#define FLIGHTSIM_INTERFACE_DESC_SIZE 0 +#endif + +#define SEREMU_INTERFACE_DESC_POS FLIGHTSIM_INTERFACE_DESC_POS+FLIGHTSIM_INTERFACE_DESC_SIZE +#ifdef SEREMU_INTERFACE +#define SEREMU_INTERFACE_DESC_SIZE 9+9+7+7 +#define SEREMU_HID_DESC_OFFSET SEREMU_INTERFACE_DESC_POS+9 +#else +#define SEREMU_INTERFACE_DESC_SIZE 0 +#endif + +#define JOYSTICK_INTERFACE_DESC_POS SEREMU_INTERFACE_DESC_POS+SEREMU_INTERFACE_DESC_SIZE +#ifdef JOYSTICK_INTERFACE +#define JOYSTICK_INTERFACE_DESC_SIZE 9+9+7 +#define JOYSTICK_HID_DESC_OFFSET JOYSTICK_INTERFACE_DESC_POS+9 +#else +#define JOYSTICK_INTERFACE_DESC_SIZE 0 +#endif + +#define MTP_INTERFACE_DESC_POS JOYSTICK_INTERFACE_DESC_POS+JOYSTICK_INTERFACE_DESC_SIZE +#ifdef MTP_INTERFACE +#define MTP_INTERFACE_DESC_SIZE 9+7+7+7 +#else +#define MTP_INTERFACE_DESC_SIZE 0 +#endif + +#define KEYMEDIA_INTERFACE_DESC_POS MTP_INTERFACE_DESC_POS+MTP_INTERFACE_DESC_SIZE +#ifdef KEYMEDIA_INTERFACE +#define KEYMEDIA_INTERFACE_DESC_SIZE 9+9+7 +#define KEYMEDIA_HID_DESC_OFFSET KEYMEDIA_INTERFACE_DESC_POS+9 +#else +#define KEYMEDIA_INTERFACE_DESC_SIZE 0 +#endif + +#define AUDIO_INTERFACE_DESC_POS KEYMEDIA_INTERFACE_DESC_POS+KEYMEDIA_INTERFACE_DESC_SIZE +#ifdef AUDIO_INTERFACE +#define AUDIO_INTERFACE_DESC_SIZE 8 + 9+10+12+9+12+10+9 + 9+9+7+11+9+7 + 9+9+7+11+9+7+9 +#else +#define AUDIO_INTERFACE_DESC_SIZE 0 +#endif + +#define MULTITOUCH_INTERFACE_DESC_POS AUDIO_INTERFACE_DESC_POS+AUDIO_INTERFACE_DESC_SIZE +#ifdef MULTITOUCH_INTERFACE +#define MULTITOUCH_INTERFACE_DESC_SIZE 9+9+7 +#define MULTITOUCH_HID_DESC_OFFSET MULTITOUCH_INTERFACE_DESC_POS+9 +#else +#define MULTITOUCH_INTERFACE_DESC_SIZE 0 +#endif + +#define EXPERIMENTAL_INTERFACE_DESC_POS MULTITOUCH_INTERFACE_DESC_POS+MULTITOUCH_INTERFACE_DESC_SIZE +#ifdef EXPERIMENTAL_INTERFACE +#define EXPERIMENTAL_INTERFACE_DESC_SIZE 9+7+7 +#define EXPERIMENTAL_HID_DESC_OFFSET MULTITOUCH_INTERFACE_DESC_POS+9 +#else +#define EXPERIMENTAL_INTERFACE_DESC_SIZE 0 +#endif + +#define CONFIG_DESC_SIZE EXPERIMENTAL_INTERFACE_DESC_POS+EXPERIMENTAL_INTERFACE_DESC_SIZE + + + +// ************************************************************** +// USB Configuration +// ************************************************************** + +// USB Configuration Descriptor. This huge descriptor tells all +// of the devices capabilities. + +PROGMEM const uint8_t usb_config_descriptor_480[CONFIG_DESC_SIZE] = { + // configuration descriptor, USB spec 9.6.3, page 264-266, Table 9-10 + 9, // bLength; + 2, // bDescriptorType; + LSB(CONFIG_DESC_SIZE), // wTotalLength + MSB(CONFIG_DESC_SIZE), + NUM_INTERFACE, // bNumInterfaces + 1, // bConfigurationValue + 0, // iConfiguration + 0xC0, // bmAttributes + 50, // bMaxPower + +#ifdef CDC_IAD_DESCRIPTOR + // interface association descriptor, USB ECN, Table 9-Z + 8, // bLength + 11, // bDescriptorType + CDC_STATUS_INTERFACE, // bFirstInterface + 2, // bInterfaceCount + 0x02, // bFunctionClass + 0x02, // bFunctionSubClass + 0x01, // bFunctionProtocol + 0, // iFunction +#endif + +#ifdef CDC_DATA_INTERFACE + // configuration for 480 Mbit/sec speed + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + CDC_STATUS_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x02, // bInterfaceClass + 0x02, // bInterfaceSubClass + 0x01, // bInterfaceProtocol + 0, // iInterface + // CDC Header Functional Descriptor, CDC Spec 5.2.3.1, Table 26 + 5, // bFunctionLength + 0x24, // bDescriptorType + 0x00, // bDescriptorSubtype + 0x10, 0x01, // bcdCDC + // Call Management Functional Descriptor, CDC Spec 5.2.3.2, Table 27 + 5, // bFunctionLength + 0x24, // bDescriptorType + 0x01, // bDescriptorSubtype + 0x01, // bmCapabilities + 1, // bDataInterface + // Abstract Control Management Functional Descriptor, CDC Spec 5.2.3.3, Table 28 + 4, // bFunctionLength + 0x24, // bDescriptorType + 0x02, // bDescriptorSubtype + 0x06, // bmCapabilities + // Union Functional Descriptor, CDC Spec 5.2.3.8, Table 33 + 5, // bFunctionLength + 0x24, // bDescriptorType + 0x06, // bDescriptorSubtype + CDC_STATUS_INTERFACE, // bMasterInterface + CDC_DATA_INTERFACE, // bSlaveInterface0 + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + CDC_ACM_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + LSB(CDC_ACM_SIZE),MSB(CDC_ACM_SIZE), // wMaxPacketSize + 5, // bInterval + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + CDC_DATA_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 2, // bNumEndpoints + 0x0A, // bInterfaceClass + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0, // iInterface + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + CDC_RX_ENDPOINT, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + LSB(CDC_RX_SIZE_480),MSB(CDC_RX_SIZE_480),// wMaxPacketSize + 0, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + CDC_TX_ENDPOINT | 0x80, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + LSB(CDC_TX_SIZE_480),MSB(CDC_TX_SIZE_480),// wMaxPacketSize + 0, // bInterval +#endif // CDC_DATA_INTERFACE + +#ifdef CDC2_DATA_INTERFACE + // configuration for 480 Mbit/sec speed + // interface association descriptor, USB ECN, Table 9-Z + 8, // bLength + 11, // bDescriptorType + CDC2_STATUS_INTERFACE, // bFirstInterface + 2, // bInterfaceCount + 0x02, // bFunctionClass + 0x02, // bFunctionSubClass + 0x01, // bFunctionProtocol + 0, // iFunction + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + CDC2_STATUS_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x02, // bInterfaceClass + 0x02, // bInterfaceSubClass + 0x01, // bInterfaceProtocol + 0, // iInterface + // CDC Header Functional Descriptor, CDC Spec 5.2.3.1, Table 26 + 5, // bFunctionLength + 0x24, // bDescriptorType + 0x00, // bDescriptorSubtype + 0x10, 0x01, // bcdCDC + // Call Management Functional Descriptor, CDC Spec 5.2.3.2, Table 27 + 5, // bFunctionLength + 0x24, // bDescriptorType + 0x01, // bDescriptorSubtype + 0x01, // bmCapabilities + 1, // bDataInterface + // Abstract Control Management Functional Descriptor, CDC Spec 5.2.3.3, Table 28 + 4, // bFunctionLength + 0x24, // bDescriptorType + 0x02, // bDescriptorSubtype + 0x06, // bmCapabilities + // Union Functional Descriptor, CDC Spec 5.2.3.8, Table 33 + 5, // bFunctionLength + 0x24, // bDescriptorType + 0x06, // bDescriptorSubtype + CDC2_STATUS_INTERFACE, // bMasterInterface + CDC2_DATA_INTERFACE, // bSlaveInterface0 + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + CDC2_ACM_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + CDC_ACM_SIZE, 0, // wMaxPacketSize + 5, // bInterval + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + CDC2_DATA_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 2, // bNumEndpoints + 0x0A, // bInterfaceClass + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0, // iInterface + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + CDC2_RX_ENDPOINT, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + LSB(CDC_RX_SIZE_480),MSB(CDC_RX_SIZE_480),// wMaxPacketSize + 0, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + CDC2_TX_ENDPOINT | 0x80, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + LSB(CDC_TX_SIZE_480),MSB(CDC_TX_SIZE_480),// wMaxPacketSize + 0, // bInterval +#endif // CDC2_DATA_INTERFACE + +#ifdef CDC3_DATA_INTERFACE + // configuration for 480 Mbit/sec speed + // interface association descriptor, USB ECN, Table 9-Z + 8, // bLength + 11, // bDescriptorType + CDC3_STATUS_INTERFACE, // bFirstInterface + 2, // bInterfaceCount + 0x02, // bFunctionClass + 0x02, // bFunctionSubClass + 0x01, // bFunctionProtocol + 0, // iFunction + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + CDC3_STATUS_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x02, // bInterfaceClass + 0x02, // bInterfaceSubClass + 0x01, // bInterfaceProtocol + 0, // iInterface + // CDC Header Functional Descriptor, CDC Spec 5.2.3.1, Table 26 + 5, // bFunctionLength + 0x24, // bDescriptorType + 0x00, // bDescriptorSubtype + 0x10, 0x01, // bcdCDC + // Call Management Functional Descriptor, CDC Spec 5.2.3.2, Table 27 + 5, // bFunctionLength + 0x24, // bDescriptorType + 0x01, // bDescriptorSubtype + 0x01, // bmCapabilities + 1, // bDataInterface + // Abstract Control Management Functional Descriptor, CDC Spec 5.2.3.3, Table 28 + 4, // bFunctionLength + 0x24, // bDescriptorType + 0x02, // bDescriptorSubtype + 0x06, // bmCapabilities + // Union Functional Descriptor, CDC Spec 5.2.3.8, Table 33 + 5, // bFunctionLength + 0x24, // bDescriptorType + 0x06, // bDescriptorSubtype + CDC3_STATUS_INTERFACE, // bMasterInterface + CDC3_DATA_INTERFACE, // bSlaveInterface0 + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + CDC3_ACM_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + CDC_ACM_SIZE, 0, // wMaxPacketSize + 5, // bInterval + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + CDC3_DATA_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 2, // bNumEndpoints + 0x0A, // bInterfaceClass + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0, // iInterface + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + CDC3_RX_ENDPOINT, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + LSB(CDC_RX_SIZE_480),MSB(CDC_RX_SIZE_480),// wMaxPacketSize + 0, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + CDC3_TX_ENDPOINT | 0x80, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + LSB(CDC_TX_SIZE_480),MSB(CDC_TX_SIZE_480),// wMaxPacketSize + 0, // bInterval +#endif // CDC3_DATA_INTERFACE + +#ifdef MIDI_INTERFACE + // configuration for 480 Mbit/sec speed + // Standard MS Interface Descriptor, + 9, // bLength + 4, // bDescriptorType + MIDI_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 2, // bNumEndpoints + 0x01, // bInterfaceClass (0x01 = Audio) + 0x03, // bInterfaceSubClass (0x03 = MIDI) + 0x00, // bInterfaceProtocol (unused for MIDI) + 0, // iInterface + // MIDI MS Interface Header, USB MIDI 6.1.2.1, page 21, Table 6-2 + 7, // bLength + 0x24, // bDescriptorType = CS_INTERFACE + 0x01, // bDescriptorSubtype = MS_HEADER + 0x00, 0x01, // bcdMSC = revision 01.00 + LSB(7+(6+6+9+9)*MIDI_NUM_CABLES), // wTotalLength + MSB(7+(6+6+9+9)*MIDI_NUM_CABLES), + // MIDI IN Jack Descriptor, B.4.3, Table B-7 (embedded), page 40 + 6, // bLength + 0x24, // bDescriptorType = CS_INTERFACE + 0x02, // bDescriptorSubtype = MIDI_IN_JACK + 0x01, // bJackType = EMBEDDED + 1, // bJackID, ID = 1 + 0, // iJack + // MIDI IN Jack Descriptor, B.4.3, Table B-8 (external), page 40 + 6, // bLength + 0x24, // bDescriptorType = CS_INTERFACE + 0x02, // bDescriptorSubtype = MIDI_IN_JACK + 0x02, // bJackType = EXTERNAL + 2, // bJackID, ID = 2 + 0, // iJack + // MIDI OUT Jack Descriptor, B.4.4, Table B-9, page 41 + 9, + 0x24, // bDescriptorType = CS_INTERFACE + 0x03, // bDescriptorSubtype = MIDI_OUT_JACK + 0x01, // bJackType = EMBEDDED + 3, // bJackID, ID = 3 + 1, // bNrInputPins = 1 pin + 2, // BaSourceID(1) = 2 + 1, // BaSourcePin(1) = first pin + 0, // iJack + // MIDI OUT Jack Descriptor, B.4.4, Table B-10, page 41 + 9, + 0x24, // bDescriptorType = CS_INTERFACE + 0x03, // bDescriptorSubtype = MIDI_OUT_JACK + 0x02, // bJackType = EXTERNAL + 4, // bJackID, ID = 4 + 1, // bNrInputPins = 1 pin + 1, // BaSourceID(1) = 1 + 1, // BaSourcePin(1) = first pin + 0, // iJack + #if MIDI_NUM_CABLES >= 2 + #define MIDI_INTERFACE_JACK_PAIR(a, b, c, d) \ + 6, 0x24, 0x02, 0x01, (a), 0, \ + 6, 0x24, 0x02, 0x02, (b), 0, \ + 9, 0x24, 0x03, 0x01, (c), 1, (b), 1, 0, \ + 9, 0x24, 0x03, 0x02, (d), 1, (a), 1, 0, + MIDI_INTERFACE_JACK_PAIR(5, 6, 7, 8) + #endif + #if MIDI_NUM_CABLES >= 3 + MIDI_INTERFACE_JACK_PAIR(9, 10, 11, 12) + #endif + #if MIDI_NUM_CABLES >= 4 + MIDI_INTERFACE_JACK_PAIR(13, 14, 15, 16) + #endif + #if MIDI_NUM_CABLES >= 5 + MIDI_INTERFACE_JACK_PAIR(17, 18, 19, 20) + #endif + #if MIDI_NUM_CABLES >= 6 + MIDI_INTERFACE_JACK_PAIR(21, 22, 23, 24) + #endif + #if MIDI_NUM_CABLES >= 7 + MIDI_INTERFACE_JACK_PAIR(25, 26, 27, 28) + #endif + #if MIDI_NUM_CABLES >= 8 + MIDI_INTERFACE_JACK_PAIR(29, 30, 31, 32) + #endif + #if MIDI_NUM_CABLES >= 9 + MIDI_INTERFACE_JACK_PAIR(33, 34, 35, 36) + #endif + #if MIDI_NUM_CABLES >= 10 + MIDI_INTERFACE_JACK_PAIR(37, 38, 39, 40) + #endif + #if MIDI_NUM_CABLES >= 11 + MIDI_INTERFACE_JACK_PAIR(41, 42, 43, 44) + #endif + #if MIDI_NUM_CABLES >= 12 + MIDI_INTERFACE_JACK_PAIR(45, 46, 47, 48) + #endif + #if MIDI_NUM_CABLES >= 13 + MIDI_INTERFACE_JACK_PAIR(49, 50, 51, 52) + #endif + #if MIDI_NUM_CABLES >= 14 + MIDI_INTERFACE_JACK_PAIR(53, 54, 55, 56) + #endif + #if MIDI_NUM_CABLES >= 15 + MIDI_INTERFACE_JACK_PAIR(57, 58, 59, 60) + #endif + #if MIDI_NUM_CABLES >= 16 + MIDI_INTERFACE_JACK_PAIR(61, 62, 63, 64) + #endif + // Standard Bulk OUT Endpoint Descriptor, B.5.1, Table B-11, pae 42 + 9, // bLength + 5, // bDescriptorType = ENDPOINT + MIDI_RX_ENDPOINT, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + LSB(MIDI_RX_SIZE_480),MSB(MIDI_RX_SIZE_480),// wMaxPacketSize + 0, // bInterval + 0, // bRefresh + 0, // bSynchAddress + // Class-specific MS Bulk OUT Endpoint Descriptor, B.5.2, Table B-12, page 42 + 4+MIDI_NUM_CABLES, // bLength + 0x25, // bDescriptorSubtype = CS_ENDPOINT + 0x01, // bJackType = MS_GENERAL + MIDI_NUM_CABLES, // bNumEmbMIDIJack = number of jacks + 1, // BaAssocJackID(1) = jack ID #1 + #if MIDI_NUM_CABLES >= 2 + 5, + #endif + #if MIDI_NUM_CABLES >= 3 + 9, + #endif + #if MIDI_NUM_CABLES >= 4 + 13, + #endif + #if MIDI_NUM_CABLES >= 5 + 17, + #endif + #if MIDI_NUM_CABLES >= 6 + 21, + #endif + #if MIDI_NUM_CABLES >= 7 + 25, + #endif + #if MIDI_NUM_CABLES >= 8 + 29, + #endif + #if MIDI_NUM_CABLES >= 9 + 33, + #endif + #if MIDI_NUM_CABLES >= 10 + 37, + #endif + #if MIDI_NUM_CABLES >= 11 + 41, + #endif + #if MIDI_NUM_CABLES >= 12 + 45, + #endif + #if MIDI_NUM_CABLES >= 13 + 49, + #endif + #if MIDI_NUM_CABLES >= 14 + 53, + #endif + #if MIDI_NUM_CABLES >= 15 + 57, + #endif + #if MIDI_NUM_CABLES >= 16 + 61, + #endif + // Standard Bulk IN Endpoint Descriptor, B.5.1, Table B-11, pae 42 + 9, // bLength + 5, // bDescriptorType = ENDPOINT + MIDI_TX_ENDPOINT | 0x80, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + LSB(MIDI_TX_SIZE_480),MSB(MIDI_TX_SIZE_480),// wMaxPacketSize + 0, // bInterval + 0, // bRefresh + 0, // bSynchAddress + // Class-specific MS Bulk IN Endpoint Descriptor, B.5.2, Table B-12, page 42 + 4+MIDI_NUM_CABLES, // bLength + 0x25, // bDescriptorSubtype = CS_ENDPOINT + 0x01, // bJackType = MS_GENERAL + MIDI_NUM_CABLES, // bNumEmbMIDIJack = number of jacks + 3, // BaAssocJackID(1) = jack ID #3 + #if MIDI_NUM_CABLES >= 2 + 7, + #endif + #if MIDI_NUM_CABLES >= 3 + 11, + #endif + #if MIDI_NUM_CABLES >= 4 + 15, + #endif + #if MIDI_NUM_CABLES >= 5 + 19, + #endif + #if MIDI_NUM_CABLES >= 6 + 23, + #endif + #if MIDI_NUM_CABLES >= 7 + 27, + #endif + #if MIDI_NUM_CABLES >= 8 + 31, + #endif + #if MIDI_NUM_CABLES >= 9 + 35, + #endif + #if MIDI_NUM_CABLES >= 10 + 39, + #endif + #if MIDI_NUM_CABLES >= 11 + 43, + #endif + #if MIDI_NUM_CABLES >= 12 + 47, + #endif + #if MIDI_NUM_CABLES >= 13 + 51, + #endif + #if MIDI_NUM_CABLES >= 14 + 55, + #endif + #if MIDI_NUM_CABLES >= 15 + 59, + #endif + #if MIDI_NUM_CABLES >= 16 + 63, + #endif +#endif // MIDI_INTERFACE + +#ifdef KEYBOARD_INTERFACE + // configuration for 480 Mbit/sec speed + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + KEYBOARD_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x03, // bInterfaceClass (0x03 = HID) + 0x01, // bInterfaceSubClass (0x01 = Boot) + 0x01, // bInterfaceProtocol (0x01 = Keyboard) + 0, // iInterface + // HID interface descriptor, HID 1.11 spec, section 6.2.1 + 9, // bLength + 0x21, // bDescriptorType + 0x11, 0x01, // bcdHID + 0, // bCountryCode + 1, // bNumDescriptors + 0x22, // bDescriptorType + LSB(sizeof(keyboard_report_desc)), // wDescriptorLength + MSB(sizeof(keyboard_report_desc)), + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + KEYBOARD_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + KEYBOARD_SIZE, 0, // wMaxPacketSize + KEYBOARD_INTERVAL, // bInterval +#endif // KEYBOARD_INTERFACE + +#ifdef MOUSE_INTERFACE + // configuration for 480 Mbit/sec speed + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + MOUSE_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x03, // bInterfaceClass (0x03 = HID) + 0x00, // bInterfaceSubClass (0x01 = Boot) + 0x00, // bInterfaceProtocol (0x02 = Mouse) + 0, // iInterface + // HID interface descriptor, HID 1.11 spec, section 6.2.1 + 9, // bLength + 0x21, // bDescriptorType + 0x11, 0x01, // bcdHID + 0, // bCountryCode + 1, // bNumDescriptors + 0x22, // bDescriptorType + LSB(sizeof(mouse_report_desc)), // wDescriptorLength + MSB(sizeof(mouse_report_desc)), + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + MOUSE_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + MOUSE_SIZE, 0, // wMaxPacketSize + MOUSE_INTERVAL, // bInterval +#endif // MOUSE_INTERFACE + +#ifdef RAWHID_INTERFACE + // configuration for 480 Mbit/sec speed + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + RAWHID_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 2, // bNumEndpoints + 0x03, // bInterfaceClass (0x03 = HID) + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0, // iInterface + // HID interface descriptor, HID 1.11 spec, section 6.2.1 + 9, // bLength + 0x21, // bDescriptorType + 0x11, 0x01, // bcdHID + 0, // bCountryCode + 1, // bNumDescriptors + 0x22, // bDescriptorType + LSB(sizeof(rawhid_report_desc)), // wDescriptorLength + MSB(sizeof(rawhid_report_desc)), + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + RAWHID_TX_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + RAWHID_TX_SIZE, 0, // wMaxPacketSize + RAWHID_TX_INTERVAL, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + RAWHID_RX_ENDPOINT, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + RAWHID_RX_SIZE, 0, // wMaxPacketSize + RAWHID_RX_INTERVAL, // bInterval +#endif // RAWHID_INTERFACE + +#ifdef FLIGHTSIM_INTERFACE + // configuration for 480 Mbit/sec speed + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + FLIGHTSIM_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 2, // bNumEndpoints + 0x03, // bInterfaceClass (0x03 = HID) + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0, // iInterface + // HID interface descriptor, HID 1.11 spec, section 6.2.1 + 9, // bLength + 0x21, // bDescriptorType + 0x11, 0x01, // bcdHID + 0, // bCountryCode + 1, // bNumDescriptors + 0x22, // bDescriptorType + LSB(sizeof(flightsim_report_desc)), // wDescriptorLength + MSB(sizeof(flightsim_report_desc)), + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + FLIGHTSIM_TX_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + FLIGHTSIM_TX_SIZE, 0, // wMaxPacketSize + FLIGHTSIM_TX_INTERVAL, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + FLIGHTSIM_RX_ENDPOINT, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + FLIGHTSIM_RX_SIZE, 0, // wMaxPacketSize + FLIGHTSIM_RX_INTERVAL, // bInterval +#endif // FLIGHTSIM_INTERFACE + +#ifdef SEREMU_INTERFACE + // configuration for 480 Mbit/sec speed + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + SEREMU_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 2, // bNumEndpoints + 0x03, // bInterfaceClass (0x03 = HID) + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0, // iInterface + // HID interface descriptor, HID 1.11 spec, section 6.2.1 + 9, // bLength + 0x21, // bDescriptorType + 0x11, 0x01, // bcdHID + 0, // bCountryCode + 1, // bNumDescriptors + 0x22, // bDescriptorType + LSB(sizeof(seremu_report_desc)), // wDescriptorLength + MSB(sizeof(seremu_report_desc)), + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + SEREMU_TX_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + SEREMU_TX_SIZE, 0, // wMaxPacketSize + SEREMU_TX_INTERVAL, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + SEREMU_RX_ENDPOINT, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + SEREMU_RX_SIZE, 0, // wMaxPacketSize + SEREMU_RX_INTERVAL, // bInterval +#endif // SEREMU_INTERFACE + +#ifdef JOYSTICK_INTERFACE + // configuration for 480 Mbit/sec speed + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + JOYSTICK_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x03, // bInterfaceClass (0x03 = HID) + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0, // iInterface + // HID interface descriptor, HID 1.11 spec, section 6.2.1 + 9, // bLength + 0x21, // bDescriptorType + 0x11, 0x01, // bcdHID + 0, // bCountryCode + 1, // bNumDescriptors + 0x22, // bDescriptorType + LSB(sizeof(joystick_report_desc)), // wDescriptorLength + MSB(sizeof(joystick_report_desc)), + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + JOYSTICK_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + JOYSTICK_SIZE, 0, // wMaxPacketSize + JOYSTICK_INTERVAL, // bInterval +#endif // JOYSTICK_INTERFACE + +#ifdef MTP_INTERFACE + // configuration for 480 Mbit/sec speed + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + MTP_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 3, // bNumEndpoints + 0x06, // bInterfaceClass (0x06 = still image) + 0x01, // bInterfaceSubClass + 0x01, // bInterfaceProtocol + 4, // iInterface + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + MTP_TX_ENDPOINT | 0x80, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + LSB(MTP_TX_SIZE_480),MSB(MTP_TX_SIZE_480), // wMaxPacketSize + 0, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + MTP_RX_ENDPOINT, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + LSB(MTP_RX_SIZE_480),MSB(MTP_RX_SIZE_480), // wMaxPacketSize + 0, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + MTP_EVENT_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + MTP_EVENT_SIZE, 0, // wMaxPacketSize + MTP_EVENT_INTERVAL_480, // bInterval +#endif // MTP_INTERFACE + +#ifdef KEYMEDIA_INTERFACE + // configuration for 480 Mbit/sec speed + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + KEYMEDIA_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x03, // bInterfaceClass (0x03 = HID) + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0, // iInterface + // HID interface descriptor, HID 1.11 spec, section 6.2.1 + 9, // bLength + 0x21, // bDescriptorType + 0x11, 0x01, // bcdHID + 0, // bCountryCode + 1, // bNumDescriptors + 0x22, // bDescriptorType + LSB(sizeof(keymedia_report_desc)), // wDescriptorLength + MSB(sizeof(keymedia_report_desc)), + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + KEYMEDIA_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + KEYMEDIA_SIZE, 0, // wMaxPacketSize + KEYMEDIA_INTERVAL, // bInterval +#endif // KEYMEDIA_INTERFACE + +#ifdef AUDIO_INTERFACE + // configuration for 480 Mbit/sec speed + // interface association descriptor, USB ECN, Table 9-Z + 8, // bLength + 11, // bDescriptorType + AUDIO_INTERFACE, // bFirstInterface + 3, // bInterfaceCount + 0x01, // bFunctionClass + 0x01, // bFunctionSubClass + 0x00, // bFunctionProtocol + 0, // iFunction + // Standard AudioControl (AC) Interface Descriptor + // USB DCD for Audio Devices 1.0, Table 4-1, page 36 + 9, // bLength + 4, // bDescriptorType, 4 = INTERFACE + AUDIO_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 0, // bNumEndpoints + 1, // bInterfaceClass, 1 = AUDIO + 1, // bInterfaceSubclass, 1 = AUDIO_CONTROL + 0, // bInterfaceProtocol + 0, // iInterface + // Class-specific AC Interface Header Descriptor + // USB DCD for Audio Devices 1.0, Table 4-2, page 37-38 + 10, // bLength + 0x24, // bDescriptorType, 0x24 = CS_INTERFACE + 0x01, // bDescriptorSubtype, 1 = HEADER + 0x00, 0x01, // bcdADC (version 1.0) + LSB(62), MSB(62), // wTotalLength + 2, // bInCollection + AUDIO_INTERFACE+1, // baInterfaceNr(1) - Transmit to PC + AUDIO_INTERFACE+2, // baInterfaceNr(2) - Receive from PC + // Input Terminal Descriptor + // USB DCD for Audio Devices 1.0, Table 4-3, page 39 + 12, // bLength + 0x24, // bDescriptorType, 0x24 = CS_INTERFACE + 0x02, // bDescriptorSubType, 2 = INPUT_TERMINAL + 1, // bTerminalID + //0x01, 0x02, // wTerminalType, 0x0201 = MICROPHONE + //0x03, 0x06, // wTerminalType, 0x0603 = Line Connector + 0x02, 0x06, // wTerminalType, 0x0602 = Digital Audio + 0, // bAssocTerminal, 0 = unidirectional + 2, // bNrChannels + 0x03, 0x00, // wChannelConfig, 0x0003 = Left & Right Front + 0, // iChannelNames + 0, // iTerminal + // Output Terminal Descriptor + // USB DCD for Audio Devices 1.0, Table 4-4, page 40 + 9, // bLength + 0x24, // bDescriptorType, 0x24 = CS_INTERFACE + 3, // bDescriptorSubtype, 3 = OUTPUT_TERMINAL + 2, // bTerminalID + 0x01, 0x01, // wTerminalType, 0x0101 = USB_STREAMING + 0, // bAssocTerminal, 0 = unidirectional + 1, // bCSourceID, connected to input terminal, ID=1 + 0, // iTerminal + // Input Terminal Descriptor + // USB DCD for Audio Devices 1.0, Table 4-3, page 39 + 12, // bLength + 0x24, // bDescriptorType, 0x24 = CS_INTERFACE + 2, // bDescriptorSubType, 2 = INPUT_TERMINAL + 3, // bTerminalID + 0x01, 0x01, // wTerminalType, 0x0101 = USB_STREAMING + 0, // bAssocTerminal, 0 = unidirectional + 2, // bNrChannels + 0x03, 0x00, // wChannelConfig, 0x0003 = Left & Right Front + 0, // iChannelNames + 0, // iTerminal + // Volume feature descriptor + 10, // bLength + 0x24, // bDescriptorType = CS_INTERFACE + 0x06, // bDescriptorSubType = FEATURE_UNIT + 0x31, // bUnitID + 0x03, // bSourceID (Input Terminal) + 0x01, // bControlSize (each channel is 1 byte, 3 channels) + 0x01, // bmaControls(0) Master: Mute + 0x02, // bmaControls(1) Left: Volume + 0x02, // bmaControls(2) Right: Volume + 0x00, // iFeature + // Output Terminal Descriptor + // USB DCD for Audio Devices 1.0, Table 4-4, page 40 + 9, // bLength + 0x24, // bDescriptorType, 0x24 = CS_INTERFACE + 3, // bDescriptorSubtype, 3 = OUTPUT_TERMINAL + 4, // bTerminalID + //0x02, 0x03, // wTerminalType, 0x0302 = Headphones + 0x02, 0x06, // wTerminalType, 0x0602 = Digital Audio + 0, // bAssocTerminal, 0 = unidirectional + 0x31, // bCSourceID, connected to feature, ID=31 + 0, // iTerminal + // Standard AS Interface Descriptor + // USB DCD for Audio Devices 1.0, Section 4.5.1, Table 4-18, page 59 + // Alternate 0: default setting, disabled zero bandwidth + 9, // bLenght + 4, // bDescriptorType = INTERFACE + AUDIO_INTERFACE+1, // bInterfaceNumber + 0, // bAlternateSetting + 0, // bNumEndpoints + 1, // bInterfaceClass, 1 = AUDIO + 2, // bInterfaceSubclass, 2 = AUDIO_STREAMING + 0, // bInterfaceProtocol + 0, // iInterface + // Alternate 1: streaming data + 9, // bLenght + 4, // bDescriptorType = INTERFACE + AUDIO_INTERFACE+1, // bInterfaceNumber + 1, // bAlternateSetting + 1, // bNumEndpoints + 1, // bInterfaceClass, 1 = AUDIO + 2, // bInterfaceSubclass, 2 = AUDIO_STREAMING + 0, // bInterfaceProtocol + 0, // iInterface + // Class-Specific AS Interface Descriptor + // USB DCD for Audio Devices 1.0, Section 4.5.2, Table 4-19, page 60 + 7, // bLength + 0x24, // bDescriptorType = CS_INTERFACE + 1, // bDescriptorSubtype, 1 = AS_GENERAL + 2, // bTerminalLink: Terminal ID = 2 + 3, // bDelay (approx 3ms delay, audio lib updates) + 0x01, 0x00, // wFormatTag, 0x0001 = PCM + // Type I Format Descriptor + // USB DCD for Audio Data Formats 1.0, Section 2.2.5, Table 2-1, page 10 + 11, // bLength + 0x24, // bDescriptorType = CS_INTERFACE + 2, // bDescriptorSubtype = FORMAT_TYPE + 1, // bFormatType = FORMAT_TYPE_I + 2, // bNrChannels = 2 + 2, // bSubFrameSize = 2 byte + 16, // bBitResolution = 16 bits + 1, // bSamFreqType = 1 frequency + LSB(44100), MSB(44100), 0, // tSamFreq + // Standard AS Isochronous Audio Data Endpoint Descriptor + // USB DCD for Audio Devices 1.0, Section 4.6.1.1, Table 4-20, page 61-62 + 9, // bLength + 5, // bDescriptorType, 5 = ENDPOINT_DESCRIPTOR + AUDIO_TX_ENDPOINT | 0x80, // bEndpointAddress + 0x09, // bmAttributes = isochronous, adaptive + LSB(AUDIO_TX_SIZE), MSB(AUDIO_TX_SIZE), // wMaxPacketSize + 4, // bInterval, 4 = every 8 micro-frames + 0, // bRefresh + 0, // bSynchAddress + // Class-Specific AS Isochronous Audio Data Endpoint Descriptor + // USB DCD for Audio Devices 1.0, Section 4.6.1.2, Table 4-21, page 62-63 + 7, // bLength + 0x25, // bDescriptorType, 0x25 = CS_ENDPOINT + 1, // bDescriptorSubtype, 1 = EP_GENERAL + 0x00, // bmAttributes + 0, // bLockDelayUnits, 1 = ms + 0x00, 0x00, // wLockDelay + // Standard AS Interface Descriptor + // USB DCD for Audio Devices 1.0, Section 4.5.1, Table 4-18, page 59 + // Alternate 0: default setting, disabled zero bandwidth + 9, // bLenght + 4, // bDescriptorType = INTERFACE + AUDIO_INTERFACE+2, // bInterfaceNumber + 0, // bAlternateSetting + 0, // bNumEndpoints + 1, // bInterfaceClass, 1 = AUDIO + 2, // bInterfaceSubclass, 2 = AUDIO_STREAMING + 0, // bInterfaceProtocol + 0, // iInterface + // Alternate 1: streaming data + 9, // bLenght + 4, // bDescriptorType = INTERFACE + AUDIO_INTERFACE+2, // bInterfaceNumber + 1, // bAlternateSetting + 2, // bNumEndpoints + 1, // bInterfaceClass, 1 = AUDIO + 2, // bInterfaceSubclass, 2 = AUDIO_STREAMING + 0, // bInterfaceProtocol + 0, // iInterface + // Class-Specific AS Interface Descriptor + // USB DCD for Audio Devices 1.0, Section 4.5.2, Table 4-19, page 60 + 7, // bLength + 0x24, // bDescriptorType = CS_INTERFACE + 1, // bDescriptorSubtype, 1 = AS_GENERAL + 3, // bTerminalLink: Terminal ID = 3 + 3, // bDelay (approx 3ms delay, audio lib updates) + 0x01, 0x00, // wFormatTag, 0x0001 = PCM + // Type I Format Descriptor + // USB DCD for Audio Data Formats 1.0, Section 2.2.5, Table 2-1, page 10 + 11, // bLength + 0x24, // bDescriptorType = CS_INTERFACE + 2, // bDescriptorSubtype = FORMAT_TYPE + 1, // bFormatType = FORMAT_TYPE_I + 2, // bNrChannels = 2 + 2, // bSubFrameSize = 2 byte + 16, // bBitResolution = 16 bits + 1, // bSamFreqType = 1 frequency + LSB(44100), MSB(44100), 0, // tSamFreq + // Standard AS Isochronous Audio Data Endpoint Descriptor + // USB DCD for Audio Devices 1.0, Section 4.6.1.1, Table 4-20, page 61-62 + 9, // bLength + 5, // bDescriptorType, 5 = ENDPOINT_DESCRIPTOR + AUDIO_RX_ENDPOINT, // bEndpointAddress + 0x05, // bmAttributes = isochronous, asynchronous + LSB(AUDIO_RX_SIZE), MSB(AUDIO_RX_SIZE), // wMaxPacketSize + 4, // bInterval, 4 = every 8 micro-frames + 0, // bRefresh + AUDIO_SYNC_ENDPOINT | 0x80, // bSynchAddress + // Class-Specific AS Isochronous Audio Data Endpoint Descriptor + // USB DCD for Audio Devices 1.0, Section 4.6.1.2, Table 4-21, page 62-63 + 7, // bLength + 0x25, // bDescriptorType, 0x25 = CS_ENDPOINT + 1, // bDescriptorSubtype, 1 = EP_GENERAL + 0x00, // bmAttributes + 0, // bLockDelayUnits, 1 = ms + 0x00, 0x00, // wLockDelay + // Standard AS Isochronous Audio Synch Endpoint Descriptor + // USB DCD for Audio Devices 1.0, Section 4.6.2.1, Table 4-22, page 63-64 + 9, // bLength + 5, // bDescriptorType, 5 = ENDPOINT_DESCRIPTOR + AUDIO_SYNC_ENDPOINT | 0x80, // bEndpointAddress + 0x11, // bmAttributes = isochronous, feedback + 4, 0, // wMaxPacketSize, 4 bytes + 4, // bInterval, 4 = 4 = every 8 micro-frames + 7, // bRefresh, + 0, // bSynchAddress +#endif + +#ifdef MULTITOUCH_INTERFACE + // configuration for 480 Mbit/sec speed + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + MULTITOUCH_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x03, // bInterfaceClass (0x03 = HID) + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0, // iInterface + // HID interface descriptor, HID 1.11 spec, section 6.2.1 + 9, // bLength + 0x21, // bDescriptorType + 0x11, 0x01, // bcdHID + 0, // bCountryCode + 1, // bNumDescriptors + 0x22, // bDescriptorType + LSB(sizeof(multitouch_report_desc)), // wDescriptorLength + MSB(sizeof(multitouch_report_desc)), + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + MULTITOUCH_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + MULTITOUCH_SIZE, 0, // wMaxPacketSize + 4, // bInterval, 4 = 1ms +#endif // MULTITOUCH_INTERFACE + +#ifdef EXPERIMENTAL_INTERFACE + // configuration for 480 Mbit/sec speed + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + EXPERIMENTAL_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 2, // bNumEndpoints + 0xFF, // bInterfaceClass (0xFF = Vendor) + 0x6A, // bInterfaceSubClass + 0xC7, // bInterfaceProtocol + 0, // iInterface + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + 1 | 0x80, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + LSB(512), MSB(512), // wMaxPacketSize + 1, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + 1, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + LSB(512), MSB(512), // wMaxPacketSize + 1, // bInterval +#endif // EXPERIMENTAL_INTERFACE +}; + + +PROGMEM const uint8_t usb_config_descriptor_12[CONFIG_DESC_SIZE] = { + // configuration descriptor, USB spec 9.6.3, page 264-266, Table 9-10 + 9, // bLength; + 2, // bDescriptorType; + LSB(CONFIG_DESC_SIZE), // wTotalLength + MSB(CONFIG_DESC_SIZE), + NUM_INTERFACE, // bNumInterfaces + 1, // bConfigurationValue + 0, // iConfiguration + 0xC0, // bmAttributes + 50, // bMaxPower + +#ifdef CDC_IAD_DESCRIPTOR + // interface association descriptor, USB ECN, Table 9-Z + 8, // bLength + 11, // bDescriptorType + CDC_STATUS_INTERFACE, // bFirstInterface + 2, // bInterfaceCount + 0x02, // bFunctionClass + 0x02, // bFunctionSubClass + 0x01, // bFunctionProtocol + 0, // iFunction +#endif + +#ifdef CDC_DATA_INTERFACE + // configuration for 12 Mbit/sec speed + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + CDC_STATUS_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x02, // bInterfaceClass + 0x02, // bInterfaceSubClass + 0x01, // bInterfaceProtocol + 0, // iInterface + // CDC Header Functional Descriptor, CDC Spec 5.2.3.1, Table 26 + 5, // bFunctionLength + 0x24, // bDescriptorType + 0x00, // bDescriptorSubtype + 0x10, 0x01, // bcdCDC + // Call Management Functional Descriptor, CDC Spec 5.2.3.2, Table 27 + 5, // bFunctionLength + 0x24, // bDescriptorType + 0x01, // bDescriptorSubtype + 0x01, // bmCapabilities + 1, // bDataInterface + // Abstract Control Management Functional Descriptor, CDC Spec 5.2.3.3, Table 28 + 4, // bFunctionLength + 0x24, // bDescriptorType + 0x02, // bDescriptorSubtype + 0x06, // bmCapabilities + // Union Functional Descriptor, CDC Spec 5.2.3.8, Table 33 + 5, // bFunctionLength + 0x24, // bDescriptorType + 0x06, // bDescriptorSubtype + CDC_STATUS_INTERFACE, // bMasterInterface + CDC_DATA_INTERFACE, // bSlaveInterface0 + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + CDC_ACM_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + CDC_ACM_SIZE, 0, // wMaxPacketSize + 16, // bInterval + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + CDC_DATA_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 2, // bNumEndpoints + 0x0A, // bInterfaceClass + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0, // iInterface + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + CDC_RX_ENDPOINT, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + LSB(CDC_RX_SIZE_12),MSB(CDC_RX_SIZE_12),// wMaxPacketSize + 0, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + CDC_TX_ENDPOINT | 0x80, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + LSB(CDC_TX_SIZE_12),MSB(CDC_TX_SIZE_12),// wMaxPacketSize + 0, // bInterval +#endif // CDC_DATA_INTERFACE + +#ifdef CDC2_DATA_INTERFACE + // configuration for 12 Mbit/sec speed + // interface association descriptor, USB ECN, Table 9-Z + 8, // bLength + 11, // bDescriptorType + CDC2_STATUS_INTERFACE, // bFirstInterface + 2, // bInterfaceCount + 0x02, // bFunctionClass + 0x02, // bFunctionSubClass + 0x01, // bFunctionProtocol + 0, // iFunction + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + CDC2_STATUS_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x02, // bInterfaceClass + 0x02, // bInterfaceSubClass + 0x01, // bInterfaceProtocol + 0, // iInterface + // CDC Header Functional Descriptor, CDC Spec 5.2.3.1, Table 26 + 5, // bFunctionLength + 0x24, // bDescriptorType + 0x00, // bDescriptorSubtype + 0x10, 0x01, // bcdCDC + // Call Management Functional Descriptor, CDC Spec 5.2.3.2, Table 27 + 5, // bFunctionLength + 0x24, // bDescriptorType + 0x01, // bDescriptorSubtype + 0x01, // bmCapabilities + 1, // bDataInterface + // Abstract Control Management Functional Descriptor, CDC Spec 5.2.3.3, Table 28 + 4, // bFunctionLength + 0x24, // bDescriptorType + 0x02, // bDescriptorSubtype + 0x06, // bmCapabilities + // Union Functional Descriptor, CDC Spec 5.2.3.8, Table 33 + 5, // bFunctionLength + 0x24, // bDescriptorType + 0x06, // bDescriptorSubtype + CDC2_STATUS_INTERFACE, // bMasterInterface + CDC2_DATA_INTERFACE, // bSlaveInterface0 + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + CDC2_ACM_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + CDC_ACM_SIZE, 0, // wMaxPacketSize + 64, // bInterval + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + CDC2_DATA_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 2, // bNumEndpoints + 0x0A, // bInterfaceClass + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0, // iInterface + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + CDC2_RX_ENDPOINT, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + CDC_RX_SIZE_12, 0, // wMaxPacketSize + 0, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + CDC2_TX_ENDPOINT | 0x80, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + CDC_TX_SIZE_12, 0, // wMaxPacketSize + 0, // bInterval +#endif // CDC2_DATA_INTERFACE + +#ifdef CDC3_DATA_INTERFACE + // configuration for 12 Mbit/sec speed + // interface association descriptor, USB ECN, Table 9-Z + 8, // bLength + 11, // bDescriptorType + CDC3_STATUS_INTERFACE, // bFirstInterface + 2, // bInterfaceCount + 0x02, // bFunctionClass + 0x02, // bFunctionSubClass + 0x01, // bFunctionProtocol + 0, // iFunction + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + CDC3_STATUS_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x02, // bInterfaceClass + 0x02, // bInterfaceSubClass + 0x01, // bInterfaceProtocol + 0, // iInterface + // CDC Header Functional Descriptor, CDC Spec 5.2.3.1, Table 26 + 5, // bFunctionLength + 0x24, // bDescriptorType + 0x00, // bDescriptorSubtype + 0x10, 0x01, // bcdCDC + // Call Management Functional Descriptor, CDC Spec 5.2.3.2, Table 27 + 5, // bFunctionLength + 0x24, // bDescriptorType + 0x01, // bDescriptorSubtype + 0x01, // bmCapabilities + 1, // bDataInterface + // Abstract Control Management Functional Descriptor, CDC Spec 5.2.3.3, Table 28 + 4, // bFunctionLength + 0x24, // bDescriptorType + 0x02, // bDescriptorSubtype + 0x06, // bmCapabilities + // Union Functional Descriptor, CDC Spec 5.2.3.8, Table 33 + 5, // bFunctionLength + 0x24, // bDescriptorType + 0x06, // bDescriptorSubtype + CDC3_STATUS_INTERFACE, // bMasterInterface + CDC3_DATA_INTERFACE, // bSlaveInterface0 + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + CDC3_ACM_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + CDC_ACM_SIZE, 0, // wMaxPacketSize + 64, // bInterval + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + CDC3_DATA_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 2, // bNumEndpoints + 0x0A, // bInterfaceClass + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0, // iInterface + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + CDC3_RX_ENDPOINT, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + CDC_RX_SIZE_12, 0, // wMaxPacketSize + 0, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + CDC3_TX_ENDPOINT | 0x80, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + CDC_TX_SIZE_12, 0, // wMaxPacketSize + 0, // bInterval +#endif // CDC3_DATA_INTERFACE + +#ifdef MIDI_INTERFACE + // configuration for 12 Mbit/sec speed + // Standard MS Interface Descriptor, + 9, // bLength + 4, // bDescriptorType + MIDI_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 2, // bNumEndpoints + 0x01, // bInterfaceClass (0x01 = Audio) + 0x03, // bInterfaceSubClass (0x03 = MIDI) + 0x00, // bInterfaceProtocol (unused for MIDI) + 0, // iInterface + // MIDI MS Interface Header, USB MIDI 6.1.2.1, page 21, Table 6-2 + 7, // bLength + 0x24, // bDescriptorType = CS_INTERFACE + 0x01, // bDescriptorSubtype = MS_HEADER + 0x00, 0x01, // bcdMSC = revision 01.00 + LSB(7+(6+6+9+9)*MIDI_NUM_CABLES), // wTotalLength + MSB(7+(6+6+9+9)*MIDI_NUM_CABLES), + // MIDI IN Jack Descriptor, B.4.3, Table B-7 (embedded), page 40 + 6, // bLength + 0x24, // bDescriptorType = CS_INTERFACE + 0x02, // bDescriptorSubtype = MIDI_IN_JACK + 0x01, // bJackType = EMBEDDED + 1, // bJackID, ID = 1 + 0, // iJack + // MIDI IN Jack Descriptor, B.4.3, Table B-8 (external), page 40 + 6, // bLength + 0x24, // bDescriptorType = CS_INTERFACE + 0x02, // bDescriptorSubtype = MIDI_IN_JACK + 0x02, // bJackType = EXTERNAL + 2, // bJackID, ID = 2 + 0, // iJack + // MIDI OUT Jack Descriptor, B.4.4, Table B-9, page 41 + 9, + 0x24, // bDescriptorType = CS_INTERFACE + 0x03, // bDescriptorSubtype = MIDI_OUT_JACK + 0x01, // bJackType = EMBEDDED + 3, // bJackID, ID = 3 + 1, // bNrInputPins = 1 pin + 2, // BaSourceID(1) = 2 + 1, // BaSourcePin(1) = first pin + 0, // iJack + // MIDI OUT Jack Descriptor, B.4.4, Table B-10, page 41 + 9, + 0x24, // bDescriptorType = CS_INTERFACE + 0x03, // bDescriptorSubtype = MIDI_OUT_JACK + 0x02, // bJackType = EXTERNAL + 4, // bJackID, ID = 4 + 1, // bNrInputPins = 1 pin + 1, // BaSourceID(1) = 1 + 1, // BaSourcePin(1) = first pin + 0, // iJack + #if MIDI_NUM_CABLES >= 2 + #define MIDI_INTERFACE_JACK_PAIR(a, b, c, d) \ + 6, 0x24, 0x02, 0x01, (a), 0, \ + 6, 0x24, 0x02, 0x02, (b), 0, \ + 9, 0x24, 0x03, 0x01, (c), 1, (b), 1, 0, \ + 9, 0x24, 0x03, 0x02, (d), 1, (a), 1, 0, + MIDI_INTERFACE_JACK_PAIR(5, 6, 7, 8) + #endif + #if MIDI_NUM_CABLES >= 3 + MIDI_INTERFACE_JACK_PAIR(9, 10, 11, 12) + #endif + #if MIDI_NUM_CABLES >= 4 + MIDI_INTERFACE_JACK_PAIR(13, 14, 15, 16) + #endif + #if MIDI_NUM_CABLES >= 5 + MIDI_INTERFACE_JACK_PAIR(17, 18, 19, 20) + #endif + #if MIDI_NUM_CABLES >= 6 + MIDI_INTERFACE_JACK_PAIR(21, 22, 23, 24) + #endif + #if MIDI_NUM_CABLES >= 7 + MIDI_INTERFACE_JACK_PAIR(25, 26, 27, 28) + #endif + #if MIDI_NUM_CABLES >= 8 + MIDI_INTERFACE_JACK_PAIR(29, 30, 31, 32) + #endif + #if MIDI_NUM_CABLES >= 9 + MIDI_INTERFACE_JACK_PAIR(33, 34, 35, 36) + #endif + #if MIDI_NUM_CABLES >= 10 + MIDI_INTERFACE_JACK_PAIR(37, 38, 39, 40) + #endif + #if MIDI_NUM_CABLES >= 11 + MIDI_INTERFACE_JACK_PAIR(41, 42, 43, 44) + #endif + #if MIDI_NUM_CABLES >= 12 + MIDI_INTERFACE_JACK_PAIR(45, 46, 47, 48) + #endif + #if MIDI_NUM_CABLES >= 13 + MIDI_INTERFACE_JACK_PAIR(49, 50, 51, 52) + #endif + #if MIDI_NUM_CABLES >= 14 + MIDI_INTERFACE_JACK_PAIR(53, 54, 55, 56) + #endif + #if MIDI_NUM_CABLES >= 15 + MIDI_INTERFACE_JACK_PAIR(57, 58, 59, 60) + #endif + #if MIDI_NUM_CABLES >= 16 + MIDI_INTERFACE_JACK_PAIR(61, 62, 63, 64) + #endif + // Standard Bulk OUT Endpoint Descriptor, B.5.1, Table B-11, pae 42 + 9, // bLength + 5, // bDescriptorType = ENDPOINT + MIDI_RX_ENDPOINT, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + LSB(MIDI_RX_SIZE_12),MSB(MIDI_RX_SIZE_12),// wMaxPacketSize + 0, // bInterval + 0, // bRefresh + 0, // bSynchAddress + // Class-specific MS Bulk OUT Endpoint Descriptor, B.5.2, Table B-12, page 42 + 4+MIDI_NUM_CABLES, // bLength + 0x25, // bDescriptorSubtype = CS_ENDPOINT + 0x01, // bJackType = MS_GENERAL + MIDI_NUM_CABLES, // bNumEmbMIDIJack = number of jacks + 1, // BaAssocJackID(1) = jack ID #1 + #if MIDI_NUM_CABLES >= 2 + 5, + #endif + #if MIDI_NUM_CABLES >= 3 + 9, + #endif + #if MIDI_NUM_CABLES >= 4 + 13, + #endif + #if MIDI_NUM_CABLES >= 5 + 17, + #endif + #if MIDI_NUM_CABLES >= 6 + 21, + #endif + #if MIDI_NUM_CABLES >= 7 + 25, + #endif + #if MIDI_NUM_CABLES >= 8 + 29, + #endif + #if MIDI_NUM_CABLES >= 9 + 33, + #endif + #if MIDI_NUM_CABLES >= 10 + 37, + #endif + #if MIDI_NUM_CABLES >= 11 + 41, + #endif + #if MIDI_NUM_CABLES >= 12 + 45, + #endif + #if MIDI_NUM_CABLES >= 13 + 49, + #endif + #if MIDI_NUM_CABLES >= 14 + 53, + #endif + #if MIDI_NUM_CABLES >= 15 + 57, + #endif + #if MIDI_NUM_CABLES >= 16 + 61, + #endif + // Standard Bulk IN Endpoint Descriptor, B.5.1, Table B-11, pae 42 + 9, // bLength + 5, // bDescriptorType = ENDPOINT + MIDI_TX_ENDPOINT | 0x80, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + LSB(MIDI_TX_SIZE_12),MSB(MIDI_TX_SIZE_12),// wMaxPacketSize + 0, // bInterval + 0, // bRefresh + 0, // bSynchAddress + // Class-specific MS Bulk IN Endpoint Descriptor, B.5.2, Table B-12, page 42 + 4+MIDI_NUM_CABLES, // bLength + 0x25, // bDescriptorSubtype = CS_ENDPOINT + 0x01, // bJackType = MS_GENERAL + MIDI_NUM_CABLES, // bNumEmbMIDIJack = number of jacks + 3, // BaAssocJackID(1) = jack ID #3 + #if MIDI_NUM_CABLES >= 2 + 7, + #endif + #if MIDI_NUM_CABLES >= 3 + 11, + #endif + #if MIDI_NUM_CABLES >= 4 + 15, + #endif + #if MIDI_NUM_CABLES >= 5 + 19, + #endif + #if MIDI_NUM_CABLES >= 6 + 23, + #endif + #if MIDI_NUM_CABLES >= 7 + 27, + #endif + #if MIDI_NUM_CABLES >= 8 + 31, + #endif + #if MIDI_NUM_CABLES >= 9 + 35, + #endif + #if MIDI_NUM_CABLES >= 10 + 39, + #endif + #if MIDI_NUM_CABLES >= 11 + 43, + #endif + #if MIDI_NUM_CABLES >= 12 + 47, + #endif + #if MIDI_NUM_CABLES >= 13 + 51, + #endif + #if MIDI_NUM_CABLES >= 14 + 55, + #endif + #if MIDI_NUM_CABLES >= 15 + 59, + #endif + #if MIDI_NUM_CABLES >= 16 + 63, + #endif +#endif // MIDI_INTERFACE + +#ifdef KEYBOARD_INTERFACE + // configuration for 12 Mbit/sec speed + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + KEYBOARD_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x03, // bInterfaceClass (0x03 = HID) + 0x01, // bInterfaceSubClass (0x01 = Boot) + 0x01, // bInterfaceProtocol (0x01 = Keyboard) + 0, // iInterface + // HID interface descriptor, HID 1.11 spec, section 6.2.1 + 9, // bLength + 0x21, // bDescriptorType + 0x11, 0x01, // bcdHID + 0, // bCountryCode + 1, // bNumDescriptors + 0x22, // bDescriptorType + LSB(sizeof(keyboard_report_desc)), // wDescriptorLength + MSB(sizeof(keyboard_report_desc)), + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + KEYBOARD_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + KEYBOARD_SIZE, 0, // wMaxPacketSize + KEYBOARD_INTERVAL, // bInterval +#endif // KEYBOARD_INTERFACE + +#ifdef MOUSE_INTERFACE + // configuration for 12 Mbit/sec speed + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + MOUSE_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x03, // bInterfaceClass (0x03 = HID) + 0x00, // bInterfaceSubClass (0x01 = Boot) + 0x00, // bInterfaceProtocol (0x02 = Mouse) + 0, // iInterface + // HID interface descriptor, HID 1.11 spec, section 6.2.1 + 9, // bLength + 0x21, // bDescriptorType + 0x11, 0x01, // bcdHID + 0, // bCountryCode + 1, // bNumDescriptors + 0x22, // bDescriptorType + LSB(sizeof(mouse_report_desc)), // wDescriptorLength + MSB(sizeof(mouse_report_desc)), + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + MOUSE_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + MOUSE_SIZE, 0, // wMaxPacketSize + MOUSE_INTERVAL, // bInterval +#endif // MOUSE_INTERFACE + +#ifdef RAWHID_INTERFACE + // configuration for 12 Mbit/sec speed + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + RAWHID_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 2, // bNumEndpoints + 0x03, // bInterfaceClass (0x03 = HID) + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0, // iInterface + // HID interface descriptor, HID 1.11 spec, section 6.2.1 + 9, // bLength + 0x21, // bDescriptorType + 0x11, 0x01, // bcdHID + 0, // bCountryCode + 1, // bNumDescriptors + 0x22, // bDescriptorType + LSB(sizeof(rawhid_report_desc)), // wDescriptorLength + MSB(sizeof(rawhid_report_desc)), + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + RAWHID_TX_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + RAWHID_TX_SIZE, 0, // wMaxPacketSize + RAWHID_TX_INTERVAL, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + RAWHID_RX_ENDPOINT, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + RAWHID_RX_SIZE, 0, // wMaxPacketSize + RAWHID_RX_INTERVAL, // bInterval +#endif // RAWHID_INTERFACE + +#ifdef FLIGHTSIM_INTERFACE + // configuration for 12 Mbit/sec speed + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + FLIGHTSIM_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 2, // bNumEndpoints + 0x03, // bInterfaceClass (0x03 = HID) + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0, // iInterface + // HID interface descriptor, HID 1.11 spec, section 6.2.1 + 9, // bLength + 0x21, // bDescriptorType + 0x11, 0x01, // bcdHID + 0, // bCountryCode + 1, // bNumDescriptors + 0x22, // bDescriptorType + LSB(sizeof(flightsim_report_desc)), // wDescriptorLength + MSB(sizeof(flightsim_report_desc)), + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + FLIGHTSIM_TX_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + FLIGHTSIM_TX_SIZE, 0, // wMaxPacketSize + FLIGHTSIM_TX_INTERVAL, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + FLIGHTSIM_RX_ENDPOINT, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + FLIGHTSIM_RX_SIZE, 0, // wMaxPacketSize + FLIGHTSIM_RX_INTERVAL, // bInterval +#endif // FLIGHTSIM_INTERFACE + +#ifdef SEREMU_INTERFACE + // configuration for 12 Mbit/sec speed + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + SEREMU_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 2, // bNumEndpoints + 0x03, // bInterfaceClass (0x03 = HID) + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0, // iInterface + // HID interface descriptor, HID 1.11 spec, section 6.2.1 + 9, // bLength + 0x21, // bDescriptorType + 0x11, 0x01, // bcdHID + 0, // bCountryCode + 1, // bNumDescriptors + 0x22, // bDescriptorType + LSB(sizeof(seremu_report_desc)), // wDescriptorLength + MSB(sizeof(seremu_report_desc)), + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + SEREMU_TX_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + SEREMU_TX_SIZE, 0, // wMaxPacketSize + SEREMU_TX_INTERVAL, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + SEREMU_RX_ENDPOINT, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + SEREMU_RX_SIZE, 0, // wMaxPacketSize + SEREMU_RX_INTERVAL, // bInterval +#endif // SEREMU_INTERFACE + +#ifdef JOYSTICK_INTERFACE + // configuration for 12 Mbit/sec speed + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + JOYSTICK_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x03, // bInterfaceClass (0x03 = HID) + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0, // iInterface + // HID interface descriptor, HID 1.11 spec, section 6.2.1 + 9, // bLength + 0x21, // bDescriptorType + 0x11, 0x01, // bcdHID + 0, // bCountryCode + 1, // bNumDescriptors + 0x22, // bDescriptorType + LSB(sizeof(joystick_report_desc)), // wDescriptorLength + MSB(sizeof(joystick_report_desc)), + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + JOYSTICK_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + JOYSTICK_SIZE, 0, // wMaxPacketSize + JOYSTICK_INTERVAL, // bInterval +#endif // JOYSTICK_INTERFACE + +#ifdef MTP_INTERFACE + // configuration for 12 Mbit/sec speed + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + MTP_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 3, // bNumEndpoints + 0x06, // bInterfaceClass (0x06 = still image) + 0x01, // bInterfaceSubClass + 0x01, // bInterfaceProtocol + 0, // iInterface + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + MTP_TX_ENDPOINT | 0x80, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + LSB(MTP_TX_SIZE_12),MSB(MTP_TX_SIZE_12),// wMaxPacketSize + 0, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + MTP_RX_ENDPOINT, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + LSB(MTP_RX_SIZE_12),MSB(MTP_RX_SIZE_12),// wMaxPacketSize + 0, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + MTP_EVENT_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + MTP_EVENT_SIZE, 0, // wMaxPacketSize + MTP_EVENT_INTERVAL_12, // bInterval +#endif // MTP_INTERFACE + +#ifdef KEYMEDIA_INTERFACE + // configuration for 12 Mbit/sec speed + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + KEYMEDIA_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x03, // bInterfaceClass (0x03 = HID) + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0, // iInterface + // HID interface descriptor, HID 1.11 spec, section 6.2.1 + 9, // bLength + 0x21, // bDescriptorType + 0x11, 0x01, // bcdHID + 0, // bCountryCode + 1, // bNumDescriptors + 0x22, // bDescriptorType + LSB(sizeof(keymedia_report_desc)), // wDescriptorLength + MSB(sizeof(keymedia_report_desc)), + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + KEYMEDIA_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + KEYMEDIA_SIZE, 0, // wMaxPacketSize + KEYMEDIA_INTERVAL, // bInterval +#endif // KEYMEDIA_INTERFACE + +#ifdef AUDIO_INTERFACE + // configuration for 12 Mbit/sec speed + // interface association descriptor, USB ECN, Table 9-Z + 8, // bLength + 11, // bDescriptorType + AUDIO_INTERFACE, // bFirstInterface + 3, // bInterfaceCount + 0x01, // bFunctionClass + 0x01, // bFunctionSubClass + 0x00, // bFunctionProtocol + 0, // iFunction + // Standard AudioControl (AC) Interface Descriptor + // USB DCD for Audio Devices 1.0, Table 4-1, page 36 + 9, // bLength + 4, // bDescriptorType, 4 = INTERFACE + AUDIO_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 0, // bNumEndpoints + 1, // bInterfaceClass, 1 = AUDIO + 1, // bInterfaceSubclass, 1 = AUDIO_CONTROL + 0, // bInterfaceProtocol + 0, // iInterface + // Class-specific AC Interface Header Descriptor + // USB DCD for Audio Devices 1.0, Table 4-2, page 37-38 + 10, // bLength + 0x24, // bDescriptorType, 0x24 = CS_INTERFACE + 0x01, // bDescriptorSubtype, 1 = HEADER + 0x00, 0x01, // bcdADC (version 1.0) + LSB(62), MSB(62), // wTotalLength + 2, // bInCollection + AUDIO_INTERFACE+1, // baInterfaceNr(1) - Transmit to PC + AUDIO_INTERFACE+2, // baInterfaceNr(2) - Receive from PC + // Input Terminal Descriptor + // USB DCD for Audio Devices 1.0, Table 4-3, page 39 + 12, // bLength + 0x24, // bDescriptorType, 0x24 = CS_INTERFACE + 0x02, // bDescriptorSubType, 2 = INPUT_TERMINAL + 1, // bTerminalID + //0x01, 0x02, // wTerminalType, 0x0201 = MICROPHONE + //0x03, 0x06, // wTerminalType, 0x0603 = Line Connector + 0x02, 0x06, // wTerminalType, 0x0602 = Digital Audio + 0, // bAssocTerminal, 0 = unidirectional + 2, // bNrChannels + 0x03, 0x00, // wChannelConfig, 0x0003 = Left & Right Front + 0, // iChannelNames + 0, // iTerminal + // Output Terminal Descriptor + // USB DCD for Audio Devices 1.0, Table 4-4, page 40 + 9, // bLength + 0x24, // bDescriptorType, 0x24 = CS_INTERFACE + 3, // bDescriptorSubtype, 3 = OUTPUT_TERMINAL + 2, // bTerminalID + 0x01, 0x01, // wTerminalType, 0x0101 = USB_STREAMING + 0, // bAssocTerminal, 0 = unidirectional + 1, // bCSourceID, connected to input terminal, ID=1 + 0, // iTerminal + // Input Terminal Descriptor + // USB DCD for Audio Devices 1.0, Table 4-3, page 39 + 12, // bLength + 0x24, // bDescriptorType, 0x24 = CS_INTERFACE + 2, // bDescriptorSubType, 2 = INPUT_TERMINAL + 3, // bTerminalID + 0x01, 0x01, // wTerminalType, 0x0101 = USB_STREAMING + 0, // bAssocTerminal, 0 = unidirectional + 2, // bNrChannels + 0x03, 0x00, // wChannelConfig, 0x0003 = Left & Right Front + 0, // iChannelNames + 0, // iTerminal + // Volume feature descriptor + 10, // bLength + 0x24, // bDescriptorType = CS_INTERFACE + 0x06, // bDescriptorSubType = FEATURE_UNIT + 0x31, // bUnitID + 0x03, // bSourceID (Input Terminal) + 0x01, // bControlSize (each channel is 1 byte, 3 channels) + 0x01, // bmaControls(0) Master: Mute + 0x02, // bmaControls(1) Left: Volume + 0x02, // bmaControls(2) Right: Volume + 0x00, // iFeature + // Output Terminal Descriptor + // USB DCD for Audio Devices 1.0, Table 4-4, page 40 + 9, // bLength + 0x24, // bDescriptorType, 0x24 = CS_INTERFACE + 3, // bDescriptorSubtype, 3 = OUTPUT_TERMINAL + 4, // bTerminalID + //0x02, 0x03, // wTerminalType, 0x0302 = Headphones + 0x02, 0x06, // wTerminalType, 0x0602 = Digital Audio + 0, // bAssocTerminal, 0 = unidirectional + 0x31, // bCSourceID, connected to feature, ID=31 + 0, // iTerminal + // Standard AS Interface Descriptor + // USB DCD for Audio Devices 1.0, Section 4.5.1, Table 4-18, page 59 + // Alternate 0: default setting, disabled zero bandwidth + 9, // bLenght + 4, // bDescriptorType = INTERFACE + AUDIO_INTERFACE+1, // bInterfaceNumber + 0, // bAlternateSetting + 0, // bNumEndpoints + 1, // bInterfaceClass, 1 = AUDIO + 2, // bInterfaceSubclass, 2 = AUDIO_STREAMING + 0, // bInterfaceProtocol + 0, // iInterface + // Alternate 1: streaming data + 9, // bLenght + 4, // bDescriptorType = INTERFACE + AUDIO_INTERFACE+1, // bInterfaceNumber + 1, // bAlternateSetting + 1, // bNumEndpoints + 1, // bInterfaceClass, 1 = AUDIO + 2, // bInterfaceSubclass, 2 = AUDIO_STREAMING + 0, // bInterfaceProtocol + 0, // iInterface + // Class-Specific AS Interface Descriptor + // USB DCD for Audio Devices 1.0, Section 4.5.2, Table 4-19, page 60 + 7, // bLength + 0x24, // bDescriptorType = CS_INTERFACE + 1, // bDescriptorSubtype, 1 = AS_GENERAL + 2, // bTerminalLink: Terminal ID = 2 + 3, // bDelay (approx 3ms delay, audio lib updates) + 0x01, 0x00, // wFormatTag, 0x0001 = PCM + // Type I Format Descriptor + // USB DCD for Audio Data Formats 1.0, Section 2.2.5, Table 2-1, page 10 + 11, // bLength + 0x24, // bDescriptorType = CS_INTERFACE + 2, // bDescriptorSubtype = FORMAT_TYPE + 1, // bFormatType = FORMAT_TYPE_I + 2, // bNrChannels = 2 + 2, // bSubFrameSize = 2 byte + 16, // bBitResolution = 16 bits + 1, // bSamFreqType = 1 frequency + LSB(44100), MSB(44100), 0, // tSamFreq + // Standard AS Isochronous Audio Data Endpoint Descriptor + // USB DCD for Audio Devices 1.0, Section 4.6.1.1, Table 4-20, page 61-62 + 9, // bLength + 5, // bDescriptorType, 5 = ENDPOINT_DESCRIPTOR + AUDIO_TX_ENDPOINT | 0x80, // bEndpointAddress + 0x09, // bmAttributes = isochronous, adaptive + LSB(AUDIO_TX_SIZE), MSB(AUDIO_TX_SIZE), // wMaxPacketSize + 1, // bInterval, 1 = every frame + 0, // bRefresh + 0, // bSynchAddress + // Class-Specific AS Isochronous Audio Data Endpoint Descriptor + // USB DCD for Audio Devices 1.0, Section 4.6.1.2, Table 4-21, page 62-63 + 7, // bLength + 0x25, // bDescriptorType, 0x25 = CS_ENDPOINT + 1, // bDescriptorSubtype, 1 = EP_GENERAL + 0x00, // bmAttributes + 0, // bLockDelayUnits, 1 = ms + 0x00, 0x00, // wLockDelay + // Standard AS Interface Descriptor + // USB DCD for Audio Devices 1.0, Section 4.5.1, Table 4-18, page 59 + // Alternate 0: default setting, disabled zero bandwidth + 9, // bLenght + 4, // bDescriptorType = INTERFACE + AUDIO_INTERFACE+2, // bInterfaceNumber + 0, // bAlternateSetting + 0, // bNumEndpoints + 1, // bInterfaceClass, 1 = AUDIO + 2, // bInterfaceSubclass, 2 = AUDIO_STREAMING + 0, // bInterfaceProtocol + 0, // iInterface + // Alternate 1: streaming data + 9, // bLenght + 4, // bDescriptorType = INTERFACE + AUDIO_INTERFACE+2, // bInterfaceNumber + 1, // bAlternateSetting + 2, // bNumEndpoints + 1, // bInterfaceClass, 1 = AUDIO + 2, // bInterfaceSubclass, 2 = AUDIO_STREAMING + 0, // bInterfaceProtocol + 0, // iInterface + // Class-Specific AS Interface Descriptor + // USB DCD for Audio Devices 1.0, Section 4.5.2, Table 4-19, page 60 + 7, // bLength + 0x24, // bDescriptorType = CS_INTERFACE + 1, // bDescriptorSubtype, 1 = AS_GENERAL + 3, // bTerminalLink: Terminal ID = 3 + 3, // bDelay (approx 3ms delay, audio lib updates) + 0x01, 0x00, // wFormatTag, 0x0001 = PCM + // Type I Format Descriptor + // USB DCD for Audio Data Formats 1.0, Section 2.2.5, Table 2-1, page 10 + 11, // bLength + 0x24, // bDescriptorType = CS_INTERFACE + 2, // bDescriptorSubtype = FORMAT_TYPE + 1, // bFormatType = FORMAT_TYPE_I + 2, // bNrChannels = 2 + 2, // bSubFrameSize = 2 byte + 16, // bBitResolution = 16 bits + 1, // bSamFreqType = 1 frequency + LSB(44100), MSB(44100), 0, // tSamFreq + // Standard AS Isochronous Audio Data Endpoint Descriptor + // USB DCD for Audio Devices 1.0, Section 4.6.1.1, Table 4-20, page 61-62 + 9, // bLength + 5, // bDescriptorType, 5 = ENDPOINT_DESCRIPTOR + AUDIO_RX_ENDPOINT, // bEndpointAddress + 0x05, // bmAttributes = isochronous, asynchronous + LSB(AUDIO_RX_SIZE), MSB(AUDIO_RX_SIZE), // wMaxPacketSize + 1, // bInterval, 1 = every frame + 0, // bRefresh + AUDIO_SYNC_ENDPOINT | 0x80, // bSynchAddress + // Class-Specific AS Isochronous Audio Data Endpoint Descriptor + // USB DCD for Audio Devices 1.0, Section 4.6.1.2, Table 4-21, page 62-63 + 7, // bLength + 0x25, // bDescriptorType, 0x25 = CS_ENDPOINT + 1, // bDescriptorSubtype, 1 = EP_GENERAL + 0x00, // bmAttributes + 0, // bLockDelayUnits, 1 = ms + 0x00, 0x00, // wLockDelay + // Standard AS Isochronous Audio Synch Endpoint Descriptor + // USB DCD for Audio Devices 1.0, Section 4.6.2.1, Table 4-22, page 63-64 + 9, // bLength + 5, // bDescriptorType, 5 = ENDPOINT_DESCRIPTOR + AUDIO_SYNC_ENDPOINT | 0x80, // bEndpointAddress + 0x11, // bmAttributes = isochronous, feedback + 3, 0, // wMaxPacketSize, 3 bytes + 1, // bInterval, 1 = every frame + 5, // bRefresh, 5 = 32ms + 0, // bSynchAddress +#endif + +#ifdef MULTITOUCH_INTERFACE + // configuration for 12 Mbit/sec speed + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + MULTITOUCH_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x03, // bInterfaceClass (0x03 = HID) + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0, // iInterface + // HID interface descriptor, HID 1.11 spec, section 6.2.1 + 9, // bLength + 0x21, // bDescriptorType + 0x11, 0x01, // bcdHID + 0, // bCountryCode + 1, // bNumDescriptors + 0x22, // bDescriptorType + LSB(sizeof(multitouch_report_desc)), // wDescriptorLength + MSB(sizeof(multitouch_report_desc)), + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + MULTITOUCH_ENDPOINT | 0x80, // bEndpointAddress + 0x03, // bmAttributes (0x03=intr) + MULTITOUCH_SIZE, 0, // wMaxPacketSize + 1, // bInterval +#endif // MULTITOUCH_INTERFACE + +#ifdef EXPERIMENTAL_INTERFACE + // configuration for 12 Mbit/sec speed + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + EXPERIMENTAL_INTERFACE, // bInterfaceNumber + 0, // bAlternateSetting + 2, // bNumEndpoints + 0xFF, // bInterfaceClass (0xFF = Vendor) + 0x6A, // bInterfaceSubClass + 0xFF, // bInterfaceProtocol + 0, // iInterface + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + 1 | 0x80, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + LSB(64), MSB(64), // wMaxPacketSize + 1, // bInterval + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + 7, // bLength + 5, // bDescriptorType + 1, // bEndpointAddress + 0x02, // bmAttributes (0x02=bulk) + LSB(64), MSB(64), // wMaxPacketSize + 1, // bInterval +#endif // EXPERIMENTAL_INTERFACE +}; + + +__attribute__ ((section(".dmabuffers"), aligned(32))) +uint8_t usb_descriptor_buffer[CONFIG_DESC_SIZE]; + + + + + +// ************************************************************** +// String Descriptors +// ************************************************************** + +// The descriptors above can provide human readable strings, +// referenced by index numbers. These descriptors are the +// actual string data + +/* defined in usb_names.h +struct usb_string_descriptor_struct { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wString[]; +}; +*/ + +extern struct usb_string_descriptor_struct usb_string_manufacturer_name + __attribute__ ((weak, alias("usb_string_manufacturer_name_default"))); +extern struct usb_string_descriptor_struct usb_string_product_name + __attribute__ ((weak, alias("usb_string_product_name_default"))); +extern struct usb_string_descriptor_struct usb_string_serial_number + __attribute__ ((weak, alias("usb_string_serial_number_default"))); + +PROGMEM const struct usb_string_descriptor_struct string0 = { + 4, + 3, + {0x0409} +}; + +PROGMEM const struct usb_string_descriptor_struct usb_string_manufacturer_name_default = { + 2 + MANUFACTURER_NAME_LEN * 2, + 3, + MANUFACTURER_NAME +}; +PROGMEM const struct usb_string_descriptor_struct usb_string_product_name_default = { + 2 + PRODUCT_NAME_LEN * 2, + 3, + PRODUCT_NAME +}; +struct usb_string_descriptor_struct usb_string_serial_number_default = { + 12, + 3, + {0,0,0,0,0,0,0,0,0,0} +}; +#ifdef MTP_INTERFACE +PROGMEM const struct usb_string_descriptor_struct usb_string_mtp = { + 2 + 3 * 2, + 3, + {'M','T','P'} +}; +#endif + +void usb_init_serialnumber(void) +{ + char buf[11]; + uint32_t i, num; + + num = HW_OCOTP_MAC0 & 0xFFFFFF; + // add extra zero to work around OS-X CDC-ACM driver bug + if (num < 10000000) num = num * 10; + ultoa(num, buf, 10); + for (i=0; i<10; i++) { + char c = buf[i]; + if (!c) break; + usb_string_serial_number_default.wString[i] = c; + } + usb_string_serial_number_default.bLength = i * 2 + 2; +} + + +// ************************************************************** +// Descriptors List +// ************************************************************** + +// This table provides access to all the descriptor data above. + +const usb_descriptor_list_t usb_descriptor_list[] = { + //wValue, wIndex, address, length + {0x0100, 0x0000, device_descriptor, sizeof(device_descriptor)}, + {0x0600, 0x0000, qualifier_descriptor, sizeof(qualifier_descriptor)}, + {0x0200, 0x0000, usb_config_descriptor_480, CONFIG_DESC_SIZE}, + {0x0700, 0x0000, usb_config_descriptor_12, CONFIG_DESC_SIZE}, +#ifdef SEREMU_INTERFACE + {0x2200, SEREMU_INTERFACE, seremu_report_desc, sizeof(seremu_report_desc)}, + {0x2100, SEREMU_INTERFACE, usb_config_descriptor_480+SEREMU_HID_DESC_OFFSET, 9}, +#endif +#ifdef KEYBOARD_INTERFACE + {0x2200, KEYBOARD_INTERFACE, keyboard_report_desc, sizeof(keyboard_report_desc)}, + {0x2100, KEYBOARD_INTERFACE, usb_config_descriptor_480+KEYBOARD_HID_DESC_OFFSET, 9}, +#endif +#ifdef MOUSE_INTERFACE + {0x2200, MOUSE_INTERFACE, mouse_report_desc, sizeof(mouse_report_desc)}, + {0x2100, MOUSE_INTERFACE, usb_config_descriptor_480+MOUSE_HID_DESC_OFFSET, 9}, +#endif +#ifdef JOYSTICK_INTERFACE + {0x2200, JOYSTICK_INTERFACE, joystick_report_desc, sizeof(joystick_report_desc)}, + {0x2100, JOYSTICK_INTERFACE, usb_config_descriptor_480+JOYSTICK_HID_DESC_OFFSET, 9}, +#endif +#ifdef RAWHID_INTERFACE + {0x2200, RAWHID_INTERFACE, rawhid_report_desc, sizeof(rawhid_report_desc)}, + {0x2100, RAWHID_INTERFACE, usb_config_descriptor_480+RAWHID_HID_DESC_OFFSET, 9}, +#endif +#ifdef FLIGHTSIM_INTERFACE + {0x2200, FLIGHTSIM_INTERFACE, flightsim_report_desc, sizeof(flightsim_report_desc)}, + {0x2100, FLIGHTSIM_INTERFACE, usb_config_descriptor_480+FLIGHTSIM_HID_DESC_OFFSET, 9}, +#endif +#ifdef KEYMEDIA_INTERFACE + {0x2200, KEYMEDIA_INTERFACE, keymedia_report_desc, sizeof(keymedia_report_desc)}, + {0x2100, KEYMEDIA_INTERFACE, usb_config_descriptor_480+KEYMEDIA_HID_DESC_OFFSET, 9}, +#endif +#ifdef MULTITOUCH_INTERFACE + {0x2200, MULTITOUCH_INTERFACE, multitouch_report_desc, sizeof(multitouch_report_desc)}, + {0x2100, MULTITOUCH_INTERFACE, usb_config_descriptor_480+MULTITOUCH_HID_DESC_OFFSET, 9}, +#endif +#ifdef MTP_INTERFACE + {0x0304, 0x0409, (const uint8_t *)&usb_string_mtp, 0}, +#endif +#ifdef EXPERIMENTAL_INTERFACE + {0x03EE, 0x0000, microsoft_os_string_desc, 18}, + {0x0000, 0xEE04, microsoft_os_compatible_id_desc, 40}, +#endif + {0x0300, 0x0000, (const uint8_t *)&string0, 0}, + {0x0301, 0x0409, (const uint8_t *)&usb_string_manufacturer_name, 0}, + {0x0302, 0x0409, (const uint8_t *)&usb_string_product_name, 0}, + {0x0303, 0x0409, (const uint8_t *)&usb_string_serial_number, 0}, + {0, 0, NULL, 0} +}; + + + + + +#endif // NUM_ENDPOINTS +//#endif // F_CPU >= 20 MHz From c24a30597dde2c936e6be1a35f511985e6dec3b2 Mon Sep 17 00:00:00 2001 From: Dave Solt Date: Sat, 4 Jul 2026 10:46:05 -0400 Subject: [PATCH 6/8] Apply upstream BUFFER_SIZE -> USB_BUFFER_SIZE rename to FT8 code Upstream renamed BUFFER_SIZE to USB_BUFFER_SIZE in SDT.h to avoid a namespace conflict with another library, and updated DSP.cpp's non-FT8 code accordingly. This left three references in the FT8 additions (PlayUsbBufferPreVol's test tone generator and ReadMicrophoneBuffer's FT8 sample path) still using the now-undefined BUFFER_SIZE macro. --- code/src/PhoenixSketch/DSP.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/code/src/PhoenixSketch/DSP.cpp b/code/src/PhoenixSketch/DSP.cpp index 2afdcad..7b7d83d 100644 --- a/code/src/PhoenixSketch/DSP.cpp +++ b/code/src/PhoenixSketch/DSP.cpp @@ -744,7 +744,7 @@ void PlayBuffer(DataBlock *data){ } #ifdef T41_USB_AUDIO -static float32_t usbTmp[BUFFER_SIZE]; +static float32_t usbTmp[USB_BUFFER_SIZE]; static float usbPhase = 0.0f; void PlayUsbBufferPreVol(DataBlock *data){ @@ -770,14 +770,14 @@ void PlayUsbBufferPreVol(DataBlock *data){ int16_t *pL = Q_usbOut_L.getBuffer(); int16_t *pR = Q_usbOut_R.getBuffer(); - for (size_t k = 0; k < BUFFER_SIZE; k++) { + for (size_t k = 0; k < USB_BUFFER_SIZE; k++) { usbTmp[k] = 0.2f * sinf(usbPhase); usbPhase += dphi; if (usbPhase >= 2.0f * PI) usbPhase -= 2.0f * PI; } - arm_float_to_q15(usbTmp, pL, BUFFER_SIZE); - arm_float_to_q15(usbTmp, pR, BUFFER_SIZE); + arm_float_to_q15(usbTmp, pL, USB_BUFFER_SIZE); + arm_float_to_q15(usbTmp, pR, USB_BUFFER_SIZE); Q_usbOut_L.playBuffer(); Q_usbOut_R.playBuffer(); @@ -1025,7 +1025,7 @@ float32_t GetMicRRMS(void){ //static int32_t counter = 0; /** - * Read in N_BLOCKS blocks of BUFFER_SIZE samples each from Q_in_R_Ex and Q_in_L_Ex + * Read in N_BLOCKS blocks of USB_BUFFER_SIZE samples each from Q_in_R_Ex and Q_in_L_Ex * AudioRecordQueue objects into the data float buffers. This is the transmit chain, * input comes from the microphone. The samples are converted to normalized floats in * the range -1 to +1. @@ -1040,7 +1040,7 @@ errno_t ReadMicrophoneBuffer(DataBlock *data) #ifdef T41_USB_AUDIO if (GetFt8Mode()) { - const uint32_t outCount = N_BLOCKS_EX * BUFFER_SIZE; + const uint32_t outCount = N_BLOCKS_EX * USB_BUFFER_SIZE; bool ok = Ft8UsbBridge_GetSamples(data->I, outCount); if (!ok) { memset(data->I, 0, outCount * sizeof(float32_t)); From 9c97e416257241653f7efa0f376c672673a52f81 Mon Sep 17 00:00:00 2001 From: Dave Solt Date: Sat, 4 Jul 2026 12:50:00 -0400 Subject: [PATCH 7/8] Degrade FT8 RX-to-PC path gracefully on a stock Teensy core Ft8UsbBridge_DrainToUSB() was the only project code calling a core_mods-only function (usb_audio_push_block) -- confirmed by searching src/ for it and the other three core_mods additions (usb_audio_pop_receive_block, usb_audio_tx_has_room, usb_audio_manual_transmit), none of which are otherwise referenced. core_mods/usb_audio.h now defines T41_CORE_HAS_USB_AUDIO_PUSH_BLOCK next to the push_block declaration, so project code can detect at compile time whether the patched core is installed. Anyone re-copying this header into a Teensyduino install must bring this #define along -- applied here to both the repo copy and the installed copy at AppData/Local/Arduino15/packages/teensy/hardware/avr/1.62.0/cores/teensy4/. When the sentinel is absent, Ft8UsbBridge_DrainToUSB() drops the buffered RX samples instead of calling the missing function, with a #warning at the top of the file explaining why. FT8 TX (PC -> radio) is unaffected since it never depended on the core patch. compile.yml gains a second, independent job (compile-usb-audio) that builds with usb=serialmidiaudio against CI's stock-installed core, so the FT8 code path actually gets compiled and the new gate gets exercised instead of silently skipping (the existing usb=serial2 job never defines USB_AUDIO/USB_MIDI_AUDIO_SERIAL, so it excludes this code entirely and proves nothing about it). --- .github/workflows/compile.yml | 34 +++++++++++++++++++++++++ code/core_mods/usb_audio.h | 5 ++++ code/src/PhoenixSketch/Ft8UsbBridge.cpp | 13 ++++++++++ 3 files changed, 52 insertions(+) diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml index 70148cb..6ded182 100644 --- a/.github/workflows/compile.yml +++ b/.github/workflows/compile.yml @@ -35,3 +35,37 @@ jobs: arduino-cli compile \ --fqbn teensy:avr:teensy41:usb=serial2,speed=600,opt=o1lto \ code/src/PhoenixSketch/PhoenixSketch.ino + + # Minimal, separate job: compiles with the USB-audio-capable board option + # (usb=serialmidiaudio) so the FT8/T41_USB_AUDIO code path actually gets + # type-checked. This core is installed stock (no core_mods applied), so + # it also verifies the T41_CORE_HAS_USB_AUDIO_PUSH_BLOCK gate in + # Ft8UsbBridge.cpp degrades cleanly instead of failing the build. + compile-usb-audio: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install arduino-cli + uses: arduino/setup-arduino-cli@v2 + + - name: Configure board support + run: | + arduino-cli config init + arduino-cli config set library.enable_unsafe_install true + arduino-cli config add board_manager.additional_urls \ + https://www.pjrc.com/teensy/package_teensy_index.json + arduino-cli core update-index + arduino-cli core install teensy:avr + + - name: Install libraries + run: | + arduino-cli lib install ArduinoJson "Adafruit MCP23017 Arduino Library" + arduino-cli lib install --git-url \ + https://github.com/chipaudette/OpenAudio_ArduinoLibrary.git + + - name: Compile sketch (USB audio, stock core) + run: | + arduino-cli compile \ + --fqbn teensy:avr:teensy41:usb=serialmidiaudio,speed=600,opt=o1lto \ + code/src/PhoenixSketch/PhoenixSketch.ino diff --git a/code/core_mods/usb_audio.h b/code/core_mods/usb_audio.h index 7d285c8..b5c6216 100644 --- a/code/core_mods/usb_audio.h +++ b/code/core_mods/usb_audio.h @@ -109,6 +109,11 @@ class AudioOutputUSB : public AudioStream audio_block_t *inputQueueArray[2]; }; +// Sentinel: lets project code detect at compile time whether this patched +// core (vs. a stock Teensyduino core) is installed. Anyone re-copying this +// file into a Teensyduino install must bring this #define along with it. +#define T41_CORE_HAS_USB_AUDIO_PUSH_BLOCK 1 + void usb_audio_push_block(const int16_t *left_data, const int16_t *right_data); bool usb_audio_tx_has_room(void); void usb_audio_manual_transmit(const int16_t *left_data, const int16_t *right_data, unsigned int samples); diff --git a/code/src/PhoenixSketch/Ft8UsbBridge.cpp b/code/src/PhoenixSketch/Ft8UsbBridge.cpp index d2ea03c..8185eae 100644 --- a/code/src/PhoenixSketch/Ft8UsbBridge.cpp +++ b/code/src/PhoenixSketch/Ft8UsbBridge.cpp @@ -13,6 +13,11 @@ #include #include "Ft8UsbBridge.h" #include "Config.h" + +#if defined(T41_USB_AUDIO) && !defined(T41_CORE_HAS_USB_AUDIO_PUSH_BLOCK) +#warning "T41: core_mods/usb_audio.{cpp,h} not applied -- FT8 RX audio-to-PC path disabled (stub); TX still works. See code/docs/EXPERIMENT_LOG.md." +#endif + static volatile bool g_ft8TxActive = false; #if defined(T41_USB_AUDIO) && (defined(USB_AUDIO) || defined(USB_MIDI_AUDIO_SERIAL)) @@ -278,6 +283,7 @@ void Ft8UsbBridge_DrainToUSB(void) if (g_ft8TxActive) return; if (RxFifoCount() < AUDIO_BLOCK_SAMPLES) return; +#if defined(T41_CORE_HAS_USB_AUDIO_PUSH_BLOCK) int16_t left_buf[AUDIO_BLOCK_SAMPLES]; int16_t right_buf[AUDIO_BLOCK_SAMPLES]; @@ -291,6 +297,13 @@ void Ft8UsbBridge_DrainToUSB(void) right_buf[k] = s; } usb_audio_push_block(left_buf, right_buf); +#else + // No core patch available: there's no way to deliver these samples to + // the PC. Drop them so the RX FIFO doesn't fill and stall; TX (PC -> + // radio) is unaffected since it doesn't depend on the core patch. + float x; + for (int k = 0; k < AUDIO_BLOCK_SAMPLES; k++) RxFifoPop(&x); +#endif } void Ft8UsbBridge_SetTxActive(bool on) From 446ce6c17124f7133103875825ba38cdf8b421b9 Mon Sep 17 00:00:00 2001 From: Dave Solt Date: Sat, 4 Jul 2026 13:17:02 -0400 Subject: [PATCH 8/8] Gate AudioInputUSB/g_usbQueueL/R on the real USB-audio interface, not just T41_USB_AUDIO usb_audio.h wraps AudioInputUSB/AudioOutputUSB themselves in #ifdef AUDIO_INTERFACE, which boards.txt only defines for USB Type = Audio / Serial+MIDI+Audio. T41_USB_AUDIO (Config.h) is independent of that board menu selection, so a usb=serial2 build previously still tried to declare AudioInputUSB g_usbIn -- compile failure ("does not name a type"), plus several call sites (Ft8UsbBridge_SetTxActive at MainBoard_AudioIO.cpp:379/402, Ft8UsbBridge_Init at MainBoard_AudioIO.cpp:570/Globals.cpp:518, Ft8UsbBridge_DrainToUSB at MainBoard_AudioIO.cpp:575) called into functions that wouldn't even be defined in that configuration -- link failures waiting to happen. Restructured Ft8UsbBridge.cpp: the RX FIFO plumbing, Ft8UsbBridge_PutRxSamples, Ft8UsbBridge_DrainToUSB (its only interface-dependent line already gated by T41_CORE_HAS_USB_AUDIO_PUSH_BLOCK), and Ft8UsbBridge_SetTxActive never touched AudioInputUSB/AudioOutputUSB in the first place, so they're now defined unconditionally -- fixing every mismatched caller above without touching the callers themselves. Only Ft8UsbBridge_Init/Ft8UsbBridge_GetSamples and the AudioInputUSB/AudioRecordQueue/AudioConnection objects they depend on stay behind the strict #if defined(T41_USB_AUDIO) && (defined(USB_AUDIO) || defined(USB_MIDI_AUDIO_SERIAL)) guard, with safe stubs (silence/no-op) in the #else so callers don't need to know which USB Type is selected. Audited every reference to AudioOutputUSB, AudioInputUSB, usb_audio_, and Ft8UsbBridge_* under src/: CAT.cpp/Loop.cpp/MainBoard_DisplayHome.cpp only call the always-defined GetFt8Mode()/SetFt8Mode(), and MainBoard_AudioIO.cpp's Q_usbOut_L/R/usbRxGainL/R (AudioPlayQueue/AudioAmplifier) don't require AUDIO_INTERFACE at all -- none of those needed changes. --- code/src/PhoenixSketch/Ft8UsbBridge.cpp | 189 ++++++++++++++---------- 1 file changed, 113 insertions(+), 76 deletions(-) diff --git a/code/src/PhoenixSketch/Ft8UsbBridge.cpp b/code/src/PhoenixSketch/Ft8UsbBridge.cpp index 8185eae..9d3e0fb 100644 --- a/code/src/PhoenixSketch/Ft8UsbBridge.cpp +++ b/code/src/PhoenixSketch/Ft8UsbBridge.cpp @@ -20,6 +20,107 @@ static volatile bool g_ft8TxActive = false; +// ───────────────────────────────────────────────────────────── +// RX PATH: 192kHz DSP audio → resample → FIFO → USB to PC +// +// None of this touches AudioInputUSB/AudioOutputUSB directly, so it's kept +// unconditional (safe to call regardless of board USB Type). The one spot +// that does need the real USB audio interface -- the final handoff to +// usb_audio_push_block() inside Ft8UsbBridge_DrainToUSB() -- is already +// gated on T41_CORE_HAS_USB_AUDIO_PUSH_BLOCK, which usb_audio.h only +// defines when the interface actually exists (see core_mods/usb_audio.h). +// ───────────────────────────────────────────────────────────── +static const uint32_t RX_FIFO_SIZE = 16384; +DMAMEM static float g_rxFifo[RX_FIFO_SIZE]; +static volatile uint32_t g_rxWriteIdx = 0; +static volatile uint32_t g_rxReadIdx = 0; + +static inline uint32_t RxNextIdx(uint32_t i) { return (i + 1u) % RX_FIFO_SIZE; } + +static inline bool RxFifoPush(float x) { + uint32_t next = RxNextIdx(g_rxWriteIdx); + if (next == g_rxReadIdx) return false; + g_rxFifo[g_rxWriteIdx] = x; + g_rxWriteIdx = next; + return true; +} + +static inline bool RxFifoPop(float *x) { + if (g_rxReadIdx == g_rxWriteIdx) return false; + *x = g_rxFifo[g_rxReadIdx]; + g_rxReadIdx = RxNextIdx(g_rxReadIdx); + return true; +} + +static inline uint32_t RxFifoCount(void) { + if (g_rxWriteIdx >= g_rxReadIdx) return g_rxWriteIdx - g_rxReadIdx; + return RX_FIFO_SIZE - g_rxReadIdx + g_rxWriteIdx; +} + +void Ft8UsbBridge_PutRxSamples(const float *in, uint32_t count) +{ + if (!in || count < 2) return; + static float srcPos = 0.0f; + const float step = 192000.0f / 44100.0f; + while (srcPos < (float)(count - 1)) { + uint32_t i0 = (uint32_t)srcPos; + uint32_t i1 = i0 + 1; + float frac = srcPos - (float)i0; + float x = in[i0] + frac * (in[i1] - in[i0]); + if (x > 1.0f) x = 1.0f; + if (x < -1.0f) x = -1.0f; + RxFifoPush(x); + srcPos += step; + } + srcPos -= (float)(count - 1); +} + +void Ft8UsbBridge_DrainToUSB(void) +{ + + if (g_ft8TxActive) return; + if (RxFifoCount() < AUDIO_BLOCK_SAMPLES) return; + +#if defined(T41_CORE_HAS_USB_AUDIO_PUSH_BLOCK) + int16_t left_buf[AUDIO_BLOCK_SAMPLES]; + int16_t right_buf[AUDIO_BLOCK_SAMPLES]; + + for (int k = 0; k < AUDIO_BLOCK_SAMPLES; k++) { + float x = 0.0f; + RxFifoPop(&x); + if (x > 1.0f) x = 1.0f; + if (x < -1.0f) x = -1.0f; + int16_t s = (int16_t)(x * 32767.0f); + left_buf[k] = s; + right_buf[k] = s; + } + usb_audio_push_block(left_buf, right_buf); +#else + // No core patch available (or no USB audio interface in this build): + // there's no way to deliver these samples to the PC. Drop them so the + // RX FIFO doesn't fill and stall; TX (PC -> radio) is unaffected since + // it doesn't depend on the core patch. + float x; + for (int k = 0; k < AUDIO_BLOCK_SAMPLES; k++) RxFifoPop(&x); +#endif +} + +void Ft8UsbBridge_SetTxActive(bool on) +{ + g_ft8TxActive = on; +} + +// ───────────────────────────────────────────────────────────── +// TX PATH: USB (WSJT-X) → resample → Phoenix SDR +// +// This is the one part of the bridge that genuinely needs a USB-audio- +// capable board (AudioInputUSB/AudioRecordQueue require AUDIO_INTERFACE, +// which usb_audio.h only defines for USB Type = Audio / Serial+MIDI+Audio). +// Ft8UsbBridge_Init()/Ft8UsbBridge_GetSamples() are called unconditionally +// from elsewhere in the project (Globals.cpp, DSP.cpp), so when the +// interface isn't available they fall back to safe stubs below instead of +// requiring every caller to match this exact gate. +// ───────────────────────────────────────────────────────────── #if defined(T41_USB_AUDIO) && (defined(USB_AUDIO) || defined(USB_MIDI_AUDIO_SERIAL)) // ----------------------------------------------------------------------------- @@ -229,86 +330,22 @@ bool Ft8UsbBridge_GetSamples(float *out, uint32_t outCount) return true; } -// ───────────────────────────────────────────────────────────── -// RX PATH: 192kHz DSP audio → resample → FIFO → USB to PC -// ───────────────────────────────────────────────────────────── -static const uint32_t RX_FIFO_SIZE = 16384; -DMAMEM static float g_rxFifo[RX_FIFO_SIZE]; -static volatile uint32_t g_rxWriteIdx = 0; -static volatile uint32_t g_rxReadIdx = 0; - -static inline uint32_t RxNextIdx(uint32_t i) { return (i + 1u) % RX_FIFO_SIZE; } - -static inline bool RxFifoPush(float x) { - uint32_t next = RxNextIdx(g_rxWriteIdx); - if (next == g_rxReadIdx) return false; - g_rxFifo[g_rxWriteIdx] = x; - g_rxWriteIdx = next; - return true; -} - -static inline bool RxFifoPop(float *x) { - if (g_rxReadIdx == g_rxWriteIdx) return false; - *x = g_rxFifo[g_rxReadIdx]; - g_rxReadIdx = RxNextIdx(g_rxReadIdx); - return true; -} - -static inline uint32_t RxFifoCount(void) { - if (g_rxWriteIdx >= g_rxReadIdx) return g_rxWriteIdx - g_rxReadIdx; - return RX_FIFO_SIZE - g_rxReadIdx + g_rxWriteIdx; -} +#else // !(defined(T41_USB_AUDIO) && (defined(USB_AUDIO) || defined(USB_MIDI_AUDIO_SERIAL))) -void Ft8UsbBridge_PutRxSamples(const float *in, uint32_t count) -{ - if (!in || count < 2) return; - static float srcPos = 0.0f; - const float step = 192000.0f / 44100.0f; - while (srcPos < (float)(count - 1)) { - uint32_t i0 = (uint32_t)srcPos; - uint32_t i1 = i0 + 1; - float frac = srcPos - (float)i0; - float x = in[i0] + frac * (in[i1] - in[i0]); - if (x > 1.0f) x = 1.0f; - if (x < -1.0f) x = -1.0f; - RxFifoPush(x); - srcPos += step; - } - srcPos -= (float)(count - 1); -} - -void Ft8UsbBridge_DrainToUSB(void) +// No USB audio interface in this build (T41_USB_AUDIO disabled, or a board +// USB Type without Audio -- e.g. plain/Dual/Triple Serial). Stub out the +// TX-path entry points so callers don't need to know which USB Type is +// selected. +void Ft8UsbBridge_Init(float sdrSampleRateHz) { - - if (g_ft8TxActive) return; - if (RxFifoCount() < AUDIO_BLOCK_SAMPLES) return; - -#if defined(T41_CORE_HAS_USB_AUDIO_PUSH_BLOCK) - int16_t left_buf[AUDIO_BLOCK_SAMPLES]; - int16_t right_buf[AUDIO_BLOCK_SAMPLES]; - - for (int k = 0; k < AUDIO_BLOCK_SAMPLES; k++) { - float x = 0.0f; - RxFifoPop(&x); - if (x > 1.0f) x = 1.0f; - if (x < -1.0f) x = -1.0f; - int16_t s = (int16_t)(x * 32767.0f); - left_buf[k] = s; - right_buf[k] = s; - } - usb_audio_push_block(left_buf, right_buf); -#else - // No core patch available: there's no way to deliver these samples to - // the PC. Drop them so the RX FIFO doesn't fill and stall; TX (PC -> - // radio) is unaffected since it doesn't depend on the core patch. - float x; - for (int k = 0; k < AUDIO_BLOCK_SAMPLES; k++) RxFifoPop(&x); -#endif + (void)sdrSampleRateHz; } -void Ft8UsbBridge_SetTxActive(bool on) +bool Ft8UsbBridge_GetSamples(float *out, uint32_t outCount) { - g_ft8TxActive = on; + if (!out || outCount == 0) return false; + memset(out, 0, outCount * sizeof(float)); + return true; } -#endif \ No newline at end of file +#endif