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.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..b5c6216 --- /dev/null +++ b/code/core_mods/usb_audio.h @@ -0,0 +1,124 @@ +/* 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]; +}; + +// 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); +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 diff --git a/code/src/PhoenixSketch/CAT.cpp b/code/src/PhoenixSketch/CAT.cpp index 6ef715c..5503a16 100644 --- a/code/src/PhoenixSketch/CAT.cpp +++ b/code/src/PhoenixSketch/CAT.cpp @@ -17,6 +17,7 @@ If not, see . */ #include "CAT.h" +#include "MainBoard_AudioIO.h" // GetFt8Mode() // Kenwood TS-480 CAT Interface (partial) // @@ -711,10 +712,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; } @@ -724,6 +737,7 @@ char *TX_write( char* cmd ){ return empty_string_p; } + char *VX_write( char* cmd ){ Debug("Got VX write"); return empty_string_p; @@ -758,7 +772,6 @@ char *PR_read( char* cmd ){ return obuf; } - /** * Poll SerialUSB1 for incoming CAT commands and process them * @@ -769,55 +782,56 @@ char *PR_read( char* cmd ){ void CheckForCATSerialEvents(void){ int i; char c; - while( ( i = SerialUSB1.available() ) > 0 ){ - c = ( char )SerialUSB1.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] ); +#else if( SerialUSB1.availableForWrite() > 0 ){ SerialUSB1.print( parser_output[i] ); - #ifdef DEBUG_CAT - Serial.print( parser_output[i] ); - #endif +#endif i++; - }else{ - SerialUSB1.flush(); } } +#ifdef T41_USB_AUDIO + Serial.flush(); +#else SerialUSB1.flush(); - #ifdef DEBUG_CAT - Serial.println(); - #endif // DEBUG_CAT +#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 6ec474d..b89d22d 100644 --- a/code/src/PhoenixSketch/Config.h +++ b/code/src/PhoenixSketch/Config.h @@ -36,20 +36,22 @@ If not, see . // 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. // 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 84ec725..7b7d83d 100644 --- a/code/src/PhoenixSketch/DSP.cpp +++ b/code/src/PhoenixSketch/DSP.cpp @@ -17,6 +17,8 @@ If not, see . */ #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]; @@ -32,6 +34,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 @@ -736,6 +743,50 @@ void PlayBuffer(DataBlock *data){ } } +#ifdef T41_USB_AUDIO +static float32_t usbTmp[USB_BUFFER_SIZE]; +static float usbPhase = 0.0f; + +void PlayUsbBufferPreVol(DataBlock *data){ + (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(); + + 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, USB_BUFFER_SIZE); + arm_float_to_q15(usbTmp, pR, USB_BUFFER_SIZE); + + Q_usbOut_L.playBuffer(); + Q_usbOut_R.playBuffer(); + } +} +#endif + + + /** * Initialize the global variables to their default startup values * 1) Configure the RXfilters @@ -941,20 +992,19 @@ DataBlock * ReceiveProcessing(const char *fname){ // Interpolate InterpolateReceiveData(&data, &RXfilters); + #if defined(T41_USB_AUDIO) && (defined(USB_AUDIO) || defined(USB_MIDI_AUDIO_SERIAL)) + Ft8UsbBridge_PutRxSamples(data.I, data.N); + #endif - // Volume adjust for audio volume setting. I and Q contain duplicate data, don't - // need to scale both + // Speaker path volume knob AdjustVolume(&data, &RXfilters); - SaveData(&data, 6); // used by the unit tests - // Play sound on the speaker + // Always feed the speaker output (works in both builds) PlayBuffer(&data); elapsed_micros_sum = elapsed_micros_sum + usec; elapsed_micros_idx_t++; - //Flag(0); - return &data; } @@ -975,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. @@ -983,7 +1033,29 @@ 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 * USB_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; + } +#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..9d3e0fb --- /dev/null +++ b/code/src/PhoenixSketch/Ft8UsbBridge.cpp @@ -0,0 +1,351 @@ +// 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" +#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; + +// ───────────────────────────────────────────────────────────── +// 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)) + +// ----------------------------------------------------------------------------- +// 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; +} + +#else // !(defined(T41_USB_AUDIO) && (defined(USB_AUDIO) || defined(USB_MIDI_AUDIO_SERIAL))) + +// 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) +{ + (void)sdrSampleRateHz; +} + +bool Ft8UsbBridge_GetSamples(float *out, uint32_t outCount) +{ + if (!out || outCount == 0) return false; + memset(out, 0, outCount * sizeof(float)); + return true; +} + +#endif diff --git a/code/src/PhoenixSketch/Ft8UsbBridge.h b/code/src/PhoenixSketch/Ft8UsbBridge.h new file mode 100644 index 0000000..37242f7 --- /dev/null +++ b/code/src/PhoenixSketch/Ft8UsbBridge.h @@ -0,0 +1,12 @@ +#ifndef FT8_USB_BRIDGE_H_ +#define FT8_USB_BRIDGE_H_ + +#include + +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 0e425b7..6338ad6 100644 --- a/code/src/PhoenixSketch/Globals.cpp +++ b/code/src/PhoenixSketch/Globals.cpp @@ -25,6 +25,8 @@ If not, see . */ #include "SDT.h" +#include "Ft8UsbBridge.h" + struct config_t ED; bool psdupdated = false; @@ -504,9 +506,19 @@ time_t getTeensy3Time() { void setup(void){ Serial.begin(115200); - SerialUSB1.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"); + 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 207eead..1ef5382 100644 --- a/code/src/PhoenixSketch/Loop.cpp +++ b/code/src/PhoenixSketch/Loop.cpp @@ -103,6 +103,9 @@ If not, see . */ #include "SDT.h" +#include "MainBoard_AudioIO.h" +#include "FrontPanel.h" +#include "Ft8UsbBridge.h" // FIFO buffer for interrupt events #define INTERRUPT_BUFFER_SIZE 16 @@ -1289,11 +1292,21 @@ 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 (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 b9add39..82f8524 100644 --- a/code/src/PhoenixSketch/MainBoard_AudioIO.cpp +++ b/code/src/PhoenixSketch/MainBoard_AudioIO.cpp @@ -70,6 +70,7 @@ If not, see . */ #include "MainBoard_AudioIO.h" +#include "Ft8UsbBridge.h" /** * The transition from analog to digital and digital to analog are handled using a fork @@ -116,6 +117,7 @@ If not, see . * 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 @@ -138,6 +140,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; +// usbOut removed from audio graph — tx_event owns USB transmit directly +// AudioOutputUSB usbOut; +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); @@ -159,7 +171,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 +// 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 @@ -169,18 +191,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. @@ -195,6 +219,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); @@ -202,6 +227,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. * @@ -277,11 +354,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): @@ -289,6 +376,7 @@ void UpdateAudioIOState(void){ 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(); @@ -311,17 +399,40 @@ void UpdateAudioIOState(void){ break; } case (ModeSm_StateId_SSB_TRANSMIT):{ + Ft8UsbBridge_SetTxActive(true); // IQ from receive continues Q_in_L.begin(); Q_in_R.begin(); // Microphone input starts - Q_in_L_Ex.begin(); + #ifdef T41_USB_AUDIO + if (GetFt8Mode()) { + // FT8 TX audio comes from g_usbQueueL/R in Ft8UsbBridge.cpp directly + Q_in_L_Ex.end(); + Q_in_R_Ex.end(); + } else { + 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); @@ -443,7 +554,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. @@ -451,6 +565,16 @@ void InitializeAudio(void){ sgtl5000_teensy.enable(); AudioMemory(500); AudioMemory_F32(10); + +#ifdef T41_USB_AUDIO + Ft8UsbBridge_Init((float)SR[SampleRate].rate); + 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 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 91e0862..75d4ff8 100644 --- a/code/src/PhoenixSketch/MainBoard_AudioIO.h +++ b/code/src/PhoenixSketch/MainBoard_AudioIO.h @@ -42,6 +42,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 9b2251b..37f335a 100644 --- a/code/src/PhoenixSketch/MainBoard_DisplayHome.cpp +++ b/code/src/PhoenixSketch/MainBoard_DisplayHome.cpp @@ -39,6 +39,8 @@ If not, see . #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; @@ -303,23 +305,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) { + 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])){ + (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); @@ -354,7 +365,9 @@ void DrawFreqBandModPane(void) { tft.print("(LSB)"); break; case USB: - tft.print("(USB)"); + // Only label FT8 when FT8 mode is enabled + if (ft8) tft.print("(FT8)"); + else tft.print("(USB)"); break; case AM: tft.print("(AM)"); diff --git a/code/src/PhoenixSketch/SDT.h b/code/src/PhoenixSketch/SDT.h index 827f554..c2e91d2 100644 --- a/code/src/PhoenixSketch/SDT.h +++ b/code/src/PhoenixSketch/SDT.h @@ -21,13 +21,26 @@ If not, see . #include #include #include "Config.h" +#include +extern AudioSynthWaveformSine sidetone_oscillator; #define RIGNAME "T41-EP SDT" -#define VERSION "Phx V1.3" +#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 + +#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