diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..8af08be7 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @xross diff --git a/.gitignore b/.gitignore index 5b2ff009..663e8294 100644 --- a/.gitignore +++ b/.gitignore @@ -1,25 +1,25 @@ -*.log -*.dSYM -*/.build_*/* -*/bin/* -*.o +# XMOS bin files *.xe -*.vcd -*.swo +*.bin +*/bin/* + +# XMOS temp files +.build* +*.a +_build* +*.i *.s *.xi -*.i -*.bin -*~ -*.a -*.swp -*.*~ -*.pyc -.build* +*.o +*/.build_*/* + +# Temp files .DS_Store -test_results.csv -_build* -**/.venv/** +*.*~ +*.swp +*.swn +*~ +*.swo # waf build files .lock-waf_* @@ -28,3 +28,23 @@ build/ .build* *.pyc xscope.xmt + +# Traces +*.gtkw +*.vcd + +# Host binaries +host_usb_mixer_control/xmos_mixer + +# Documentation build +*.pdf + +# Misc +*.log +*.dSYM +*.vcd +*.pyc +**/.venv/** +**/.vscode/** +**.egg-info +*tests/logs/* diff --git a/Brewfile b/Brewfile deleted file mode 100644 index 2fd6d0b8..00000000 --- a/Brewfile +++ /dev/null @@ -1,7 +0,0 @@ -tap 'homebrew/core' - -brew 'perl' -brew 'cpanm' - -brew 'python@2' -brew 'pipenv' diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a5db13c9..d95b28c8 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,31 +1,160 @@ lib_xua Change Log ================== +UNRELEASED +---------- + + * FIXED: Memory corruption due to erroneous initialisation of mixer weights when not in use (#152) + * FIXED: UserHostActive() not being called as expected (#326) + * FIXED: Exception when entering DSD mode + +3.4.0 +----- + + * ADDED: Unit tests for mixer functionality + * ADDED: Host mixer control applications (for Win/macOS) + * CHANGED: Small tidies to mixer implementation + * CHANGED: Improved mixer control channel communication protocol to avoid + deadlock situations + * CHANGED: By default, output volume processing occurs in mixer task, if + present. Previously occurred in decouple task + * CHANGED: Some optimisations in sample transfer from decouple task + * FIXED: Exception on startup when USB input disabled + * FIXED: Full 32bit volume processing only applied when required + * FIXED: Setting OUT_VOLUME_AFTER_MIX to zero now has the expected effect + + * Changes to dependencies: + + - lib_xud: 2.2.1 -> 2.2.2 + +3.3.1 +----- + + * CHANGED: Documentation updates + + * Changes to dependencies: + + - lib_spdif: 4.1.0 -> 4.2.1 + +3.3.0 +----- + + * CHANGED: Define ADAT_RX renamed to XUA_ADAT_RX_EN + * CHANGED: Define ADAT_TX renamed to XUA_ADAT_TX_EN + * CHANGED: Define SPDIF_RX renamed to XUA_SPDIF_RX_EN + * CHANGED: Define SELF_POWERED changed to XUA_POWERMODE and associated + defines + * CHANGED: Drive strength of I2S clock lines upped to 8mA on xCORE.ai + * CHANGED: ADC datalines sampled on falling edge of clock in TDM mode + * CHANGED: Improved startup behaviour of TDM clocks + * FIXED: Intermittent underflow at MAX_FREQ on input stream start due to + insufficient packet buffering + * FIXED: Decouple buffer accounting to avoid corruption of samples + + * Changes to dependencies: + + - lib_adat: Added dependency 1.0.1 + + - lib_xud: 2.1.0 -> 2.2.1 + +3.2.0 +----- + + * CHANGED: Updated tests to use lib_locks (was legacy module_locks) + * CHANGED: Exclude HID Report functions unless the HID feature is enabled + * CHANGED: Explicit feedback EP enabled by default (see + UAC_FORCE_FEEDBACK_EP) + * FIXED: Incorrect conditional compilation of HID report code + * FIXED: Input/output descriptors written when input/output not enabled. + (Audio class 1.0 mode using XUA_USB_DESCRIPTOR_OVERWRITE_RATE_RES) + + * Changes to dependencies: + + - lib_dsp: 5.0.0 -> 6.2.1 + + - lib_locks: Added dependency 2.1.0 + + - lib_logging: 3.0.0 -> 3.1.1 + + - lib_mic_array: 4.0.0 -> 4.5.0 + + - lib_spdif: 4.0.0 -> 4.1.0 + + - lib_xassert: 4.0.0 -> 4.1.0 + + - lib_xud: 2.0.0 -> 2.1.0 + +3.1.0 +----- + + * CHANGED: Removed logic from HID API functions allowing a Report ID of 0 to + be used as "all/any" Report + +3.0.0 +----- + + * ADDED: Support for HID Report IDs + * CHANGED: Renamed the HID API file xua_hid_report_descriptor.h to + xua_hid_report.h + * REMOVED: Support for HID Reports containing controls from mixed usage + pages + +2.1.1 +----- + + * CHANGED: Setting of HID report items + +2.1.0 +----- + + * CHANGED: Updated clock blocks to support lib_xud v2.0.0 + + * Changes to dependencies: + + - lib_xud: 1.2.0 -> 2.0.0 + +2.0.0 +----- + + * ADDED: Function to get a Report item description + * ADDED: Support for multiple flash specs defined by DFU_FLASH_DEVICE + * ADDED: Nullable c_aud_ctl chan-end optimisation for fixed rate devices + * CHANGED: Check HID Usage Page when changing a Report item description + * CHANGED: HID event ID from list to bit and byte location in HID Report + * CHANGED: Interface to UserHIDRecordEvent() + 1.3.0 ----- + * ADDED: Build default HID Report descriptor at boot-time + * ADDED: Function to return length of HID Report * CHANGED: Move HID descriptors to ease maintenance + * CHANGED: Move legacy tests to separate directory + * CHANGED: Replace unused GPI-specific HID event names with generic ones + * CHANGED: HID Report to return multiple bytes + * CHANGED: NO_USB conditional compilation switch with XUA_USB_EN + * CHANGED: Clock blocks used for BCLK and MCLK + * CHANGED: Arguments no longer supported by XUD_Main + * REMOVED: Support for XS1 based devices 1.2.0 ----- * ADDED: Updates for xcore.ai/XS3 compatibility * ADDED: Makefile.Win32 for xmosdfu on Windows + * CHANGED: Use XMOS Public Licence Version 1 * FIXED: Bump default BCD device number to v1.2.0 * FIXED: xmosdfu now fails with an error when given a directory (#119) * FIXED: Compilation errors related to HID code * FIXED: Runtime error when using mic array interface - * CHANGED: Use XMOS Public Licence Version 1 * FIXED: Automate HID Report Descriptor length in AC1 HID Descriptor - * CHANGED: Move HID descriptors to ease maintenance 1.1.1 ----- - * RESOLVED: Zero length input packets generated before enumeration causing I2S - timing pushout at startup - * CHANGED: Pin Python package versions - * REMOVED: not necessary cpanfile + * CHANGED: Pin Python package versions + * FIXED: Zero length input packets generated before enumeration causing + I2S timing pushout at startup 1.1.0 ----- @@ -45,13 +174,12 @@ lib_xua Change Log * ADDED: Support for USB HID Set Idle request * ADDED: Pre-processor symbols to enable single-threaded, dual-PDM microphone operation - * FIXED: Descriptors for XUA_ADAPTIVE incorrectly defined for IN endpoint * ADDED: Guards to user_hid.h and xua_hid.h * ADDED: UAC1 HID support for AC Stop (End Call), Volume Increment and Volume Decrement - * CHANGE: UAC1 HID to report function keys f21 through f24 as specified by + * CHANGED: UAC1 HID to report function keys f21 through f24 as specified by customer - * CHANGE: HID interface for user to set and clear events from global + * CHANGED: HID interface for user to set and clear events from global variable to function * CHANGE HID report descriptor to use generic events instead of GPI events, to report Key-phrase detection as AC Search, and to report end-call @@ -61,11 +189,24 @@ lib_xua Change Log * ADDED: Override USB descriptor with sampling frequency and bit-resolution set at boot time. * ADDED: Global pointer to allow external access to masterClockFreq + * FIXED: Descriptors for XUA_ADAPTIVE incorrectly defined for IN endpoint + + * Changes to dependencies: + + - lib_spdif: 3.1.0 -> 4.0.0 + + - lib_xassert: 3.0.1 -> 4.0.0 0.2.1 ----- - * HOTFIX: Fix descriptors for XUA_ADAPTIVE + * FIXED: Fix descriptors for XUA_ADAPTIVE + + * Changes to dependencies: + + - lib_logging: 2.1.1 -> 3.0.0 + + - lib_xud: 0.1.0 -> 0.2.0 0.2.0 ----- @@ -73,17 +214,17 @@ lib_xua Change Log * ADDED: Initial library documentation * ADDED: Application note AN00247: Using lib_xua with lib_spdif (transmit) * ADDED: Separate callbacks for input/output audio stream start/stop - * CHANGE: I2S hardware resources no longer used globally and must be passed + * CHANGED: I2S hardware resources no longer used globally and must be passed to XUA_AudioHub() - * CHANGE: XUA_AudioHub() no longer pars S/PDIF transmitter task - * CHANGE: Moved to lib_spdif (from module_spdif_tx & module_spdif_rx) - * CHANGE: Define NUM_PDM_MICS renamed to XUA_NUM_PDM_MICS - * CHANGE: Define NO_USB renamed to XUA_USB_EN - * CHANGE: Build files updated to support new "xcommon" behaviour in xwaf. - * RESOLVED: wChannelConfig in UAC1 descriptor set according to output channel + * CHANGED: XUA_AudioHub() no longer pars S/PDIF transmitter task + * CHANGED: Moved to lib_spdif (from module_spdif_tx & module_spdif_rx) + * CHANGED: Define NUM_PDM_MICS renamed to XUA_NUM_PDM_MICS + * CHANGED: Define NO_USB renamed to XUA_USB_EN + * CHANGED: Build files updated to support new "xcommon" behaviour in xwaf. + * FIXED: wChannelConfig in UAC1 descriptor set according to output channel count - * RESOLVED: Indexing of ADAT channel strings (#18059) - * RESOLVED: Rebooting device fails when PLL config "not reset" bit is set + * FIXED: Indexing of ADAT channel strings (#18059) + * FIXED: Rebooting device fails when PLL config "not reset" bit is set * Changes to dependencies: @@ -99,20 +240,19 @@ lib_xua Change Log ----- * ADDED: Application note AN00246: Simple USB Audio Device using lib_xua - * CHANGE: xmosdfu emits warning if empty image read via upload - * CHANGE: Simplified mclk port sharing - no longer uses unsafe pointer - * RESOLVED: Runtime exception issues when incorrect feedback calculated + * CHANGED: xmosdfu emits warning if empty image read via upload + * CHANGED: Simplified mclk port sharing - no longer uses unsafe pointer + * FIXED: Runtime exception issues when incorrect feedback calculated (introduced in sc_usb_audio 6.13) - * RESOLVED: Output sample counter reset on stream start. Caused playback + * FIXED: Output sample counter reset on stream start. Caused playback issues on some Linux based hosts 0.1.1 ----- - * RESOLVED: Configurations where I2S_CHANS_DAC and I2S_CHANS_ADC are both 0 - now build - * RESOLVED: Deadlock in mixer when MAX_MIX_COUNT > 0 for larger channel - counts + * FIXED: Configurations where I2S_CHANS_DAC and I2S_CHANS_ADC are both 0 now + build + * FIXED: Deadlock in mixer when MAX_MIX_COUNT > 0 for larger channel counts * Changes to dependencies: @@ -123,31 +263,31 @@ lib_xua Change Log 0.1.0 ----- - * ADDED: FB_USE_REF_CLOCK to allow feedback generation from xCORE - internal reference - * ADDED: Linux Makefile for xmosdfu host application - * ADDED: Raspberry Pi Makefile for xmosdfu host application - * ADDED: Documentation of PID argument to xmosdfu - * ADDED: Optional build time microphone delay line (MIC_BUFFER_DEPTH) - * CHANGE: Removal of audManage_if, users should define their own - interfaces as required - * CHANGE: Vendor specific control interface in UAC1 descriptor now has a + * ADDED: FB_USE_REF_CLOCK to allow feedback generation from xCORE internal + reference + * ADDED: Linux Makefile for xmosdfu host application + * ADDED: Raspberry Pi Makefile for xmosdfu host application + * ADDED: Documentation of PID argument to xmosdfu + * ADDED: Optional build time microphone delay line (MIC_BUFFER_DEPTH) + * CHANGED: Removal of audManage_if, users should define their own interfaces + as required + * CHANGED: Vendor specific control interface in UAC1 descriptor now has a string descriptor so it shows up with a descriptive name in Windows Device Manager - * CHANGE: DFU_BCD_DEVICE removed (now uses BCD_DEVICE) - * CHANGE: Renaming in descriptors.h to avoid clashes with application - * CHANGE: Make device reboot function no-argument (was one channel end) - * RESOLVED: FIR gain compensation for PDM mics set incorrectly for divide of + * CHANGED: DFU_BCD_DEVICE removed (now uses BCD_DEVICE) + * CHANGED: Renaming in descriptors.h to avoid clashes with application + * CHANGED: Make device reboot function no-argument (was one channel end) + * FIXED: FIR gain compensation for PDM mics set incorrectly for divide of 8 - * RESOLVED: Incorrect xmosdfu DYLD path in test script code - * RESOLVED: xmosdfu cannot find XMOS device on modern MacBook Pro (#17897) - * RESOLVED: Issue when feedback is initially incorrect when two SOF's are - not yet received - * RESOLVED: AUDIO_TILE and PDM_TILE may now share the same value/tile - * RESOLVED: Cope with out of order interface numbers in xmosdfu - * RESOLVED: DSD playback not functional on xCORE-200 (introduced in + * FIXED: Incorrect xmosdfu DYLD path in test script code + * FIXED: xmosdfu cannot find XMOS device on modern MacBook Pro (#17897) + * FIXED: Issue when feedback is initially incorrect when two SOF's are not + yet received + * FIXED: AUDIO_TILE and PDM_TILE may now share the same value/tile + * FIXED: Cope with out of order interface numbers in xmosdfu + * FIXED: DSD playback not functional on xCORE-200 (introduced in sc_usb_audio 6.14) - * RESOLVED: Improvements made to clock sync code in TDM slave mode + * FIXED: Improvements made to clock sync code in TDM slave mode Legacy release history @@ -157,105 +297,105 @@ Legacy release history 7.4.1 ----- - - RESOLVED: Exception due to null chanend when using NO_USB + - FIXED: Exception due to null chanend when using NO_USB 7.4.0 ----- - - RESOLVED: PID_DFU now based on AUDIO_CLASS. This potentially caused issues + - FIXED: PID_DFU now based on AUDIO_CLASS. This potentially caused issues with UAC1 DFU 7.3.0 ----- - - CHANGE: Example OSX DFU host app updated to now take PID as runtime + - CHANGED: Example OSX DFU host app updated to now take PID as runtime argument. This enabled multiple XMOS devices to be attached to the host during DFU process 7.2.0 ----- - ADDED: DFU to UAC1 descriptors (guarded by DFU and FORCE_UAC1_DFU) - - RESOLVED: Removed 'reinterpretation to type of larger alignment' warnings - - RESOLVED: DFU flash code run on tile[0] even if XUD_TILE and AUDIO_IO_TILE are not 0 + - FIXED: Removed 'reinterpretation to type of larger alignment' warnings + - FIXED: DFU flash code run on tile[0] even if XUD_TILE and AUDIO_IO_TILE are not 0 7.1.0 ----- - ADDED: UserBufferManagementInit() to reset any state required in UserBufferManagement() - ADDED: I2S output up-sampling (enabled when AUD_TO_USB_RATIO is > 1) - ADDED: PDM Mic decimator output rate can now be controlled independently (via AUD_TO_MICS_RATIO) - - CHANGE: Rename I2S input down-sampling (enabled when AUD_TO_USB_RATIO is > 1, rather than via I2S_DOWNSAMPLE_FACTOR) - - RESOLVED: Crosstalk between input channels when I2S input down-sampling is enabled - - RESOLVED: Mic decimation data tables properly sized when mic sample-rate < USB audio sample-rate + - CHANGED: Rename I2S input down-sampling (enabled when AUD_TO_USB_RATIO is > 1, rather than via I2S_DOWNSAMPLE_FACTOR) + - FIXED: Crosstalk between input channels when I2S input down-sampling is enabled + - FIXED: Mic decimation data tables properly sized when mic sample-rate < USB audio sample-rate 7.0.1 ----- - - RESOLVED: PDM microphone decimation issue at some sample rates caused by integration + - FIXED: PDM microphone decimation issue at some sample rates caused by integration 7.0.0 ------ - ADDED: I2S down-sampling (I2S_DOWNSAMPLE_FACTOR) - ADDED: I2S resynchronisation when in slave mode (CODEC_MASTER=1) - - CHANGE: Various memory optimisations when MAX_FREQ = MIN_FREQ - - CHANGE: Memory optimisations in audio buffering - - CHANGE: Various memory optimisations in UAC1 mode - - CHANGE: user_pdm_process() API change - - CHANGE: PDM Mic decimator table now related to MIN_FREQ (memory optimisation) - - RESOLVED: Audio request interrupt handler properly eliminated + - CHANGED: Various memory optimisations when MAX_FREQ = MIN_FREQ + - CHANGED: Memory optimisations in audio buffering + - CHANGED: Various memory optimisations in UAC1 mode + - CHANGED: user_pdm_process() API change + - CHANGED: PDM Mic decimator table now related to MIN_FREQ (memory optimisation) + - FIXED: Audio request interrupt handler properly eliminated 6.30.0 ------ - - RESOLVED: Number of PDM microphone channels configured now based on NUM_PDM_MICS define - (previously hard-coded) - - RESOLVED: PDM microphone clock divide now based MCLK defines (previously hard-coded) - - CHANGE: Second microphone decimation core only run if NUM_PDM_MICS > 4 + - FIXED: Number of PDM microphone channels configured now based on NUM_PDM_MICS define + (previously hard-coded) + - FIXED: PDM microphone clock divide now based MCLK defines (previously hard-coded) + - CHANGED: Second microphone decimation core only run if NUM_PDM_MICS > 4 6.20.0 ------ - - RESOLVED: Intra-frame sample delays of 1/2 samples on input streaming in TDM mode - - RESOLVED: Build issue with NUM_USB_CHAN_OUT set to 0 and MIXER enabled - - RESOLVED: SPDIF_TX_INDEX not defined build warning only emitted when SPDIF_TX defined - - RESOLVED: Failure to enter DFU mode when configured without input volume control + - FIXED: Intra-frame sample delays of 1/2 samples on input streaming in TDM mode + - FIXED: Build issue with NUM_USB_CHAN_OUT set to 0 and MIXER enabled + - FIXED: SPDIF_TX_INDEX not defined build warning only emitted when SPDIF_TX defined + - FIXED: Failure to enter DFU mode when configured without input volume control 6.19.0 ------ - - RESOLVED: SPDIF_TX_INDEX not defined build warning only emitted when SPDIF_TX defined - - RESOLVED: Failure to enter DFU mode when configured without input volume control + - FIXED: SPDIF_TX_INDEX not defined build warning only emitted when SPDIF_TX defined + - FIXED: Failure to enter DFU mode when configured without input volume control 6.18.1 ------ - - ADDED: Vendor Specific control interface added to UAC1 descriptors to allow control of - XVSM params from Windows (via lib_usb) + - ADDED: Vendor Specific control interface added to UAC1 descriptors to allow control of + XVSM params from Windows (via lib_usb) 6.18.0 ------ - - ADDED: Call to VendorRequests() and VendorRequests_Init() to Endpoint 0 - - ADDED: VENDOR_REQUESTS_PARAMS define to allow for custom parameters to VendorRequest calls - - RESOLVED: FIR gain compensation set appropriately in lib_mic_array usage - - CHANGE: i_dsp interface renamed i_audManage + - ADDED: Call to VendorRequests() and VendorRequests_Init() to Endpoint 0 + - ADDED: VENDOR_REQUESTS_PARAMS define to allow for custom parameters to VendorRequest calls + - FIXED: FIR gain compensation set appropriately in lib_mic_array usage + - CHANGED: i_dsp interface renamed i_audManage 6.16.0 ------ - ADDED: Call to UserBufferManagement() - ADDED: PDM_MIC_INDEX in devicedefines.h and usage - - CHANGE: pdm_buffer() task now combinable - - CHANGE: Audio I/O task now takes i_dsp interface as a parameter - - CHANGE: Removed built-in support for A/U series internal ADC - - CHANGE: User PDM Microphone processing now uses an interface (previously function call) + - CHANGED: pdm_buffer() task now combinable + - CHANGED: Audio I/O task now takes i_dsp interface as a parameter + - CHANGED: Removed built-in support for A/U series internal ADC + - CHANGED: User PDM Microphone processing now uses an interface (previously function call) 6.15.2 ------ - - RESOLVED: interrupt.h (used in audio buffering) now compatible with xCORE-200 ABI + - FIXED: interrupt.h (used in audio buffering) now compatible with xCORE-200 ABI 6.15.1 ------ - - RESOLVED: DAC data mis-alignment issue in TDM/I2S slave mode - - CHANGE: Updates to support API changes in lib_mic_array version 2.0 + - FIXED: DAC data mis-alignment issue in TDM/I2S slave mode + - CHANGED: Updates to support API changes in lib_mic_array version 2.0 6.15.0 ------ - - RESOLVED: UAC 1.0 descriptors now support multi-channel volume control (previously were + - FIXED: UAC 1.0 descriptors now support multi-channel volume control (previously were hard-coded as stereo) - - CHANGE: Removed 32kHz sample-rate support when PDM microphones enabled (lib_mic_array + - CHANGED: Removed 32kHz sample-rate support when PDM microphones enabled (lib_mic_array currently does not support non-integer decimation factors) 6.14.0 @@ -269,75 +409,75 @@ Legacy release history list and UAC 1.0 descriptors - ADDED: Support for the use and integration of PDM microphones (including PDM to PCM conversion) via lib_mic_array - - RESOLVED: MIDI data not accepted after "sleep" in OSX 10.11 (El Capitan) - related to sc_xud + - FIXED: MIDI data not accepted after "sleep" in OSX 10.11 (El Capitan) - related to sc_xud issue #17092 - - CHANGE: Asynchronous feedback system re-implemented to allow for the first two ADDED + - CHANGED: Asynchronous feedback system re-implemented to allow for the first two ADDED changelog items - - CHANGE: Hardware divider used to generate bit-clock from master clock (xCORE-200 only). + - CHANGED: Hardware divider used to generate bit-clock from master clock (xCORE-200 only). Allows easy support for greater number of master-clock to sample-rate ratios. - - CHANGE: module_queue no longer uses any assert module/lib + - CHANGED: module_queue no longer uses any assert module/lib 6.13.0 ------ - ADDED: Device now uses implicit feedback when input stream is available (previously explicit feedback pipe always used). This saves chanend/EP resources and means less processing burden for the host. Previous behaviour available by enabling UAC_FORCE_FEEDBACK_EP - - RESOLVED: Exception when SPDIF_TX and ADAT_TX both enabled due to clock-block being configured + - FIXED: Exception when SPDIF_TX and ADAT_TX both enabled due to clock-block being configured after already started. Caused by SPDIF_TX define check typo - - RESOLVED: DFU flag address changed to properly conform to memory address range allocated to + - FIXED: DFU flag address changed to properly conform to memory address range allocated to apps by tools - - RESOLVED: Build failure when DFU disabled - - RESOLVED: Build issue when I2S_CHANS_ADC/DAC set to 0 and CODEC_MASTER enabled - - RESOLVED: Typo in MCLK_441 checking for MIN_FREQ define - - CHANGE: Mixer and non-mixer channel comms scheme (decouple <-> audio path) now identical - - CHANGE: Input stream buffering modified such that during overflow older samples are removed + - FIXED: Build failure when DFU disabled + - FIXED: Build issue when I2S_CHANS_ADC/DAC set to 0 and CODEC_MASTER enabled + - FIXED: Typo in MCLK_441 checking for MIN_FREQ define + - CHANGED: Mixer and non-mixer channel comms scheme (decouple <-> audio path) now identical + - CHANGED: Input stream buffering modified such that during overflow older samples are removed rather than ignoring most recent samples. Removes any chance of stale input packets being sent to host - - CHANGE: module_queue (in sc_usb_audio) now uses lib_xassert rather than module_xassert + - CHANGED: module_queue (in sc_usb_audio) now uses lib_xassert rather than module_xassert 6.12.6 ------ - - RESOLVED: Build error when DFU is disabled - - RESOLVED: Build error when I2S_CHANS_ADC or I2S_CHANS_DAC set to 0 and CODEC_MASTER enabled + - FIXED: Build error when DFU is disabled + - FIXED: Build error when I2S_CHANS_ADC or I2S_CHANS_DAC set to 0 and CODEC_MASTER enabled 6.12.5 ------ - - RESOLVED: Stream issue when NUM_USB_CHAN_IN < I2S_CHANS_ADC + - FIXED: Stream issue when NUM_USB_CHAN_IN < I2S_CHANS_ADC 6.12.4 ------ - - RESOLVED: DFU fail when DSD enabled and USB library not running on tile[0] + - FIXED: DFU fail when DSD enabled and USB library not running on tile[0] 6.12.3 ------ - - RESOLVED: Method for storing persistent state over a DFU reboot modified to improve resilience + - FIXED: Method for storing persistent state over a DFU reboot modified to improve resilience against code-base and tools changes 6.12.2 ------ - - RESOLVED: Reboot code (used for DFU) failure in tools versions > 14.0.2 (xCORE-200 only) - - RESOLVED: Run-time exception in mixer when MAX_MIX_COUNT > 0 (xCORE-200 only) - - RESOLVED: MAX_MIX_COUNT checked properly for mix strings in string table - - CHANGE: DFU code re-written to use an XC interface. The flash-part may now be connected + - FIXED: Reboot code (used for DFU) failure in tools versions > 14.0.2 (xCORE-200 only) + - FIXED: Run-time exception in mixer when MAX_MIX_COUNT > 0 (xCORE-200 only) + - FIXED: MAX_MIX_COUNT checked properly for mix strings in string table + - CHANGED: DFU code re-written to use an XC interface. The flash-part may now be connected to a separate tile to the tile running USB code - - CHANGE: DFU code can now use quad-SPI flash - - CHANGE: Example xmos_dfu application now uses a list of PIDs to allow adding PIDs easier. + - CHANGED: DFU code can now use quad-SPI flash + - CHANGED: Example xmos_dfu application now uses a list of PIDs to allow adding PIDs easier. --listdevices command also added. - - CHANGE: I2S_CHANS_PER_FRAME and I2S_WIRES_xxx defines tidied + - CHANGED: I2S_CHANS_PER_FRAME and I2S_WIRES_xxx defines tidied 6.12.1 ------ - - RESOLVED: Fixes to TDM input timing/sample-alignment when BCLK=MCLK - - RESOLVED: Various minor fixes to allow ADAT_RX to run on xCORE 200 MC AUDIO hardware - - CHANGE: Moved from old SPDIF define to SPDIF_TX + - FIXED: Fixes to TDM input timing/sample-alignment when BCLK=MCLK + - FIXED: Various minor fixes to allow ADAT_RX to run on xCORE 200 MC AUDIO hardware + - CHANGED: Moved from old SPDIF define to SPDIF_TX 6.12.0 ------ - ADDED: Checks for XUD_200_SERIES define where required - - RESOLVED: Run-time exception due to decouple interrupt not entering correct issue mode + - FIXED: Run-time exception due to decouple interrupt not entering correct issue mode (affects XCORE-200 only) - - CHANGE: SPDIF Tx Core may now reside on a different tile from I2S - - CHANGE: I2C ports now in structure to match new module_i2c_singleport/shared API. + - CHANGED: SPDIF Tx Core may now reside on a different tile from I2S + - CHANGED: I2C ports now in structure to match new module_i2c_singleport/shared API. * Changes to dependencies: @@ -347,48 +487,48 @@ Legacy release history 6.11.3 ------ - - RESOLVED: (Major) Streaming issue when mixer not enabled (introduced in 6.11.2) + - FIXED: (Major) Streaming issue when mixer not enabled (introduced in 6.11.2) 6.11.2 ------ - - RESOLVED: (Major) Enumeration issue when MAX_MIX_COUNT > 0 only. Introduced in mixer + - FIXED: (Major) Enumeration issue when MAX_MIX_COUNT > 0 only. Introduced in mixer optimisations in 6.11.0. Only affects designs using mixer functionality. - - RESOLVED: (Normal) Audio buffering request system modified such that the mixer output is + - FIXED: (Normal) Audio buffering request system modified such that the mixer output is not silent when in underflow case (i.e. host output stream not active) This issue was introduced with the addition of DSD functionality and only affects designs using mixer functionality. - - RESOLVED: (Minor) Potential build issue due to duplicate labels in inline asm in + - FIXED: (Minor) Potential build issue due to duplicate labels in inline asm in set_interrupt_handler macro - - RESOLVED: (Minor) BCD_DEVICE define in devicedefines.h now guarded by ifndef (caused issues + - FIXED: (Minor) BCD_DEVICE define in devicedefines.h now guarded by ifndef (caused issues with DFU test build configs. - - RESOLVED: (Minor) String descriptor for Clock Selector unit incorrectly reported - - RESOLVED: (Minor) BCD_DEVICE in devicedefines.h now guarded by #ifndef (Caused issues with + - FIXED: (Minor) String descriptor for Clock Selector unit incorrectly reported + - FIXED: (Minor) BCD_DEVICE in devicedefines.h now guarded by #ifndef (Caused issues with default DFU test build configs. - - CHANGE: HID report descriptor defines added to shared user_hid.h - - CHANGE: Now uses module_adat_rx from sc_adat (local module_usb_audio_adat removed) + - CHANGED: HID report descriptor defines added to shared user_hid.h + - CHANGED: Now uses module_adat_rx from sc_adat (local module_usb_audio_adat removed) 6.11.1 ------ - ADDED: ADAT transmit functionality, including SMUX. See ADAT_TX and ADAT_TX_INDEX. - - RESOLVED: (Normal) Build issue with CODEC_MASTER (xCore is I2S slave) enabled - - RESOLVED: (Minor) Channel ordering issue in when TDM and CODEC_MASTER mode enabled - - RESOLVED: (Normal) DFU fails when SPDIF_RX enabled due to clock block being shared between SPDIF + - FIXED: (Normal) Build issue with CODEC_MASTER (xCore is I2S slave) enabled + - FIXED: (Minor) Channel ordering issue in when TDM and CODEC_MASTER mode enabled + - FIXED: (Normal) DFU fails when SPDIF_RX enabled due to clock block being shared between SPDIF core and FlashLib 6.11.0 ------ - ADDED: Basic TDM I2S functionality added. See I2S_CHANS_PER_FRAME and I2S_MODE_TDM - - CHANGE: Various optimisations in 'mixer' core to improve performance for higher + - CHANGED: Various optimisations in 'mixer' core to improve performance for higher channel counts including the use of XC unsafe pointers instead of inline ASM - - CHANGE: Mixer mapping disabled when MAX_MIX_COUNT is 0 since this is wasted processing. - - CHANGE: Descriptor changes to allow for channel input/output channel count up to 32 + - CHANGED: Mixer mapping disabled when MAX_MIX_COUNT is 0 since this is wasted processing. + - CHANGED: Descriptor changes to allow for channel input/output channel count up to 32 (previous limit was 18) 6.10.0 ------ - - CHANGE: Endpoint management for iAP EA Native Transport now merged into buffer() core. + - CHANGED: Endpoint management for iAP EA Native Transport now merged into buffer() core. Previously was separate core (as added in 6.8.0). - - CHANGE: Minor optimisation to I2S port code for inputs from ADC + - CHANGED: Minor optimisation to I2S port code for inputs from ADC 6.9.0 ----- @@ -396,35 +536,35 @@ Legacy release history supported (4 channels at 96kHz). - ADDED: Explicit build warnings if sample rate/depth & channel combination exceeds available USB bus bandwidth. - - RESOLVED: (Major) Reinstated ADAT input functionality, including descriptors and clock + - FIXED: (Major) Reinstated ADAT input functionality, including descriptors and clock generation/control and stream configuration defines/tables. - - RESOLVED: (Major) S/PDIF/ADAT sample transfer code in audio() (from ClockGen()) moved to + - FIXED: (Major) S/PDIF/ADAT sample transfer code in audio() (from ClockGen()) moved to aid timing. - - CHANGE: Modifying mix map now only affects specified mix, previous was applied to all + - CHANGED: Modifying mix map now only affects specified mix, previous was applied to all mixes. CS_XU_MIXSEL control selector now takes values 0 to MAX_MIX_COUNT + 1 (with 0 affecting all mixes). - - CHANGE: Channel c_dig_rx is no longer nullable, assists with timing due to removal of + - CHANGED: Channel c_dig_rx is no longer nullable, assists with timing due to removal of null checks inserted by compiler. - - CHANGE: ADAT SMUX selection now based on device sample frequency rather than selected + - CHANGED: ADAT SMUX selection now based on device sample frequency rather than selected stream format - Endpoint 0 now configures clockgen() on a sample-rate change rather than stream start. 6.8.0 ----- - ADDED: Evaluation support for iAP EA Native Transport endpoints - - RESOLVED: (Minor) Reverted change in 6.5.1 release where sample rate listing in Audio Class + - FIXED: (Minor) Reverted change in 6.5.1 release where sample rate listing in Audio Class 1.0 descriptors was trimmed (previously 4 rates were always reported). This change appears to highlight a Windows (only) enumeration issue with the Input & Output configs - - RESOLVED: (Major) Mixer functionality re-instated, including descriptors and various required + - FIXED: (Major) Mixer functionality re-instated, including descriptors and various required updates compatibility with 13 tools - - RESOLVED: (Major) Endpoint 0 was requesting an out of bounds channel whilst requesting level data - - RESOLVED: (Major) Fast mix code not operates correctly in 13 tools, assembler inserting long jmp + - FIXED: (Major) Endpoint 0 was requesting an out of bounds channel whilst requesting level data + - FIXED: (Major) Fast mix code not operates correctly in 13 tools, assembler inserting long jmp instructions - - RESOLVED: (Minor) LED level meter code now compatible with 13 tools (shared mem access) - - RESOLVED (Minor) Ordering of level data from the device now matches channel ordering into + - FIXED: (Minor) LED level meter code now compatible with 13 tools (shared mem access) + - FIXED: (Minor) Ordering of level data from the device now matches channel ordering into mixer (previously the device input data and the stream from host were swapped) - - CHANGE: Level meter buffer naming now resemble functionality + - CHANGED: Level meter buffer naming now resemble functionality Legacy release history diff --git a/Jenkinsfile b/Jenkinsfile index 092948c7..d37e570e 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,4 +1,4 @@ -@Library('xmos_jenkins_shared_library@v0.16.2') _ +@Library('xmos_jenkins_shared_library@v0.24.0') _ getApproval() @@ -14,7 +14,7 @@ pipeline { stages { stage('Basic tests') { agent { - label 'x86_64&&brew' + label 'x86_64 && linux' } stages { stage('Get view') { @@ -24,12 +24,39 @@ pipeline { } stage('Library checks') { steps { - xcoreLibraryChecks("${REPO}") + xcoreLibraryChecks("${REPO}", false) } } - stage('Tests') { - steps { - runXmostest("${REPO}", 'tests') + stage('Testing') { + failFast true + parallel { + stage('Tests') { + steps { + dir("${REPO}/tests"){ + viewEnv(){ + withVenv{ + runPytest('--numprocesses=4') + } + } + } + } + } + stage('Unity tests') { + steps { + dir("${REPO}") { + dir('tests') { + dir('xua_unit_tests') { + withVenv { + runWaf('.', "configure clean build --target=xcore200") + viewEnv() { + runPython("TARGET=XCORE200 pytest -s") + } + } + } + } + } + } + } } } stage('xCORE builds') { @@ -40,6 +67,8 @@ pipeline { runXdoc('doc') } } + // Archive all the generated .pdf docs + archiveArtifacts artifacts: "${REPO}/**/pdf/*.pdf", fingerprint: true, allowEmptyArchive: true } } } @@ -54,7 +83,7 @@ pipeline { parallel { stage('Build Linux host app') { agent { - label 'x86_64&&brew&&linux' + label 'x86_64&&linux' } steps { xcorePrepareSandbox("${VIEW}", "${REPO}") @@ -70,13 +99,19 @@ pipeline { } stage('Build Mac host app') { agent { - label 'x86_64&&brew&&macOS' + label 'x86_64&&macOS' } steps { xcorePrepareSandbox("${VIEW}", "${REPO}") dir("${REPO}/${REPO}/host/xmosdfu") { sh 'make -f Makefile.OSX64' } + dir("${REPO}/host_usb_mixer_control") { + sh 'make -f Makefile.OSX' + sh 'mkdir OSX/x86' + sh 'mv xmos_mixer OSX/x86/xmos_mixer' + archiveArtifacts artifacts: "OSX/x86/xmos_mixer", fingerprint: true + } } post { cleanup { @@ -110,7 +145,17 @@ pipeline { dir("${REPO}") { checkout scm dir("${REPO}/host/xmosdfu") { - runVS('nmake /f Makefile.Win32') + withVS("vcvars32.bat") { + bat "nmake /f Makefile.Win32" + } + } + dir("host_usb_mixer_control") { + withVS() { + bat 'msbuild host_usb_mixer_control.vcxproj /property:Configuration=Release /property:Platform=x64' + } + sh 'mkdir Win/x64' + sh 'mv bin/Release/x64/host_usb_mixer_control.exe Win/x64/xmos_mixer.exe' + archiveArtifacts artifacts: "Win/x64/xmos_mixer.exe", fingerprint: true } } } @@ -124,7 +169,7 @@ pipeline { } stage('Update') { agent { - label 'x86_64&&brew' + label 'x86_64 && linux' } steps { updateViewfiles() diff --git a/README.rst b/README.rst index 78c6145c..075a55c8 100644 --- a/README.rst +++ b/README.rst @@ -1,25 +1,31 @@ lib_xua -======= +####### + +:Version: 3.4.0 +:Vendor: XMOS + + +:Scope: General Use Summary -------- +******* -USB Audio Shared Components for use in the XMOS USB Audio Refererence Designs. +lib_xua contains shared components for use in the XMOS USB Audio (XUA) Reference Designs. -This library enables the development of USB Audio devices on the XMOS xCORE architecture. +These components enable the development of USB Audio devices on the XMOS xCORE architecture. Features -........ +======== -Key features of the various applications in this repository are as follows +Key features of the various components in this repository are as follows - USB Audio Class 1.0/2.0 Compliant -- Fully Asynchronous operation +- Fully Asynchronous operation (synchronous mode as an option) - Support for the following sample frequencies: 8, 11.025, 12, 16, 32, 44.1, 48, 88.2, 96, 176.4, 192, 352.8, 384kHz -- Input/output channel and individual volume/mute controls supported +- Volume/mute controls for input/output (for both master and individual channels) - Support for dynamically selectable output audio formats (e.g. resolution) @@ -33,30 +39,64 @@ Key features of the various applications in this repository are as follows - ADAT input +- Synchronisation to external digital streams i.e. S/PDIF or ADAT (when in asynchronous mode) + +- I2S slave & master modes + +- TDM slave & master modes + - MIDI input/output (Compliant to USB Class Specification for MIDI devices) -- DSD output (Native and DoP mode) at DSD64 and DSD128 rates +- DSD output ("native" and DoP mode) at DSD64 and DSD128 rates - Mixer with flexible routing -- Simple playback controls via Human Interface Device (HID) +- Simple playback controls via USB Human Interface Device (HID) Class -- Support for operation with Apple devices (requires software module sc_mfi for MFI licensees only - please contact XMOS) +Note, not all features may be supported at all sample frequencies, simultaneously or on all devices. +Some features may also require specific host driver support. -Note, not all features may be supported at all sample frequencies, simultaneously or on all devices. Some features also require specific host driver support. +Host System Requirements +======================== +USB Audio devices built using `lib_xua` have the following host system requirements. -Software version and dependencies -................................. +- Mac OSX version 10.6 or later -The CHANGELOG contains information about the current and previous versions. -For a list of direct dependencies, look for DEPENDENT_MODULES in lib_xua/module_build_info. +- Windows Vista, 7, 8 or 10 with Thesycon Audio Class 2.0 driver for Windows (Tested against version 3.20). Please contact XMOS for details. + +- Windows Vista, 7, 8 or 10 with built-in USB Audio Class 1.0 driver. + +Older versions of Windows are not guaranteed to operate as expected. Devices are also expected to operate with various Linux distributions including mobile variants. Related Application Notes -......................... +========================= The following application notes use this library: - * AN000246 - Simple USB Audio Device using lib_xua - * AN000247 - Using lib_xua with lib_spdif (transmit) - * AN000248 - Using lib_xua with lib_mic_array + * AN000246 - Simple USB Audio Device using lib_xua + * AN000247 - Using lib_xua with lib_spdif (transmit) + * AN000248 - Using lib_xua with lib_mic_array + +Required Software (dependencies) +================================ + + * lib_locks (www.github.com/xmos/lib_locks) + * lib_logging (www.github.com/xmos/lib_logging) + * lib_mic_array (www.github.com/xmos/lib_mic_array) + * lib_xassert (www.github.com/xmos/lib_xassert) + * lib_dsp (www.github.com/xmos/lib_dsp) + * lib_spdif (www.github.com/xmos/lib_spdif) + * lib_xud (www.github.com/xmos/lib_xud) + * lib_adat (www.github.com/xmos/lib_adat) + +Documentation +============= + +You can find the documentation for this software in the /doc directory of the package. + +Support +======= + +This package is supported by XMOS Ltd. Issues can be raised against the software at: http://www.xmos.com/support + diff --git a/doc_dfu/building_xmos_dfu.rst b/doc/dfu/rst/building_xmos_dfu.rst similarity index 100% rename from doc_dfu/building_xmos_dfu.rst rename to doc/dfu/rst/building_xmos_dfu.rst diff --git a/doc_dfu/index.rst b/doc/dfu/rst/dfu.rst similarity index 100% rename from doc_dfu/index.rst rename to doc/dfu/rst/dfu.rst diff --git a/doc_dfu/factory_image.rst b/doc/dfu/rst/factory_image.rst similarity index 100% rename from doc_dfu/factory_image.rst rename to doc/dfu/rst/factory_image.rst diff --git a/doc_dfu/overview.rst b/doc/dfu/rst/overview.rst similarity index 100% rename from doc_dfu/overview.rst rename to doc/dfu/rst/overview.rst diff --git a/doc_dfu/thesycon_dfu.rst b/doc/dfu/rst/thesycon_dfu.rst similarity index 100% rename from doc_dfu/thesycon_dfu.rst rename to doc/dfu/rst/thesycon_dfu.rst diff --git a/doc/dfu/rst/xdoc.conf b/doc/dfu/rst/xdoc.conf new file mode 100644 index 00000000..9c958518 --- /dev/null +++ b/doc/dfu/rst/xdoc.conf @@ -0,0 +1,3 @@ +XMOSNEWSTYLE=2 +SOURCE_INCLUDE_DIRS=../../../lib_xua/host/xmosdfu +SPHINX_MASTER_DOC=dfu diff --git a/doc_dfu/xmos_dfu.rst b/doc/dfu/rst/xmos_dfu.rst similarity index 100% rename from doc_dfu/xmos_dfu.rst rename to doc/dfu/rst/xmos_dfu.rst diff --git a/doc_dfu/.gitignore b/doc_dfu/.gitignore deleted file mode 100644 index e35d8850..00000000 --- a/doc_dfu/.gitignore +++ /dev/null @@ -1 +0,0 @@ -_build diff --git a/doc_dfu/LICENSE.rst b/doc_dfu/LICENSE.rst deleted file mode 100644 index ca48f20f..00000000 --- a/doc_dfu/LICENSE.rst +++ /dev/null @@ -1,84 +0,0 @@ -******************************* -XMOS PUBLIC LICENCE: Version 1 -******************************* - -Subject to the conditions and limitations below, permission is hereby granted by XMOS LIMITED (“XMOS”), free of charge, to any person or entity obtaining a copy of the XMOS Software. - -**1. Definitions** - -**“Applicable Patent Rights”** means: (a) where XMOS is the grantor of the rights, (i) claims of patents that are now or in future owned by or assigned to XMOS and (ii) that cover subject matter contained in the Software, but only to the extent it is necessary to use, reproduce or distribute the Software without infringement; and (b) where you are the grantor of the rights, (i) claims of patents that are now or in future owned by or assigned to you and (ii) that cover the subject matter contained in your Derivatives, taken alone or in combination with the Software. - -**“Compiled Code”** means any compiled, binary, machine readable or executable version of the Source Code. - -**“Contributor”** means any person or entity that creates or contributes to the creation of Derivatives. - -**“Derivatives”** means any addition to, deletion from and/or change to the substance, structure of the Software, any previous Derivatives, the combination of the Derivatives and the Software and/or any respective portions thereof. - -**“Source Code”** means the human readable code that is suitable for making modifications but excluding any Compiled Code. - -**“Software”** means the software and associated documentation files which XMOS makes available and which contain a notice identifying the software as original XMOS software and referring to the software being subject to the terms of this XMOS Public Licence. - -This Licence refers to XMOS Software and does not relate to any XMOS hardware or devices which are protected by intellectual property rights (including patent and trade marks) which may be sold to you under a separate agreement. - - -**2. Licence** - -**Permitted Uses, Conditions and Restrictions.** Subject to the conditions below, XMOS grants you a worldwide, royalty free, non-exclusive licence, to the extent of any Patent Rights to do the following: - -2.1 **Unmodified Software.** You may use, copy, display, publish, distribute and make available unmodified copies of the Software: - -2.1.1 for personal or academic, non-commercial purposes; or - -2.1.2 for commercial purposes provided the Software is at all times used on a device designed, licensed or developed by XMOS and, provided that in each instance (2.1.1 and 2.1.2): - -(a) you must retain and reproduce in all copies of the Software the copyright and proprietary notices and disclaimers of XMOS as they appear in the Software, and keep intact all notices and disclaimers in the Software files that refer to this Licence; and - -(b) you must include a copy of this Licence with every copy of the Software and documentation you publish, distribute and make available and you may not offer or impose any terms on such Software that alter or restrict this Licence or the intent of such Licence, except as permitted below (Additional Terms). - -The licence above does not include any Compiled Code which XMOS may make available under a separate support and licence agreement. - -2.2 **Derivatives.** You may create and modify Derivatives and use, copy, display, publish, distribute and make available Derivatives: - -2.2.1 for personal or academic, non-commercial purposes; or - -2.2.2 for commercial purposes, provided the Derivatives are at all times used on a device designed, licensed or developed by XMOS and, provided that in each instance (2.2.1 and 2.2.2): - -(a) you must comply with the terms of clause 2.1 with respect to the Derivatives; - -(b) you must copy (to the extent it doesn’t already exist) the notice below in each file of the Derivatives, and ensure all the modified files carry prominent notices stating that you have changed the files and the date of any change; and - -(c) if you sublicence, distribute or otherwise make the Software and/or the Derivatives available for commercial purposes, you must provide that the Software and Derivatives are at all times used on a device designed, licensed or developed by XMOS. - -Without limitation to these terms and clause 3 below, the Source Code and Compiled Code to your Derivatives may at your discretion (but without obligation) be released, copied, displayed, published, distributed and made available; and if you elect to do so, it must be under the terms of this Licence including the terms of the licence at clauses 2.2.1, 2.2.2 and clause 3 below. - -2.3 **Distribution of Executable Versions.** If you distribute or make available Derivatives, you must include a prominent notice in the code itself as well as in all related documentation, stating that the Source Code of the Software from which the Derivatives are based is available under the terms of this Licence, with information on how and where to obtain such Source Code. - -**3. Your Grant of Rights.** In consideration and as a condition to this Licence, you grant to any person or entity receiving or distributing any Derivatives, a non-exclusive, royalty free, perpetual, irrevocable license under your Applicable Patent Rights and all other intellectual property rights owned or controlled by you, to use, copy, display, publish, distribute and make available your Derivatives of the same scope and extent as XMOS’s licence under clause 2.2 above. - -**4. Combined Products.** You may create a combined product by combining Software, Derivatives and other code not covered by this Licence as a single application or product. In such instance, you must comply with the requirements of this Licence for any portion of the Software and/or Derivatives. - -**5. Additional Terms.** You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations and/or other rights consistent with the term of this Licence (“Additional Terms”) to any legitimate recipients of the Software and/or Derivatives. The terms on which you provide such Additional Terms are on your sole responsibility and you shall indemnify, defend and hold XMOS harmless against any claims asserted against XMOS. - -**6. New Versions.** XMOS may publish revised and/or new versions of this Licence from time to time to accommodate changes to the Licence terms, new versions, updates and bug fixes of the Software. Each version will be given a distinguishing version number. Once Software has been published under a particular version of this Licence, you may continue to use it under the terms of that version. You may also choose to use the latest version of the Software under any subsequent version published by XMOS. Only XMOS shall have the right to modify these terms. - -**7. IPR and Ownership** -Any rights, including all intellectual property rights and all trademarks not expressly granted herein are reserved in full by the authors or copyright holders. Any requests for additional permissions by XMOS including any rights to use XMOS trademarks, should be made (without obligation) to XMOS at **support@xmos.com** - -Nothing herein shall limit any rights that XMOS is otherwise entitled to under the doctrines of patent exhaustion, implied license, or legal estoppel. Neither the name of the authors, the copyright holders or any contributors may be used to endorse or promote any Derivatives from this Software without specific written permission. Any attempt to deal with the Software which does not comply with this Licence shall be void and shall automatically terminate any rights granted under this licence (including any licence of any intellectual property rights granted herein). -Subject to the licences granted under this Licence any Contributor retains all rights, title and interest in and to any Derivatives made by Contributor subject to the underlying rights of XMOS in the Software. XMOS shall retain all rights, title and interest in the Software and any Derivatives made by XMOS (“XMOS Derivatives”). XMOS Derivatives will not automatically be subject to this Licence and XMOS shall be entitled to licence such rights on any terms (without obligation) as it sees fit. - -**8. Termination** - -8.1 This Licence will automatically terminate immediately, without notice to you, if: - -(a) you fail to comply with the terms of this Licence; and/or - -(b) you directly or indirectly commence any action for patent or intellectual property right infringement against XMOS, or any parent, group, affiliate or subsidiary of XMOS; provided XMOS did not first commence an action or patent infringement against you in that instance; and/or - -(c) the terms of this Licence are held by any court of competent jurisdiction to be unenforceable in whole or in part. - -**9. Critical Applications.** Unless XMOS has agreed in writing with you an agreement specifically governing use of the Goods in military, aerospace, automotive or medically related functions (collectively and individually hereinafter referred to as "Special Use"), any permitted use of the Software excludes Special Use. Notwithstanding any agreement between XMOS and you for Special Use, Special Use shall be at your own risk, and you shall fully indemnify XMOS against any damages, losses, costs and claims (direct and indirect) arising out of any Special Use. - -**10. NO WARRANTY OR SUPPORT.** 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 XMOS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, WARRANTY, CIVIL TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE INCLUDING GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES EVEN IF SUCH PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES AND NOT WITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE. IN SOME JURISDICTIONS PARTIES ARE UNABLE TO LIMIT LIABILTY IN THIS WAY, IF THIS APPLIES TO YOUR JURISDICTION THIS LIABILITY CLAUSE ABOVE MAY NOT APPLY. NOTWITHSTANDING THE ABOVE, IN NO EVENT SHALL XMOS’s TOTAL LIABILITY TO YOU FOR ALL DAMAGES, LOSS OR OTHERWISE EXCEED $50. - -**11. Governing Law and Jurisdiction.** This Licence constitutes the entire agreement between the parties with respect to the subject matter hereof. The Licence shall be governed by the laws of England and the conflict of laws and UN Convention on Contracts for the International Sale of Goods, shall not apply. diff --git a/doc_dfu/Makefile b/doc_dfu/Makefile deleted file mode 100644 index df405939..00000000 --- a/doc_dfu/Makefile +++ /dev/null @@ -1 +0,0 @@ -SOURCE_INCLUDE_DIRS=../lib_xua/host/xmosdfu diff --git a/examples/AN00246_xua_example/Makefile b/examples/AN00246_xua_example/Makefile index 9bdea14b..4efaa759 100644 --- a/examples/AN00246_xua_example/Makefile +++ b/examples/AN00246_xua_example/Makefile @@ -1,17 +1,19 @@ APP_NAME = app_xua_simple -TARGET = xk-audio-216-mc.xn +TARGET = xk-audio-316-mc.xn # The flags passed to xcc when building the application -XCC_FLAGS = -fcomment-asm -Xmapper --map -Xmapper MAPFILE -O3 -report -fsubword-select -save-temps \ - -g -Wno-unused-function -Wno-timing -DXUD_SERIES_SUPPORT=XUD_X200_SERIES -DUSB_TILE=tile[1] -fxscope +XCC_FLAGS = -O3 -report \ + -g -Wno-unused-function \ + -DXUD_CORE_CLOCK=600 \ + -DUSB_TILE=tile[0] \ + -fxscope \ + -DUAC_FORCE_FEEDBACK_EP=0 -#-DSDA_HIGH=2 -DSCL_HIGH=1 -fxscope - # The USED_MODULES variable lists other module used by the application. These # modules will extend the SOURCE_DIRS, INCLUDE_DIRS and LIB_DIRS variables. # Modules are expected to be in the directory above the BASE_DIR directory. -USED_MODULES = lib_xua lib_device_control lib_xud lib_i2c +USED_MODULES = lib_xua lib_xud lib_i2c #============================================================================= # The following part of the Makefile includes the common build infrastructure @@ -19,4 +21,3 @@ USED_MODULES = lib_xua lib_device_control lib_xud lib_i2c XMOS_MAKE_PATH ?= ../.. include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common - diff --git a/examples/AN00246_xua_example/doc/rst/AN00246.rst b/examples/AN00246_xua_example/doc/rst/AN00246_xua_example.rst similarity index 75% rename from examples/AN00246_xua_example/doc/rst/AN00246.rst rename to examples/AN00246_xua_example/doc/rst/AN00246_xua_example.rst index 9627120a..a3fda049 100644 --- a/examples/AN00246_xua_example/doc/rst/AN00246.rst +++ b/examples/AN00246_xua_example/doc/rst/AN00246_xua_example.rst @@ -11,7 +11,7 @@ Introduction The XMOS USB Audio (XUA) library provides an implemention of USB Audio Class versions 1.0 and 2.0. This application note demonstrates the implementation of a basic USB Audio Device on -the xCORE-200 MC Audio board. +the xCORE.ai Multichannel (MC) Audio board (XK-AUDIO-316-MC). The Makefile @@ -26,14 +26,10 @@ The Makefile also includes:: USED_MODULES = .. lib_xud .. -``lib_xud`` library requires some flags for correct operation. Firstly the +``lib_xud`` library requires some flags for correct operation. Namely the tile on which ``lib_xud`` will be execute, for example:: - XCC_FLAGS = .. -DUSB_TILE=tile[1] .. - -Secondly, the architecture of the target device, for example:: - - XCC_FLAGS = .. -DXUD_SERIES_SUPPORT=XUD_X200_SERIES .. + XCC_FLAGS = .. -DUSB_TILE=tile[0] .. Includes ........ @@ -52,23 +48,22 @@ be included in your code to use the library. :start-on: include "xua.h" :end-on: include "xud_device.h" -Allocating hardware resources +Allocating Hardware Resources ............................. -A basic implementation of a USB Audio device (i.e. simple stereo input and output via I2S) +A basic implementation of a USB Audio device (i.e. simple stereo output via I2S) using ``lib_xua`` requires the follow pins: - I2S Bit Clock (from xCORE to DAC) - I2S L/R clock (from xCORE to DAC) - I2S Data line (from xCORE to DAC) - - I2S Data line (from ADC to xCORE) - Audio Master clock (from clock source to xCORE) .. note:: This application note assumes xCORE is I2S bus master -On an xCORE the pins are controlled by ``ports``. The application therefore declares various ``ports`` +In the xCORE architecture the I/O pins are controlled and accessed by ``ports``. The application therefore declares various ``ports`` for this purpose: .. literalinclude:: app_xua_simple.xc @@ -76,8 +71,8 @@ for this purpose: :end-on: in port p_mclk_in ``lib_xua`` also requires two ports for internally calculating USB feedback. Please refer to -the ``lib_xua`` library documentation for further details. The additonal input port for the master -clock is required since USB and S/PDIF do not reside of the same tiles on the example hardware. +the ``lib_xua`` library documentation for further details. The additional input port for the master +clock is required since USB and S/PDIF do not reside of the same tiles on the xCORE.ai MC Audio Board. These ports are declared as follows: @@ -95,11 +90,11 @@ Again, for the same reasoning as the master-clock ports, two master-clock clock- - one on each tile. -Other declarations +Other Declarations .................. ``lib_xua`` currently requires the manual declaration of tables for the endpoint types for -``lib_xud`` and the calling the main XUD funtion in a par (``XUD_Main()``). +``lib_xud`` and the calling the main XUD function in a par (``XUD_Main()``). For a simple application the following endpoints are required: @@ -112,12 +107,12 @@ These are declared as follows: :start-on: /* Endpoint type tables :end-on: XUD_EpType epTypeTableIn -The application main() function +The Application main() Function ------------------------------- The ``main()`` function sets up the tasks in the application. -Various channels are required in order to allow the required tasks to communcate. +Various channels are required in order to allow the required tasks to communicate. These must first be declared: .. literalinclude:: app_xua_simple.xc @@ -134,6 +129,9 @@ using the xC ``par`` construct: This code starts the low-level USB task, an Endpoint 0 task, an Audio buffering task and a task to handle the audio I/O (i.e. I2S signalling). +It also runs a small function ``ctrlPort()`` that simply writes some values to an I/O port to configure some external +hardware (it enables analogue power supplies and correctly routes the master clock) and then closes. + Configuration ............. @@ -162,9 +160,10 @@ implentation e.g. master clock frequencies and must be defined. Please see the Demo Hardware Setup ------------------- -To run the demo, connect a USB cable to power the xCORE-200 MC Audio board -and plug the xTAG to the board and connect the xTAG USB cable to your -development machine. +To run the demo, use a USB cable to connect the on-board xTAG debug adapter (marked ``DEBUG``) to your development computer. +Use another USB cable to connect the USB receptacle marked ``USB DEVICE`` to the device you wish to play audio from. + +Plug a device capable of receiving analogue audio (i.e. an amplified speaker) to the 3.5mm jack marked ``OUT 1/2``. .. figure:: images/hw_setup.* :width: 80% @@ -173,40 +172,27 @@ development machine. |newpage| -Launching the demo application +Launching the Demo Application ------------------------------ -Once the demo example has been built either from the command line using xmake or -via the build mechanism of xTIMEcomposer studio it can be executed on the xCORE-200 -MC Audio board. +Once the demo example has been built from the command line using ``xmake`` +it can be executed on the xCORE.ai MC Audio Board. Once built there will be a ``bin/`` directory within the project which contains the binary for the xCORE device. The xCORE binary has a XMOS standard .xe extension. -Launching from the command line +Launching from the Command Line ............................... From the command line you use the ``xrun`` tool to download and run the code on the xCORE device:: - xrun --xscope bin/app_xua_simple.xe + xrun ./bin/app_xua_simple.xe Once this command has executed the application will be running on the -xCORE-200 MC Audio Board +xCORE.ai MC Audio Board -Launching from xTIMEcomposer Studio -................................... - -From xTIMEcomposer Studio use the run mechanism to download code to xCORE device. -Select the xCORE binary from the ``bin/`` directory, right click and go to Run -Configurations. Double click on xCORE application to create a new run configuration, -enable the xSCOPE I/O mode in the dialog box and then -select Run. - -Once this command has executed the application will be running on the -xCORE-200 MC Audio board. - -Running the application +Running the Application ....................... Once running the device will be detected as a USB Audio device - note, Windows operating @@ -237,11 +223,11 @@ References |newpage| -Full source code listing +Full Source Code Listing ------------------------ -Source code for main.xc -....................... +Source Code for app_xua_simple.xc +................................. .. literalinclude:: app_xua_simple.xc :largelisting: diff --git a/examples/AN00246_xua_example/doc/rst/images/hw_setup.jpg b/examples/AN00246_xua_example/doc/rst/images/hw_setup.jpg index 8998805d..6bc1ba74 100644 Binary files a/examples/AN00246_xua_example/doc/rst/images/hw_setup.jpg and b/examples/AN00246_xua_example/doc/rst/images/hw_setup.jpg differ diff --git a/examples/AN00246_xua_example/doc/rst/xdoc.conf b/examples/AN00246_xua_example/doc/rst/xdoc.conf index 37460abf..2404f912 100644 --- a/examples/AN00246_xua_example/doc/rst/xdoc.conf +++ b/examples/AN00246_xua_example/doc/rst/xdoc.conf @@ -1,2 +1,3 @@ -XMOSNEWSTYLE=1 -SOURCE_INCLUDE_DIRS=../../src \ No newline at end of file +XMOSNEWSTYLE=2 +SOURCE_INCLUDE_DIRS=../../src +SPHINX_MASTER_DOC=AN00246_xua_example diff --git a/examples/AN00246_xua_example/src/app_xua_simple.xc b/examples/AN00246_xua_example/src/app_xua_simple.xc index 131a04db..51d55dde 100644 --- a/examples/AN00246_xua_example/src/app_xua_simple.xc +++ b/examples/AN00246_xua_example/src/app_xua_simple.xc @@ -1,13 +1,13 @@ -// Copyright 2017-2021 XMOS LIMITED. +// Copyright 2017-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. /* A very simple *example* of a USB audio application (and as such is un-verified for production) * - * It uses the main blocks from the lib_xua + * It uses the main blocks from the lib_xua * - * - 2 in/ 2 out I2S only + * - 2 channels out I2S only * - No DFU - * - I2S only + * - I2S only * */ @@ -19,7 +19,6 @@ /* Port declarations. Note, the defines come from the xn file */ buffered out port:32 p_i2s_dac[] = {PORT_I2S_DAC0}; /* I2S Data-line(s) */ -buffered in port:32 p_i2s_adc[] = {PORT_I2S_ADC0}; /* I2S Data-line(s) */ buffered out port:32 p_lrclk = PORT_I2S_LRCLK; /* I2S Bit-clock */ buffered out port:32 p_bclk = PORT_I2S_BCLK; /* I2S L/R-clock */ @@ -31,15 +30,22 @@ in port p_for_mclk_count = PORT_MCLK_COUNT; /* Extra port for count in port p_mclk_in_usb = PORT_MCLK_IN_USB; /* Extra master clock input for the USB tile */ /* Clock-block declarations */ -clock clk_audio_bclk = on tile[0]: XS1_CLKBLK_4; /* Bit clock */ -clock clk_audio_mclk = on tile[0]: XS1_CLKBLK_5; /* Master clock */ -clock clk_audio_mclk_usb = on tile[1]: XS1_CLKBLK_1; /* Master clock for USB tile */ +clock clk_audio_bclk = on tile[1]: XS1_CLKBLK_4; /* Bit clock */ +clock clk_audio_mclk = on tile[1]: XS1_CLKBLK_5; /* Master clock */ +clock clk_audio_mclk_usb = on tile[0]: XS1_CLKBLK_1; /* Master clock for USB tile */ /* Endpoint type tables - informs XUD what the transfer types for each Endpoint in use and also * if the endpoint wishes to be informed of USB bus resets */ XUD_EpType epTypeTableOut[] = {XUD_EPTYPE_CTL | XUD_STATUS_ENABLE, XUD_EPTYPE_ISO}; XUD_EpType epTypeTableIn[] = {XUD_EPTYPE_CTL | XUD_STATUS_ENABLE, XUD_EPTYPE_ISO}; +/* Port declarations for I2C to config ADC's */ +on tile[0]: port p_scl = XS1_PORT_1L; +on tile[0]: port p_sda = XS1_PORT_1M; + +/* See hwsupport.xc */ +void ctrlPort(); + int main() { /* Channels for lib_xud */ @@ -51,26 +57,25 @@ int main() /* Channel for audio data between buffering cores and AudioHub/IO core */ chan c_aud; - + /* Channel for communicating control messages from EP0 to the rest of the device (via the buffering cores) */ chan c_aud_ctl; par { - /* Low level USB device layer core */ - on tile[1]: XUD_Main(c_ep_out, 2, c_ep_in, 2, - c_sof, epTypeTableOut, epTypeTableIn, - null, null, -1 , + /* Low level USB device layer core */ + on tile[0]: XUD_Main(c_ep_out, 2, c_ep_in, 2, + c_sof, epTypeTableOut, epTypeTableIn, XUD_SPEED_HS, XUD_PWR_SELF); - + /* Endpoint 0 core from lib_xua */ /* Note, since we are not using many features we pass in null for quite a few params.. */ - on tile[1]: XUA_Endpoint0(c_ep_out[0], c_ep_in[0], c_aud_ctl, null, null, null, null); + on tile[0]: XUA_Endpoint0(c_ep_out[0], c_ep_in[0], c_aud_ctl, null, null, null, null); /* Buffering cores - handles audio data to/from EP's and gives/gets data to/from the audio I/O core */ /* Note, this spawns two cores */ - on tile[1]: { - + on tile[0]: { + /* Connect master-clock clock-block to clock-block pin */ set_clock_src(clk_audio_mclk_usb, p_mclk_in_usb); /* Clock clock-block from mclk pin */ set_port_clock(p_for_mclk_count, clk_audio_mclk_usb); /* Clock the "count" port from the clock block */ @@ -81,9 +86,11 @@ int main() } /* AudioHub/IO core does most of the audio IO i.e. I2S (also serves as a hub for all audio) */ - on tile[0]: XUA_AudioHub(c_aud, clk_audio_mclk, clk_audio_bclk, p_mclk_in, p_lrclk, p_bclk, p_i2s_dac, p_i2s_adc); + on tile[1]: XUA_AudioHub(c_aud, clk_audio_mclk, clk_audio_bclk, p_mclk_in, p_lrclk, p_bclk, p_i2s_dac, null); + + on tile[0]: ctrlPort(); } - + return 0; } diff --git a/examples/AN00246_xua_example/src/cs5368.h b/examples/AN00246_xua_example/src/cs5368.h deleted file mode 100644 index d2de278d..00000000 --- a/examples/AN00246_xua_example/src/cs5368.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2017-2021 XMOS LIMITED. -// This Software is subject to the terms of the XMOS Public Licence: Version 1. -#ifndef _CS5368_H_ -#define _CS5368_H_ - -//Address on I2C bus -#define CS5368_I2C_ADDR (0x4C) - -//Register Addresess -#define CS5368_CHIP_REV 0x00 -#define CS5368_GCTL_MDE 0x01 -#define CS5368_OVFL_ST 0x02 -#define CS5368_OVFL_MSK 0x03 -#define CS5368_HPF_CTRL 0x04 -#define CS5368_PWR_DN 0x06 -#define CS5368_MUTE_CTRL 0x08 -#define CS5368_SDO_EN 0x0a - -#endif /* _CS5368_H_ */ diff --git a/examples/AN00246_xua_example/src/hwsupport.xc b/examples/AN00246_xua_example/src/hwsupport.xc index 7507ed8c..5f019752 100644 --- a/examples/AN00246_xua_example/src/hwsupport.xc +++ b/examples/AN00246_xua_example/src/hwsupport.xc @@ -1,146 +1,56 @@ -// Copyright 2016-2021 XMOS LIMITED. +// Copyright 2017-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. - +#include #include -#include -#include - #include "xua.h" -#include "i2c.h" /* From lib_i2c */ +#include "../../shared/apppll.h" -#include "cs5368.h" -#include "cs4384.h" +on tile[0]: out port p_ctrl = XS1_PORT_8D; -port p_i2c = on tile[0]:XS1_PORT_4A; +/* p_ctrl: + * [0:3] - Unused + * [4] - EN_3v3_N + * [5] - EN_3v3A + * [6] - EXT_PLL_SEL (CS2100:0, SI: 1) + * [7] - MCLK_DIR (Out:0, In: 1) + */ +#define EXT_PLL_SEL__MCLK_DIR (0x80) -/* General output port bit definitions */ -#define P_GPIO_DSD_MODE (1 << 0) /* DSD mode select 0 = 8i/8o I2S, 1 = 8o DSD*/ -#define P_GPIO_DAC_RST_N (1 << 1) -#define P_GPIO_USB_SEL0 (1 << 2) -#define P_GPIO_USB_SEL1 (1 << 3) -#define P_GPIO_VBUS_EN (1 << 4) -#define P_GPIO_PLL_SEL (1 << 5) /* 1 = CS2100, 0 = Phaselink clock source */ -#define P_GPIO_ADC_RST_N (1 << 6) -#define P_GPIO_MCLK_FSEL (1 << 7) /* Select frequency on Phaselink clock. 0 = 24.576MHz for 48k, 1 = 22.5792MHz for 44.1k.*/ - -#define DAC_REGWRITE(reg, val) result = i2c.write_reg(CS4384_I2C_ADDR, reg, val); -#define DAC_REGREAD(reg) data = i2c.read_reg(CS4384_I2C_ADDR, reg, result); -#define ADC_REGWRITE(reg, val) result = i2c.write_reg(CS5368_I2C_ADDR, reg, val); - -out port p_gpio = on tile[0]:XS1_PORT_8C; - -void AudioHwConfig2(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC, client interface i2c_master_if i2c) +/* Note, this runs on Tile[0] */ +void ctrlPort() { - unsigned char gpioVal = 0; - i2c_regop_res_t result; - - /* Set master clock select appropriately and put ADC and DAC into reset */ - if (mClk == MCLK_441) + // Drive control port to turn on 3V3 and set MCLK_DIR + // Note, "soft-start" to reduce current spike + // Note, 3v3_EN is inverted + for (int i = 0; i < 30; i++) { - gpioVal = P_GPIO_USB_SEL0 | P_GPIO_USB_SEL1; + p_ctrl <: EXT_PLL_SEL__MCLK_DIR | 0x30; /* 3v3: off, 3v3A: on */ + delay_microseconds(5); + p_ctrl <: EXT_PLL_SEL__MCLK_DIR | 0x20; /* 3v3: on, 3v3A: on */ + delay_microseconds(5); } - else - { - gpioVal = P_GPIO_USB_SEL0 | P_GPIO_USB_SEL1 | P_GPIO_MCLK_FSEL; - } - - p_gpio <: gpioVal; - - /* Allow MCLK to settle */ - delay_microseconds(20000); - - /* Take ADC out of reset */ - gpioVal |= P_GPIO_ADC_RST_N; - p_gpio <: gpioVal; - - /* Configure ADC for I2S slave mode via I2C */ - unsigned dif = 0, mode = 0; - dif = 0x01; /* I2S */ - mode = 0x03; /* Slave mode all speeds */ - - /* Reg 0x01: (GCTL) Global Mode Control Register - * Bit[7]: CP-EN: Manages control-port mode - * Bit[6]: CLKMODE: Setting puts part in 384x mode - * Bit[5:4]: MDIV[1:0]: Set to 01 for /2 - * Bit[3:2]: DIF[1:0]: Data Format: 0x01 for I2S, 0x02 for TDM - * Bit[1:0]: MODE[1:0]: Mode: 0x11 for slave mode - */ - ADC_REGWRITE(CS5368_GCTL_MDE, 0b10010000 | (dif << 2) | mode); - - - /* Reg 0x06: (PDN) Power Down Register */ - /* Bit[7:6]: Reserved - * Bit[5]: PDN-BG: When set, this bit powers-own the bandgap reference - * Bit[4]: PDM-OSC: Controls power to internal oscillator core - * Bit[3:0]: PDN: When any bit is set all clocks going to that channel pair are turned off - */ - ADC_REGWRITE(CS5368_PWR_DN, 0b00000000); - - /* Configure DAC with PCM values. Note 2 writes to mode control to enable/disable freeze/power down */ - /* Take DAC out of reset */ - gpioVal |= P_GPIO_DAC_RST_N; - p_gpio <: gpioVal; - - delay_microseconds(500); - - /* Mode Control 1 (Address: 0x02) */ - /* bit[7] : Control Port Enable (CPEN) : Set to 1 for enable - * bit[6] : Freeze controls (FREEZE) : Set to 1 for freeze - * bit[5] : PCM/DSD Selection (DSD/PCM) : Set to 0 for PCM - * bit[4:1] : DAC Pair Disable (DACx_DIS) : All Dac Pairs enabled - * bit[0] : Power Down (PDN) : Powered down - */ - DAC_REGWRITE(CS4384_MODE_CTRL, 0b11000001); - - /* PCM Control (Address: 0x03) */ - /* bit[7:4] : Digital Interface Format (DIF) : 0b0001 for I2S up to 24bit - * bit[3:2] : Reserved - * bit[1:0] : Functional Mode (FM) : 0x00 - single-speed mode (4-50kHz) - * : 0x01 - double-speed mode (50-100kHz) - * : 0x10 - quad-speed mode (100-200kHz) - * : 0x11 - auto-speed detect (32 to 200kHz) - * (note, some Mclk/SR ratios not supported in auto) - * - */ - unsigned char regVal = 0; - if(samFreq < 50000) - regVal = 0b00010100; - else if(samFreq < 100000) - regVal = 0b00010101; - else //if(samFreq < 200000) - regVal = 0b00010110; - - DAC_REGWRITE(CS4384_PCM_CTRL, regVal); - - /* Mode Control 1 (Address: 0x02) */ - /* bit[7] : Control Port Enable (CPEN) : Set to 1 for enable - * bit[6] : Freeze controls (FREEZE) : Set to 0 for freeze - * bit[5] : PCM/DSD Selection (DSD/PCM) : Set to 0 for PCM - * bit[4:1] : DAC Pair Disable (DACx_DIS) : All Dac Pairs enabled - * bit[0] : Power Down (PDN) : Not powered down - */ - DAC_REGWRITE(CS4384_MODE_CTRL, 0b10000000); - - /* Kill the i2c task */ - i2c.shutdown(); - return; } +/* Configures the external audio hardware at startup. Note this runs on Tile[1] */ void AudioHwInit() { - /* Set USB Mux to micro-b */ - /* ADC and DAC in reset */ - p_gpio <: P_GPIO_USB_SEL0 | P_GPIO_USB_SEL1; + /* Wait for power supply to come up */ + delay_milliseconds(100); + + /* Use xCORE Secondary PLL to generate *fixed* master clock */ + AppPllEnable_SampleRate(DEFAULT_FREQ); + + delay_milliseconds(100); + + /* DAC setup: For basic I2S input we don't need any register setup. DACs will clock auto detect etc. + * It holds DAC in reset until it gets clocks anyway. + * Note, this example doesn't use the ADC's + */ } -void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, - unsigned sampRes_DAC, unsigned sampRes_ADC) +/* Configures the external audio hardware for the required sample frequency */ +void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC) { - i2c_master_if i2c[1]; - par - { - i2c_master_single_port(i2c, 1, p_i2c, 10, 0, 1, 0); - AudioHwConfig2(samFreq, mClk, dsdMode, sampRes_DAC, sampRes_ADC, i2c[0]); - } + AppPllEnable_SampleRate(samFreq); } diff --git a/examples/AN00246_xua_example/src/xk-audio-216-mc.xn b/examples/AN00246_xua_example/src/xk-audio-216-mc.xn deleted file mode 100644 index c2e90f16..00000000 --- a/examples/AN00246_xua_example/src/xk-audio-216-mc.xn +++ /dev/null @@ -1,88 +0,0 @@ - - - Board - XS2 MC Audio - - tileref tile[2] - tileref usb_tile - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - --> - - - - - - - - - - --> - --> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/AN00246_xua_example/src/xk-audio-316-mc.xn b/examples/AN00246_xua_example/src/xk-audio-316-mc.xn new file mode 100644 index 00000000..8b4334e5 --- /dev/null +++ b/examples/AN00246_xua_example/src/xk-audio-316-mc.xn @@ -0,0 +1,92 @@ + + + Board + xcore.ai MC Audio Board + + + tileref tile[2] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/AN00246_xua_example/src/xua_conf.h b/examples/AN00246_xua_example/src/xua_conf.h index e0a0c36b..0d92b9f7 100644 --- a/examples/AN00246_xua_example/src/xua_conf.h +++ b/examples/AN00246_xua_example/src/xua_conf.h @@ -1,13 +1,13 @@ -// Copyright 2017-2021 XMOS LIMITED. +// Copyright 2017-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. -#ifndef _XUA_CONF_H_ +#ifndef _XUA_CONF_H_ #define _XUA_CONF_H_ #define NUM_USB_CHAN_OUT 2 /* Number of channels from host to device */ -#define NUM_USB_CHAN_IN 2 /* Number of channels from device to host */ +#define NUM_USB_CHAN_IN 0 /* Number of channels from device to host */ #define I2S_CHANS_DAC 2 /* Number of I2S channels out of xCORE */ -#define I2S_CHANS_ADC 2 /* Number of I2S channels in to xCORE */ +#define I2S_CHANS_ADC 0 /* Number of I2S channels in to xCORE */ #define MCLK_441 (512 * 44100) /* 44.1kHz family master clock frequency */ #define MCLK_48 (512 * 48000) /* 48kHz family master clock frequency */ #define MIN_FREQ 48000 /* Minimum sample rate */ @@ -19,7 +19,7 @@ #define VENDOR_ID 0x20B1 #define PRODUCT_STR_A2 "XUA Example" #define PRODUCT_STR_A1 "XUA Example" -#define PID_AUDIO_1 1 +#define PID_AUDIO_1 1 #define PID_AUDIO_2 2 #define XUA_DFU_EN 0 /* Disable DFU (for simplicity of example */ #define MIC_DUAL_ENABLED 0 // Use multi-threaded design diff --git a/examples/AN00247_xua_example_spdif_tx/Makefile b/examples/AN00247_xua_example_spdif_tx/Makefile index cedcb1c7..046f3659 100644 --- a/examples/AN00247_xua_example_spdif_tx/Makefile +++ b/examples/AN00247_xua_example_spdif_tx/Makefile @@ -1,11 +1,13 @@ -APP_NAME = app_xua_simple +APP_NAME = app_xua_spdiftx -TARGET = xk-audio-216-mc.xn +TARGET = xk-audio-316-mc.xn # The flags passed to xcc when building the application -XCC_FLAGS = -fcomment-asm -Xmapper --map -Xmapper MAPFILE -O3 -report -fsubword-select -save-temps \ - -g -Wno-unused-function -Wno-timing -DXUD_SERIES_SUPPORT=XUD_X200_SERIES -DUSB_TILE=tile[1] \ - -DSDA_HIGH=2 -DSCL_HIGH=1 -fxscope +XCC_FLAGS = -O3 -report \ + -g -Wno-unused-function \ + -DXUD_CORE_CLOCK=600 \ + -DUSB_TILE=tile[0] \ + -fxscope # The USED_MODULES variable lists other module used by the application. These # modules will extend the SOURCE_DIRS, INCLUDE_DIRS and LIB_DIRS variables. @@ -18,4 +20,3 @@ USED_MODULES = lib_xua lib_xud lib_spdif XMOS_MAKE_PATH ?= ../.. include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common - diff --git a/examples/AN00247_xua_example_spdif_tx/doc/rst/AN00247.rst b/examples/AN00247_xua_example_spdif_tx/doc/rst/AN00247_xua_example_spdif_tx.rst similarity index 81% rename from examples/AN00247_xua_example_spdif_tx/doc/rst/AN00247.rst rename to examples/AN00247_xua_example_spdif_tx/doc/rst/AN00247_xua_example_spdif_tx.rst index d36ba8e4..79f7cfcc 100644 --- a/examples/AN00247_xua_example_spdif_tx/doc/rst/AN00247.rst +++ b/examples/AN00247_xua_example_spdif_tx/doc/rst/AN00247_xua_example_spdif_tx.rst @@ -8,13 +8,13 @@ Overview Introduction ............ -The XMOS USB Audio (XUA) library provides an implemention of USB Audio Class versions 1.0 and 2.0. +The XMOS USB Audio (XUA) library provides an implementation of USB Audio Class versions 1.0 and 2.0. This application note demonstrates the implementation of a basic USB Audio Device with -S/PDIF transmit functionality the xCORE-200 MC Audio board. +S/PDIF transmit functionality the xCORE.ai Multichannel (MC) Audio board. To reduce complexity this application note does not enable any other audio interfaces other that S/PDIF transmit -(i.e. no I2S). Readers are encouraged to read applicaition note AN00246 in conjunction with this application +(i.e. no I2S). Readers are encouraged to read application note AN00246 in conjunction with this application note. @@ -30,14 +30,11 @@ The Makefile also includes:: USED_MODULES = .. lib_xud .. -``lib_xud`` library requires some flags for correct operation. Firstly the +``lib_xud`` library requires some flags for correct operation. Namely the tile on which ``lib_xud`` will be execute, for example:: - XCC_FLAGS = .. -DUSB_TILE=tile[1] .. + XCC_FLAGS = .. -DUSB_TILE=tile[0] .. -Secondly, the architecture of the target device, for example:: - - XCC_FLAGS = .. -DXUD_SERIES_SUPPORT=XUD_X200_SERIES .. Includes -------- @@ -45,21 +42,21 @@ Includes This application requires the system header that defines XMOS xCORE specific defines for declaring and initialising hardware: -.. literalinclude:: app_xua_simple.xc +.. literalinclude:: app_xua_spdiftx.xc :start-on: include :end-before: include "xua.h" The XUA library functions are defined in ``xua.h``. This header must be included in your code to use the library. -.. literalinclude:: app_xua_simple.xc +.. literalinclude:: app_xua_spdiftx.xc :start-on: include "xua.h" :end-on: include "xud_device.h" The application uses the S/PDIF transmitter from ``lib_spdif``. This header must be included in your code. -.. literalinclude:: app_xua_simple.xc +.. literalinclude:: app_xua_spdiftx.xc :start-on: /* From lib_spdif :end-on: include "spdif.h" @@ -69,7 +66,7 @@ Declarations Allocating hardware resources for lib_xua ......................................... -A minimal implementation of a USB Audio device, without I2S functionalilty, +A minimal implementation of a USB Audio device, without I2S functionality, using ``lib_xua`` requires the follow pins: - Audio Master clock (from clock source to xCORE) @@ -77,23 +74,23 @@ using ``lib_xua`` requires the follow pins: On an xCORE the pins are controlled by ``ports``. The application therefore declares a port for the master clock input signal. -.. literalinclude:: app_xua_simple.xc +.. literalinclude:: app_xua_spdiftx.xc :start-on: /* Lib_xua port declaration :end-on: in port p_mclk_in ``lib_xua`` also requires two ports for internally calculating USB feedback. Please refer to -the ``lib_xua`` library documentation for further details. The additonal input port for the master +the ``lib_xua`` library documentation for further details. The additional input port for the master clock is required since USB and S/PDIF do not reside of the same tiles on the example hardware. These ports are declared as follows: -.. literalinclude:: app_xua_simple.xc +.. literalinclude:: app_xua_spdiftx.xc :start-on: /* Resources for USB feedback :end-on: in port p_mclk_in_usb In addition to ``port`` resources two clock-block resources are also required: -.. literalinclude:: app_xua_simple.xc +.. literalinclude:: app_xua_spdiftx.xc :start-on: /* Clock-block :end-on: clock clk_audio_mclk_usb @@ -106,14 +103,14 @@ Allocating hardware resources for lib_spdif The S/PDIF transmitter requires a single (buffered) 1-bit port: -.. literalinclude:: app_xua_simple.xc +.. literalinclude:: app_xua_spdiftx.xc :start-on: /* Lib_spdif port :end-on: buffered out port This port must be clocked from the audio master clock. This application note chooses to declare an extra clock-block as follows: -.. literalinclude:: app_xua_simple.xc +.. literalinclude:: app_xua_spdiftx.xc :start-on: clock clk_spdif_tx :end-before: /* Lib_xua @@ -126,19 +123,19 @@ Other declarations For a simple application the following endpoints are required: - - ``Control`` enpoint zero + - ``Control`` endpoint zero - ``Isochonous`` endpoint for each direction for audio data to/from the USB host These are declared as follows: -.. literalinclude:: app_xua_simple.xc +.. literalinclude:: app_xua_spdiftx.xc :start-on: /* Endpoint type tables :end-on: XUD_EpType epTypeTableIn Configuring lib_xua ------------------- -``lib_xua`` must be configued to enable S/PDIF Tx functionality. +``lib_xua`` must be configured to enable S/PDIF Tx functionality. ``lib_xua`` has many parameters than can be configured at build time, some examples include: @@ -164,26 +161,26 @@ The application main() function The ``main()`` function sets up the tasks in the application. -Various channels are required in order to allow the required tasks to communcate. +Various channels are required in order to allow the required tasks to communicate. These must first be declared: -.. literalinclude:: app_xua_simple.xc +.. literalinclude:: app_xua_spdiftx.xc :start-on: /* Channels for lib_xud :end-on: chan c_spdif_tx The rest of the ``main()`` function starts all of the tasks in parallel using the xC ``par`` construct: -.. literalinclude:: app_xua_simple.xc +.. literalinclude:: app_xua_spdiftx.xc :start-on: par :end-before: return 0 This code starts the low-level USB task, an Endpoint 0 task, an Audio buffering task and a task to handle -the audio I/O. Note, since there is no I2S funcitonality in this example this task simply forwards samples to the +the audio I/O. Note, since there is no I2S functionality in this example this task simply forwards samples to the SPDIF transmitter task. In addition the ``spdif_tx()`` task is also run. Note that the ``spdif_tx_port_config()`` function is called before a nested ``par`` of ``spdif_tx()`` and ``XUA_AudioHub()``. -This is because of the "shared" nature of ``p_mclk_in`` and avoids a parrallel usage check failure by the XMOS toolchain. +This is because of the "shared" nature of ``p_mclk_in`` and avoids a parallel usage check failure by the XMOS tool-chain. |appendix| |newpage| @@ -191,9 +188,10 @@ This is because of the "shared" nature of ``p_mclk_in`` and avoids a parrallel u Demo Hardware Setup ------------------- -To run the demo, connect a USB cable to power the xCORE-200 MC Audio board -and plug the xTAG to the board and connect the xTAG USB cable to your -development machine. +To run the demo, use a USB cable to connect the on-board xTAG debug adapter (marked DEBUG) to your development computer. +Use another USB cable to connect the USB receptacle marked USB DEVICE to the device you wish to play audio from. + +A device capable of receiving an S/PDIF signal (ie. a speaker) should be connected to COAX TX. .. figure:: images/hw_setup.* :width: 80% @@ -206,7 +204,7 @@ Launching the demo application ------------------------------ Once the demo example has been built either from the command line using xmake or -via the build mechanism of xTIMEcomposer studio it can be executed on the xCORE-200 +via the build mechanism of xTIMEcomposer studio it can be executed on the xCORE.ai MC Audio board. Once built there will be a ``bin/`` directory within the project which contains @@ -221,7 +219,7 @@ on the xCORE device:: xrun --xscope bin/app_xua_simple.xe Once this command has executed the application will be running on the -xCORE-200 MC Audio Board +xCORE.ai MC Audio Board Launching from xTIMEcomposer Studio ................................... @@ -233,7 +231,7 @@ enable the xSCOPE I/O mode in the dialog box and then select Run. Once this command has executed the application will be running on the -xCORE-200 MC Audio board. +xCORE.ai MC Audio board. Running the application ....................... @@ -272,7 +270,7 @@ Full source code listing Source code for main.xc ....................... -.. literalinclude:: app_xua_simple.xc +.. literalinclude:: app_xua_spdiftx.xc :largelisting: |newpage| diff --git a/examples/AN00247_xua_example_spdif_tx/doc/rst/images/hw_setup.jpg b/examples/AN00247_xua_example_spdif_tx/doc/rst/images/hw_setup.jpg index 8998805d..fe397eb8 100644 Binary files a/examples/AN00247_xua_example_spdif_tx/doc/rst/images/hw_setup.jpg and b/examples/AN00247_xua_example_spdif_tx/doc/rst/images/hw_setup.jpg differ diff --git a/examples/AN00247_xua_example_spdif_tx/doc/rst/xdoc.conf b/examples/AN00247_xua_example_spdif_tx/doc/rst/xdoc.conf index 37460abf..7a9d95cf 100644 --- a/examples/AN00247_xua_example_spdif_tx/doc/rst/xdoc.conf +++ b/examples/AN00247_xua_example_spdif_tx/doc/rst/xdoc.conf @@ -1,2 +1,3 @@ -XMOSNEWSTYLE=1 -SOURCE_INCLUDE_DIRS=../../src \ No newline at end of file +XMOSNEWSTYLE=2 +SOURCE_INCLUDE_DIRS=../../src +SPHINX_MASTER_DOC=AN00247_xua_example_spdif_tx diff --git a/examples/AN00247_xua_example_spdif_tx/src/app_xua_simple.xc b/examples/AN00247_xua_example_spdif_tx/src/app_xua_spdiftx.xc similarity index 83% rename from examples/AN00247_xua_example_spdif_tx/src/app_xua_simple.xc rename to examples/AN00247_xua_example_spdif_tx/src/app_xua_spdiftx.xc index 704479b8..9e5600ea 100644 --- a/examples/AN00247_xua_example_spdif_tx/src/app_xua_simple.xc +++ b/examples/AN00247_xua_example_spdif_tx/src/app_xua_spdiftx.xc @@ -1,9 +1,9 @@ -// Copyright 2017-2021 XMOS LIMITED. +// Copyright 2017-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. /* A very simple *example* of a USB audio application (and as such is un-verified for production) * - * It uses the main blocks from the lib_xua + * It uses the main blocks from the lib_xua * * - S/PDIF output only * - No DFU @@ -20,9 +20,9 @@ #include "spdif.h" /* Lib_spdif port declarations. Note, the defines come from the xn file */ -buffered out port:32 p_spdif_tx = PORT_SPDIF_OUT; /* SPDIF transmit port */ +buffered out port:32 p_spdif_tx = PORT_COAX_OUT; /* SPDIF transmit port */ -clock clk_spdif_tx = on tile[0]: XS1_CLKBLK_4; /* Clock block for S/PDIF transmit */ +clock clk_spdif_tx = on tile[1]: XS1_CLKBLK_4; /* Clock block for S/PDIF transmit */ /* Lib_xua port declarations. Note, the defines come from the xn file */ in port p_mclk_in = PORT_MCLK_IN; /* Master clock for the audio IO tile */ @@ -32,14 +32,17 @@ in port p_for_mclk_count = PORT_MCLK_COUNT; /* Extra port for count in port p_mclk_in_usb = PORT_MCLK_IN_USB; /* Extra master clock input for the USB tile */ /* Clock-block declarations */ -clock clk_audio_mclk = on tile[0]: XS1_CLKBLK_5; /* Master clock */ -clock clk_audio_mclk_usb = on tile[1]: XS1_CLKBLK_1; /* Master clock for USB tile */ +clock clk_audio_mclk = on tile[1]: XS1_CLKBLK_5; /* Master clock */ +clock clk_audio_mclk_usb = on tile[0]: XS1_CLKBLK_1; /* Master clock for USB tile */ /* Endpoint type tables - informs XUD what the transfer types for each Endpoint in use and also * if the endpoint wishes to be informed of USB bus resets */ XUD_EpType epTypeTableOut[] = {XUD_EPTYPE_CTL | XUD_STATUS_ENABLE, XUD_EPTYPE_ISO}; XUD_EpType epTypeTableIn[] = {XUD_EPTYPE_CTL | XUD_STATUS_ENABLE, XUD_EPTYPE_ISO}; +/* From hwsupport.h */ +void ctrlPort(); + int main() { /* Channels for lib_xud */ @@ -51,7 +54,7 @@ int main() /* Channel for audio data between buffering cores and AudioHub/IO core */ chan c_aud; - + /* Channel for communicating control messages from EP0 to the rest of the device (via the buffering cores) */ chan c_aud_ctl; @@ -60,17 +63,17 @@ int main() par { - /* Low level USB device layer core */ - on tile[1]: XUD_Main(c_ep_out, 2, c_ep_in, 2, c_sof, epTypeTableOut, epTypeTableIn, null, null, -1, XUD_SPEED_HS, XUD_PWR_SELF); - + /* Low level USB device layer core */ + on tile[0]: XUD_Main(c_ep_out, 2, c_ep_in, 2, c_sof, epTypeTableOut, epTypeTableIn, XUD_SPEED_HS, XUD_PWR_SELF); + /* Endpoint 0 core from lib_xua */ /* Note, since we are not using many features we pass in null for quite a few params.. */ - on tile[1]: XUA_Endpoint0(c_ep_out[0], c_ep_in[0], c_aud_ctl, null, null, null, null); + on tile[0]: XUA_Endpoint0(c_ep_out[0], c_ep_in[0], c_aud_ctl, null, null, null, null); /* Buffering cores - handles audio data to/from EP's and gives/gets data to/from the audio I/O core */ /* Note, this spawns two cores */ - on tile[1]: { - + on tile[0]: { + /* Connect master-clock clock-block to clock-block pin */ set_clock_src(clk_audio_mclk_usb, p_mclk_in_usb); /* Clock clock-block from mclk pin */ set_port_clock(p_for_mclk_count, clk_audio_mclk_usb); /* Clock the "count" port from the clock block */ @@ -78,28 +81,32 @@ int main() XUA_Buffer(c_ep_out[1], c_ep_in[1], c_sof, c_aud_ctl, p_for_mclk_count, c_aud); } - + /* AudioHub() (I2S) and S/SPDIF Tx are on the same tile */ - on tile[0]: { + on tile[1]: { /* Setup S/PDIF tx port from clock etc - note we do this before par to avoid parallel usage */ spdif_tx_port_config(p_spdif_tx, clk_spdif_tx, p_mclk_in, 7); - + + start_clock(clk_spdif_tx); + par { while(1) { /* Run the S/PDIF transmitter task */ - spdif_tx(p_spdif_tx, c_spdif_tx); + spdif_tx(p_spdif_tx, c_spdif_tx); } - + /* AudioHub/IO core does most of the audio IO i.e. I2S (also serves as a hub for all audio) */ /* Note, since we are not using I2S we pass in null for LR and Bit clock ports and the I2S dataline ports */ XUA_AudioHub(c_aud, clk_audio_mclk, null, p_mclk_in, null, null, null, null, c_spdif_tx); } } + + on tile[0]: ctrlPort(); } - + return 0; } diff --git a/examples/AN00247_xua_example_spdif_tx/src/hwsupport.xc b/examples/AN00247_xua_example_spdif_tx/src/hwsupport.xc index 203fa38e..5f019752 100644 --- a/examples/AN00247_xua_example_spdif_tx/src/hwsupport.xc +++ b/examples/AN00247_xua_example_spdif_tx/src/hwsupport.xc @@ -1,53 +1,56 @@ -// Copyright 2016-2021 XMOS LIMITED. +// Copyright 2017-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. - +#include #include -#include - #include "xua.h" +#include "../../shared/apppll.h" -/* General output port bit definitions */ -#define P_GPIO_DSD_MODE (1 << 0) /* DSD mode select 0 = 8i/8o I2S, 1 = 8o DSD*/ -#define P_GPIO_DAC_RST_N (1 << 1) -#define P_GPIO_USB_SEL0 (1 << 2) -#define P_GPIO_USB_SEL1 (1 << 3) -#define P_GPIO_VBUS_EN (1 << 4) -#define P_GPIO_PLL_SEL (1 << 5) /* 1 = CS2100, 0 = Phaselink clock source */ -#define P_GPIO_ADC_RST_N (1 << 6) -#define P_GPIO_MCLK_FSEL (1 << 7) /* Select frequency on Phaselink clock. 0 = 24.576MHz for 48k, 1 = 22.5792MHz for 44.1k.*/ +on tile[0]: out port p_ctrl = XS1_PORT_8D; +/* p_ctrl: + * [0:3] - Unused + * [4] - EN_3v3_N + * [5] - EN_3v3A + * [6] - EXT_PLL_SEL (CS2100:0, SI: 1) + * [7] - MCLK_DIR (Out:0, In: 1) + */ +#define EXT_PLL_SEL__MCLK_DIR (0x80) -out port p_gpio = on tile[0]:XS1_PORT_8C; - -void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, - unsigned sampRes_DAC, unsigned sampRes_ADC) +/* Note, this runs on Tile[0] */ +void ctrlPort() { - unsigned char data[1] = {0}; - unsigned char gpioVal = 0; - - /* Set master clock select appropriately and put ADC and DAC into reset */ - if (mClk == MCLK_441) + // Drive control port to turn on 3V3 and set MCLK_DIR + // Note, "soft-start" to reduce current spike + // Note, 3v3_EN is inverted + for (int i = 0; i < 30; i++) { - gpioVal = P_GPIO_USB_SEL0 | P_GPIO_USB_SEL1; + p_ctrl <: EXT_PLL_SEL__MCLK_DIR | 0x30; /* 3v3: off, 3v3A: on */ + delay_microseconds(5); + p_ctrl <: EXT_PLL_SEL__MCLK_DIR | 0x20; /* 3v3: on, 3v3A: on */ + delay_microseconds(5); } - else - { - gpioVal = P_GPIO_USB_SEL0 | P_GPIO_USB_SEL1 | P_GPIO_MCLK_FSEL; - } - - /* Note, DAC and ADC held in reset */ - p_gpio <: gpioVal; - - /* Allow MCLK to settle */ - delay_microseconds(20000); - - return; } +/* Configures the external audio hardware at startup. Note this runs on Tile[1] */ void AudioHwInit() { - /* Set USB Mux to micro-b */ - /* ADC and DAC in reset */ - p_gpio <: P_GPIO_USB_SEL0 | P_GPIO_USB_SEL1; + /* Wait for power supply to come up */ + delay_milliseconds(100); + + /* Use xCORE Secondary PLL to generate *fixed* master clock */ + AppPllEnable_SampleRate(DEFAULT_FREQ); + + delay_milliseconds(100); + + /* DAC setup: For basic I2S input we don't need any register setup. DACs will clock auto detect etc. + * It holds DAC in reset until it gets clocks anyway. + * Note, this example doesn't use the ADC's + */ +} + +/* Configures the external audio hardware for the required sample frequency */ +void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC) +{ + AppPllEnable_SampleRate(samFreq); } diff --git a/examples/AN00247_xua_example_spdif_tx/src/xk-audio-216-mc.xn b/examples/AN00247_xua_example_spdif_tx/src/xk-audio-216-mc.xn deleted file mode 100644 index c2e90f16..00000000 --- a/examples/AN00247_xua_example_spdif_tx/src/xk-audio-216-mc.xn +++ /dev/null @@ -1,88 +0,0 @@ - - - Board - XS2 MC Audio - - tileref tile[2] - tileref usb_tile - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> - --> - - - - - - - - - - --> - --> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/AN00247_xua_example_spdif_tx/src/xk-audio-316-mc.xn b/examples/AN00247_xua_example_spdif_tx/src/xk-audio-316-mc.xn new file mode 100644 index 00000000..9da4b479 --- /dev/null +++ b/examples/AN00247_xua_example_spdif_tx/src/xk-audio-316-mc.xn @@ -0,0 +1,92 @@ + + + Board + xcore.ai MC Audio Board + + + tileref tile[2] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/AN00247_xua_example_spdif_tx/src/xua_conf.h b/examples/AN00247_xua_example_spdif_tx/src/xua_conf.h index 8fee8c82..6459cb7f 100644 --- a/examples/AN00247_xua_example_spdif_tx/src/xua_conf.h +++ b/examples/AN00247_xua_example_spdif_tx/src/xua_conf.h @@ -1,32 +1,31 @@ -// Copyright 2017-2021 XMOS LIMITED. +// Copyright 2017-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. -#ifndef _XUA_CONF_H_ +#ifndef _XUA_CONF_H_ #define _XUA_CONF_H_ -#define NUM_USB_CHAN_OUT 2 -#define NUM_USB_CHAN_IN 0 -#define I2S_CHANS_DAC 0 -#define I2S_CHANS_ADC 0 -#define MCLK_441 (512 * 44100) -#define MCLK_48 (512 * 48000) -#define MIN_FREQ 48000 -#define MAX_FREQ 48000 +#define NUM_USB_CHAN_OUT (2) +#define NUM_USB_CHAN_IN (0) +#define I2S_CHANS_DAC (0) +#define I2S_CHANS_ADC (0) +#define MCLK_441 (512 * 44100) +#define MCLK_48 (512 * 48000) +#define MIN_FREQ (48000) +#define MAX_FREQ (48000) #define EXCLUDE_USB_AUDIO_MAIN -#define XUA_SPDIF_TX_EN 1 -#define SPDIF_TX_INDEX 0 -#define VENDOR_STR "XMOS" -#define VENDOR_ID 0x20B1 -#define PRODUCT_STR_A2 "XUA SPDIF Example" -#define PRODUCT_STR_A1 "XUA SPDIF Example" -#define PID_AUDIO_1 1 -#define PID_AUDIO_2 2 -#define AUDIO_CLASS 2 -#define AUDIO_CLASS_FALLBACK 0 -#define BCD_DEVICE 0x1234 -#define XUA_DFU_EN 0 -#define MIC_DUAL_ENABLED 0 // Use multi-threaded design +#define XUA_SPDIF_TX_EN (1) +#define SPDIF_TX_INDEX (0) +#define VENDOR_STR "XMOS" +#define VENDOR_ID 0x20B1 +#define PRODUCT_STR_A2 "XUA SPDIF Example" +#define PRODUCT_STR_A1 "XUA SPDIF Example" +#define PID_AUDIO_1 (1) +#define PID_AUDIO_2 (2) +#define AUDIO_CLASS (2) +#define AUDIO_CLASS_FALLBACK (0) +#define BCD_DEVICE (0x1234) +#define XUA_DFU_EN (0) #endif diff --git a/examples/AN00247_xua_example_spdif_tx/src/xud_conf.h b/examples/AN00247_xua_example_spdif_tx/src/xud_conf.h deleted file mode 100644 index 3fc13baf..00000000 --- a/examples/AN00247_xua_example_spdif_tx/src/xud_conf.h +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2017-2021 XMOS LIMITED. -// This Software is subject to the terms of the XMOS Public Licence: Version 1. - -#include "xua_conf.h" - -/* TODO */ -#define XUD_UAC_NUM_USB_CHAN_OUT NUM_USB_CHAN_OUT -#define XUD_UAC_NUM_USB_CHAN_IN NUM_USB_CHAN_IN diff --git a/examples/AN00248_xua_example_pdm_mics/Makefile b/examples/AN00248_xua_example_pdm_mics/Makefile index 8681d0b5..b43c1925 100644 --- a/examples/AN00248_xua_example_pdm_mics/Makefile +++ b/examples/AN00248_xua_example_pdm_mics/Makefile @@ -3,9 +3,10 @@ APP_NAME = app_xua_simple TARGET = mic_array_ref.xn # The flags passed to xcc when building the application -XCC_FLAGS = -fcomment-asm -Xmapper --map -Xmapper MAPFILE -O3 -report -fsubword-select -save-temps \ - -g -Wno-unused-function -Wno-timing -DXUD_SERIES_SUPPORT=XUD_X200_SERIES -DUSB_TILE=tile[1] \ - -DSDA_HIGH=2 -DSCL_HIGH=1 -fxscope +XCC_FLAGS = -fcomment-asm -Xmapper --map -Xmapper MAPFILE -O3 -report -save-temps \ + -g -Wno-unused-function -Wno-timing -DXUD_SERIES_SUPPORT=XUD_X200_SERIES \ + -DXUD_CORE_CLOCK=600 -DUSB_TILE=tile[1] -DSDA_HIGH=2 -DSCL_HIGH=1 -fxscope \ + -DUAC_FORCE_FEEDBACK_EP=0 # The USED_MODULES variable lists other module used by the application. These # modules will extend the SOURCE_DIRS, INCLUDE_DIRS and LIB_DIRS variables. @@ -18,4 +19,3 @@ USED_MODULES = lib_xua lib_xud lib_mic_array XMOS_MAKE_PATH ?= ../.. include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common - diff --git a/examples/AN00248_xua_example_pdm_mics/doc/rst/AN00248.rst b/examples/AN00248_xua_example_pdm_mics/doc/rst/AN00248_xua_example_pdm_mics.rst similarity index 96% rename from examples/AN00248_xua_example_pdm_mics/doc/rst/AN00248.rst rename to examples/AN00248_xua_example_pdm_mics/doc/rst/AN00248_xua_example_pdm_mics.rst index fb8b963d..2541855a 100644 --- a/examples/AN00248_xua_example_pdm_mics/doc/rst/AN00248.rst +++ b/examples/AN00248_xua_example_pdm_mics/doc/rst/AN00248_xua_example_pdm_mics.rst @@ -36,15 +36,11 @@ The Makefile therefore also includes this lib:: USED_MODULES = .. lib_xud .. -``lib_xud`` library requires some flags for correct operation. Firstly the +``lib_xud`` library requires some flags for correct operation. Namely the tile on which ``lib_xud`` will be executed, for example:: XCC_FLAGS = .. -DUSB_TILE=tile[1] .. -Secondly, the architecture of the target device, for example:: - - XCC_FLAGS = .. -DXUD_SERIES_SUPPORT=XUD_X200_SERIES .. - Includes -------- @@ -228,7 +224,7 @@ The ``mic_array_pdm_rx()`` task expects the PDM microphone port to be clocked fr Demo Hardware Setup ------------------- -To run the demo, connect a USB cable to power the xCORE-200 MC Audio board +To run the demo, connect a USB cable to power the xCORE-200 Array Microphone board and plug the xTAG to the board and connect the xTAG USB cable to your development machine. @@ -244,7 +240,7 @@ Launching the demo application Once the demo example has been built either from the command line using xmake or via the build mechanism of xTIMEcomposer studio it can be executed on the xCORE-200 -MC Audio board. +Array Microphone board. Once built there will be a ``bin/`` directory within the project which contains the binary for the xCORE device. The xCORE binary has a XMOS standard .xe extension. @@ -258,7 +254,7 @@ on the xCORE device:: xrun --xscope bin/app_xua_simple.xe Once this command has executed the application will be running on the -xCORE-200 MC Audio Board +xCORE-200 Array Microphone board Launching from xTIMEcomposer Studio ................................... @@ -270,7 +266,7 @@ enable the xSCOPE I/O mode in the dialog box and then select Run. Once this command has executed the application will be running on the -xCORE-200 MC Audio board. +xCORE-200 Array Microphone board. Running the application ....................... diff --git a/examples/AN00248_xua_example_pdm_mics/doc/rst/xdoc.conf b/examples/AN00248_xua_example_pdm_mics/doc/rst/xdoc.conf index 37460abf..d43a7c1a 100644 --- a/examples/AN00248_xua_example_pdm_mics/doc/rst/xdoc.conf +++ b/examples/AN00248_xua_example_pdm_mics/doc/rst/xdoc.conf @@ -1,2 +1,3 @@ -XMOSNEWSTYLE=1 -SOURCE_INCLUDE_DIRS=../../src \ No newline at end of file +XMOSNEWSTYLE=2 +SOURCE_INCLUDE_DIRS=../../src +SPHINX_MASTER_DOC=AN00248_xua_example_pdm_mics diff --git a/examples/AN00248_xua_example_pdm_mics/src/app_xua_simple.xc b/examples/AN00248_xua_example_pdm_mics/src/app_xua_simple.xc index 6b7b299e..d3b4c085 100644 --- a/examples/AN00248_xua_example_pdm_mics/src/app_xua_simple.xc +++ b/examples/AN00248_xua_example_pdm_mics/src/app_xua_simple.xc @@ -1,9 +1,9 @@ -// Copyright 2017-2021 XMOS LIMITED. +// Copyright 2017-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. /* A very simple *example* of a USB audio application (and as such is un-verified for production) * - * It uses the main blocks from the lib_xua with the addition of PDM mic support using lib_mic_array + * It uses the main blocks from the lib_xua with the addition of PDM mic support using lib_mic_array * * - No DFU * @@ -24,7 +24,7 @@ in port p_pdm_mclk = PORT_PDM_MCLK; /* Master clock f in buffered port:32 p_pdm_mics = PORT_PDM_DATA; /* Port for PDM mic data */ -clock clk_pdm = on tile[0]: XS1_CLKBLK_1; /* Clock-block for PDM mics */ +clock clk_pdm = on tile[0]: XS1_CLKBLK_1; /* Clock-block for PDM mics */ /* Lib_xua port declarations. Note, the defines come from the xn file */ @@ -52,7 +52,7 @@ int main() /* Channel for audio data between buffering cores and AudioHub/IO core */ chan c_aud; - + /* Channel for communicating control messages from EP0 to the rest of the device (via the buffering cores) */ chan c_aud_ctl; @@ -64,26 +64,26 @@ int main() par { - /* Low level USB device layer core */ - on tile[1]: XUD_Main(c_ep_out, 2, c_ep_in, 2, c_sof, epTypeTableOut, epTypeTableIn, null, null, -1, XUD_SPEED_HS, XUD_PWR_BUS); - + /* Low level USB device layer core */ + on tile[1]: XUD_Main(c_ep_out, 2, c_ep_in, 2, c_sof, epTypeTableOut, epTypeTableIn, XUD_SPEED_HS, XUD_PWR_BUS); + /* Endpoint 0 core from lib_xua */ /* Note, since we are not using many features we pass in null for quite a few params.. */ on tile[1]: XUA_Endpoint0(c_ep_out[0], c_ep_in[0], c_aud_ctl, null, null, null, null); - on tile[1]: + on tile[1]: { /* Connect master-clock clock-block to clock-block pin */ set_clock_src(clk_audio_mclk, p_mclk_in); /* Clock clock-block from mclk pin */ set_port_clock(p_for_mclk_count, clk_audio_mclk); /* Clock the "count" port from the clock block */ /* Note, AudioHub() will start the clock */ - + par { /* Buffering task - handles audio data to/from EP's and gives/gets data to/from the audio I/O core */ /* Note, this spawns two cores */ XUA_Buffer(c_ep_out[1], c_ep_in[1], c_sof, c_aud_ctl, p_for_mclk_count, c_aud); - + /* AudioHub/IO core does most of the audio IO i.e. I2S (also serves as a hub for all audio) */ /* Note, since we are not using I2S we pass in null for LR and Bit clock ports and the I2S dataline ports */ XUA_AudioHub(c_aud, clk_audio_mclk, null, p_mclk_in, null, null, null, null, c_mic_pcm); @@ -105,7 +105,7 @@ int main() { /* PDM receive I/O task */ mic_array_pdm_rx(p_pdm_mics, c_4x_pdm_mic_0, c_4x_pdm_mic_1); - + /* Run two decimator tasks for 8 mics */ mic_array_decimate_to_pcm_4ch(c_4x_pdm_mic_0, c_ds_output[0], MIC_ARRAY_NO_INTERNAL_CHANS); mic_array_decimate_to_pcm_4ch(c_4x_pdm_mic_1, c_ds_output[1], MIC_ARRAY_NO_INTERNAL_CHANS); @@ -114,7 +114,7 @@ int main() } } } - + return 0; } diff --git a/examples/AN00248_xua_example_pdm_mics/src/audiohw.xc b/examples/AN00248_xua_example_pdm_mics/src/audiohw.xc index 288b2d0b..0dea7a46 100644 --- a/examples/AN00248_xua_example_pdm_mics/src/audiohw.xc +++ b/examples/AN00248_xua_example_pdm_mics/src/audiohw.xc @@ -1,4 +1,4 @@ -// Copyright 2017-2021 XMOS LIMITED. +// Copyright 2017-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #include @@ -16,7 +16,7 @@ void AudioHwInit() /* DAC in reset */ p_gpio <: 0; - return; + return; } /* Configures the external audio hardware for the required sample frequency */ diff --git a/examples/AN00248_xua_example_pdm_mics/src/hid_report_descriptor.h b/examples/AN00248_xua_example_pdm_mics/src/hid_report_descriptor.h new file mode 100644 index 00000000..70e01717 --- /dev/null +++ b/examples/AN00248_xua_example_pdm_mics/src/hid_report_descriptor.h @@ -0,0 +1,168 @@ +// Copyright 2021-2022 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#ifndef __hid_report_descriptor_h__ +#define __hid_report_descriptor_h__ + +#include "xua_hid_report.h" + +#if 0 +/* Existing static report descriptor kept for reference */ +unsigned char hidReportDescriptor[] = +{ + 0x05, 0x0c, /* Usage Page (Consumer Device) */ + 0x09, 0x01, /* Usage (Consumer Control) */ + 0xa1, 0x01, /* Collection (Application) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x25, 0x01, /* Logical Maximum (1) */ + 0x09, 0xb0, /* Usage (Play) */ + 0x09, 0xb5, /* Usage (Scan Next Track) */ + 0x09, 0xb6, /* Usage (Scan Previous Track) */ + 0x09, 0xe9, /* Usage (Volume Up) */ + 0x09, 0xea, /* Usage (Volume Down) */ + 0x09, 0xe2, /* Usage (Mute) */ + 0x75, 0x01, /* Report Size (1) */ + 0x95, 0x06, /* Report Count (6) */ + 0x81, 0x02, /* Input (Data, Var, Abs) */ + 0x95, 0x02, /* Report Count (2) */ + 0x81, 0x01, /* Input (Cnst, Ary, Abs) */ + 0xc0 /* End collection */ +}; +#endif + +/* + * Define non-configurable items in the HID Report descriptor. + */ +static const USB_HID_Short_Item_t hidCollectionApplication = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_MAIN, HID_REPORT_ITEM_TAG_COLLECTION), + .data = { 0x01, 0x00 } }; +static const USB_HID_Short_Item_t hidCollectionEnd = { + .header = HID_REPORT_SET_HEADER(0, HID_REPORT_ITEM_TYPE_MAIN, HID_REPORT_ITEM_TAG_END_COLLECTION), + .data = { 0x00, 0x00 } }; + +static const USB_HID_Short_Item_t hidInputConstArray = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_MAIN, HID_REPORT_ITEM_TAG_INPUT), + .data = { 0x01, 0x00 } }; +static const USB_HID_Short_Item_t hidInputDataVar = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_MAIN, HID_REPORT_ITEM_TAG_INPUT), + .data = { 0x02, 0x00 } }; + +static const USB_HID_Short_Item_t hidLogicalMaximum0 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_LOGICAL_MAXIMUM), + .data = { 0x00, 0x00 } }; +static const USB_HID_Short_Item_t hidLogicalMaximum1 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_LOGICAL_MAXIMUM), + .data = { 0x01, 0x00 } }; +static const USB_HID_Short_Item_t hidLogicalMinimum0 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_LOGICAL_MINIMUM), + .data = { 0x00, 0x00 } }; + +static const USB_HID_Short_Item_t hidReportCount2 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_COUNT), + .data = { 0x02, 0x00 } }; +static const USB_HID_Short_Item_t hidReportCount6 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_COUNT), + .data = { 0x06, 0x00 } }; +static const USB_HID_Short_Item_t hidReportSize1 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_SIZE), + .data = { 0x01, 0x00 } }; + +static const USB_HID_Short_Item_t hidUsageConsumerControl = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .data = { 0x01, 0x00 } }; + +/* + * Define the HID Report Descriptor Item, Usage Page, Report ID and length for each HID Report + * For internal purposes, a report element with ID of 0 must be included if report IDs are not being used. + */ +static const USB_HID_Report_Element_t hidReportPageConsumer = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_USAGE_PAGE), + .item.data = { USB_HID_USAGE_PAGE_ID_CONSUMER, 0x00 }, + .location = HID_REPORT_SET_LOC( 0, 2, 0, 0 ) +}; + +/* + * Define configurable items in the HID Report descriptor. + */ +static USB_HID_Report_Element_t hidUsageByte0Bit5 = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .item.data = { 0xE2, 0x00 }, + .location = HID_REPORT_SET_LOC(0, 0, 0, 5) +}; // Mute +static USB_HID_Report_Element_t hidUsageByte0Bit4 = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .item.data = { 0xEA, 0x00 }, + .location = HID_REPORT_SET_LOC(0, 0, 0, 4) +}; // Vol- +static USB_HID_Report_Element_t hidUsageByte0Bit3 = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .item.data = { 0xE9, 0x00 }, + .location = HID_REPORT_SET_LOC(0, 0, 0, 3) +}; // Vol+ +static USB_HID_Report_Element_t hidUsageByte0Bit2 = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .item.data = { 0xB6, 0x00 }, + .location = HID_REPORT_SET_LOC(0, 0, 0, 2) +}; // Scan Prev +static USB_HID_Report_Element_t hidUsageByte0Bit1 = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .item.data = { 0xB5, 0x00 }, + .location = HID_REPORT_SET_LOC(0, 0, 0, 1) +}; // Scan Next +static USB_HID_Report_Element_t hidUsageByte0Bit0 = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .item.data = { 0xB0, 0x00 }, + .location = HID_REPORT_SET_LOC(0, 0, 0, 0) +}; // Play + +/* + * List the configurable elements in the HID Report descriptor. + */ +static USB_HID_Report_Element_t* const hidConfigurableElements[] = { + &hidUsageByte0Bit0, + &hidUsageByte0Bit1, + &hidUsageByte0Bit2, + &hidUsageByte0Bit3, + &hidUsageByte0Bit4, + &hidUsageByte0Bit5 +}; + +/* + * List HID Reports, one per Report ID. This should be a usage page item with the relevant + * If not using report IDs - still have one with report ID 0 + */ +static const USB_HID_Report_Element_t* const hidReports[] = { + &hidReportPageConsumer +}; + +/* + * List all items in the HID Report descriptor. + */ +static const USB_HID_Short_Item_t* const hidReportDescriptorItems[] = { + &(hidReportPageConsumer.item), + &hidUsageConsumerControl, + &hidCollectionApplication, + &hidLogicalMinimum0, + &hidLogicalMaximum1, + &(hidUsageByte0Bit0.item), + &(hidUsageByte0Bit1.item), + &(hidUsageByte0Bit2.item), + &(hidUsageByte0Bit3.item), + &(hidUsageByte0Bit4.item), + &(hidUsageByte0Bit5.item), + &hidReportSize1, + &hidReportCount6, + &hidInputDataVar, + &hidLogicalMaximum0, + &hidReportCount2, + &hidInputConstArray, + &hidCollectionEnd +}; + +/* + * Define the number of HID Reports + * Due to XC not supporting designated initializers, this constant has a hard-coded value. + * It must equal ( sizeof hidReports / sizeof ( USB_HID_Report_Element_t* )) + */ +#define HID_REPORT_COUNT ( 1 ) + +#endif // __hid_report_descriptor_h__ diff --git a/examples/AN00248_xua_example_pdm_mics/src/mic_array_ref.xn b/examples/AN00248_xua_example_pdm_mics/src/mic_array_ref.xn index 66caa132..a9e1b9b4 100644 --- a/examples/AN00248_xua_example_pdm_mics/src/mic_array_ref.xn +++ b/examples/AN00248_xua_example_pdm_mics/src/mic_array_ref.xn @@ -81,7 +81,7 @@ - + diff --git a/examples/AN00248_xua_example_pdm_mics/src/xua_conf.h b/examples/AN00248_xua_example_pdm_mics/src/xua_conf.h index 191d8953..7f181048 100644 --- a/examples/AN00248_xua_example_pdm_mics/src/xua_conf.h +++ b/examples/AN00248_xua_example_pdm_mics/src/xua_conf.h @@ -1,7 +1,7 @@ -// Copyright 2017-2021 XMOS LIMITED. +// Copyright 2017-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. -#ifndef _XUA_CONF_H_ +#ifndef _XUA_CONF_H_ #define _XUA_CONF_H_ #define NUM_USB_CHAN_OUT 0 diff --git a/examples/shared/apppll.h b/examples/shared/apppll.h new file mode 100644 index 00000000..1312c0ab --- /dev/null +++ b/examples/shared/apppll.h @@ -0,0 +1,109 @@ +// Copyright 2022 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#include +#include "xassert.h" + +// App PLL setup +#define APP_PLL_CTL_BYPASS (0) // 0 = no bypass, 1 = bypass. +#define APP_PLL_CTL_INPUT_SEL (0) // 0 = XTAL, 1 = sysPLL +#define APP_PLL_CTL_ENABLE (1) // 0 = disabled, 1 = enabled. + +// 24MHz in, 24.576MHz out, integer mode +// Found exact solution: IN 24000000.0, OUT 24576000.0, VCO 2457600000.0, RD 5, FD 512, OD 10, FOD 10 +#define APP_PLL_CTL_OD_48 (4) // Output divider = (OD+1) +#define APP_PLL_CTL_F_48 (511) // FB divider = (F+1)/2 +#define APP_PLL_CTL_R_48 (4) // Ref divider = (R+1) + +#define APP_PLL_CTL_48 ((APP_PLL_CTL_BYPASS << 29) | (APP_PLL_CTL_INPUT_SEL << 28) | (APP_PLL_CTL_ENABLE << 27) |\ + (APP_PLL_CTL_OD_48 << 23) | (APP_PLL_CTL_F_48 << 8) | APP_PLL_CTL_R_48) + +// Fractional divide is M/N +#define APP_PLL_FRAC_EN_48 (0) // 0 = disabled +#define APP_PLL_FRAC_NPLUS1_CYCLES_48 (0) // M value is this reg value + 1. +#define APP_PLL_FRAC_TOTAL_CYCLES_48 (0) // N value is this reg value + 1. +#define APP_PLL_FRAC_48 ((APP_PLL_FRAC_EN_48 << 31) | (APP_PLL_FRAC_NPLUS1_CYCLES_48 << 8) | APP_PLL_FRAC_TOTAL_CYCLES_48) + +// 24MHz in, 22.5792MHz out (44.1kHz * 512), frac mode +// Found exact solution: IN 24000000.0, OUT 22579200.0, VCO 2257920000.0, RD 5, FD 470.400 (m = 2, n = 5), OD 5, FOD 10 +#define APP_PLL_CTL_OD_441 (4) // Output divider = (OD+1) +#define APP_PLL_CTL_F_441 (469) // FB divider = (F+1)/2 +#define APP_PLL_CTL_R_441 (4) // Ref divider = (R+1) + +#define APP_PLL_CTL_441 ((APP_PLL_CTL_BYPASS << 29) | (APP_PLL_CTL_INPUT_SEL << 28) | (APP_PLL_CTL_ENABLE << 27) |\ + (APP_PLL_CTL_OD_441 << 23) | (APP_PLL_CTL_F_441 << 8) | APP_PLL_CTL_R_441) + +#define APP_PLL_FRAC_EN_44 (1) // 1 = enabled +#define APP_PLL_FRAC_NPLUS1_CYCLES_44 (1) // M value is this reg value + 1. +#define APP_PLL_FRAC_TOTAL_CYCLES_44 (4) // N value is this reg value + 1.define APP_PLL_CTL_R_441 (4) // Ref divider = (R+1) +#define APP_PLL_FRAC_44 ((APP_PLL_FRAC_EN_44 << 31) | (APP_PLL_FRAC_NPLUS1_CYCLES_44 << 8) | APP_PLL_FRAC_TOTAL_CYCLES_44) + +#define APP_PLL_DIV_INPUT_SEL (1) // 0 = sysPLL, 1 = app_PLL +#define APP_PLL_DIV_DISABLE (0) // 1 = disabled (pin connected to X1D11), 0 = enabled divider output to pin. +#define APP_PLL_DIV_VALUE (4) // Divide by N+1 - remember there's a /2 also afterwards for 50/50 duty cycle. +#define APP_PLL_DIV ((APP_PLL_DIV_INPUT_SEL << 31) | (APP_PLL_DIV_DISABLE << 16) | APP_PLL_DIV_VALUE) + +/* TODO support more than two freqs..*/ +void AppPllEnable(int32_t clkFreq_hz) +{ + switch(clkFreq_hz) + { + case 44100*512: + + // Disable the PLL + write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, (APP_PLL_CTL_441 & 0xF7FFFFFF)); + // Enable the PLL to invoke a reset on the appPLL. + write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, APP_PLL_CTL_441); + // Must write the CTL register twice so that the F and R divider values are captured using a running clock. + write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, APP_PLL_CTL_441); + // Now disable and re-enable the PLL so we get the full 5us reset time with the correct F and R values. + write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, (APP_PLL_CTL_441 & 0xF7FFFFFF)); + write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, APP_PLL_CTL_441); + + // Set the fractional divider if used + write_node_config_reg(tile[0], XS1_SSWITCH_SS_APP_PLL_FRAC_N_DIVIDER_NUM, APP_PLL_FRAC_44); + + break; + + case 48000*512: + + // Disable the PLL + write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, (APP_PLL_CTL_48 & 0xF7FFFFFF)); + // Enable the PLL to invoke a reset on the appPLL. + write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, APP_PLL_CTL_48); + // Must write the CTL register twice so that the F and R divider values are captured using a running clock. + write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, APP_PLL_CTL_48); + // Now disable and re-enable the PLL so we get the full 5us reset time with the correct F and R values. + write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, (APP_PLL_CTL_48 & 0xF7FFFFFF)); + write_node_config_reg(tile[1], XS1_SSWITCH_SS_APP_PLL_CTL_NUM, APP_PLL_CTL_48); + + // Set the fractional divider if used + write_node_config_reg(tile[0], XS1_SSWITCH_SS_APP_PLL_FRAC_N_DIVIDER_NUM, APP_PLL_FRAC_48); + + break; + + default: + assert(0); + break; + } + + // Wait for PLL output frequency to stabilise due to fractional divider enable + delay_microseconds(100); + + // Turn on the clock output + write_node_config_reg(tile[0], XS1_SSWITCH_SS_APP_CLK_DIVIDER_NUM, APP_PLL_DIV); +} + +void AppPllEnable_SampleRate(int32_t sampleRate_hz) +{ + assert(sampleRate_hz >= 22050); + + if(sampleRate_hz % 22050 == 0) + { + AppPllEnable(44100*512); + } + else + { + AppPllEnable(48000*512); + } +} + diff --git a/host_usb_mixer_control/.makefile b/host_usb_mixer_control/.makefile new file mode 100644 index 00000000..595ea033 --- /dev/null +++ b/host_usb_mixer_control/.makefile @@ -0,0 +1,10 @@ +all: + @echo ======================================================= + @echo Build complete [module only - cannot be run on its own] + @echo ======================================================= + +clean: + @echo ======================================================= + @echo Build clean [module only - cannot be run on its own] + @echo ======================================================= + diff --git a/host_usb_mixer_control/.project b/host_usb_mixer_control/.project new file mode 100644 index 00000000..875c0c73 --- /dev/null +++ b/host_usb_mixer_control/.project @@ -0,0 +1,77 @@ + + + host_usb_mixer_control + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + ?name? + + + + org.eclipse.cdt.make.core.append_environment + true + + + org.eclipse.cdt.make.core.autoBuildTarget + all + + + org.eclipse.cdt.make.core.buildArguments + + + + org.eclipse.cdt.make.core.buildCommand + xmake + + + org.eclipse.cdt.make.core.cleanBuildTarget + clean + + + org.eclipse.cdt.make.core.contents + org.eclipse.cdt.make.core.activeConfigSettings + + + org.eclipse.cdt.make.core.enableAutoBuild + false + + + org.eclipse.cdt.make.core.enableCleanBuild + true + + + org.eclipse.cdt.make.core.enableFullBuild + true + + + org.eclipse.cdt.make.core.fullBuildTarget + all + + + org.eclipse.cdt.make.core.stopOnError + true + + + org.eclipse.cdt.make.core.useDefaultBuildCmd + true + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.core.cnature + + diff --git a/host_usb_mixer_control/Makefile.OSX b/host_usb_mixer_control/Makefile.OSX new file mode 100644 index 00000000..105a91f5 --- /dev/null +++ b/host_usb_mixer_control/Makefile.OSX @@ -0,0 +1,2 @@ +all: + g++ -g -o xmos_mixer usb_mixer.cpp mixer_app.cpp -I. -IOSX OSX/libusb-1.0.0.dylib -arch x86_64 diff --git a/host_usb_mixer_control/Makefile.Win b/host_usb_mixer_control/Makefile.Win new file mode 100644 index 00000000..52f13116 --- /dev/null +++ b/host_usb_mixer_control/Makefile.Win @@ -0,0 +1,5 @@ +!if [set SDKPath=C:\Program^ Files\XMOS\tusbaudiosdk] +!endif + +all: + msbuild host_usb_mixer_control.vcxproj /property:Configuration=Release /property:Platform=x64 diff --git a/host_usb_mixer_control/OSX/libusb-1.0.0.dylib b/host_usb_mixer_control/OSX/libusb-1.0.0.dylib new file mode 100644 index 00000000..0e2e0778 Binary files /dev/null and b/host_usb_mixer_control/OSX/libusb-1.0.0.dylib differ diff --git a/host_usb_mixer_control/OSX/libusb.h b/host_usb_mixer_control/OSX/libusb.h new file mode 100644 index 00000000..11263803 --- /dev/null +++ b/host_usb_mixer_control/OSX/libusb.h @@ -0,0 +1,1233 @@ +/* + * Public libusb header file + * Copyright (C) 2007-2008 Daniel Drake + * Copyright (c) 2001 Johannes Erdfelt + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __LIBUSB_H__ +#define __LIBUSB_H__ + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** \def libusb_cpu_to_le16 + * \ingroup misc + * Convert a 16-bit value from host-endian to little-endian format. On + * little endian systems, this function does nothing. On big endian systems, + * the bytes are swapped. + * \param x the host-endian value to convert + * \returns the value in little-endian byte order + */ +#define libusb_cpu_to_le16(x) ({ \ + union { \ + uint8_t b8[2]; \ + uint16_t b16; \ + } _tmp; \ + uint16_t _tmp2 = (uint16_t)(x); \ + _tmp.b8[1] = _tmp2 >> 8; \ + _tmp.b8[0] = _tmp2 & 0xff; \ + _tmp.b16; \ +}) + +/** \def libusb_le16_to_cpu + * \ingroup misc + * Convert a 16-bit value from little-endian to host-endian format. On + * little endian systems, this function does nothing. On big endian systems, + * the bytes are swapped. + * \param x the little-endian value to convert + * \returns the value in host-endian byte order + */ +#define libusb_le16_to_cpu libusb_cpu_to_le16 + +/* standard USB stuff */ + +/** \ingroup desc + * Device and/or Interface Class codes */ +enum libusb_class_code { + /** In the context of a \ref libusb_device_descriptor "device descriptor", + * this bDeviceClass value indicates that each interface specifies its + * own class information and all interfaces operate independently. + */ + LIBUSB_CLASS_PER_INTERFACE = 0, + + /** Audio class */ + LIBUSB_CLASS_AUDIO = 1, + + /** Communications class */ + LIBUSB_CLASS_COMM = 2, + + /** Human Interface Device class */ + LIBUSB_CLASS_HID = 3, + + /** Printer dclass */ + LIBUSB_CLASS_PRINTER = 7, + + /** Picture transfer protocol class */ + LIBUSB_CLASS_PTP = 6, + + /** Mass storage class */ + LIBUSB_CLASS_MASS_STORAGE = 8, + + /** Hub class */ + LIBUSB_CLASS_HUB = 9, + + /** Data class */ + LIBUSB_CLASS_DATA = 10, + + /** Class is vendor-specific */ + LIBUSB_CLASS_VENDOR_SPEC = 0xff +}; + +/** \ingroup desc + * Descriptor types as defined by the USB specification. */ +enum libusb_descriptor_type { + /** Device descriptor. See libusb_device_descriptor. */ + LIBUSB_DT_DEVICE = 0x01, + + /** Configuration descriptor. See libusb_config_descriptor. */ + LIBUSB_DT_CONFIG = 0x02, + + /** String descriptor */ + LIBUSB_DT_STRING = 0x03, + + /** Interface descriptor. See libusb_interface_descriptor. */ + LIBUSB_DT_INTERFACE = 0x04, + + /** Endpoint descriptor. See libusb_endpoint_descriptor. */ + LIBUSB_DT_ENDPOINT = 0x05, + + /** HID descriptor */ + LIBUSB_DT_HID = 0x21, + + /** HID report descriptor */ + LIBUSB_DT_REPORT = 0x22, + + /** Physical descriptor */ + LIBUSB_DT_PHYSICAL = 0x23, + + /** Hub descriptor */ + LIBUSB_DT_HUB = 0x29 +}; + +/* Descriptor sizes per descriptor type */ +#define LIBUSB_DT_DEVICE_SIZE 18 +#define LIBUSB_DT_CONFIG_SIZE 9 +#define LIBUSB_DT_INTERFACE_SIZE 9 +#define LIBUSB_DT_ENDPOINT_SIZE 7 +#define LIBUSB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ +#define LIBUSB_DT_HUB_NONVAR_SIZE 7 + +#define LIBUSB_ENDPOINT_ADDRESS_MASK 0x0f /* in bEndpointAddress */ +#define LIBUSB_ENDPOINT_DIR_MASK 0x80 + +/** \ingroup desc + * Endpoint direction. Values for bit 7 of the + * \ref libusb_endpoint_descriptor::bEndpointAddress "endpoint address" scheme. + */ +enum libusb_endpoint_direction { + /** In: device-to-host */ + LIBUSB_ENDPOINT_IN = 0x80, + + /** Out: host-to-device */ + LIBUSB_ENDPOINT_OUT = 0x00 +}; + +#define LIBUSB_TRANSFER_TYPE_MASK 0x03 /* in bmAttributes */ + +/** \ingroup desc + * Endpoint transfer type. Values for bits 0:1 of the + * \ref libusb_endpoint_descriptor::bmAttributes "endpoint attributes" field. + */ +enum libusb_transfer_type { + /** Control endpoint */ + LIBUSB_TRANSFER_TYPE_CONTROL = 0, + + /** Isochronous endpoint */ + LIBUSB_TRANSFER_TYPE_ISOCHRONOUS = 1, + + /** Bulk endpoint */ + LIBUSB_TRANSFER_TYPE_BULK = 2, + + /** Interrupt endpoint */ + LIBUSB_TRANSFER_TYPE_INTERRUPT = 3 +}; + +/** \ingroup misc + * Standard requests, as defined in table 9-3 of the USB2 specifications */ +enum libusb_standard_request { + /** Request status of the specific recipient */ + LIBUSB_REQUEST_GET_STATUS = 0x00, + + /** Clear or disable a specific feature */ + LIBUSB_REQUEST_CLEAR_FEATURE = 0x01, + + /* 0x02 is reserved */ + + /** Set or enable a specific feature */ + LIBUSB_REQUEST_SET_FEATURE = 0x03, + + /* 0x04 is reserved */ + + /** Set device address for all future accesses */ + LIBUSB_REQUEST_SET_ADDRESS = 0x05, + + /** Get the specified descriptor */ + LIBUSB_REQUEST_GET_DESCRIPTOR = 0x06, + + /** Used to update existing descriptors or add new descriptors */ + LIBUSB_REQUEST_SET_DESCRIPTOR = 0x07, + + /** Get the current device configuration value */ + LIBUSB_REQUEST_GET_CONFIGURATION = 0x08, + + /** Set device configuration */ + LIBUSB_REQUEST_SET_CONFIGURATION = 0x09, + + /** Return the selected alternate setting for the specified interface */ + LIBUSB_REQUEST_GET_INTERFACE = 0x0A, + + /** Select an alternate interface for the specified interface */ + LIBUSB_REQUEST_SET_INTERFACE = 0x0B, + + /** Set then report an endpoint's synchronization frame */ + LIBUSB_REQUEST_SYNCH_FRAME = 0x0C +}; + +/** \ingroup misc + * Request type bits of the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field in control + * transfers. */ +enum libusb_request_type { + /** Standard */ + LIBUSB_REQUEST_TYPE_STANDARD = (0x00 << 5), + + /** Class */ + LIBUSB_REQUEST_TYPE_CLASS = (0x01 << 5), + + /** Vendor */ + LIBUSB_REQUEST_TYPE_VENDOR = (0x02 << 5), + + /** Reserved */ + LIBUSB_REQUEST_TYPE_RESERVED = (0x03 << 5) +}; + +/** \ingroup misc + * Recipient bits of the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field in control + * transfers. Values 4 through 31 are reserved. */ +enum libusb_request_recipient { + /** Device */ + LIBUSB_RECIPIENT_DEVICE = 0x00, + + /** Interface */ + LIBUSB_RECIPIENT_INTERFACE = 0x01, + + /** Endpoint */ + LIBUSB_RECIPIENT_ENDPOINT = 0x02, + + /** Other */ + LIBUSB_RECIPIENT_OTHER = 0x03 +}; + +#define LIBUSB_ISO_SYNC_TYPE_MASK 0x0C + +/** \ingroup desc + * Synchronization type for isochronous endpoints. Values for bits 2:3 of the + * \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in + * libusb_endpoint_descriptor. + */ +enum libusb_iso_sync_type { + /** No synchronization */ + LIBUSB_ISO_SYNC_TYPE_NONE = 0, + + /** Asynchronous */ + LIBUSB_ISO_SYNC_TYPE_ASYNC = 1, + + /** Adaptive */ + LIBUSB_ISO_SYNC_TYPE_ADAPTIVE = 2, + + /** Synchronous */ + LIBUSB_ISO_SYNC_TYPE_SYNC = 3 +}; + +#define LIBUSB_ISO_USAGE_TYPE_MASK 0x30 + +/** \ingroup desc + * Usage type for isochronous endpoints. Values for bits 4:5 of the + * \ref libusb_endpoint_descriptor::bmAttributes "bmAttributes" field in + * libusb_endpoint_descriptor. + */ +enum libusb_iso_usage_type { + /** Data endpoint */ + LIBUSB_ISO_USAGE_TYPE_DATA = 0, + + /** Feedback endpoint */ + LIBUSB_ISO_USAGE_TYPE_FEEDBACK = 1, + + /** Implicit feedback Data endpoint */ + LIBUSB_ISO_USAGE_TYPE_IMPLICIT = 2 +}; + +/** \ingroup desc + * A structure representing the standard USB device descriptor. This + * descriptor is documented in section 9.6.1 of the USB 2.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_device_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE LIBUSB_DT_DEVICE in this + * context. */ + uint8_t bDescriptorType; + + /** USB specification release number in binary-coded decimal. A value of + * 0x0200 indicates USB 2.0, 0x0110 indicates USB 1.1, etc. */ + uint16_t bcdUSB; + + /** USB-IF class code for the device. See \ref libusb_class_code. */ + uint8_t bDeviceClass; + + /** USB-IF subclass code for the device, qualified by the bDeviceClass + * value */ + uint8_t bDeviceSubClass; + + /** USB-IF protocol code for the device, qualified by the bDeviceClass and + * bDeviceSubClass values */ + uint8_t bDeviceProtocol; + + /** Maximum packet size for endpoint 0 */ + uint8_t bMaxPacketSize0; + + /** USB-IF vendor ID */ + uint16_t idVendor; + + /** USB-IF product ID */ + uint16_t idProduct; + + /** Device release number in binary-coded decimal */ + uint16_t bcdDevice; + + /** Index of string descriptor describing manufacturer */ + uint8_t iManufacturer; + + /** Index of string descriptor describing product */ + uint8_t iProduct; + + /** Index of string descriptor containing device serial number */ + uint8_t iSerialNumber; + + /** Number of possible configurations */ + uint8_t bNumConfigurations; +}; + +/** \ingroup desc + * A structure representing the standard USB endpoint descriptor. This + * descriptor is documented in section 9.6.3 of the USB 2.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_endpoint_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_ENDPOINT LIBUSB_DT_ENDPOINT in + * this context. */ + uint8_t bDescriptorType; + + /** The address of the endpoint described by this descriptor. Bits 0:3 are + * the endpoint number. Bits 4:6 are reserved. Bit 7 indicates direction, + * see \ref libusb_endpoint_direction. + */ + uint8_t bEndpointAddress; + + /** Attributes which apply to the endpoint when it is configured using + * the bConfigurationValue. Bits 0:1 determine the transfer type and + * correspond to \ref libusb_transfer_type. Bits 2:3 are only used for + * isochronous endpoints and correspond to \ref libusb_iso_sync_type. + * Bits 4:5 are also only used for isochronous endpoints and correspond to + * \ref libusb_iso_usage_type. Bits 6:7 are reserved. + */ + uint8_t bmAttributes; + + /** Maximum packet size this endpoint is capable of sending/receiving. */ + uint16_t wMaxPacketSize; + + /** Interval for polling endpoint for data transfers. */ + uint8_t bInterval; + + /** For audio devices only: the rate at which synchronization feedback + * is provided. */ + uint8_t bRefresh; + + /** For audio devices only: the address if the synch endpoint */ + uint8_t bSynchAddress; + + /** Extra descriptors. If libusb encounters unknown endpoint descriptors, + * it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. */ + int extra_length; +}; + +/** \ingroup desc + * A structure representing the standard USB interface descriptor. This + * descriptor is documented in section 9.6.5 of the USB 2.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_interface_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_INTERFACE LIBUSB_DT_INTERFACE + * in this context. */ + uint8_t bDescriptorType; + + /** Number of this interface */ + uint8_t bInterfaceNumber; + + /** Value used to select this alternate setting for this interface */ + uint8_t bAlternateSetting; + + /** Number of endpoints used by this interface (excluding the control + * endpoint). */ + uint8_t bNumEndpoints; + + /** USB-IF class code for this interface. See \ref libusb_class_code. */ + uint8_t bInterfaceClass; + + /** USB-IF subclass code for this interface, qualified by the + * bInterfaceClass value */ + uint8_t bInterfaceSubClass; + + /** USB-IF protocol code for this interface, qualified by the + * bInterfaceClass and bInterfaceSubClass values */ + uint8_t bInterfaceProtocol; + + /** Index of string descriptor describing this interface */ + uint8_t iInterface; + + /** Array of endpoint descriptors. This length of this array is determined + * by the bNumEndpoints field. */ + const struct libusb_endpoint_descriptor *endpoint; + + /** Extra descriptors. If libusb encounters unknown interface descriptors, + * it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. */ + int extra_length; +}; + +/** \ingroup desc + * A collection of alternate settings for a particular USB interface. + */ +struct libusb_interface { + /** Array of interface descriptors. The length of this array is determined + * by the num_altsetting field. */ + const struct libusb_interface_descriptor *altsetting; + + /** The number of alternate settings that belong to this interface */ + int num_altsetting; +}; + +/** \ingroup desc + * A structure representing the standard USB configuration descriptor. This + * descriptor is documented in section 9.6.3 of the USB 2.0 specification. + * All multiple-byte fields are represented in host-endian format. + */ +struct libusb_config_descriptor { + /** Size of this descriptor (in bytes) */ + uint8_t bLength; + + /** Descriptor type. Will have value + * \ref libusb_descriptor_type::LIBUSB_DT_CONFIG LIBUSB_DT_CONFIG + * in this context. */ + uint8_t bDescriptorType; + + /** Total length of data returned for this configuration */ + uint16_t wTotalLength; + + /** Number of interfaces supported by this configuration */ + uint8_t bNumInterfaces; + + /** Identifier value for this configuration */ + uint8_t bConfigurationValue; + + /** Index of string descriptor describing this configuration */ + uint8_t iConfiguration; + + /** Configuration characteristics */ + uint8_t bmAttributes; + + /** Maximum power consumption of the USB device from this bus in this + * configuration when the device is fully opreation. Expressed in units + * of 2 mA. */ + uint8_t MaxPower; + + /** Array of interfaces supported by this configuration. The length of + * this array is determined by the bNumInterfaces field. */ + const struct libusb_interface *interface; + + /** Extra descriptors. If libusb encounters unknown configuration + * descriptors, it will store them here, should you wish to parse them. */ + const unsigned char *extra; + + /** Length of the extra descriptors, in bytes. */ + int extra_length; +}; + +/** \ingroup asyncio + * Setup packet for control transfers. */ +struct libusb_control_setup { + /** Request type. Bits 0:4 determine recipient, see + * \ref libusb_request_recipient. Bits 5:6 determine type, see + * \ref libusb_request_type. Bit 7 determines data transfer direction, see + * \ref libusb_endpoint_direction. + */ + uint8_t bmRequestType; + + /** Request. If the type bits of bmRequestType are equal to + * \ref libusb_request_type::LIBUSB_REQUEST_TYPE_STANDARD + * "LIBUSB_REQUEST_TYPE_STANDARD" then this field refers to + * \ref libusb_standard_request. For other cases, use of this field is + * application-specific. */ + uint8_t bRequest; + + /** Value. Varies according to request */ + uint16_t wValue; + + /** Index. Varies according to request, typically used to pass an index + * or offset */ + uint16_t wIndex; + + /** Number of bytes to transfer */ + uint16_t wLength; +}; + +#define LIBUSB_CONTROL_SETUP_SIZE (sizeof(struct libusb_control_setup)) + +/* libusb */ + +struct libusb_context; +struct libusb_device; +struct libusb_device_handle; + +/** \ingroup lib + * Structure representing a libusb session. The concept of individual libusb + * sessions allows for your program to use two libraries (or dynamically + * load two modules) which both independently use libusb. This will prevent + * interference between the individual libusb users - for example + * libusb_set_debug() will not affect the other user of the library, and + * libusb_exit() will not destroy resources that the other user is still + * using. + * + * Sessions are created by libusb_init() and destroyed through libusb_exit(). + * If your application is guaranteed to only ever include a single libusb + * user (i.e. you), you do not have to worry about contexts: pass NULL in + * every function call where a context is required. The default context + * will be used. + * + * For more information, see \ref contexts. + */ +typedef struct libusb_context libusb_context; + +/** \ingroup dev + * Structure representing a USB device detected on the system. This is an + * opaque type for which you are only ever provided with a pointer, usually + * originating from libusb_get_device_list(). + * + * Certain operations can be performed on a device, but in order to do any + * I/O you will have to first obtain a device handle using libusb_open(). + * + * Devices are reference counted with libusb_device_ref() and + * libusb_device_unref(), and are freed when the reference count reaches 0. + * New devices presented by libusb_get_device_list() have a reference count of + * 1, and libusb_free_device_list() can optionally decrease the reference count + * on all devices in the list. libusb_open() adds another reference which is + * later destroyed by libusb_close(). + */ +typedef struct libusb_device libusb_device; + + +/** \ingroup dev + * Structure representing a handle on a USB device. This is an opaque type for + * which you are only ever provided with a pointer, usually originating from + * libusb_open(). + * + * A device handle is used to perform I/O and other operations. When finished + * with a device handle, you should call libusb_close(). + */ +typedef struct libusb_device_handle libusb_device_handle; + +/** \ingroup misc + * Error codes. Most libusb functions return 0 on success or one of these + * codes on failure. + */ +enum libusb_error { + /** Success (no error) */ + LIBUSB_SUCCESS = 0, + + /** Input/output error */ + LIBUSB_ERROR_IO = -1, + + /** Invalid parameter */ + LIBUSB_ERROR_INVALID_PARAM = -2, + + /** Access denied (insufficient permissions) */ + LIBUSB_ERROR_ACCESS = -3, + + /** No such device (it may have been disconnected) */ + LIBUSB_ERROR_NO_DEVICE = -4, + + /** Entity not found */ + LIBUSB_ERROR_NOT_FOUND = -5, + + /** Resource busy */ + LIBUSB_ERROR_BUSY = -6, + + /** Operation timed out */ + LIBUSB_ERROR_TIMEOUT = -7, + + /** Overflow */ + LIBUSB_ERROR_OVERFLOW = -8, + + /** Pipe error */ + LIBUSB_ERROR_PIPE = -9, + + /** System call interrupted (perhaps due to signal) */ + LIBUSB_ERROR_INTERRUPTED = -10, + + /** Insufficient memory */ + LIBUSB_ERROR_NO_MEM = -11, + + /** Operation not supported or unimplemented on this platform */ + LIBUSB_ERROR_NOT_SUPPORTED = -12, + + /** Other error */ + LIBUSB_ERROR_OTHER = -99 +}; + +/** \ingroup asyncio + * Transfer status codes */ +enum libusb_transfer_status { + /** Transfer completed without error. Note that this does not indicate + * that the entire amount of requested data was transferred. */ + LIBUSB_TRANSFER_COMPLETED, + + /** Transfer failed */ + LIBUSB_TRANSFER_ERROR, + + /** Transfer timed out */ + LIBUSB_TRANSFER_TIMED_OUT, + + /** Transfer was cancelled */ + LIBUSB_TRANSFER_CANCELLED, + + /** For bulk/interrupt endpoints: halt condition detected (endpoint + * stalled). For control endpoints: control request not supported. */ + LIBUSB_TRANSFER_STALL, + + /** Device was disconnected */ + LIBUSB_TRANSFER_NO_DEVICE, + + /** Device sent more data than requested */ + LIBUSB_TRANSFER_OVERFLOW +}; + +/** \ingroup asyncio + * libusb_transfer.flags values */ +enum libusb_transfer_flags { + /** Report short frames as errors */ + LIBUSB_TRANSFER_SHORT_NOT_OK = 1<<0, + + /** Automatically free() transfer buffer during libusb_free_transfer() */ + LIBUSB_TRANSFER_FREE_BUFFER = 1<<1, + + /** Automatically call libusb_free_transfer() after callback returns. + * If this flag is set, it is illegal to call libusb_free_transfer() + * from your transfer callback, as this will result in a double-free + * when this flag is acted upon. */ + LIBUSB_TRANSFER_FREE_TRANSFER = 1<<2 +}; + +/** \ingroup asyncio + * Isochronous packet descriptor. */ +struct libusb_iso_packet_descriptor { + /** Length of data to request in this packet */ + unsigned int length; + + /** Amount of data that was actually transferred */ + unsigned int actual_length; + + /** Status code for this packet */ + enum libusb_transfer_status status; +}; + +struct libusb_transfer; + +/** \ingroup asyncio + * Asynchronous transfer callback function type. When submitting asynchronous + * transfers, you pass a pointer to a callback function of this type via the + * \ref libusb_transfer::callback "callback" member of the libusb_transfer + * structure. libusb will call this function later, when the transfer has + * completed or failed. See \ref asyncio for more information. + * \param transfer The libusb_transfer struct the callback function is being + * notified about. + */ +typedef void (*libusb_transfer_cb_fn)(struct libusb_transfer *transfer); + +/** \ingroup asyncio + * The generic USB transfer structure. The user populates this structure and + * then submits it in order to request a transfer. After the transfer has + * completed, the library populates the transfer with the results and passes + * it back to the user. + */ +struct libusb_transfer { + /** Handle of the device that this transfer will be submitted to */ + libusb_device_handle *dev_handle; + + /** A bitwise OR combination of \ref libusb_transfer_flags. */ + uint8_t flags; + + /** Address of the endpoint where this transfer will be sent. */ + unsigned char endpoint; + + /** Type of the endpoint from \ref libusb_transfer_type */ + unsigned char type; + + /** Timeout for this transfer in millseconds. A value of 0 indicates no + * timeout. */ + unsigned int timeout; + + /** The status of the transfer. Read-only, and only for use within + * transfer callback function. + * + * If this is an isochronous transfer, this field may read COMPLETED even + * if there were errors in the frames. Use the + * \ref libusb_iso_packet_descriptor::status "status" field in each packet + * to determine if errors occurred. */ + enum libusb_transfer_status status; + + /** Length of the data buffer */ + int length; + + /** Actual length of data that was transferred. Read-only, and only for + * use within transfer callback function. Not valid for isochronous + * endpoint transfers. */ + int actual_length; + + /** Callback function. This will be invoked when the transfer completes, + * fails, or is cancelled. */ + libusb_transfer_cb_fn callback; + + /** User context data to pass to the callback function. */ + void *user_data; + + /** Data buffer */ + unsigned char *buffer; + + /** Number of isochronous packets. Only used for I/O with isochronous + * endpoints. */ + int num_iso_packets; + + /** Isochronous packet descriptors, for isochronous transfers only. */ + struct libusb_iso_packet_descriptor iso_packet_desc +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + [] /* valid C99 code */ +#else + [0] /* non-standard, but usually working code */ +#endif + ; +}; + +int libusb_init(libusb_context **ctx); +void libusb_exit(libusb_context *ctx); +void libusb_set_debug(libusb_context *ctx, int level); + +ssize_t libusb_get_device_list(libusb_context *ctx, + libusb_device ***list); +void libusb_free_device_list(libusb_device **list, int unref_devices); +libusb_device *libusb_ref_device(libusb_device *dev); +void libusb_unref_device(libusb_device *dev); + +int libusb_get_configuration(libusb_device_handle *dev, int *config); +int libusb_get_device_descriptor(libusb_device *dev, + struct libusb_device_descriptor *desc); +int libusb_get_active_config_descriptor(libusb_device *dev, + struct libusb_config_descriptor **config); +int libusb_get_config_descriptor(libusb_device *dev, uint8_t config_index, + struct libusb_config_descriptor **config); +int libusb_get_config_descriptor_by_value(libusb_device *dev, + uint8_t bConfigurationValue, struct libusb_config_descriptor **config); +void libusb_free_config_descriptor(struct libusb_config_descriptor *config); +uint8_t libusb_get_bus_number(libusb_device *dev); +uint8_t libusb_get_device_address(libusb_device *dev); +int libusb_get_max_packet_size(libusb_device *dev, unsigned char endpoint); + +int libusb_open(libusb_device *dev, libusb_device_handle **handle); +void libusb_close(libusb_device_handle *dev_handle); +libusb_device *libusb_get_device(libusb_device_handle *dev_handle); + +int libusb_set_configuration(libusb_device_handle *dev, int configuration); +int libusb_claim_interface(libusb_device_handle *dev, int iface); +int libusb_release_interface(libusb_device_handle *dev, int iface); + +libusb_device_handle *libusb_open_device_with_vid_pid(libusb_context *ctx, + uint16_t vendor_id, uint16_t product_id); + +int libusb_set_interface_alt_setting(libusb_device_handle *dev, + int interface_number, int alternate_setting); +int libusb_clear_halt(libusb_device_handle *dev, unsigned char endpoint); +int libusb_reset_device(libusb_device_handle *dev); + +int libusb_kernel_driver_active(libusb_device_handle *dev, int interface); +int libusb_detach_kernel_driver(libusb_device_handle *dev, int interface); +int libusb_attach_kernel_driver(libusb_device_handle *dev, int interface); + +/* async I/O */ + +/** \ingroup asyncio + * Get the data section of a control transfer. This convenience function is here + * to remind you that the data does not start until 8 bytes into the actual + * buffer, as the setup packet comes first. + * + * Calling this function only makes sense from a transfer callback function, + * or situations where you have already allocated a suitably sized buffer at + * transfer->buffer. + * + * \param transfer a transfer + * \returns pointer to the first byte of the data section + */ +static inline unsigned char *libusb_control_transfer_get_data( + struct libusb_transfer *transfer) +{ + return transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; +} + +/** \ingroup asyncio + * Get the control setup packet of a control transfer. This convenience + * function is here to remind you that the control setup occupies the first + * 8 bytes of the transfer data buffer. + * + * Calling this function only makes sense from a transfer callback function, + * or situations where you have already allocated a suitably sized buffer at + * transfer->buffer. + * + * \param transfer a transfer + * \returns a casted pointer to the start of the transfer data buffer + */ +static inline struct libusb_control_setup *libusb_control_transfer_get_setup( + struct libusb_transfer *transfer) +{ + return (struct libusb_control_setup *) transfer->buffer; +} + +/** \ingroup asyncio + * Helper function to populate the setup packet (first 8 bytes of the data + * buffer) for a control transfer. The wIndex, wValue and wLength values should + * be given in host-endian byte order. + * + * \param buffer buffer to output the setup packet into + * \param bmRequestType see the + * \ref libusb_control_setup::bmRequestType "bmRequestType" field of + * \ref libusb_control_setup + * \param bRequest see the + * \ref libusb_control_setup::bRequest "bRequest" field of + * \ref libusb_control_setup + * \param wValue see the + * \ref libusb_control_setup::wValue "wValue" field of + * \ref libusb_control_setup + * \param wIndex see the + * \ref libusb_control_setup::wIndex "wIndex" field of + * \ref libusb_control_setup + * \param wLength see the + * \ref libusb_control_setup::wLength "wLength" field of + * \ref libusb_control_setup + */ +static inline void libusb_fill_control_setup(unsigned char *buffer, + uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + uint16_t wLength) +{ + struct libusb_control_setup *setup = (struct libusb_control_setup *) buffer; + setup->bmRequestType = bmRequestType; + setup->bRequest = bRequest; + setup->wValue = libusb_cpu_to_le16(wValue); + setup->wIndex = libusb_cpu_to_le16(wIndex); + setup->wLength = libusb_cpu_to_le16(wLength); +} + +struct libusb_transfer *libusb_alloc_transfer(int iso_packets); +int libusb_submit_transfer(struct libusb_transfer *transfer); +int libusb_cancel_transfer(struct libusb_transfer *transfer); +void libusb_free_transfer(struct libusb_transfer *transfer); + +/** \ingroup asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for a control transfer. + * + * If you pass a transfer buffer to this function, the first 8 bytes will + * be interpreted as a control setup packet, and the wLength field will be + * used to automatically populate the \ref libusb_transfer::length "length" + * field of the transfer. Therefore the recommended approach is: + * -# Allocate a suitably sized data buffer (including space for control setup) + * -# Call libusb_fill_control_setup() + * -# If this is a host-to-device transfer with a data stage, put the data + * in place after the setup packet + * -# Call this function + * -# Call libusb_submit_transfer() + * + * It is also legal to pass a NULL buffer to this function, in which case this + * function will not attempt to populate the length field. Remember that you + * must then populate the buffer and length fields later. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param buffer data buffer. If provided, this function will interpret the + * first 8 bytes as a setup packet and infer the transfer length from that. + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_control_transfer( + struct libusb_transfer *transfer, libusb_device_handle *dev_handle, + unsigned char *buffer, libusb_transfer_cb_fn callback, void *user_data, + unsigned int timeout) +{ + struct libusb_control_setup *setup = (struct libusb_control_setup *) buffer; + transfer->dev_handle = dev_handle; + transfer->endpoint = 0; + transfer->type = LIBUSB_TRANSFER_TYPE_CONTROL; + transfer->timeout = timeout; + transfer->buffer = buffer; + if (setup) + transfer->length = LIBUSB_CONTROL_SETUP_SIZE + + libusb_le16_to_cpu(setup->wLength); + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for a bulk transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_bulk_transfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, unsigned char endpoint, + unsigned char *buffer, int length, libusb_transfer_cb_fn callback, + void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_BULK; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for an interrupt transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_interrupt_transfer( + struct libusb_transfer *transfer, libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *buffer, int length, + libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_INTERRUPT; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for an isochronous transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param num_iso_packets the number of isochronous packets + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_iso_transfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, unsigned char endpoint, + unsigned char *buffer, int length, int num_iso_packets, + libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->num_iso_packets = num_iso_packets; + transfer->user_data = user_data; + transfer->callback = callback; +} + +/** \ingroup asyncio + * Convenience function to set the length of all packets in an isochronous + * transfer, based on the num_iso_packets field in the transfer structure. + * + * \param transfer a transfer + * \param length the length to set in each isochronous packet descriptor + * \see libusb_get_max_packet_size() + */ +static inline void libusb_set_iso_packet_lengths( + struct libusb_transfer *transfer, unsigned int length) +{ + int i; + for (i = 0; i < transfer->num_iso_packets; i++) + transfer->iso_packet_desc[i].length = length; +} + +/** \ingroup asyncio + * Convenience function to locate the position of an isochronous packet + * within the buffer of an isochronous transfer. + * + * This is a thorough function which loops through all preceding packets, + * accumulating their lengths to find the position of the specified packet. + * Typically you will assign equal lengths to each packet in the transfer, + * and hence the above method is sub-optimal. You may wish to use + * libusb_get_iso_packet_buffer_simple() instead. + * + * \param transfer a transfer + * \param packet the packet to return the address of + * \returns the base address of the packet buffer inside the transfer buffer, + * or NULL if the packet does not exist. + * \see libusb_get_iso_packet_buffer_simple() + */ +static inline unsigned char *libusb_get_iso_packet_buffer( + struct libusb_transfer *transfer, unsigned int packet) +{ + int i; + size_t offset = 0; + int _packet; + + /* oops..slight bug in the API. packet is an unsigned int, but we use + * signed integers almost everywhere else. range-check and convert to + * signed to avoid compiler warnings. FIXME for libusb-2. */ + if (packet > INT_MAX) + return NULL; + _packet = packet; + + if (_packet >= transfer->num_iso_packets) + return NULL; + + for (i = 0; i < _packet; i++) + offset += transfer->iso_packet_desc[i].length; + + return transfer->buffer + offset; +} + +/** \ingroup asyncio + * Convenience function to locate the position of an isochronous packet + * within the buffer of an isochronous transfer, for transfers where each + * packet is of identical size. + * + * This function relies on the assumption that every packet within the transfer + * is of identical size to the first packet. Calculating the location of + * the packet buffer is then just a simple calculation: + * buffer + (packet_size * packet) + * + * Do not use this function on transfers other than those that have identical + * packet lengths for each packet. + * + * \param transfer a transfer + * \param packet the packet to return the address of + * \returns the base address of the packet buffer inside the transfer buffer, + * or NULL if the packet does not exist. + * \see libusb_get_iso_packet_buffer() + */ +static inline unsigned char *libusb_get_iso_packet_buffer_simple( + struct libusb_transfer *transfer, unsigned int packet) +{ + int _packet; + + /* oops..slight bug in the API. packet is an unsigned int, but we use + * signed integers almost everywhere else. range-check and convert to + * signed to avoid compiler warnings. FIXME for libusb-2. */ + if (packet > INT_MAX) + return NULL; + _packet = packet; + + if (_packet >= transfer->num_iso_packets) + return NULL; + + return transfer->buffer + (transfer->iso_packet_desc[0].length * _packet); +} + +/* sync I/O */ + +int libusb_control_transfer(libusb_device_handle *dev_handle, + uint8_t request_type, uint8_t request, uint16_t value, uint16_t index, + unsigned char *data, uint16_t length, unsigned int timeout); + +int libusb_bulk_transfer(libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, + int *actual_length, unsigned int timeout); + +int libusb_interrupt_transfer(libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, + int *actual_length, unsigned int timeout); + +/** \ingroup desc + * Retrieve a descriptor from the default control pipe. + * This is a convenience function which formulates the appropriate control + * message to retrieve the descriptor. + * + * \param dev a device handle + * \param desc_type the descriptor type, see \ref libusb_descriptor_type + * \param desc_index the index of the descriptor to retrieve + * \param data output buffer for descriptor + * \param length size of data buffer + * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure + */ +static inline int libusb_get_descriptor(libusb_device_handle *dev, + uint8_t desc_type, uint8_t desc_index, unsigned char *data, int length) +{ + return libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN, + LIBUSB_REQUEST_GET_DESCRIPTOR, (desc_type << 8) | desc_index, 0, data, + length, 1000); +} + +/** \ingroup desc + * Retrieve a descriptor from a device. + * This is a convenience function which formulates the appropriate control + * message to retrieve the descriptor. The string returned is Unicode, as + * detailed in the USB specifications. + * + * \param dev a device handle + * \param desc_index the index of the descriptor to retrieve + * \param langid the language ID for the string descriptor + * \param data output buffer for descriptor + * \param length size of data buffer + * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure + * \see libusb_get_string_descriptor_ascii() + */ +static inline int libusb_get_string_descriptor(libusb_device_handle *dev, + uint8_t desc_index, uint16_t langid, unsigned char *data, int length) +{ + return libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN, + LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_STRING << 8) | desc_index, + langid, data, length, 1000); +} + +int libusb_get_string_descriptor_ascii(libusb_device_handle *dev, + uint8_t index, unsigned char *data, int length); + +/* polling and timeouts */ + +int libusb_try_lock_events(libusb_context *ctx); +void libusb_lock_events(libusb_context *ctx); +void libusb_unlock_events(libusb_context *ctx); +int libusb_event_handling_ok(libusb_context *ctx); +int libusb_event_handler_active(libusb_context *ctx); +void libusb_lock_event_waiters(libusb_context *ctx); +void libusb_unlock_event_waiters(libusb_context *ctx); +int libusb_wait_for_event(libusb_context *ctx, struct timeval *tv); + +int libusb_handle_events_timeout(libusb_context *ctx, struct timeval *tv); +int libusb_handle_events(libusb_context *ctx); +int libusb_handle_events_locked(libusb_context *ctx, struct timeval *tv); +int libusb_get_next_timeout(libusb_context *ctx, struct timeval *tv); + +/** \ingroup poll + * File descriptor for polling + */ +struct libusb_pollfd { + /** Numeric file descriptor */ + int fd; + + /** Event flags to poll for from . POLLIN indicates that you + * should monitor this file descriptor for becoming ready to read from, + * and POLLOUT indicates that you should monitor this file descriptor for + * nonblocking write readiness. */ + short events; +}; + +/** \ingroup poll + * Callback function, invoked when a new file descriptor should be added + * to the set of file descriptors monitored for events. + * \param fd the new file descriptor + * \param events events to monitor for, see \ref libusb_pollfd for a + * description + * \param user_data User data pointer specified in + * libusb_set_pollfd_notifiers() call + * \see libusb_set_pollfd_notifiers() + */ +typedef void (*libusb_pollfd_added_cb)(int fd, short events, void *user_data); + +/** \ingroup poll + * Callback function, invoked when a file descriptor should be removed from + * the set of file descriptors being monitored for events. After returning + * from this callback, do not use that file descriptor again. + * \param fd the file descriptor to stop monitoring + * \param user_data User data pointer specified in + * libusb_set_pollfd_notifiers() call + * \see libusb_set_pollfd_notifiers() + */ +typedef void (*libusb_pollfd_removed_cb)(int fd, void *user_data); + +const struct libusb_pollfd **libusb_get_pollfds(libusb_context *ctx); +void libusb_set_pollfd_notifiers(libusb_context *ctx, + libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb, + void *user_data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/host_usb_mixer_control/README b/host_usb_mixer_control/README new file mode 100644 index 00000000..a73ac326 --- /dev/null +++ b/host_usb_mixer_control/README @@ -0,0 +1,117 @@ +The XMOS USB Audio Reference Design, by default, contains an 18x8 mixer unit +(note that at sample rates above 96Khz only the first two outputs are +enabled). + +----WINDOWS REQUIREMENTS---- +Building the mixer on Windows requires the tusbaudio SDK from Thesycon. The +default location for the SDK is C:\Program Files\XMOS\tusbaudiosdk\ +If it can be found on a different path then this can be changed in +Makefile.Win. + +The mixer app on windows makes use of a USB dynamic library, also from Thesycon. +If required please contact thesycon.de for support. +---------------------------- + +This unit takes input takes 18 inputs: USB OUT channels 1..10 and +DEVICE IN channels 1..6,9..10 and produces 8 outputs: Mixer Output +1..8 + +Before the mixer there is an unit that allows the selection of the 18 mixer inputs +from all the possible device inputs (DAW and physical audio). This is +an extension unit with id 50 in the descriptors + +After the mixer unit there is are channel map units for each output terminal: +Each of these outputs can select a source from one of 28 channels sources: USB OUT +channels 1..10, DEVICE IN channels 1..10 and Mixer Output 1..8 + +The channel map units are extension unit with init ids 51 and 52. This unit +lets you implement arbitrary routings including loopbacks. + +The mixer is controlled on macOS via the command line utility +xmos_mixer. Running this application requires having the +libusb-1.0.0.dylib in the dynamic library load path. Sourcing the +setup.sh script will do this. Source code for the application is +provided as a guide on how to communicate with the device. + +Here are the commands for the mixer application (note that the USB +audio reference design has only one unit so the mixer_id argument +should always be 0): + + --help + + --display-info + +Show information about the device. + + --display-mixer-nodes mixer_id + +Display all the weights of all the mixer nodes (and their id) of a particular mixer. + + --display-min mixer_id + +Display the minimum allowable weights of a particular mixer. + + --display-max mixer_id + +Display the maximum allowable weights of a particular mixer. + + --display-res mixer_id + +Display the resolution of a particular mixer. + + --set-value mixer_id mixer_unit value + +Set the weight value in the mixer. The second argument should +correspond to the values shown by the --display-unit command. Values +can range from -127db to +128db with the special value -inf for mute. + + --get-value mixer_id mixer_unit + +Get the weight value in the mixer. The second argument should +correspond to the values shown by the --display-unit command. Values +can range from -127db to +128db with the special value -inf for mute. + + --set-mixer-source mixer_id, dst_channel_id, src_channel_id + +Allows the selection of the mixer inputs. Sets mixer input (dst) to src + + --display-current-mixer-sources mixer_id + +Displays the current inputs to a particular mixer + + --display-available-mixer-sources mixer_id + +Displays all the input channels available that can be fed into the inputs of a particular mixer + + --set-aud-channel-map dst src + +Sets a channel map value for the device audio output + + --display-aud-channel-map + +Show audio output channel map i.e. for each audio output of the device what the source is. + + --display-aud-channel-map-sources + +Show the available audio output channel map sources. + + --set-daw-channel-map dst src + +Sets a channel map value for the DAW output to the host + + --display-daw-channel-map + +Show audio output channel map i.e. for each DAW output to host, what the source is. + + --display-daw-channel-map-sources + +Show the DAW output channel map sources. + + --get-mixer-levels-input + + --get-mixer-levels-output + + --vendor-audio-request-get bRequest, ControlSelector, ChannelNumber, UnitId + + --vendor-audio-request-set bRequest, ControlSelector, ChannelNumber, UnitId, Data[0], Data[1],... + diff --git a/host_usb_mixer_control/Win/global.h b/host_usb_mixer_control/Win/global.h new file mode 100644 index 00000000..48f6bc49 --- /dev/null +++ b/host_usb_mixer_control/Win/global.h @@ -0,0 +1,48 @@ +// Copyright 2022-2023 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +/************************************************************************ + * + * Module: global.h + * Description: + * APP global includes, constants, declarations, etc. + * + * Author(s): + * Udo Eberhardt + * + * Companies: + * Thesycon GmbH, Germany http://www.thesycon.de + * + ************************************************************************/ + +#ifndef __global_h__ +#define __global_h__ + +// define the Windows versions supported by the application +#define _WIN32_WINNT 0x0500 //Windows 2000 or later +//#define _WIN32_WINNT 0x0501 //Windows XP or later +//#define _WIN32_WINNT 0x0600 //Windows Vista or later +//#define _WIN32_WINNT 0x0A00 //Windows 10 or later + +// exclude rarely-used stuff from Windows headers +#define WIN32_LEAN_AND_MEAN + +#include + +#include +#include + + +// version defs +//#include "version.h" + +// libwn.h pulls in windows.h +#include "libwn.h" +// TUSBAUDIO driver API +#include "tusbaudioapi.h" +#include "TUsbAudioApiDll.h" + + +#endif // __global_h__ + +/*************************** EOF **************************************/ diff --git a/host_usb_mixer_control/host_usb_mixer_control.vcxproj b/host_usb_mixer_control/host_usb_mixer_control.vcxproj new file mode 100644 index 00000000..8282d708 --- /dev/null +++ b/host_usb_mixer_control/host_usb_mixer_control.vcxproj @@ -0,0 +1,177 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {84eacf4f-e405-4909-b440-a04a84a3f8c8} + + + C:\Program Files\XMOS\tusbaudiosdk + 10.0 + + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + Application + true + v142 + Unicode + + + Application + false + v142 + false + Unicode + + + + + + + + + + + + + + + + + + + + + + + .\bin\$(Configuration)\$(PlatformName)\ + $(Configuration)\$(PlatformName)_$(PlatformToolset) + false + + + .\bin\$(Configuration)\$(PlatformName)\ + $(Configuration)\$(PlatformName)_$(PlatformToolset) + false + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level4 + + + _DEBUG;_AMD64;_CONSOLE;_UNICODE;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) + true + .;.\Win;$(SDKPath)\source\shared;$(SDKPath)\source\inc;$(SDKPath)\source\libwn_min;$(SDKPath)\source\libwtl;$(SDKPath)\source\tusbaudioapi_inc + ProgramDatabase + true + Async + + + + Console + true + %(AdditionalDependencies) + false + + + true + true + true + + + + + Level4 + true + false + + + _AMD64;_CONSOLE;_UNICODE;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) + true + .;.\Win;$(SDKPath)\source\shared;$(SDKPath)\source\inc;$(SDKPath)\source\libwn_min;$(SDKPath)\source\libwtl;$(SDKPath)\source\tusbaudioapi_inc + true + Async + + + + Console + true + true + true + %(AdditionalDependencies) + false + + + true + + + + + + + + + + + + + + \ No newline at end of file diff --git a/host_usb_mixer_control/host_usb_mixer_control.vcxproj.filters b/host_usb_mixer_control/host_usb_mixer_control.vcxproj.filters new file mode 100644 index 00000000..a20e02b1 --- /dev/null +++ b/host_usb_mixer_control/host_usb_mixer_control.vcxproj.filters @@ -0,0 +1,33 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/host_usb_mixer_control/host_usb_mixer_control.vcxproj.user b/host_usb_mixer_control/host_usb_mixer_control.vcxproj.user new file mode 100644 index 00000000..0f14913f --- /dev/null +++ b/host_usb_mixer_control/host_usb_mixer_control.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/host_usb_mixer_control/mixer_app.cpp b/host_usb_mixer_control/mixer_app.cpp new file mode 100644 index 00000000..dbde3ddf --- /dev/null +++ b/host_usb_mixer_control/mixer_app.cpp @@ -0,0 +1,718 @@ +// Copyright 2022-2023 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +#include +#include +#include +#include "usb_mixer.h" + +#define MIXER_UNIT_DISPLAY_VALUE 2 +#define MIXER_UNIT_DISPLAY_MIN 3 +#define MIXER_UNIT_DISPLAY_MAX 4 +#define MIXER_UNIT_DISPLAY_RES 5 + +// TODO +// res, min, max + +int mixer_init(void) +{ + /* Open the connection to the USB mixer */ + if (usb_mixer_connect() == USB_MIXER_FAILURE) + { + return USB_MIXER_FAILURE; + } + + + return USB_MIXER_SUCCESS; +} + +int mixer_deinit(void) { + // Close the connection to the USB mixer + if (usb_mixer_disconnect() == USB_MIXER_FAILURE) { + return USB_MIXER_FAILURE; + } + + return USB_MIXER_SUCCESS; +} + +int mixer_display(unsigned int mixer_index, unsigned int type) { + int i = 0; + int j = 0; + + int num_inputs = usb_mixer_get_num_inputs(mixer_index); + int num_outputs = usb_mixer_get_num_outputs(mixer_index); + + + printf("\n"); + switch (type) { + case MIXER_UNIT_DISPLAY_VALUE: + //mixer_update_all_values(mixer_index); + printf(" Mixer Values (%d)\n", mixer_index); + printf(" ----------------\n\n"); + break; + case MIXER_UNIT_DISPLAY_MIN: + printf(" Mixer Ranges Min (%d)\n", mixer_index); + printf(" --------------------\n\n"); + break; + case MIXER_UNIT_DISPLAY_MAX: + printf(" Mixer Ranges Max (%d)\n", mixer_index); + printf(" --------------------\n\n"); + break; + case MIXER_UNIT_DISPLAY_RES: + printf(" Mixer Ranges Res (%d)\n", mixer_index); + printf(" --------------------\n\n"); + break; + default: + return USB_MIXER_FAILURE; + break; + } + + printf(" \t\t\t"); + printf("Mixer Outputs\n"); + printf("\t\t "); + for (i = 0; i < num_outputs; i++) { + printf(" %d", i+1); + } + printf("\n"); + for (i = 0; i < num_inputs; i++) { + printf(" %-20s", usb_mixer_get_input_name(mixer_index,i)); + for (j = 0; j < num_outputs; j++) { + switch (type) { + case MIXER_UNIT_DISPLAY_VALUE: + { + double mixNodeVal = usb_mixer_get_value(mixer_index, (i*num_outputs)+j); + int nodeid = (i*num_outputs)+j; + + if (mixNodeVal <= -127.996)// todo shoud be < min + { + printf("\t%3d:[ %s ]", nodeid,"-inf"); + } + else + { + printf("\t%3d:[%08.03f]", nodeid, mixNodeVal); + } + } + break; + case MIXER_UNIT_DISPLAY_MIN: + { + int nodeid = (i*num_outputs)+j; + printf("\t%3d:[%08.03f]", nodeid, usb_mixer_get_min(mixer_index, (i*num_outputs)+j)) ; + } + break; + case MIXER_UNIT_DISPLAY_MAX: + { + int nodeid = (i*num_outputs)+j; + printf("\t%3d:[%08.03f]", nodeid, usb_mixer_get_max(mixer_index, (i*num_outputs)+j)) ; + } + break; + case MIXER_UNIT_DISPLAY_RES: + { + int nodeid = (i*num_outputs)+j; + printf("\t%3d:[%08.03f]", nodeid, usb_mixer_get_res(mixer_index, (i*num_outputs)+j)) ; + } + break; + default: + return USB_MIXER_FAILURE; + break; + } + } + printf("\n"); + } + printf("\n"); + + return USB_MIXER_SUCCESS; +} + +/* Displays basic mixer information */ +int mixer_display_info(void) +{ + unsigned int i = 0; + int num_mixers = usb_mixer_get_num_mixers(); + + printf("\n"); + printf(" Mixer Info\n"); + printf(" ----------\n\n"); + printf(" Mixers : %d\n\n", num_mixers); + + for (i = 0; i < num_mixers; i++) + { + int num_inputs = usb_mixer_get_num_inputs(i); + int num_outputs = usb_mixer_get_num_outputs(i); + + + printf(" Mixer %d\n", i); + printf(" -------\n"); + + printf(" Inputs : %d\n" + " Outputs : %d\n\n", num_inputs, num_outputs); + + printf(" Mixer Output Labels:\n"); + for(int j = 0; j < num_outputs; j++) + { + printf(" %d: %s\n", j,usb_mixer_get_output_name(i,j)); + } + + //printf("\n Selectable Inputs (%d): \n", usb_mixsel_get_input_count(i)); + //for(int j = 0; j < usb_mixsel_get_input_count(i); j++) + //{ + // printf(" %d: %s\n", j, usb_mixsel_get_input_string(i,j)); + //} + } + + printf("\n"); + + return USB_MIXER_SUCCESS; +} + +void display_available_mixer_sources(int mixIndex) +{ + printf("\n"); + printf(" Available Mixer Sources (%d)\n", mixIndex); + printf(" -------------------------\n\n"); + + for(int j = 0; j < usb_mixsel_get_input_count(mixIndex); j++) + { + printf(" %d: %s\n", j, usb_mixsel_get_input_string(mixIndex,j)); + } +} + +/* Gets the current mixer inputs from the device an displays them */ +void display_mixer_sources(int mixerIndex) +{ + printf("\n"); + printf(" Current Mixer Sources (%d)\n", mixerIndex); + printf(" -------------------------\n\n"); + + /* Note, mixSel output cound and mixer input chan count should be the same! */ + printf(" Number of mixer sources: %d\n", usb_mixsel_get_output_count(mixerIndex)); + + /* Get the current channel number for every mixer input */ + for(int i = 0; i < usb_mixsel_get_output_count(mixerIndex); i++) + { + int inputChan = (int)usb_mixsel_get_state(mixerIndex, i); + char *str = usb_mixer_get_input_name(mixerIndex,inputChan); + printf(" Mixer input %d: Source chan id: %d (%s)\n", i, inputChan, str); + } +} + +/* set mixer source */ +void set_mixer_source(unsigned mixerIndex, unsigned dst, unsigned src) +{ + usb_mixsel_set_state(mixerIndex, dst, src); + + /* String lookup */ + char *str = usb_mixer_get_input_name(mixerIndex, dst); + int state = usb_mixsel_get_state(mixerIndex, dst); + + printf("\n Set mixer(%d) input %d to device input %d (%s)\n", mixerIndex, dst, state, str); +} + +void display_aud_channel_map() +{ + printf("\n"); + printf(" Audio Output Channel Map\n"); + printf(" ------------------------\n\n"); + + for (int i=0;i>8), offset&0xff, unitId, data); +} + +void print_levels(const char* levelTitle, unsigned char* levels, int levelBytes) +{ + unsigned levelCount = levelBytes/2; + unsigned short* levelData = (unsigned short*) levels; + + printf("\n %s Level Data\n" + " ----------------------\n\n" + "%d bytes (%d channels) returned:\n" + , levelTitle, levelBytes, levelCount); + + for(int i = 0; i +#include +#include +#include "usb_mixer.h" + +#if defined(__APPLE__) + #include "libusb.h" +#elif defined(_WIN32) + #include "global.h" + //driver interface + TUsbAudioApiDll gDrvApi; +#endif + +// TODO we dont really need to store mixer input strings +// Currently res, max, min dont get populated + +#define XMOS_VID 0x20b1 +#define XMOS_DEBUG_PID 0xF7D1 + +#define USB_REQUEST_TO_DEV 0x21 /* D7 Data direction: 0 (Host to device) + * D6:5 Type: 01 (Class) + * D4:0 Receipient: 1 (Interface) */ +#define USB_REQUEST_FROM_DEV 0xa1 + +#define USB_CS_INTERFACE 0x24 +#define USB_INPUT_TERM_TYPE 0x02 +#define USB_MIXER_UNIT_TYPE 0x04 +#define USB_FEATURE_UNIT_TYPE 0x06 + +#define INPUT_TERMINAL USB_INPUT_TERM_TYPE +#define EXTENSION_UNIT 0x9 +#define CS_INTERFACE USB_CS_INTERFACE +#define FEATURE_UNIT 0x06 + +#define CS_XU_SEL 0x6 +#define MU_MIXER_CONTROL 0x1 + +// Output from PC +#define USB_STREAMING 0x01 +// Input to device +//#define MICROPHONE 0x02 + +#define ID_XU_OUT 51 +#define ID_XU_IN 52 + +#define OFFSET_BLENGTH 0 +#define OFFSET_BDESCRIPTORTYPE 1 +#define OFFSET_BDESCRIPTORSUBTYPE 2 +#define OFFSET_BUNITID 3 + +#define OFFSET_FU_BSOURCEID 4 + +#define OFFSET_XU_BNRINPINS 6 +#define OFFSET_XU_BSOURCEID 7 + +#define OFFSET_IT_WTERMINALTYPE 5 +#define OFFSET_IT_BNRCHANNELS 8 +#define OFFSET_IT_ICHANNELNAMES 13 + +typedef struct +{ + double min; + double max; + double res; + double weight; +} mixer_node; + +typedef struct +{ + unsigned int id; + unsigned int num_inputs; + char input_names[USB_MIXER_INPUTS][USB_MIXER_MAX_NAME_LEN]; /* Current mixer input names - + * we dont really need to store these */ + int input_connections[USB_MIXER_INPUTS]; + unsigned int num_outputs; + char output_names[USB_MIXER_INPUTS][USB_MIXER_MAX_NAME_LEN]; + unsigned int num_inPins; + mixer_node nodes[USB_MIXER_INPUTS * USB_MIXER_OUTPUTS]; +} usb_mixer_device; + +typedef struct { + int cur; + int default_value; + char name[USB_MIXER_MAX_NAME_LEN]; + enum usb_chan_type ctype; +}channel_map_node; + +typedef struct { + int numInputs; + int numOutputs; + channel_map_node map[USB_MAX_CHANNEL_MAP_SIZE]; +}channel_mapp; + +typedef struct +{ + unsigned int id; + unsigned int numInputs; + char inputStrings[USB_MIXER_INPUTS*4][USB_MIXER_MAX_NAME_LEN]; /* Complete list of all possible inputs */ + unsigned int numOutputs; + unsigned int state[USB_MIXER_INPUTS]; +} t_usb_mixSel; + +typedef struct { + unsigned int device_open; + unsigned int num_usb_mixers; + usb_mixer_device usb_mixer[USB_MIXERS]; + t_usb_mixSel usb_mixSel[USB_MIXERS]; + + channel_mapp audChannelMap; + channel_mapp usbChannelMap; + + +} usb_mixer_handle; + +#if defined(__APPLE__) +typedef unsigned char char_style; +#elif defined(_WIN32) +typedef char char_style; +#endif + +static usb_mixer_handle *usb_mixers = NULL; + +#if defined(__APPLE__) + static libusb_device_handle *devh = NULL; +#elif defined(_WIN32) + static TUsbAudioHandle devh; +#endif + +/* Issue a generic control/class GET request to a specific unit in the Audio Interface */ +int usb_audio_class_get(unsigned char bRequest, unsigned char cs, unsigned char cn, unsigned short unitID, unsigned short wLength, unsigned char *data) +{ +#if defined(__APPLE__) + return libusb_control_transfer(devh, + USB_REQUEST_FROM_DEV, + bRequest, + (cs<<8) | cn, /* wValue */ + (unitID & 0xff) << 8 | 0x0, + data, + wLength, + 0); +#elif defined(_WIN32) + return gDrvApi.TUSBAUDIO_AudioControlRequestGet(devh, + unitID, + bRequest, + cs, + cn, + data, + wLength, + NULL, + 1000); +#endif +} + +/* Issue a generic control/class SET request to a specific unit in the Audio Interface */ +int usb_audio_class_set(unsigned char bRequest, unsigned char cs, unsigned char cn, unsigned short unitID, unsigned short wLength, unsigned char *data) +{ +#if defined(__APPLE__) + return libusb_control_transfer(devh, + USB_REQUEST_TO_DEV, + bRequest, + (cs<<8) | cn, /* wValue */ + (unitID & 0xff) << 8 | 0x0, + data, + wLength, + 0); +#elif defined(_WIN32) + return gDrvApi.TUSBAUDIO_AudioControlRequestSet(devh, + unitID, + bRequest, + cs, + cn, + data, + wLength, + NULL, + 1000); +#endif +} + +/* Note, this never get cached in an object since it can change on the device side */ +int usb_mixer_mem_get(unsigned int mixer, unsigned offset, unsigned char *data) +{ +#if defined(__APPLE__) + return libusb_control_transfer(devh, + USB_REQUEST_FROM_DEV, /* nRequest */ + MEM, /* bRequest */ + offset, /* wValue */ + (usb_mixers->usb_mixer[mixer].id & 0xff) << 8 | 0x0, /* wIndex */ + data, 64, 0); +#elif defined(_WIN32) + return gDrvApi.TUSBAUDIO_AudioControlRequestGet(devh, + usb_mixers->usb_mixer[mixer].id, + MEM, + 0, // cs + offset, //was cn + &data, + 64, + NULL, + 1000); +#endif +} + +static const unsigned char *findUnit(const unsigned char *descs, int length, int id) +{ + const unsigned char *interface_data = descs; + while (length) + { + const unsigned char *interface_len = interface_data; + int bDescriptorType = *(interface_len + 1); + if (bDescriptorType == CS_INTERFACE) + { + int unit_id = *(interface_len + 3); + if (unit_id == id) + { + return interface_len; + } + } + interface_data+=*interface_len; + length -= *interface_len; + } + return NULL; +} + +static int get_num_mixer_units(const unsigned char *data, int length) { + const unsigned char *interface_data = data; + int interface_len = length; + int num_mixer_units_found = 0; + + while (interface_len) { + const unsigned char *interfaces = interface_data; + int interface_type = *(interfaces + 1); + int unit_type = *(interfaces + 2); + if (interface_type == USB_CS_INTERFACE && unit_type == USB_MIXER_UNIT_TYPE) { + num_mixer_units_found++; + } + interface_data+=*interfaces; + interface_len -= *interfaces; + } + + return num_mixer_units_found; +} + +static double dev_get_mixer_value(unsigned int mixer, unsigned int nodeId) +{ + short data; + unsigned char cs = 0; /* Device doesnt use CS for getting/setting mixer weights */ + usb_audio_class_get(CUR, cs, nodeId, usb_mixers->usb_mixer[mixer].id, 2,(unsigned char *) &data); + return ((double) data / 256); +} + +/* Populates min, max, res */ +static unsigned short dev_get_mixer_range(unsigned int mixer, unsigned int channel, + double *min, double *max, double *res) +{ + short data[64]; + + short min2, max2, res2; + + usb_audio_class_get(RANGE, MU_MIXER_CONTROL, channel, usb_mixers->usb_mixer[mixer].id, 8, (unsigned char *) data); + + min2 = data[1]; + max2 = data[2]; + res2 = data[3]; + //printf("%f, %f, %f\n", (double)min2/256, (double)max2/256, (double) res2/256); + *min = (double)min2/256; + *max = (double)max2/256; + *res = (double)res2/256; + + return 0; +} + +int dev_get_channel_map(int channel, int unitId) +{ + short data; + usb_audio_class_get(CUR, 0, channel, unitId, 2,(unsigned char *) &data); + return data; +} + +static int dev_set_channel_map(int channel, int val, int unitId) +{ + short value = val; + usb_audio_class_set(CUR, 0, channel, unitId, 1, (unsigned char *)&value); + return 0; +} + +static int mixer_update_all_nodes(unsigned int mixer_index) +{ + int i = 0; + int j = 0; + double min, max, res; + + for (i = 0; i < usb_mixers->usb_mixer[mixer_index].num_inputs; i++) + { + for (j = 0; j < usb_mixers->usb_mixer[mixer_index].num_outputs; j++) + { + dev_get_mixer_range(mixer_index, (i*usb_mixers->usb_mixer[mixer_index].num_outputs)+j, &min, &max, &res); + + usb_mixers->usb_mixer[mixer_index].nodes[(i*usb_mixers->usb_mixer[mixer_index].num_outputs)+j].min = min; + usb_mixers->usb_mixer[mixer_index].nodes[(i*usb_mixers->usb_mixer[mixer_index].num_outputs)+j].max = max; + usb_mixers->usb_mixer[mixer_index].nodes[(i*usb_mixers->usb_mixer[mixer_index].num_outputs)+j].res = res; + //printf("%f, %f, %f\n", min, max, res); + + usb_mixers->usb_mixer[mixer_index].nodes[(i*usb_mixers->usb_mixer[mixer_index].num_outputs)+j].weight = + dev_get_mixer_value(mixer_index, (i*usb_mixers->usb_mixer[mixer_index].num_outputs)+j); + } + } + return 0; +} + + +/* Start at unit %id, find it in descs, keep recursively parsing up path(s) until get to Input Term and add strings */ +int addStrings(const unsigned char *data, int length, int mixer_index, int id, int chanCount) +{ + const unsigned char *currentUnitPtr = NULL; + + /* Find this unit in the descs */ + currentUnitPtr = findUnit(data, length, id); +#if defined(_WIN32) + TUsbAudioStatus st; +#endif + + if(currentUnitPtr != NULL) + { + /* Check if unit is a Input term */ + if(*(currentUnitPtr+OFFSET_BDESCRIPTORSUBTYPE) == INPUT_TERMINAL) + { + + /* Get channel names */ +#ifdef DEBUG + printf("Input terminal found on path (ID: %d): %d channels, total: %d\n",*(currentUnitPtr+OFFSET_BUNITID), + *(currentUnitPtr+OFFSET_IT_BNRCHANNELS), chanCount); +#endif + + int iChannelNames = *(currentUnitPtr+OFFSET_IT_ICHANNELNAMES); + int wTerminalType = *(currentUnitPtr+OFFSET_IT_WTERMINALTYPE); + +#ifdef DEBUG + printf("iChannelNames: %d wTerminalType: %d\n", iChannelNames, wTerminalType); + + printf("Channels found:\n"); + +#endif + for(int i = 0; i < *(currentUnitPtr+OFFSET_IT_BNRCHANNELS); i++) + { + char_style mixer_input_name[USB_MIXER_MAX_NAME_LEN]; +#if defined(__APPLE__) + memset(mixer_input_name, 0 ,USB_MIXER_MAX_NAME_LEN); +#elif defined(_WIN32) + WCHAR mixer_input_name_wchar[USB_MIXER_MAX_NAME_LEN]; +#endif + if (wTerminalType == 1) + { + strcpy(usb_mixers->usb_mixSel[mixer_index].inputStrings[chanCount], "DAW - "); + + //usb_mixers->channel_maps[usb_mixers->num_channel_maps].ctype = (enum usb_chan_type) USB_CHAN_OUT; + + usb_mixers->audChannelMap.numOutputs = usb_mixers->audChannelMap.numOutputs +1; + + usb_mixers->audChannelMap.map[usb_mixers->audChannelMap.numInputs].ctype = (enum usb_chan_type) USB_CHAN_OUT; + usb_mixers->usbChannelMap.map[usb_mixers->usbChannelMap.numInputs].ctype = (enum usb_chan_type) USB_CHAN_OUT; + + } + else + { + strcpy(usb_mixers->usb_mixSel[mixer_index].inputStrings[chanCount], "AUD - "); + + //usb_mixers->channel_maps[usb_mixers->num_channel_maps].ctype = (enum usb_chan_type) USB_CHAN_IN; + + usb_mixers->audChannelMap.map[usb_mixers->audChannelMap.numInputs].ctype = (enum usb_chan_type) USB_CHAN_IN; + usb_mixers->usbChannelMap.map[usb_mixers->usbChannelMap.numInputs].ctype = (enum usb_chan_type) USB_CHAN_IN; + + + usb_mixers->usbChannelMap.numOutputs = usb_mixers->usbChannelMap.numOutputs +1; + } + /* Get relevant string descriptor */ +#if defined(__APPLE__) + libusb_get_string_descriptor_ascii(devh, iChannelNames+i, mixer_input_name, + USB_MIXER_MAX_NAME_LEN - strlen(usb_mixers->usb_mixSel[mixer_index].inputStrings[chanCount])); +#elif defined(_WIN32) + st = gDrvApi.TUSBAUDIO_GetUsbStringDescriptorString(devh, iChannelNames + i, 0, mixer_input_name_wchar, USB_MIXER_MAX_NAME_LEN - strlen(usb_mixers->usb_mixSel[mixer_index].inputStrings[chanCount])); + if (TSTATUS_SUCCESS != st) { + return USB_MIXER_FAILURE; + } + wcstombs(mixer_input_name, mixer_input_name_wchar, USB_MIXER_MAX_NAME_LEN); +#endif + strcat(usb_mixers->usb_mixSel[mixer_index].inputStrings[chanCount], (char *)mixer_input_name); + + /* Add to channel mappers also */ + //strcat(usb_mixers->channel_maps[usb_mixers->num_channel_maps].name, (char *)mixer_input_name); + strcat(usb_mixers->audChannelMap.map[usb_mixers->audChannelMap.numInputs ].name, (char *)mixer_input_name); + strcat(usb_mixers->usbChannelMap.map[usb_mixers->audChannelMap.numInputs].name, (char *)mixer_input_name); + + usb_mixers->audChannelMap.numInputs = usb_mixers->audChannelMap.numInputs +1; + usb_mixers->usbChannelMap.numInputs = usb_mixers->usbChannelMap.numInputs +1; + + //usb_mixers->num_channel_maps = usb_mixers->num_channel_maps+1; + chanCount++; + } + +#ifdef DEBUG + int meh = chanCount - *(currentUnitPtr+OFFSET_IT_BNRCHANNELS); + for(int i = 0; i < *(currentUnitPtr+OFFSET_IT_BNRCHANNELS); i++) + { + printf("%d: %s\n", i,usb_mixers->usb_mixSel[mixer_index].inputStrings[meh+i]); + } + + printf("\n\n"); +#endif + } + else + { + /* Unit not a input terminal, keep going... */ + if(*(currentUnitPtr+OFFSET_BDESCRIPTORSUBTYPE) == FEATURE_UNIT) + { + chanCount = addStrings(data, length, mixer_index, *(currentUnitPtr+OFFSET_FU_BSOURCEID), chanCount); + } + else if(*(currentUnitPtr+OFFSET_BDESCRIPTORSUBTYPE) == EXTENSION_UNIT) + { + /* Multiple inputs for Extention units */ + for (int i = 0; i < *(currentUnitPtr+OFFSET_XU_BNRINPINS); i++) + { + chanCount = addStrings(data, length, mixer_index, *(currentUnitPtr+OFFSET_XU_BSOURCEID+i), chanCount); + } + } + else + { + fprintf(stderr,"ERROR: Currently don't support this unit: %d\n", + *(currentUnitPtr+OFFSET_BDESCRIPTORSUBTYPE)); + exit(1); + } + } + } + else + { + fprintf(stderr,"ERROR: Couldn't find unit %d in descs\n", id ); + exit(1); + } + + return chanCount; +} + +/* Returns the source of an mix sel output */ +static unsigned char get_mixsel_value(unsigned int mixer, unsigned int channel) +{ + + unsigned char bRequest = CUR; + //unsigned cs = CS_XU_SEL; + unsigned char cs = 1; /* Note, currently the host app configures all mix sel's indentically, so if we get one they all should match */ + unsigned char cn = channel; + unsigned short unitId = usb_mixers->usb_mixSel[mixer].id; + unsigned short wLength = 1; + unsigned char data[64]; + + usb_audio_class_get(CUR, cs, cn, unitId, wLength, (unsigned char *)data); + + return data[0]; +} + +static int get_mixer_info(const unsigned char *data, int length, unsigned int mixer_index +#if defined(__APPLE__) + ,libusb_config_descriptor *config_desc +#endif +) +{ + const unsigned char *interface_data = data; + int interface_len = length; + int num_mixer_units_found = 0; + //const unsigned char *current_input_term_unit_ptr = NULL; + //int current_input_term_unit_index = 0; + //const unsigned char *current_feature_unit_ptr = NULL; + int devChanInputCount = 0; + + while (interface_len) + { + const unsigned char *interfaces = interface_data; + int interface_type = *(interfaces + 1); /* bDescriptorType */ + int unit_type = *(interfaces + 2); /* bDescriptorSubType */ + + /* Look for a mixer unit */ + if (interface_type == USB_CS_INTERFACE && unit_type == USB_MIXER_UNIT_TYPE) + { + int unit_id = *(interfaces + 3); /* bUnitId */ + int bNrInPins = *(interfaces + 4); + int num_in = *(interfaces + 4); /* bNrInPins - NOTE This is pins NOT channels!! */ + /* Total number of inputs is the sum of the channel counts in the input + * clusters. We need to inspect the sources to gain channel counts */ + int chansIn = 0; +#ifdef DEBUG + printf("Found Mixer Unit %d with %d inputs\n", unit_id, bNrInPins); + printf("Inspecting mixer inputs... \n\n"); +#endif + /* For each input pin need to find out inspect its output cluster */ + for (int i = 1; i <= bNrInPins; i++) + { + int sourceId = *(interfaces+4+i); +#ifdef DEBUG + printf("baSourceID(%d): %d\n", i, sourceId); +#endif + + /* Find the unit in the config desc */ + int descsLength = length; + const unsigned char *descsData = data; + int found = 0; + int bDescriptorSubType; + int bDescriptorType; + int bUnitId; + + while(descsLength) + { + int currentLength = *descsData; + bDescriptorSubType = *(descsData + 2); + bDescriptorType = *(descsData + 1); + + if(bDescriptorType == USB_CS_INTERFACE) + { + if(bDescriptorSubType == USB_FEATURE_UNIT_TYPE || + bDescriptorSubType == USB_INPUT_TERM_TYPE || + bDescriptorSubType == EXTENSION_UNIT ) + { + bUnitId = *(descsData+3); + if(bUnitId == sourceId) + { + found = 1; + break; + } + } + } + + descsData+=currentLength; + descsLength -= currentLength; + } + + if(found) + { + int bNrChannelsOffset = 0; + int bNrChannels; + + /* We found the unit in the descs. Now inspect channel cluster */ +#ifdef DEBUG + printf("Found unit %d, type %d\n", bUnitId, bDescriptorSubType); +#endif + /* We are looking for bNrChannels in the descs, this is in a different location in desc depending + * on unit type */ + switch(bDescriptorSubType) + { + case USB_INPUT_TERM_TYPE: + bNrChannelsOffset = 8; + bNrChannels = *(descsData+bNrChannelsOffset); + break; + case EXTENSION_UNIT: + bNrChannelsOffset = 7 + *(descsData+6); + bNrChannels = *(descsData+bNrChannelsOffset); + + break; + default: + printf("ERR\n"); + exit(1); + break; + } +#ifdef DEBUG + printf("Output chan count: %d\n\n", bNrChannels); +#endif + chansIn += bNrChannels; + + } + else + { + fprintf(stderr,"ERROR: Mixer input connected to something we dont understand...\n"); + exit(1); + } + } + + /* get number of output channels straight from mixer unit descriptor: bNrChannels */ + int num_out = *(interfaces + 5 + num_in); +#ifdef DEBUG + printf("Mixer Unit parse complete: bUnitId: %d, Total Input Chans: %d, Output Chans: %d\n\n", unit_id, chansIn, num_out); +#endif + usb_mixers->usb_mixer[mixer_index].id = unit_id; + usb_mixers->usb_mixer[mixer_index].num_inputs = chansIn; + usb_mixers->usb_mixer[mixer_index].num_inPins = bNrInPins; + usb_mixers->usb_mixer[mixer_index].num_outputs = num_out; + + /* Go through all input pins */ + const unsigned char *in_unit_start_ptr = interfaces + 5; + // const unsigned char *currentUnitPtr = NULL; + // int current_input_term_unit_id = 0; + + /* We expect this to be a single input from an XU, but we'll keep it slightly generic here */ + for (int num = 0; num < usb_mixers->usb_mixer[mixer_index].num_inPins; num++) + { + /* Save source ID */ + usb_mixers->usb_mixer[mixer_index].input_connections[num] = *(in_unit_start_ptr+num); +#ifdef DEBUG + printf("Inspecting for Input Terms from mixer unit input pin %d (id: %d)\n", + num,usb_mixers->usb_mixer[mixer_index].input_connections[num]); +#endif + + devChanInputCount = addStrings(data, length, mixer_index, + usb_mixers->usb_mixer[mixer_index].input_connections[num], devChanInputCount); + + } + + /* The the first input pin at the mix select for the moment. + * probably should be checking if its an XU here */ + usb_mixers->usb_mixSel[mixer_index].id = usb_mixers->usb_mixer[mixer_index].input_connections[0]; + usb_mixers->usb_mixSel[mixer_index].numInputs = devChanInputCount; + usb_mixers->usb_mixSel[mixer_index].numOutputs = chansIn; + + /* Set up mixer output names */ + char_style mixer_output_name[USB_MIXER_MAX_NAME_LEN]; + unsigned int iChannelNames = *(interfaces + 10 + bNrInPins); +#if defined(_WIN32) + WCHAR mixer_output_name_wchar[USB_MIXER_MAX_NAME_LEN]; +#endif + + //OSX + for (int i = 0; i < usb_mixers->usb_mixer[mixer_index].num_outputs; i++) + { + /* Get relevant string descriptor */ + strcpy(usb_mixers->usb_mixer[mixer_index].output_names[i], "MIX - "); +#if defined(__APPLE__) + libusb_get_string_descriptor_ascii(devh, iChannelNames+i, mixer_output_name, + USB_MIXER_MAX_NAME_LEN - strlen(usb_mixers->usb_mixSel[mixer_index].inputStrings[i])); +#elif defined(_WIN32) + TUsbAudioStatus st = gDrvApi.TUSBAUDIO_GetUsbStringDescriptorString(devh, iChannelNames + i, 0, mixer_output_name_wchar, USB_MIXER_MAX_NAME_LEN - strlen(usb_mixers->usb_mixSel[mixer_index].inputStrings[i])); + if (TSTATUS_SUCCESS != st) { + return USB_MIXER_FAILURE; + } + wcstombs(mixer_output_name, mixer_output_name_wchar, USB_MIXER_MAX_NAME_LEN); +#endif + strcat(usb_mixers->usb_mixer[mixer_index].output_names[i], (char *)mixer_output_name); + } + } + + interface_data+=*interfaces; + interface_len -= *interfaces; + } + + return num_mixer_units_found; +} +//OSX +static int find_xmos_device(unsigned int id) +{ + int i = 0; + int found = -1; +#if defined(__APPLE__) + libusb_device *dev; + libusb_device **devs; + + libusb_get_device_list(NULL, &devs); + while ((dev = devs[i]) != NULL) + { + struct libusb_device_descriptor desc; + libusb_get_device_descriptor(dev, &desc); + // printf("VID = 0x%x, PID = 0x%x\n", desc.idVendor, desc.idProduct); + if (desc.idVendor == XMOS_VID && desc.idProduct < XMOS_DEBUG_PID) + { + found++; + if (found == id) + { + id = i; + } + } + i++; + } + if (found == -1) { + fprintf(stderr, "ERROR :: No device detected\n"); + return USB_MIXER_FAILURE; + } + else if (found > 0) { + fprintf(stderr, "ERROR :: Multiple devices detected\n"); + return USB_MIXER_FAILURE; + } + +#elif defined(_WIN32) + TUsbAudioStatus st; + + unsigned int devcnt = gDrvApi.TUSBAUDIO_GetDeviceCount(); + if (devcnt == 0) { + fprintf(stderr, "ERROR :: No device detected\n"); + return USB_MIXER_FAILURE; + } + else if (devcnt > 1) { + fprintf(stderr, "ERROR :: Multiple devices detected\n"); + return USB_MIXER_FAILURE; + } + + st = gDrvApi.TUSBAUDIO_OpenDeviceByIndex(0, &devh); +#endif + if ( +#if defined(__APPLE__) + libusb_open(devs[id], &devh) < 0 +#elif defined(_WIN32) + TSTATUS_SUCCESS != st +#endif + ) + { + devh = 0; + // skip + } + else + { +#if defined(__APPLE__) + libusb_config_descriptor *config_desc = NULL; + libusb_get_active_config_descriptor(devs[id], &config_desc); + if (config_desc != NULL) + { + //unsigned int num_mixers_found = 0; + + usb_mixers->device_open = 1; + usb_mixers->num_usb_mixers = 0; + + for (int j = 0; j < config_desc->bNumInterfaces; j++) + { + const libusb_interface_descriptor *inter_desc = + ((libusb_interface *)&config_desc->interface[j])->altsetting; + + usb_mixers->num_usb_mixers += get_num_mixer_units(inter_desc->extra, inter_desc->extra_length); + } + + for (int j = 0; j < config_desc->bNumInterfaces; j++) + { + const libusb_interface_descriptor *inter_desc = + ((libusb_interface *)&config_desc->interface[j])->altsetting; + get_mixer_info(inter_desc->extra, inter_desc->extra_length, j, config_desc); + } + } +#elif defined(_WIN32) + unsigned int numBytes = 0; + unsigned char descBuffer[64 * 1024]; + + st = gDrvApi.TUSBAUDIO_GetUsbConfigDescriptor(devh, descBuffer, 64 * 1024, &numBytes); + if (TSTATUS_SUCCESS != st) { + return USB_MIXER_FAILURE; + } + + usb_mixers->num_usb_mixers = get_num_mixer_units(descBuffer, numBytes); + get_mixer_info(descBuffer, numBytes, 0); +#endif + /* Init channel maps from device */ + for(int i = 0; i < usb_mixers->audChannelMap.numOutputs; i++) + { + usb_mixers->audChannelMap.map[i].cur = dev_get_channel_map(i, ID_XU_OUT); + + } + + for(int i = 0; i < usb_mixers->usbChannelMap.numOutputs; i++) + { + usb_mixers->usbChannelMap.map[i].cur = dev_get_channel_map(i, ID_XU_IN); + //printf("%d\n", usb_mixers->usbChannelMap.map[i].cur); + } + + /* Now add the mix outputs */ + for(int i = 0; i < usb_mixers->num_usb_mixers; i++) + { + for(int j = 0; j < usb_mixers->usb_mixer[i].num_outputs;j++) + { + //strcpy(usb_mixers->channel_maps[usb_mixers->num_channel_maps].name, usb_mixers->usb_mixer[i].output_names[j]); + //usb_mixers->channel_maps[usb_mixers->num_channel_maps].ctype = (enum usb_chan_type) USB_CHAN_MIXER; + //usb_mixers->num_channel_maps = usb_mixers->num_channel_maps+1; + + usb_mixers->audChannelMap.map[usb_mixers->audChannelMap.numInputs].ctype = (enum usb_chan_type) USB_CHAN_MIXER; + strcpy(usb_mixers->audChannelMap.map[usb_mixers->audChannelMap.numInputs].name, usb_mixers->usb_mixer[i].output_names[j]); + usb_mixers->audChannelMap.numInputs = usb_mixers->audChannelMap.numInputs +1; + + usb_mixers->usbChannelMap.map[usb_mixers->usbChannelMap.numInputs].ctype = (enum usb_chan_type) USB_CHAN_MIXER; + strcpy(usb_mixers->usbChannelMap.map[usb_mixers->usbChannelMap.numInputs].name, usb_mixers->usb_mixer[i].output_names[j]); + usb_mixers->usbChannelMap.numInputs = usb_mixers->usbChannelMap.numInputs +1; + } + } + + if(devh) + { + /* Populate mixer input strings */ + for(int i = 0; i < usb_mixers->num_usb_mixers; i++) + { + mixer_update_all_nodes(i); + + /* Get current each mixer input and populate channel number state and strings from device */ + for (int j = 0; j < usb_mixers->usb_mixSel[i].numOutputs; j++) + { + /* Get value from device */ + int inputChan = get_mixsel_value(i, j); + + usb_mixers->usb_mixSel[i].state[j] = inputChan; + + /* Look up channel string */ + strcpy(usb_mixers->usb_mixer[i].input_names[j], usb_mixers->usb_mixSel[i].inputStrings[inputChan]); + } + } + } + } +#if defined(__APPLE__) + libusb_free_device_list(devs, 1); +#endif + return devh ? 0 : -1; +} + +// End of libusb interface functions + +int usb_mixer_connect() +{ + // Allocate internal storage + usb_mixers = (usb_mixer_handle *)malloc(sizeof(usb_mixer_handle)); + memset(usb_mixers, 0, sizeof(usb_mixer_handle)); + +#if defined(_WIN32) + gDrvApi.LoadByGUID(_T("{E5A2658B-817D-4A02-A1DE-B628A93DDF5D}")); + TUsbAudioStatus st = gDrvApi.TUSBAUDIO_EnumerateDevices(); +#endif + + if ( +#if defined(__APPLE__) + libusb_init(NULL) < 0 +#elif defined(_WIN32) + TSTATUS_SUCCESS != st +#endif + ) + { + // printf("libusb_init failure\n"); + return USB_MIXER_FAILURE; + } + + if (find_xmos_device(0) < 0) + { + // printf("find_xmos_device failure\n"); + return USB_MIXER_FAILURE; + } + + return USB_MIXER_SUCCESS; +} + +int usb_mixer_disconnect() { +#if defined(__APPLE__) + libusb_close(devh); + libusb_exit(NULL); +#elif(_WIN32) + if (devh != 0) + { + gDrvApi.TUSBAUDIO_CloseDevice(devh); + } +#endif + + free(usb_mixers); + + return USB_MIXER_SUCCESS; +} + +/* Getter for num_usb_mixers */ +int usb_mixer_get_num_mixers() +{ + return usb_mixers->num_usb_mixers; +} + +int usb_mixer_get_num_outputs(unsigned int mixer) +{ + return usb_mixers->usb_mixer[mixer].num_outputs; +} + +int usb_mixer_get_num_inputs(unsigned int mixer) +{ + return usb_mixers->usb_mixer[mixer].num_inputs; +} + +int usb_mixer_get_layout(unsigned int mixer, unsigned int *inputs, unsigned int *outputs) { + *inputs = usb_mixers->usb_mixer[mixer].num_inputs; + *outputs = usb_mixers->usb_mixer[mixer].num_outputs; + return 0; +} + +/* MixSel getters and setters */ +char *usb_mixsel_get_input_string(unsigned int mixer, unsigned int input) +{ + return usb_mixers->usb_mixSel[mixer].inputStrings[input]; +} + +int usb_mixsel_get_input_count(unsigned int mixer) +{ + return usb_mixers->usb_mixSel[mixer].numInputs; +} + +int usb_mixsel_get_output_count(unsigned int mixer) +{ + return usb_mixers->usb_mixSel[mixer].numOutputs; +} + +char *usb_mixer_get_input_name(unsigned int mixer, unsigned int input) +{ + return usb_mixers->usb_mixer[mixer].input_names[input]; +} + +char *usb_mixer_get_output_name(unsigned int mixer, unsigned int output) +{ + return usb_mixers->usb_mixer[mixer].output_names[output]; +} + +unsigned char usb_mixsel_get_state(unsigned int mixer, unsigned int channel) +{ + return usb_mixers->usb_mixSel[mixer].state[channel]; +} + +void usb_mixsel_set_state(unsigned int mixer, unsigned int dst, unsigned int src) +{ + // Write to device + // Note, we are updating inputs to all mixers here with a hard-coded 0, though the device allows + // for separate input mapping per mixer + unsigned bRequest = CUR; + unsigned cs = 0; + unsigned cn = usb_mixers->usb_mixSel[mixer].id; + unsigned wLength = 1; + usb_audio_class_set(CUR, 0, dst, usb_mixers->usb_mixSel[mixer].id, wLength, (unsigned char *)&src); + + // Update object state + usb_mixers->usb_mixSel[mixer].state[dst] = src; + + // Update local object strings + // TODO we don't really need to store strings since we can look them up...*/ + strcpy(usb_mixers->usb_mixer[mixer].input_names[dst], usb_mixers->usb_mixSel[mixer].inputStrings[src]); +} + +double usb_mixer_get_value(unsigned int mixer, unsigned int nodeId) +{ + return (double)usb_mixers->usb_mixer[mixer].nodes[nodeId].weight; +} + +double usb_mixer_get_res(unsigned int mixer, unsigned int nodeId) +{ + return (double)usb_mixers->usb_mixer[mixer].nodes[nodeId].res; +} + +double usb_mixer_get_min(unsigned int mixer, unsigned int nodeId) +{ + return (double)usb_mixers->usb_mixer[mixer].nodes[nodeId].min; +} + +double usb_mixer_get_max(unsigned int mixer, unsigned int nodeId) +{ + return (double)usb_mixers->usb_mixer[mixer].nodes[nodeId].max; +} + +int usb_mixer_set_value(unsigned int mixer, unsigned int nodeId, double val) +{ + /* check if update required */ + if(usb_mixers->usb_mixer[mixer].nodes[nodeId].weight != val) + { + /* update local object */ + usb_mixers->usb_mixer[mixer].nodes[nodeId].weight= val; + + /* write to device */ + short value = (short) (val * 256); + + unsigned char cs = 0; /* Device doesnt use CS for setting/getting mixer nodes */ + unsigned char cn = nodeId & 0xff; + usb_audio_class_set(CUR, cs, cn, usb_mixers->usb_mixer[mixer].id, 2, (unsigned char *)&value); + + } + return 0; +} + +int usb_mixer_get_range(unsigned int mixer, unsigned int mixer_unit, int *min, int *max, int *res) +{ + // range 0x02 + return 0; +} + +int usb_get_usb_channel_map(int channel) +{ + return usb_mixers->usbChannelMap.map[channel].cur; +} + +int usb_get_aud_channel_map(int channel) +{ + return usb_mixers->audChannelMap.map[channel].cur; +} + +int usb_set_aud_channel_map(int channel, int val) +{ + /* Check if update required */ + if(usb_mixers->audChannelMap.map[channel].cur != val) + { + /* Update local object */ + usb_mixers->audChannelMap.map[channel].cur = val; + + /* Write setting to dev */ + dev_set_channel_map(channel, val, ID_XU_OUT); + } + return 0; +} + +int usb_set_usb_channel_map(int channel, int val) +{ + /* Check if update required */ + if(usb_mixers->usbChannelMap.map[channel].cur != val) + { + /* Update local object */ + usb_mixers->usbChannelMap.map[channel].cur = val; + + /* Write setting to dev */ + dev_set_channel_map(channel, val, ID_XU_IN); + } + return 0; +} + +enum usb_chan_type usb_get_aud_channel_map_type(int channel) +{ + return usb_mixers->audChannelMap.map[channel].ctype; +} + +enum usb_chan_type usb_get_usb_channel_map_type(int channel) +{ + return usb_mixers->usbChannelMap.map[channel].ctype; +} + +char *usb_get_aud_channel_map_name(int channel) +{ + return usb_mixers->audChannelMap.map[channel].name; +} + +char *usb_get_usb_channel_map_name(int channel) +{ + return usb_mixers->usbChannelMap.map[channel].name; +} + +int usb_get_aud_channel_map_num_outputs() +{ + return usb_mixers->audChannelMap.numOutputs; +} +int usb_get_usb_channel_map_num_outputs() +{ + return usb_mixers->usbChannelMap.numOutputs; +} + +int usb_get_aud_channel_map_num_inputs() +{ + return usb_mixers->audChannelMap.numInputs; +} +int usb_get_usb_channel_map_num_inputs() +{ + return usb_mixers->usbChannelMap.numInputs; +} diff --git a/host_usb_mixer_control/usb_mixer.h b/host_usb_mixer_control/usb_mixer.h new file mode 100644 index 00000000..1c8bf70a --- /dev/null +++ b/host_usb_mixer_control/usb_mixer.h @@ -0,0 +1,108 @@ +// Copyright 2022-2023 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +#define USB_MIXER_SUCCESS 0 +#define USB_MIXER_FAILURE -1 + +#define USB_MIXERS 1 +#define USB_MIXER_INPUTS 18 +#define USB_MIXER_OUTPUTS 8 +#define USB_MAX_CHANNEL_MAP_SIZE 40 +#define USB_MIXER_MAX_NAME_LEN 64 + +enum usb_chan_type { + USB_CHAN_OUT=0, + USB_CHAN_IN=1, + USB_CHAN_MIXER=2 +}; + +/* A.14 Audio Class-Specific Request Codes */ +#define REQUEST_CODE_UNDEFINED 0x00 +#define CUR (1) +#define RANGE (2) +#define MEM (3) + +int usb_mixer_connect(); +int usb_mixer_disconnect(); + +/* MIXER UNIT(s) INTERFACE */ + +/* Returns total number of mixers in device */ +int usb_mixer_get_num_mixers(); + +/* Returns number of inputs and outputs for a selected mixer */ +int usb_mixer_get_layout(unsigned int mixer, unsigned int *inputs, unsigned int *outputs); + +/* Returns the name for a selected mixer input */ +char *usb_mixer_get_input_name(unsigned int mixer, unsigned int input); + +/* Returns the name for a selected mixer output */ +char *usb_mixer_get_output_name(unsigned int mixer, unsigned int output); + +/* Returns the current value of a selected mixer unit */ +double usb_mixer_get_value(unsigned int mixer, unsigned int mixer_unit); + +/* Sets the current value for a selected mixer unit */ +int usb_mixer_set_value(unsigned int mixer, unsigned int mixer_unit, double val); + +/* Returns the range values for a selected mixer unit */ +int usb_mixer_get_range(unsigned int mixer, unsigned int mixer_unit, double *min, double *max, double *res); + +/* Returns the number of bytes read from a mem request, data is stored in data */ +int usb_mixer_mem_get(unsigned int mixer, unsigned offset, unsigned char *data); + + +/* INPUT / OUTPUT / MIXER MAPPING UNIT INTERFACE */ + +/* Get the number of selectable inputs */ +int usb_mixsel_get_input_count(unsigned int mixer); + +/* Get the string of a input */ +char *usb_mixsel_get_input_string(unsigned int mixer, unsigned int channel); + +int usb_mixsel_get_output_count(unsigned int mixer); + +int usb_mixer_get_num_outputs(unsigned int mixer); + +int usb_mixer_get_num_inputs(unsigned int mixer); + +unsigned char usb_mixsel_get_state(unsigned int mixer, unsigned int channel); + +void usb_mixsel_set_state(unsigned int mixer, unsigned int dst, unsigned int src); + +int usb_set_usb_channel_map(int channel, int val); + + +/* Get the current map for a specified input / output / mixer channel */ +int usb_get_usb_channel_map(int channel); +int usb_get_aud_channel_map(int channel); + +/* Maps an input / output / mixer channel to another input / output / mixer channel */ +int usb_set_aud_channel_map(int channel, int val); +int usb_set_usb_channel_map(int channel, int val); + +/* Gets the name of a specified channel */ +char *usb_get_aud_channel_map_name(int channel); +char *usb_get_usb_channel_map_name(int channel); + +/* Get the type of a channel map */ +enum usb_chan_type usb_get_aud_channel_map_type(int channel); +enum usb_chan_type usb_get_usb_channel_map_type(int channel); + +int usb_get_aud_channel_map_num_outputs(); +int usb_get_usb_channel_map_num_outputs(); + +int usb_get_aud_channel_map_num_inputs(); +int usb_get_usb_channel_map_num_inputs(); + +/* CUSTOM/GENERIC AUDIO CLASS REQUESTS */ + +int usb_audio_class_get(unsigned char bRequest, unsigned char cs, unsigned char cn, unsigned short unitID, unsigned short wLength, unsigned char *data); + +int usb_audio_class_set(unsigned char bRequest, unsigned char cs, unsigned char cn, unsigned short unitID, unsigned short wLength, unsigned char *data); + +double usb_mixer_get_res(unsigned int mixer, unsigned int nodeId); + +double usb_mixer_get_min(unsigned int mixer, unsigned int nodeId) ; + +double usb_mixer_get_max(unsigned int mixer, unsigned int nodeId) ; diff --git a/lib_xua/.cproject b/lib_xua/.cproject deleted file mode 100644 index 08cfa4f2..00000000 --- a/lib_xua/.cproject +++ /dev/null @@ -1,860 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/lib_xua/.project b/lib_xua/.project deleted file mode 100644 index c752d2f7..00000000 --- a/lib_xua/.project +++ /dev/null @@ -1,42 +0,0 @@ - - - lib_xua - - - - - - com.xmos.cdt.core.ProjectInfoSyncBuilder - - - - - com.xmos.cdt.core.LegacyProjectCheckerBuilder - - - - - com.xmos.cdt.core.ModulePathBuilder - - - - - org.eclipse.cdt.managedbuilder.core.genmakebuilder - clean,full,incremental, - - - - - org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder - full,incremental, - - - - - - org.eclipse.cdt.core.cnature - org.eclipse.cdt.managedbuilder.core.managedBuildNature - org.eclipse.cdt.managedbuilder.core.ScannerConfigNature - com.xmos.cdt.core.XdeProjectNature - - diff --git a/lib_xua/.xproject b/lib_xua/.xproject deleted file mode 100644 index 2a5ae266..00000000 --- a/lib_xua/.xproject +++ /dev/null @@ -1 +0,0 @@ -lib_xuaXM-012639-SM diff --git a/lib_xua/api/xua.h b/lib_xua/api/xua.h index c52ff1a7..b3136b84 100644 --- a/lib_xua/api/xua.h +++ b/lib_xua/api/xua.h @@ -1,20 +1,22 @@ -// Copyright 2017-2021 XMOS LIMITED. +// Copyright 2017-2023 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. +#ifndef _XUA_H_ +#define _XUA_H_ -#ifndef __XUA_H__ -#define __XUA_H__ +#include #include "xua_conf_full.h" -#if __XC__ || __STDC__ +#ifndef __ASSEMBLER__ #include "xua_audiohub.h" - #include "xua_endpoint0.h" - #include "xua_buffer.h" + #include "xua_mixer.h" #endif -#if __XC__ +#ifdef __XC__ + #include "xua_clocking.h" + #include "xua_midi.h" #if XUA_NUM_PDM_MICS > 0 #include "xua_pdm_mic.h" #endif diff --git a/lib_xua/api/xua_audiohub.h b/lib_xua/api/xua_audiohub.h index e66296cb..4d018c1f 100644 --- a/lib_xua/api/xua_audiohub.h +++ b/lib_xua/api/xua_audiohub.h @@ -1,9 +1,9 @@ -// Copyright 2011-2021 XMOS LIMITED. +// Copyright 2011-2023 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. -#ifndef __XUA_AUDIOHUB_H__ -#define __XUA_AUDIOHUB_H__ +#ifndef _XUA_AUDIOHUB_H_ +#define _XUA_AUDIOHUB_H_ -#if __XC__ +#ifdef __XC__ #include "xccompat.h" #include "xs1.h" @@ -22,7 +22,7 @@ * * \param clk_audio_mclk Nullable clockblock to be clocked from master clock * - * \param clk_audio_mclk Nullable clockblock to be clocked from i2s clock + * \param clk_audio_bclk Nullable clockblock to be clocked from i2s bit clock * * \param p_mclk_in Master clock inport port (must be 1-bit) * @@ -34,21 +34,23 @@ * * \param p_i2s_adc Nullable array of ports for I2S data input lines * - * \param c_dig channel connected to the clockGen() thread for + * \param c_spdif_tx Channel connected to S/PDIF transmiter core from lib_spdif + * + * \param c_dig Channel connected to the clockGen() thread for * receiving/transmitting samples */ -void XUA_AudioHub(chanend ?c_aud, - clock ?clk_audio_mclk, - clock ?clk_audio_bclk, +void XUA_AudioHub(chanend ?c_aud, + clock ?clk_audio_mclk, + clock ?clk_audio_bclk, in port p_mclk_in, buffered _XUA_CLK_DIR port:32 ?p_lrclk, buffered _XUA_CLK_DIR port:32 ?p_bclk, - buffered out port:32 (&?p_i2s_dac)[I2S_WIRES_DAC], + buffered out port:32 (&?p_i2s_dac)[I2S_WIRES_DAC], buffered in port:32 (&?p_i2s_adc)[I2S_WIRES_ADC] -#if (XUA_SPDIF_TX_EN) //&& (SPDIF_TX_TILE != AUDIO_IO_TILE) +#if (XUA_SPDIF_TX_EN) || defined(__DOXYGEN__) , chanend c_spdif_tx #endif -#if((SPDIF_RX) || (ADAT_RX)) +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN || defined(__DOXYGEN__)) , chanend c_dig #endif #if (XUD_TILE != 0) && (AUDIO_IO_TILE == 0) && (XUA_DFU_EN == 1) @@ -78,4 +80,4 @@ void UserBufferManagementInit(); void UserBufferManagement(unsigned sampsFromUsbToAudio[], unsigned sampsFromAudioToUsb[]); -#endif // __XUA_AUDIOHUB_H__ +#endif // _XUA_AUDIOHUB_H_ diff --git a/lib_xua/api/xua_buffer.h b/lib_xua/api/xua_buffer.h index 302e3f67..d7d182d7 100644 --- a/lib_xua/api/xua_buffer.h +++ b/lib_xua/api/xua_buffer.h @@ -1,72 +1,63 @@ -// Copyright 2011-2021 XMOS LIMITED. +// Copyright 2011-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #ifndef __XUA_BUFFER_H__ #define __XUA_BUFFER_H__ #if __XC__ -#include "xua.h" +#include "xua_clocking.h" /* Required for pll_ref_if */ -/** USB Audio Buffering Core. +/** USB Audio Buffering Core(s). * - * This function buffers USB audio data between the XUD layer and the decouple - * thread. Most of the chanend parameters to the function should be connected to - * XUD_Manager() + * This function buffers USB audio data between the XUD and the audio subsystem. + * Most of the chanend parameters to the function should be connected to + * XUD_Manager(). The uses two cores. * - * \param c_aud_out Audio OUT endpoint channel connected to the XUD - * \param c_aud_in Audio IN endpoint channel connected to the XUD - * \param c_aud_fb Audio feedback endpoint channel connected to the XUD - * \param c_midi_from_host MIDI OUT endpoint channel connected to the XUD - * \param c_midi_to_host MIDI IN endpoint channel connected to the XUD - * \param c_int Audio clocking interrupt endpoint channel connected to the XUD - * \param c_clk_int Optional chanend connected to the clockGen() thread if present - * \param c_sof Start of frame channel connected to the XUD - * \param c_aud_ctl Audio control channel connected to Endpoint0() - * \param p_off_mclk A port that is clocked of the MCLK input (not the MCLK input itself) + * \param c_aud_out Audio OUT endpoint channel connected to the XUD + * \param c_aud_in Audio IN endpoint channel connected to the XUD + * \param c_aud_fb Audio feedback endpoint channel connected to the XUD + * \param c_midi_from_host MIDI OUT endpoint channel connected to the XUD + * \param c_midi_to_host MIDI IN endpoint channel connected to the XUD + * \param c_midi Channel connected to MIDI core + * \param c_int Audio clocking interrupt endpoint channel connected to the XUD + * \param c_clk_int Optional chanend connected to the clockGen() thread if present + * \param c_sof Start of frame channel connected to the XUD + * \param c_aud_ctl Audio control channel connected to Endpoint0() + * \param p_off_mclk A port that is clocked of the MCLK input (not the MCLK input itself) + * \param c_aud Channel connected to XUA_AudioHub() core + * \param i_pll_ref Interface to task that toggles reference pin to CS2100 */ - void XUA_Buffer( chanend c_aud_out, -#if (NUM_USB_CHAN_IN > 0) +#if (NUM_USB_CHAN_IN > 0) || defined(__DOXYGEN__) chanend c_aud_in, #endif #if (NUM_USB_CHAN_IN == 0) || defined (UAC_FORCE_FEEDBACK_EP) chanend c_aud_fb, #endif -#ifdef MIDI +#if defined(MIDI) || defined(__DOXYGEN__) chanend c_midi_from_host, chanend c_midi_to_host, chanend c_midi, #endif -#ifdef IAP - chanend c_iap_from_host, - chanend c_iap_to_host, -#ifdef IAP_INT_EP - chanend c_iap_to_host_int, -#endif - chanend c_iap, -#ifdef IAP_EA_NATIVE_TRANS - chanend c_iap_ea_native_out, - chanend c_iap_ea_native_in, - chanend c_iap_ea_native_ctrl, - chanend c_iap_ea_native_data, -#endif -#endif -#if (SPDIF_RX) || (ADAT_RX) +#if XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN || defined(__DOXYGEN__) chanend ?c_int, chanend ?c_clk_int, #endif chanend c_sof, chanend c_aud_ctl, in port p_off_mclk -#if( 0 < HID_CONTROLS ) +#if (HID_CONTROLS) , chanend c_hid #endif , chanend c_aud +#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) || defined(__DOXYGEN__) + , client interface pll_ref_if i_pll_ref +#endif ); void XUA_Buffer_Ep(chanend c_aud_out, -#if (NUM_USB_CHAN_IN > 0) +#if (NUM_USB_CHAN_IN > 0) || defined(__DOXYGEN__) chanend c_aud_in, #endif #if (NUM_USB_CHAN_IN == 0) || defined (UAC_FORCE_FEEDBACK_EP) @@ -77,32 +68,21 @@ void XUA_Buffer_Ep(chanend c_aud_out, chanend c_midi_to_host, chanend c_midi, #endif -#ifdef IAP - chanend c_iap_from_host, - chanend c_iap_to_host, -#ifdef IAP_INT_EP - chanend c_iap_to_host_int, -#endif - chanend c_iap, -#ifdef IAP_EA_NATIVE_TRANS - chanend c_iap_ea_native_out, - chanend c_iap_ea_native_in, - chanend c_iap_ea_native_ctrl, - chanend c_iap_ea_native_data, -#endif -#endif -#if (SPDIF_RX) || (ADAT_RX) +#if (XUA_SPDIF_RX_EN) || (XUA_ADAT_RX_EN) chanend ?c_int, chanend ?c_clk_int, #endif chanend c_sof, chanend c_aud_ctl, in port p_off_mclk -#if( 0 < HID_CONTROLS ) +#if (HID_CONTROLS) , chanend c_hid #endif #ifdef CHAN_BUFF_CTRL , chanend c_buff_ctrl +#endif +#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) || defined(__DOXYGEN__) + , client interface pll_ref_if i_pll_ref #endif ); @@ -116,7 +96,5 @@ void XUA_Buffer_Decouple(chanend c_audio_out , chanend c_buff_ctrl #endif ); - #endif - #endif diff --git a/lib_xua/src/core/clocking/clocking.h b/lib_xua/api/xua_clocking.h similarity index 53% rename from lib_xua/src/core/clocking/clocking.h rename to lib_xua/api/xua_clocking.h index 10dff9ee..c4028009 100644 --- a/lib_xua/src/core/clocking/clocking.h +++ b/lib_xua/api/xua_clocking.h @@ -1,20 +1,32 @@ -// Copyright 2011-2021 XMOS LIMITED. +// Copyright 2011-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #ifndef _CLOCKING_H_ #define _CLOCKING_H_ +#include + +interface pll_ref_if +{ + void toggle(); + void init(); + void toggle_timed(int relative); +}; + +[[distributable]] +void PllRefPinTask(server interface pll_ref_if i_pll_ref, out port p_sync); + /** Clock generation and digital audio I/O handling. * * \param c_spdif_rx channel connected to S/PDIF receive thread * \param c_adat_rx channel connect to ADAT receive thread - * \param p port to output clock signal to drive external frequency synthesizer + * \param i_pll_ref interface to taslk that outputs clock signal to drive external frequency synthesizer * \param c_audio channel connected to the audio() thread * \param c_clk_ctl channel connected to Endpoint0() for configuration of the * clock * \param c_clk_int channel connected to the decouple() thread for clock interrupts */ -void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, out port p, chanend c_audio, chanend c_clk_ctl, chanend c_clk_int); +void clockGen(streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interface pll_ref_if i_pll_ref, chanend c_audio, chanend c_clk_ctl, chanend c_clk_int); #endif diff --git a/lib_xua/api/xua_conf_default.h b/lib_xua/api/xua_conf_default.h index 38db0d98..8b6242cd 100644 --- a/lib_xua/api/xua_conf_default.h +++ b/lib_xua/api/xua_conf_default.h @@ -1,4 +1,4 @@ -// Copyright 2011-2021 XMOS LIMITED. +// Copyright 2011-2023 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. /* * @brief Defines relating to device configuration and customisation of lib_xua @@ -11,29 +11,20 @@ #include "xua_conf.h" #endif - - /* Default tile arrangement */ /** * @brief Location (tile) of audio I/O. Default: 0 */ #ifndef AUDIO_IO_TILE -#define AUDIO_IO_TILE 0 +#define AUDIO_IO_TILE (0) #endif /** * @brief Location (tile) of audio I/O. Default: 0 */ #ifndef XUD_TILE -#define XUD_TILE 0 -#endif - -/** - * @brief Location (tile) of IAP. Default: AUDIO_IO_TILE - */ -#ifndef IAP_TILE -#define IAP_TILE AUDIO_IO_TILE +#define XUD_TILE (0) #endif /** @@ -57,11 +48,18 @@ #define PDM_TILE AUDIO_IO_TILE #endif -/** - * @brief Disable USB functionalty just leaving AudioHub +/** + * @brief Location (tile) of reference signal to CS2100. Default: AUDIO_IO_TILE + */ +#ifndef PLL_REF_TILE +#define PLL_REF_TILE AUDIO_IO_TILE +#endif + +/** + * @brief Disable USB functionalty just leaving AudioHub */ #ifndef XUA_USB_EN -#define XUA_USB_EN 1 +#define XUA_USB_EN (1) #endif /** @@ -83,7 +81,7 @@ /** * @brief Number of DSD output channels. Default: 0 (disabled) */ -#if defined(DSD_CHANS_DAC) +#if defined(DSD_CHANS_DAC) && (DSD_CHANS_DAC != 0) #if defined(NATIVE_DSD) && (NATIVE_DSD == 0) #undef NATIVE_DSD #else @@ -93,20 +91,25 @@ #define DSD_CHANS_DAC 0 #endif +#define XUA_PCM_FORMAT_I2S (0) +#define XUA_PCM_FORMAT_TDM (1) -/* TODO not required */ -#ifndef I2S_MODE_TDM -#define I2S_MODE_TDM 0 +#ifdef XUA_PCM_FORMAT + #if (XUA_PCM_FORMAT != XUA_PCM_FORMAT_I2S) && (XUA_PCM_FORMAT != XUA_PCM_FORMAT_TDM) + #error Bad value for XUA_PCM_FORMAT + #endif +#else + #define XUA_PCM_FORMAT XUA_PCM_FORMAT_I2S #endif /** * @brief Channels per I2S frame. * * - * Default: 2 i.e standard stereo I2S (8 if using TDM i.e. I2S_MODE_TDM). + * Default: 2 i.e standard stereo I2S (8 if using TDM i.e. XUA_PCM_FORMAT_TDM). * **/ #ifndef I2S_CHANS_PER_FRAME - #if (I2S_MODE_TDM == 1) + #if (XUA_PCM_FORMAT == XUA_PCM_FORMAT_TDM) #define I2S_CHANS_PER_FRAME 8 #else #define I2S_CHANS_PER_FRAME 2 @@ -183,7 +186,7 @@ */ #if (I2S_DOWNSAMPLE_MONO_IN == 1) #define I2S_DOWNSAMPLE_CHANS_IN (I2S_CHANS_ADC / 2) - #if ((I2S_DOWNSAMPLE_FACTOR_IN > 1) && (I2S_MODE_TDM == 1)) + #if ((I2S_DOWNSAMPLE_FACTOR_IN > 1) && (XUA_PCM_FORMAT == XUA_PCM_FORMAT_TDM)) #error Mono I2S input downsampling is not avaliable in TDM mode #endif #else @@ -333,13 +336,8 @@ /** * @brief Enables ADAT Tx. Default: 0 (Disabled) */ -#ifndef ADAT_TX -#define ADAT_TX (0) -#endif - -/* Tidy up old SPDIF usage */ -#if defined(ADAT_TX) && (ADAT_TX == 0) -#undef ADAT_TX +#ifndef XUA_ADAT_TX_EN +#define XUA_ADAT_TX_EN (0) #endif /** @@ -354,15 +352,15 @@ /** * @brief Enables SPDIF Rx. Default: 0 (Disabled) */ -#ifndef SPDIF_RX -#define SPDIF_RX (0) +#ifndef XUA_SPDIF_RX_EN +#define XUA_SPDIF_RX_EN (0) #endif /** * @brief Enables ADAT Rx. Default: 0 (Disabled) */ -#ifndef ADAT_RX -#define ADAT_RX (0) +#ifndef XUA_ADAT_RX_EN +#define XUA_ADAT_RX_EN (0) #endif /** @@ -371,9 +369,9 @@ * * Default: NONE (Must be defined by app when SPDIF_RX enabled) */ -#if (SPDIF_RX) || defined (__DOXYGEN__) +#if (XUA_SPDIF_RX_EN) || defined (__DOXYGEN__) #ifndef SPDIF_RX_INDEX - #error SPDIF_RX_INDEX not defined and SPDIF_RX defined + #error SPDIF_RX_INDEX not defined and XUA_SPDIF_RX_EN defined #define SPDIF_RX_INDEX 0 /* Default define for doxygen */ #endif #endif @@ -382,11 +380,11 @@ * @brief ADAT Rx first channel index. defines which channels ADAT will be input on. * Note, indexed from 0. * - * Default: NONE (Must be defined by app when ADAT_RX enabled) + * Default: NONE (Must be defined by app when XUA_ADAT_RX_EN is true) */ -#if (ADAT_RX) || defined(__DOXYGEN__) +#if (XUA_ADAT_RX_EN) || defined(__DOXYGEN__) #ifndef ADAT_RX_INDEX - #error ADAT_RX_INDEX not defined and ADAT_RX defined + #error ADAT_RX_INDEX not defined and XUA_ADAT_RX_EN is true #define ADAT_RX_INDEX (0) /* Default define for doxygen */ #endif @@ -395,7 +393,7 @@ #endif #endif -#if ADAT_RX +#if (XUA_ADAT_RX_EN) /* Setup input stream formats for ADAT */ #if(MAX_FREQ > 96000) @@ -433,7 +431,8 @@ #define HID_CONTROLS (0) #endif -/* @brief Defines whether XMOS device runs as master (i.e. drives LR and Bit clocks) +/** + * @brief Defines whether XMOS device runs as master (i.e. drives LR and Bit clocks) * * 0: XMOS is I2S master. 1: CODEC is I2s master. * @@ -452,7 +451,6 @@ #define SERIAL_STR "" #endif - /** * @brief Vendor String used by the device. This is also pre-pended to various strings used by the design. * @@ -959,43 +957,32 @@ /* Power */ +#define XUA_POWERMODE_SELF (0) +#define XUA_POWERMODE_BUS (1) /** - * @brief Report as self to the host when enabled, else reports as bus-powered. This affects descriptors - * and XUD usage. + * @brief Report as self or bus powered device. This affects descriptors + * and XUD usage and is important for USB compliance * - * Default: 0 (Disabled) + * Default: XUA_POWERMODE_BUS */ -#ifndef SELF_POWERED -#define SELF_POWERED (0) -#endif - -/* Tidy-up historical ifndef usage */ -#if defined(SELF_POWERED) && (SELF_POWERED==0) -#undef SELF_POWERED +#ifndef XUA_POWERMODE +#define XUA_POWERMODE XUA_POWERMODE_BUS #endif /** * @brief Power drawn from the host (in mA x 2) * - * Default: 0 when SELF_POWERED enabled else 250 (500mA) + * Default: 0 when self-powered, else 250 (500mA) */ -#ifdef SELF_POWERED +#if (XUA_POWERMODE == XUA_POWERMODE_SELF) /* Default to taking no power from the bus in self-powered mode */ - #ifndef BMAX_POWER - #define BMAX_POWER 0 + #ifndef _XUA_BMAX_POWER + #define _XUA_BMAX_POWER (0) #endif #else /* Default to taking 500mA from the bus in bus-powered mode */ - #ifndef BMAX_POWER - #define BMAX_POWER 250 - #endif -#endif - -#ifndef XUD_PWR_CFG - #ifdef SELF_POWERED - #define XUD_PWR_CFG XUD_PWR_SELF - #else - #define XUD_PWR_CFG XUD_PWR_BUS + #ifndef _XUA_BMAX_POWER + #define _XUA_BMAX_POWER (250) #endif #endif @@ -1010,17 +997,12 @@ #define MIXER (0) #endif -/* Tidy up old ifndef usage */ -#if defined(MIXER) && (MIXER == 0) -#undef MIXER -#endif - /** * @brief Number of seperate mixes to perform * * Default: 8 if MIXER enabled, else 0 */ -#ifdef MIXER +#if (MIXER) #ifndef MAX_MIX_COUNT #define MAX_MIX_COUNT (8) #endif @@ -1100,82 +1082,55 @@ #define VOLUME_RES_MIXER (0x100) #endif -/* Handle out volume control in the mixer */ -#if defined(OUT_VOLUME_IN_MIXER) && (OUT_VOLUME_IN_MIXER==0) -#undef OUT_VOLUME_IN_MIXER -#else -#if defined(MIXER) -// Disabled by default -//#define OUT_VOLUME_IN_MIXER -#endif +/* Handle out volume control in the mixer - enabled by default */ +#ifndef OUT_VOLUME_IN_MIXER +#define OUT_VOLUME_IN_MIXER (1) #endif -/* Apply out volume controls after the mix */ -#if defined(OUT_VOLUME_AFTER_MIX) && (OUT_VOLUME_AFTER_MIX==0) -#undef OUT_VOLUME_AFTER_MIX -#else -#if defined(MIXER) && defined(OUT_VOLUME_IN_MIXER) -// Enabled by default -#define OUT_VOLUME_AFTER_MIX -#endif +/* Apply out volume controls after the mix. Only relevant when OUT_VOLUME_IN_MIXER enabled. Enabled by default */ +#ifndef OUT_VOLUME_AFTER_MIX +#define OUT_VOLUME_AFTER_MIX (1) #endif -/* Handle in volume control in the mixer */ -#if defined(IN_VOLUME_IN_MIXER) && (IN_VOLUME_IN_MIXER==0) -#undef IN_VOLUME_IN_MIXER -#else -#if defined(MIXER) -/* Disabled by default */ -//#define IN_VOLUME_IN_MIXER -#endif +/* Handle in volume control in the mixer - disabled by default */ +#ifndef IN_VOLUME_IN_MIXER +#define IN_VOLUME_IN_MIXER (0) #endif -/* Apply in volume controls after the mix */ -#if defined(IN_VOLUME_AFTER_MIX) && (IN_VOLUME_AFTER_MIX==0) -#undef IN_VOLUME_AFTER_MIX -#else -#if defined(MIXER) && defined(IN_VOLUME_IN_MIXER) -// Enabled by default -#define IN_VOLUME_AFTER_MIX -#endif +/* Apply in volume controls after the mix. Only relebant when IN_VOLUMNE_IN MIXER enabled. Enabled by default */ +#ifndef IN_VOLUME_AFTER_MIX +#define IN_VOLUME_AFTER_MIX (1) #endif -/* IAP */ -#if defined(IAP) && (IAP == 0) -#undef IAP +/* Always enable explicit feedback EP, even when input stream is present */ +#ifndef UAC_FORCE_FEEDBACK_EP +#define UAC_FORCE_FEEDBACK_EP (1) #endif -/* IAP Interrupt endpoint */ -#if defined(IAP_INT_EP) && (IAP_INT_EP == 0) -#undef IAP_INT_EP -#endif - -/* IAP EA Native Transport */ -#if defined(IAP_EA_NATIVE_TRANS) && (IAP_EA_NATIVE_TRANS == 0) -#undef IAP_EA_NATIVE_TRANS -#endif - -#if defined(IAP_EA_NATIVE_TRANS) || defined(__DOXYGEN__) -/** - * @brief Number of supported EA Native Interface Alternative settings. - * - * Only 1 supported - */ -#ifndef IAP_EA_NATIVE_TRANS_ALT_COUNT - #define IAP_EA_NATIVE_TRANS_ALT_COUNT 1 -#endif - -#if (IAP_EA_NATIVE_TRANS_ALT_COUNT > 1) - /* Only 1 supported */ - #error -#endif -#endif - - #if (defined(UAC_FORCE_FEEDBACK_EP) && UAC_FORCE_FEEDBACK_EP == 0) #undef UAC_FORCE_FEEDBACK_EP #endif +/* Synchronisation defines */ +#define XUA_SYNCMODE_ASYNC (1) // USB_ENDPOINT_SYNCTYPE_ASYNC +#define XUA_SYNCMODE_ADAPT (2) // USB_ENDPOINT_SYNCTYPE_ADAPT +#define XUA_SYNCMODE_SYNC (3) // USB_ENDPOINT_SYNCTYPE_SYNC + +#ifndef XUA_SYNCMODE +#define XUA_SYNCMODE XUA_SYNCMODE_ASYNC +#endif + +#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) + #if (XUA_SPDIF_RX_EN|| ADAT_RX) + #error "Digital input streams not supported in Sync mode" + #endif +#endif + + +/*********************************************************/ +/*** Internal defines below here. NOT FOR MODIFICATION ***/ +/*********************************************************/ + #ifndef __ASSEMBLER__ /* Endpoint addresses enums */ enum USBEndpointNumber_In @@ -1185,7 +1140,7 @@ enum USBEndpointNumber_In ENDPOINT_NUMBER_IN_FEEDBACK, #endif ENDPOINT_NUMBER_IN_AUDIO, -#if (SPDIF_RX) || (ADAT_RX) +#if (XUA_SPDIF_RX_EN) || (XUA_ADAT_RX_EN) ENDPOINT_NUMBER_IN_INTERRUPT, /* Audio interrupt/status EP */ #endif #ifdef MIDI @@ -1222,50 +1177,44 @@ enum USBEndpointNumber_Out XUA_ENDPOINT_COUNT_OUT /* End marker */ }; - #ifndef XUA_ENDPOINT_COUNT_CUSTOM_OUT -#define XUA_ENDPOINT_COUNT_CUSTOM_OUT 0 +#define XUA_ENDPOINT_COUNT_CUSTOM_OUT (0) #endif #ifndef XUA_ENDPOINT_COUNT_CUSTOM_IN -#define XUA_ENDPOINT_COUNT_CUSTOM_IN 0 +#define XUA_ENDPOINT_COUNT_CUSTOM_IN (0) #endif -#define ENDPOINT_COUNT_IN (XUA_ENDPOINT_COUNT_IN + XUA_ENDPOINT_COUNT_CUSTOM_IN) -#define ENDPOINT_COUNT_OUT (XUA_ENDPOINT_COUNT_OUT + XUA_ENDPOINT_COUNT_CUSTOM_OUT) +#define ENDPOINT_COUNT_IN (XUA_ENDPOINT_COUNT_IN + XUA_ENDPOINT_COUNT_CUSTOM_IN) +#define ENDPOINT_COUNT_OUT (XUA_ENDPOINT_COUNT_OUT + XUA_ENDPOINT_COUNT_CUSTOM_OUT) -#endif +#endif /* __ASSEMBLER__ */ -/*** Internal defines below here. NOT FOR MODIFICATION ***/ +#define AUDIO_STOP_FOR_DFU (0x12345678) +#define AUDIO_START_FROM_DFU (0x87654321) +#define AUDIO_REBOOT_FROM_DFU (0xa5a5a5a5) -#define AUDIO_STOP_FOR_DFU (0x12345678) -#define AUDIO_START_FROM_DFU (0x87654321) -#define AUDIO_REBOOT_FROM_DFU (0xa5a5a5a5) - -#define MAX_VOL (0x20000000) - -#if defined(SU1_ADC_ENABLE) && (SU1_ADC_ENABLE == 0) -#undef SU1_ADC_ENABLE -#endif +/* Result of db_to_mult(MAX_VOLUME, 8, 29) */ +#define MAX_VOLUME_MULT (0x20000000) #if defined(LEVEL_METER_LEDS) && !defined(LEVEL_UPDATE_RATE) -#define LEVEL_UPDATE_RATE 400000 +#define LEVEL_UPDATE_RATE (400000) #endif /* The number of clock ticks to wait for the audio feeback to stabalise * Note, feedback always counts 128 SOFs (16ms @ HS, 128ms @ FS) */ #ifndef FEEDBACK_STABILITY_DELAY_HS -#define FEEDBACK_STABILITY_DELAY_HS (2000000) +#define FEEDBACK_STABILITY_DELAY_HS (2000000) #endif #ifndef FEEDBACK_STABILITY_DELAY_FS -#define FEEDBACK_STABILITY_DELAY_FS (20000000) +#define FEEDBACK_STABILITY_DELAY_FS (20000000) #endif /* Length of clock unit/clock-selector units */ -#if (SPDIF_RX) && (ADAT_RX) +#if (XUA_SPDIF_RX_EN) && (XUA_ADAT_RX_EN) #define NUM_CLOCKS (3) -#elif (SPDIF_RX) || (ADAT_RX) +#elif (XUA_SPDIF_RX_EN) || (XUA_ADAT_RX_EN) #define NUM_CLOCKS (2) #else #define NUM_CLOCKS (1) @@ -1346,9 +1295,9 @@ enum USBEndpointNumber_Out /* Some defines that allow us to remove unused code */ /* Useful for dropping lower part of macs in volume processing... */ -#if (FS_STREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS > 24) || (FS_STREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS > 24) || \ - (FS_STREAM_FORMAT_OUTPUT_3_RESOLUTION_BITS > 24) || (HS_STREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS > 24) || \ - (HS_STREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS > 24) || (HS_STREAM_FORMAT_OUTPUT_3_RESOLUTION_BITS > 24) +#if (FS_STREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS > 24) || (HS_STREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS > 24) || \ + (((FS_STREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS > 24) || (HS_STREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS > 24)) && (OUTPUT_FORMAT_COUNT > 1)) || \ + (((FS_STREAM_FORMAT_OUTPUT_3_RESOLUTION_BITS > 24) || (HS_STREAM_FORMAT_OUTPUT_3_RESOLUTION_BITS > 24)) && (OUTPUT_FORMAT_COUNT > 2)) #define STREAM_FORMAT_OUTPUT_RESOLUTION_32BIT_USED 1 #else #define STREAM_FORMAT_OUTPUT_RESOLUTION_32BIT_USED 0 @@ -1480,6 +1429,7 @@ enum USBEndpointNumber_Out #define _XUA_CLK_DIR out #endif -#if (CODEC_MASTER == 1) && (DSD_CHANS_DAC != 0) -#error CODEC_MASTER with DSD is currently unsupported +#if (CODEC_MASTER == 1) && (DSD_CHANS_DAC != 0) +#error CODEC_MASTER with DSD is currently unsupported #endif + diff --git a/lib_xua/api/xua_conf_full.h b/lib_xua/api/xua_conf_full.h index ff4bfd0b..14c93bf2 100644 --- a/lib_xua/api/xua_conf_full.h +++ b/lib_xua/api/xua_conf_full.h @@ -1,7 +1,7 @@ -// Copyright 2017-2021 XMOS LIMITED. +// Copyright 2017-2023 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. -#ifndef __XUA_CONF_FULL_H__ -#define __XUA_CONF_FULL_H__ +#ifndef _XUA_CONF_FULL_H_ +#define _XUA_CONF_FULL_H_ #ifdef __xua_conf_h_exists__ #include "xua_conf.h" @@ -9,6 +9,4 @@ #include "xua_conf_default.h" - - #endif diff --git a/lib_xua/api/xua_endpoint0.h b/lib_xua/api/xua_endpoint0.h index b7cfdfc6..efa6cb62 100644 --- a/lib_xua/api/xua_endpoint0.h +++ b/lib_xua/api/xua_endpoint0.h @@ -1,33 +1,41 @@ -// Copyright 2011-2021 XMOS LIMITED. +// Copyright 2011-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #ifndef _XUA_ENDPOINT0_H_ #define _XUA_ENDPOINT0_H_ -#include "xua.h" #include "dfu_interface.h" #include "vendorrequests.h" #if __XC__ /** Function implementing Endpoint 0 for enumeration, control and configuration - * of USB audio devices. It uses the descriptors defined in ``descriptors_2.h``. - * - * \param c_ep0_out Chanend connected to the XUD_Manager() out endpoint array - * \param c_ep0_in Chanend connected to the XUD_Manager() in endpoint array - * \param c_audioCtrl Chanend connected to the decouple thread for control - * audio (sample rate changes etc.) - * \param c_mix_ctl Optional chanend to be connected to the mixer thread if - * present - * \param c_clk_ctl Optional chanend to be connected to the clockgen thread if - * present. - * \param c_usb_test Optional chanend to be connected to XUD if test modes required. + * of USB audio devices. It uses the descriptors defined in ``xua_ep0_descriptors.h``. * + * \param c_ep0_out Chanend connected to the XUD_Manager() out endpoint array + * \param c_ep0_in Chanend connected to the XUD_Manager() in endpoint array + * \param c_audioCtrl Chanend connected to the decouple thread for control + * audio (sample rate changes etc.). Note when nulled, the + * audio device only supports single sample rate/format and + * DFU is not supported either since this channel is used + * to carry messages about format, rate and DFU state + * \param c_mix_ctl Optional chanend to be connected to the mixer core(s) if + * present + * \param c_clk_ctl Optional chanend to be connected to the clockgen core if + * present + * \param dfuInterface Interface to DFU task (this task must be run on a tile + * connected to boot flash. * \param c_EANativeTransport_ctrl Optional chanend to be connected to EA Native * endpoint manager if present */ -void XUA_Endpoint0(chanend c_ep0_out, chanend c_ep0_in, chanend c_audioCtrl, - chanend ?c_mix_ctl,chanend ?c_clk_ctl, chanend ?c_EANativeTransport_ctr, client interface i_dfu ?dfuInterface - VENDOR_REQUESTS_PARAMS_DEC_); +void XUA_Endpoint0(chanend c_ep0_out, + chanend c_ep0_in, chanend ?c_audioCtrl, + chanend ?c_mix_ctl, chanend ?c_clk_ctl, + chanend ?c_EANativeTransport_ctrl, + client interface i_dfu ?dfuInterface +#if !defined(__DOXYGEN__) + VENDOR_REQUESTS_PARAMS_DEC_ +#endif +); /** Function to set the Vendor ID value * diff --git a/lib_xua/src/midi/usb_midi.h b/lib_xua/api/xua_midi.h similarity index 63% rename from lib_xua/src/midi/usb_midi.h rename to lib_xua/api/xua_midi.h index d1a03adc..81a03eed 100644 --- a/lib_xua/src/midi/usb_midi.h +++ b/lib_xua/api/xua_midi.h @@ -1,34 +1,40 @@ -// Copyright 2011-2021 XMOS LIMITED. +// Copyright 2011-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. -#ifndef __usb_midi_h__ -#define __usb_midi_h__ +#ifndef _XUA_MIDI_H_ +#define _XUA_MIDI_H_ #include "xua.h" -/** USB MIDI I/O thread. +#ifndef MIDI_SHIFT_TX +#define MIDI_SHIFT_TX (0) +#endif + +/** USB MIDI I/O task. * - * This function passes MIDI data from USB to UART I/O. + * This function passes MIDI data between XUA_Buffer and MIDI UART I/O. * - * \param p_midi_in 1-bit input port for MIDI - * \param p_midi_out 1-bit output port for MIDI - * \param clk_midi clock block used for clockin the UART; should have - * a rate of 100MHz - * \param c_midi chanend connected to the decouple() thread - * \param cable_number the cable number of the MIDI implementation. + * \param p_midi_in 1-bit input port for MIDI + * \param p_midi_out 1-bit output port for MIDI + * \param clk_midi Clock block used for clockin the UART; should have + * a rate of 100MHz + * \param c_midi Chanend connected to the decouple() thread + * \param cable_number The cable number of the MIDI implementation. * This should be set to 0. **/ void usb_midi( #if (MIDI_RX_PORT_WIDTH == 4) -buffered in port:4 ?p_midi_in, + buffered in port:4 ?p_midi_in, #else -buffered in port:1 ?p_midi_in, + buffered in port:1 ?p_midi_in, #endif port ?p_midi_out, clock ?clk_midi, chanend ?c_midi, - unsigned cable_number, - chanend ?c_iap, chanend ?c_i2c, // iOS stuff + unsigned cable_number +#ifdef IAP + , chanend ?c_iap, chanend ?c_i2c, // iOS stuff port ?p_scl, port ?p_sda +#endif ); #define MAX_USB_MIDI_PACKET_SIZE 1024 @@ -72,4 +78,4 @@ INLINE void midi_send_ack(chanend c) { #define MIDI_RATE (31250) #define MIDI_BITTIME (XS1_TIMER_MHZ * 1000000 / MIDI_RATE) #define MIDI_BITTIME_2 (MIDI_BITTIME>>1) -#endif // __usb_midi_h__ +#endif // _XUA_MIDI_H_ diff --git a/lib_xua/src/core/mixer/mixer.h b/lib_xua/api/xua_mixer.h similarity index 59% rename from lib_xua/src/core/mixer/mixer.h rename to lib_xua/api/xua_mixer.h index 38e89ec3..dfe561a7 100644 --- a/lib_xua/src/core/mixer/mixer.h +++ b/lib_xua/api/xua_mixer.h @@ -1,7 +1,9 @@ -// Copyright 2011-2021 XMOS LIMITED. +// Copyright 2011-2023 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. -#ifndef __mixer_h__ -#define __mixer_h__ +#ifndef _XUA_MIXER_H_ +#define _XUA_MIXER_H_ + +#include "xua.h" enum mix_ctl_cmd { SET_SAMPLES_TO_HOST_MAP, @@ -31,4 +33,14 @@ enum mix_ctl_cmd { */ void mixer(chanend c_to_host, chanend c_to_audio, chanend c_mix_ctl); +#define XUA_MIXER_OFFSET_OUT (0) +#define XUA_MIXER_OFFSET_IN (NUM_USB_CHAN_OUT) +#define XUA_MIXER_OFFSET_MIX (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN) +#define XUA_MIXER_OFFSET_OFF (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT) + +/* Defines uses for DB to actual muliplier conversion */ +#define XUA_MIXER_MULT_FRAC_BITS (25) +#define XUA_MIXER_DB_FRAC_BITS (8) +#define XUA_MIXER_MAX_MULT (1< + + + + + + + + + + diff --git a/lib_xua/doc/rst/api.rst b/lib_xua/doc/rst/api.rst new file mode 100644 index 00000000..4285cd53 --- /dev/null +++ b/lib_xua/doc/rst/api.rst @@ -0,0 +1,12 @@ + +.. _sec_api: + +API Reference +************* + +.. toctree:: + + api_defines + api_user_functions + api_component + diff --git a/lib_xua/doc/rst/api_component.rst b/lib_xua/doc/rst/api_component.rst new file mode 100644 index 00000000..47c73eb3 --- /dev/null +++ b/lib_xua/doc/rst/api_component.rst @@ -0,0 +1,35 @@ +.. _sec_api_component: + +Component API +============= + +The following functions can be called from the top level main of an +application and implement the various components described in +:ref:`usb_audio_sec_architecture`. + +When using the USB audio framework the ``c_ep_in`` array is always +composed in the following order: + + * Endpoint 0 (in) + * Audio Feedback endpoint (if output enabled) + * Audio IN endpoint (if input enabled) + * MIDI IN endpoint (if MIDI enabled) + * Clock Interrupt endpoint + +The array ``c_ep_out`` is always composed in the following order: + + * Endpoint 0 (out) + * Audio OUT endpoint (if output enabled) + * MIDI OUT endpoint (if MIDI enabled) + +.. doxygenfunction:: XUA_Endpoint0 + +.. doxygenfunction:: XUA_Buffer + +.. doxygenfunction:: XUA_AudioHub + +.. doxygenfunction:: mixer + +.. doxygenfunction:: clockGen + +.. doxygenfunction:: usb_midi diff --git a/lib_xua/doc/rst/api_defines.rst b/lib_xua/doc/rst/api_defines.rst new file mode 100644 index 00000000..76904020 --- /dev/null +++ b/lib_xua/doc/rst/api_defines.rst @@ -0,0 +1,170 @@ + +.. _sec_api_defines: + +Configuration Defines +===================== + +An application using the USB audio framework needs to have defines set for configuration. +Defaults for these defines are found in ``xua_conf_default.h``. + +These defines should be over-ridden in an optional header file ``xua_conf.h`` file or in the ``Makefile`` +for a relevant build configuration. + +This section fully documents all of the settable defines and their default values (where appropriate). + +Code Location (tile) +-------------------- + +.. doxygendefine:: AUDIO_IO_TILE +.. doxygendefine:: XUD_TILE +.. doxygendefine:: MIDI_TILE +.. doxygendefine:: SPDIF_TX_TILE +.. doxygendefine:: PDM_TILE +.. doxygendefine:: PLL_REF_TILE + +Channel Counts +-------------- + +.. doxygendefine:: NUM_USB_CHAN_OUT +.. doxygendefine:: NUM_USB_CHAN_IN +.. doxygendefine:: I2S_CHANS_DAC +.. doxygendefine:: I2S_CHANS_ADC + +Frequencies and Clocks +---------------------- + +.. doxygendefine:: MAX_FREQ +.. doxygendefine:: MIN_FREQ +.. doxygendefine:: DEFAULT_FREQ +.. doxygendefine:: MCLK_441 +.. doxygendefine:: MCLK_48 + +Audio Class +----------- + +.. doxygendefine:: AUDIO_CLASS +.. doxygendefine:: AUDIO_CLASS_FALLBACK +.. doxygendefine:: FULL_SPEED_AUDIO_2 + + +Feature Configuration +--------------------- + +MIDI +^^^^ + +.. doxygendefine:: MIDI +.. doxygendefine:: MIDI_RX_PORT_WIDTH + +S/PDIF +^^^^^^ + +.. doxygendefine:: XUA_SPDIF_TX_EN +.. doxygendefine:: SPDIF_TX_INDEX +.. doxygendefine:: XUA_SPDIF_RX_EN +.. doxygendefine:: SPDIF_RX_INDEX + +ADAT +^^^^ + +.. doxygendefine:: XUA_ADAT_RX_EN +.. doxygendefine:: ADAT_RX_INDEX + +PDM Microphones +^^^^^^^^^^^^^^^ + +.. doxygendefine:: XUA_NUM_PDM_MICS + +DFU +^^^ + +.. doxygendefine:: XUA_DFU_EN + +.. .. doxygendefine:: DFU_FLASH_DEVICE + +HID +^^^ + +.. doxygendefine:: HID_CONTROLS + + +CODEC Interface +^^^^^^^^^^^^^^^ + +.. doxygendefine:: CODEC_MASTER + + +USB Device Configuration +------------------------ + +.. doxygendefine:: VENDOR_STR +.. doxygendefine:: VENDOR_ID +.. doxygendefine:: PRODUCT_STR +.. doxygendefine:: PRODUCT_STR_A2 +.. doxygendefine:: PRODUCT_STR_A1 +.. doxygendefine:: PID_AUDIO_1 +.. doxygendefine:: PID_AUDIO_2 +.. doxygendefine:: BCD_DEVICE + + +Stream Formats +-------------- + +Output/Playback +^^^^^^^^^^^^^^^ + +.. doxygendefine:: OUTPUT_FORMAT_COUNT + +.. doxygendefine:: STREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS +.. doxygendefine:: STREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS +.. doxygendefine:: STREAM_FORMAT_OUTPUT_3_RESOLUTION_BITS + +.. doxygendefine:: HS_STREAM_FORMAT_OUTPUT_1_SUBSLOT_BYTES +.. doxygendefine:: HS_STREAM_FORMAT_OUTPUT_2_SUBSLOT_BYTES +.. doxygendefine:: HS_STREAM_FORMAT_OUTPUT_3_SUBSLOT_BYTES + +.. doxygendefine:: FS_STREAM_FORMAT_OUTPUT_1_SUBSLOT_BYTES +.. doxygendefine:: FS_STREAM_FORMAT_OUTPUT_2_SUBSLOT_BYTES +.. doxygendefine:: FS_STREAM_FORMAT_OUTPUT_3_SUBSLOT_BYTES + +.. doxygendefine:: STREAM_FORMAT_OUTPUT_1_DATAFORMAT +.. doxygendefine:: STREAM_FORMAT_OUTPUT_2_DATAFORMAT +.. doxygendefine:: STREAM_FORMAT_OUTPUT_3_DATAFORMAT + +Input/Recording +^^^^^^^^^^^^^^^ + +.. doxygendefine:: INPUT_FORMAT_COUNT + +.. doxygendefine:: STREAM_FORMAT_INPUT_1_RESOLUTION_BITS + +.. doxygendefine:: HS_STREAM_FORMAT_INPUT_1_SUBSLOT_BYTES + +.. doxygendefine:: FS_STREAM_FORMAT_INPUT_1_SUBSLOT_BYTES + +.. doxygendefine:: STREAM_FORMAT_INPUT_1_DATAFORMAT + +Volume Control +-------------- + +.. doxygendefine:: OUTPUT_VOLUME_CONTROL +.. doxygendefine:: INPUT_VOLUME_CONTROL +.. doxygendefine:: MIN_VOLUME +.. doxygendefine:: MAX_VOLUME +.. doxygendefine:: VOLUME_RES + +Mixing +------ + +.. doxygendefine:: MIXER +.. doxygendefine:: MAX_MIX_COUNT +.. doxygendefine:: MIX_INPUTS +.. doxygendefine:: MIN_MIXER_VOLUME +.. doxygendefine:: MAX_MIXER_VOLUME +.. doxygendefine:: VOLUME_RES_MIXER + +Power +----- + +.. doxygendefine:: XUA_POWERMODE + diff --git a/lib_xua/doc/rst/api_user_functions.rst b/lib_xua/doc/rst/api_user_functions.rst new file mode 100644 index 00000000..e0728647 --- /dev/null +++ b/lib_xua/doc/rst/api_user_functions.rst @@ -0,0 +1,74 @@ +Required User Function Definitions +================================== + +The following functions need to be defined by an application using the XMOS USB Audio framework. + +External Audio Hardware Configuration Functions +----------------------------------------------- + +.. c:function:: void AudioHwInit(chanend ?c_codec) + + This function is called when the audio core starts after the + device boots up and should initialize the external audio harware e.g. clocking, DAC, ADC etc + + :param c_codec: An optional chanend that was original passed into + :c:func:`audio` that can be used to communicate + with other cores. + + +.. c:function:: void AudioHwConfig(unsigned samFreq, unsigned mclk, chanend ?c_codec, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC) + + This function is called when the audio core starts or changes + sample rate. It should configure the extenal audio hardware to run at the specified + sample rate given the supplied master clock frequency. + + :param samFreq: The sample frequency in Hz that the hardware should be configured to (in Hz). + + :param mclk: The master clock frequency that is required in Hz. + + :param c_codec: An optional chanend that was original passed into + :c:func:`audio` that can be used to communicate + with other cores. + + :param dsdMode: Signifies if the audio hardware should be configured for DSD operation + + :param sampRes_DAC: The sample resolution of the DAC stream + + :param sampRes_ADC: The sample resolution of the ADC stream + + +Audio Streaming Functions +------------------------- + +The following functions can be optionally used by the design. They can be useful for mute lines etc. + +.. c:function:: void AudioStreamStart(void) + + This function is called when the audio stream from device to host + starts. + +.. c:function:: void AudioStreamStop(void) + + This function is called when the audio stream from device to host stops. + +Host Active +----------- + +The following function can be used to signal that the device is connected to a valid host. + +This is called on a change in state. + +.. c:function:: void AudioStreamStart(int active) + + :param active: Indicates if the host is active or not. 1 for active else 0. + + +HID Controls +------------ + +The following function is called when the device wishes to read physical user input (buttons etc). + +.. c:function:: void UserReadHIDButtons(unsigned char hidData[]) + + :param hidData: The function should write relevant HID bits into this array. The bit ordering and functionality is defined by the HID report descriptor used. + diff --git a/lib_xua/doc/rst/arch.rst b/lib_xua/doc/rst/arch.rst new file mode 100644 index 00000000..448fa55a --- /dev/null +++ b/lib_xua/doc/rst/arch.rst @@ -0,0 +1,92 @@ + +.. _usb_audio_sec_architecture: + +Software Architecture +********************* + +This section describes the required software architecture of a USB Audio device implemented using `lib_xua`, its dependencies and other supporting libraries. + +`lib_xua` provides fundamental building blocks for producing USB Audio products on XMOS devices. Every system is required to have the components from `lib_xua` listed in :ref:`usb_audio_shared_components`. + +.. tabularcolumns:: lp{5cm}l +.. _usb_audio_shared_components: +.. list-table:: Required XUA Components + :header-rows: 1 + :widths: 40 60 + + * - Component + - Description + * - Endpoint 0 + - Provides the logic for Endpoint 0 which handles + enumeration and control of the device including DFU related requests. + * - Endpoint buffer + - Buffers endpoint data packets to and from the host. Manages delivery of audio packets between the endpoint buffer + component and the audio components. It can also handle volume control processing. Note, this currently utilises two cores + * - AudioHub + - Handles audio I/O over I2S and manages audio data + to/from other digital audio I/O components. + +In addition low-level USB I/0 is required and is provided by the external dependency `lib_xud` + +.. tabularcolumns:: lp{5cm}l +.. list-table:: Additional Components Required + :header-rows: 1 + :widths: 100 60 + + * - Component + - Description + * - XMOS USB Device Driver (XUD) + - Handles the low level USB I/O. + +In addition :ref:`usb_audio_optional_components` shows optional components that can be added/enabled from within `lib_xua` + +.. tabularcolumns:: lp{5cm}l +.. _usb_audio_optional_components: +.. list-table:: Optional Components + :header-rows: 1 + :widths: 40 60 + + * - Component + - Description + * - Mixer + - Allows digital mixing of input and output channels. It can also + handle volume control instead of the decoupler. + * - Clockgen + - Drives an external frequency generator (PLL) and manages + changes between internal clocks and external clocks arising + from digital input. + * - MIDI + - Outputs and inputs MIDI over a serial UART interface. + +`lib_xua` also provides optional support for integrating with the following external dependencies listed in :ref:`usb_audio_external_components` + +.. tabularcolumns:: lp{5cm}l +.. _usb_audio_external_components: +.. list-table:: External Components + :header-rows: 1 + :widths: 40 60 + + * - Component + - Description + * - S/PDIF Transmitter (lib_spdif) + - Outputs samples of an S/PDIF digital audio interface. + * - S/PDIF Receiver (lib_spdif) + - Inputs samples of an S/PDIF digital audio interface (requires the + clockgen component). + * - ADAT Receiver (lib_adat) + - Inputs samples of an ADAT digital audio interface (requires the + clockgen component). + * - PDM Microphones (lib_mic_array) + - Receives PDM data from microphones and performs PDM to PCM conversion + +.. _usb_audio_threads: + +.. figure:: images/threads-crop.* + :width: 100% + + USB Audio Core Diagram + +:ref:`usb_audio_threads` shows how the components interact with each +other in a typical system. The green circles represent cores with arrows indicating inter-core communications. + + diff --git a/lib_xua/doc/rst/feat.rst b/lib_xua/doc/rst/feat.rst index 07978c29..3dbeb2d8 100644 --- a/lib_xua/doc/rst/feat.rst +++ b/lib_xua/doc/rst/feat.rst @@ -1,117 +1,24 @@ -Features & Options ------------------- +Additional Features +******************* + +The previous chapter describes the use of core functionality contained within ``lib_xua`` +This seciton details enabling additional features with supported external dependencies, for example, +``lib_xua`` can provide S/PDIF output though the used of ``lib_spdif`` + +Where something must be defined, it is recommended this is done in `xua_conf.h` but could also be done in the application Makefile. + +.. toctree:: + + S/PDIF Transmit + S/PDIF Receive + + + -The previous sections describes only the basic core set of ``lib_xua`` details on enabling additional features e.g. S/PDIF are discussed in this section. -Where something must be defined, it is recommened this is done in `xua_conf.h` but could also be done in the application Makefile. -For each feature steps are listed for if calling ``lib_xua`` functions manually - if using the "codeless" programming model then these steps informational only. -Each section also includes a sub-section on enabling the feature using the "codeless" model. -For full details of all options please see the API section - -I2S/TDM -~~~~~~~ - -I2S/TDM is typically fundamental to most products and is built into the ``XUA_AudioHub()`` core. - -In order to enable I2S on must declare an array of ports for the data-lines (one for each direction):: - - /* Port declarations. Note, the defines come from the xn file */ - buffered out port:32 p_i2s_dac[] = {PORT_I2S_DAC0}; /* I2S Data-line(s) */ - buffered in port:32 p_i2s_adc[] = {PORT_I2S_ADC0}; /* I2S Data-line(s) */ - -Ports for the sample and bit clocks are also required:: - - buffered out port:32 p_lrclk = PORT_I2S_LRCLK; /* I2S Bit-clock */ - buffered out port:32 p_bclk = PORT_I2S_BCLK; /* I2S L/R-clock */ - -.. note:: - - All of these ports must be buffered, width 32. Based on whether the xCORE is bus slave/master the ports must be declared as input/output respectively - -These ports must then be passed to the ``XUA_AudioHub()`` task appropriately. - -I2S functionality also requires two clock-blocks, one for bit and sample clock e.g.:: - - /* Clock-block declarations */ - clock clk_audio_bclk = on tile[0]: XS1_CLKBLK_4; /* Bit clock */ - clock clk_audio_mclk = on tile[0]: XS1_CLKBLK_5; /* Master clock */ - -These hardware resources must be passed into the call to ``XUA_AudioHub()``:: - - /* AudioHub/IO core does most of the audio IO i.e. I2S (also serves as a hub for all audio) */ - on tile[0]: XUA_AudioHub(c_aud, clk_audio_mclk, clk_audio_bclk, p_mclk_in, p_lrclk, p_bclk); - - -Codeless Programming Model -.......................... - -All ports and hardware resources are already fully declared, one must simply set the following: - - * `I2S_CHANS_DAC` must be set to the desired number of output channels via I2S - * `I2S_CHANS_ADC` must be set to the desired number of input channels via I2S - * `AUDIO_IO_TILE` must be set to the tile where the physical I2S connections reside - -For configuration options, master vs slave, TDM etc please see the API section. - - -|newpage| - -S/PDIF Transmit -~~~~~~~~~~~~~~~ - -``lib_xua`` supports the development of devices with S/PDIF transmit functionality through the use of -``lib_spdif``. The XMOS S/PDIF transmitter runs in a single core and supports rates up to 192kHz. - -The S/PDIF transmitter core takes PCM audio samples via a channel and outputs them in S/PDIF format to a port. -Samples are provided to the S/PDIF transmitter task from the ``XUA_AudioHub()`` task. - -The channel should be declared a normal:: - - chan c_spdif_tx - - -In order to use the S/PDIF transmmiter with ``lib_xua`` hardware resources must be declared e.g:: - - buffered out port:32 p_spdif_tx = PORT_SPDIF_OUT; /* SPDIF transmit port */ - -This port should be clocked from the master-clock, ``lib_spdif`` provides a helper function for setting up the port:: - - spdif_tx_port_config(p_spdif_tx2, clk_audio_mclk, p_mclk_in, delay); - -.. note:: If sharing the master-clock port and clockblock with ``XUA_AudioHub()`` (or any other task) then this setup - should be done before running the tasks in a ``par`` statement. - -Finally the S/PDIF transmitter task must be run - passing in the port and channel for communication with ``XUA_AudioHub``. -For example:: - - par - { - while(1) - { - /* Run the S/PDIF transmitter task */ - spdif_tx(p_spdif_tx2, c_spdif_tx); - } - - /* AudioHub/IO core does most of the audio IO i.e. I2S (also serves as a hub for all audio) */ - /* Note, since we are not using I2S we pass in null for LR and Bit clock ports and the I2S dataline ports */ - XUA_AudioHub(c_aud, clk_audio_mclk, null, p_mclk_in, null, null, null, null, c_spdif_tx); - } - -For further details please see the documentation, application notes and examples provided for ``lib_spdif``. - -Codeless Programming Model -.......................... - -If using the codeless programming method one must simply ensure the following: - - * `PORT_SPDIF_OUT` is correctly defined in the XN file - * `XUA_SPDIF_TX_EN` should be defined as non-zero - * `SPDIF_TX_TILE` is correctly defined (note, this defaults to `AUDIO_IO_TILE`) - -For further configuration options please see the API section. diff --git a/lib_xua/doc/rst/feat_spdif_rx.rst b/lib_xua/doc/rst/feat_spdif_rx.rst new file mode 100644 index 00000000..0753371d --- /dev/null +++ b/lib_xua/doc/rst/feat_spdif_rx.rst @@ -0,0 +1,52 @@ + +S/PDIF Receive +============== + +``lib_xua`` supports the development of devices with S/PDIF receive functionality through the use of +``lib_spdif``. The XMOS S/PDIF receiver runs in a single core and supports rates up to 192kHz. + +The S/PDIF receiver inputs data via a port and outputs samples via a channel. It requires a 1-bit port +which must be 4-bit buffered. For example:: + + buffered in port:4 p_spdif_rx = PORT_SPDIF_IN; + +It also requires a clock-block, for example:: + + clock clk_spd_rx = XS1_CLKBLK_1; + +Finally, a channel for the output samples must be declared, note, this should be a streaming channel:: + + streaming chan c_spdif_rx; + +The S/PDIF receiver should be called on the appropriate tile:: + + spdif_rx(c_spdif_rx,p_spdif_rx,clk_spd_rx,192000); + +.. note:: + + It is recomended to use the value 192000 for the ``sample_freq_estimate`` parameter + +With the steps above an S/PDIF stream can be captured by the xCORE. To be functionally useful the audio +master clock must be able to synchronise to this external digital stream. Additionally, the host can be +notified regarding changes in the validity of this stream, it's frequency etc. To synchronise to external +streams the codebase assumes the use of an external Cirrus Logic CS2100 device. + +The ``ClockGen()`` task from ``lib_xua`` provides the reference signal to the CS2100 device and also handles +recording of clock validity etc. See :ref:`usb_audio_sec_clock_recovery` for full details regarding ``ClockGen()``. + +It also provides a small FIFO for S/PDIF samples before they are forwarded to the ``AudioHub`` core. +As such it requires to be inserted in the communication path between the S/PDIF receiver and the +``AudioHub`` core. For example:: + + chan c_dig_rx; + streaming chan c_spdif_rx; + + par + { + SpdifReceive(..., c_spdif_rx, ...); + + clockGen(c_spdif_rx, ..., c_dig_rx, ...); + + XUA_AudioHub(..., c_dig_rx, ...); + } + diff --git a/lib_xua/doc/rst/feat_spdif_tx.rst b/lib_xua/doc/rst/feat_spdif_tx.rst new file mode 100644 index 00000000..b8cf9c5d --- /dev/null +++ b/lib_xua/doc/rst/feat_spdif_tx.rst @@ -0,0 +1,47 @@ + +S/PDIF Transmit +=============== + +``lib_xua`` supports the development of devices with S/PDIF transmit functionality through the use of +``lib_spdif``. The XMOS S/PDIF transmitter runs in a single core and supports rates up to 192kHz. + +The S/PDIF transmitter core takes PCM audio samples via a channel and outputs them in S/PDIF format to a port. +Samples are provided to the S/PDIF transmitter task from the ``XUA_AudioHub()`` task. + +The channel should be declared as normal:: + + chan c_spdif_tx + + +In order to use the S/PDIF transmitter with ``lib_xua`` a 1-bit port must be declared e.g:: + + buffered out port:32 p_spdif_tx = PORT_SPDIF_OUT; /* SPDIF transmit port */ + +This port should be clocked from the master-clock, ``lib_spdif`` provides a helper function for setting up the port:: + + spdif_tx_port_config(p_spdif_tx, clk_audio_mclk, p_mclk_in, delay); + +.. note:: If sharing the master-clock port and clockblock with ``XUA_AudioHub()`` (or any other task) then this setup + should be done before running the tasks in a ``par`` statement. + +Finally the S/PDIF transmitter task must be run - passing in the port and channel for communication with ``XUA_AudioHub``. +For example:: + + par + { + while(1) + { + /* Run the S/PDIF transmitter task */ + spdif_tx(p_spdif_tx, c_spdif_tx); + } + + /* AudioHub/IO core does most of the audio IO i.e. I2S (also serves as + * a hub for all audio). + * Note, since we are not using I2S we pass in null for LR and Bit + * clock ports and the I2S dataline ports */ + XUA_AudioHub(c_aud, clk_audio_mclk, null, p_mclk_in, null, null, + null, null, c_spdif_tx); + } + +For further details please see the documentation, application notes and examples provided for ``lib_spdif``. + diff --git a/lib_xua/doc/rst/hw.rst b/lib_xua/doc/rst/hw.rst index 93424b33..b9aab949 100644 --- a/lib_xua/doc/rst/hw.rst +++ b/lib_xua/doc/rst/hw.rst @@ -4,59 +4,118 @@ XMOS USB Audio Hardware Platforms A range of hardware platforms for evaluating USB Audio on XMOS devices. -Specific, in depth, details for each platform/board are out of scope of this library documentation however, the features of the most popular platform are described below with the view of providing a worked example. +Specific details for each platform/board are out of scope of this library documentation however, the features of the most popular platform are described below with the view of providing a worked example. Please also see application note AN00246. - -xCORE-200 Multi-Channel Audio Board +xCORE.AI Multichannel Audio Board ................................... -`The XMOS xCORE-200 Multi-channel Audio board `_ -(XK-AUDIO-216-MC) is a complete hardware and reference software platform targeted at up to 32-channel USB and networked audio applications, such as DJ decks and mixers. +The XMOS xCORE.ai Multichannel Audio board (XK-AUDIO-316-MC) is a complete hardware and reference software platform targeted at up to 32-channel USB audio applications, such as DJ decks and mixers and other musical instrument interfaces. The board can also be used to prototype products with smaller feature sets or HiFi style products. -The Multichannel Audio Platform hardware is based around the XE216-512-TQ128 multicore microcontroller; an dual-tile xCORE-200 device with an integrated High Speed USB 2.0 PHY, RGMII (Gigabit Ethernet) interface and 16 logical cores delivering up to 2000MIPS of deterministic and responsive processing power. +The Multichannel Audio Platform hardware is based around the XU316-1024-TQ128-C24 multicore microcontroller; an dual-tile xCORE.ai device with an integrated High Speed USB 2.0 PHY and 16 logical cores delivering up to 3200MIPS of deterministic and responsive processing power. -Exploiting the flexible programmability of the xCORE-200 architecture, the Multi-channel Audio Platform supports either USB or network audio source, streaming 8 analogue input and 8 analogue output audio channels simultaneously - at up to 192kHz. +Exploiting the flexible programmability of the xCORE.ai architecture, the Multi-channel Audio Platform supports either USB or network audio source, streaming 8 analogue input and 8 analogue output audio channels simultaneously - at up to 192kHz. -For full details regarding the hardware please refer to `xCORE-200 Multichannel Audio Platform Hardware Manual `_. - -The reference board has an associated firmware application that uses `lib_xua` to implemented a USB Audio Devicce. Full details of this application can be found in the USB Audio Design Guide. +The reference board has an associated firmware application that uses `lib_xua` to implemented a USB Audio Device. Full details of this application can be found later in this document. Analogue Input & Output +++++++++++++++++++++++ -A total of eight single-ended analog input channels are provided via 3.5mm stereo jacks. Each is fed into a CirrusLogic CS5368 ADC. -Similarly a total of eight single-ended analog output channels are provided. Each is fed into a CirrusLogic CS4384 DAC. +A total of eight single-ended analog input channels are provided via 3.5mm stereo jacks. These inputs feed into a pair of quad-channel PCM1865 ADCs from Texas Instruments. -The four digital I2S/TDM input and output channels are mapped to the xCORE input/outputs through a header array. This jumper allows channel selection when the ADC/DAC is used in TDM mode +A total of eight single-ended analog output channels are provided. These a fed from a for PCM5122 stereo DAC's from Texas instruments. + +ADC's and DAC's are configured via an I2C bus. + +The four digital I2S/TDM input and output channels are mapped to the xCORE input/outputs through a header array. These jumpers allow channel selection when the ADC/DAC is used in TDM mode Digital Input & Output ++++++++++++++++++++++ Optical and coaxial digital audio transmitters are used to provide digital audio input output in formats such as IEC60958 consumer mode (S/PDIF) and ADAT. -The output data streams from the xCORE-200 are re-clocked using the external master clock to synchronise the data into the audio clock domain. This is achieved using simple external D-type flip-flops. +The output data streams from the xCORE are re-clocked using the external master clock to synchronise the data into the audio clock domain. This is achieved using simple external D-type flip-flops. MIDI ++++ -MIDI I/O is provided on the board via standard 5-pin DIN connectors. The signals are buffered using 5V line drivers and are then connected to 1-bit ports on the xCORE-200, via a 5V to 3.3V buffer. +MIDI I/O is provided on the board via standard 5-pin DIN connectors. The signals are buffered using 5V line drivers and are then connected to 1-bit ports on the xCORE, via a 5V to 3.3V buffer. Audio Clocking ++++++++++++++ -A flexible clocking scheme is provided for both audio and other system services. In order to accommodate a multitude of clocking options, the low-jitter master clock is generated locally using a frequency multiplier PLL chip. The chip used is a Phaselink PL611-01, which is pre-programmed to provide a 24MHz clock from its CLK0 output, and either 24.576 MHz or 22.5792MHz from its CLK1 output. +In order to accommodate a multitude of clocking options a flexible clocking scheme is provided for the audio subsystem. -The 24MHz fixed output is provided to the xCORE-200 device as the main processor clock. It also provides the reference clock to a Cirrus Logic CS2100, which provides a very low jitter audio clock from a synchronisation signal provided from the xCORE-200. +Three methods of generating an audio master clock are provided on the board: + + * A Cirrus Logic CS2100-CP PLL device. The CS2100 features both a clock generator and clock multiplier/jitter reduced clock frequency synthesizer (clean up) and can generate a low jitter audio clock based on a synchronisation signal provided by the xCORE + + * A Skyworks Si5351B PLL device. The Si5351 is an I2C configurable clock generator that is ideally suited for replacing crystals, crystal oscillators, VCXOs, phase-locked loops (PLLs), and fanout buffers. + + * xCORE.ai devices are equipped with a secondary (or 'application') PLL which can be used to generate audio clocks + +Selection between these methods is done via writing to bits 6 and 7 of PORT 8D on tile[0]. + +Either the locally generated clock (from the PL611) or the recovered low jitter clock (from the CS2100) may be selected to clock the audio stages; the xCORE-200, the ADC/DAC and Digital output stages. Selection is controlled via an additional I/O, bit 5 of PORT 8C, see :ref:`hw_316_ctrlport`. + +.. _hw_316_ctrlport: + +Control I/O ++++++++++++ + +4 bits of PORT 8C are used to control external hardware on the board. This is described in :ref:`table_316_ctrlport`. + +.. _table_316_ctrlport: + +.. table:: PORT 8C functionality + :class: horizontal-borders vertical_borders + + +--------+-----------------------------------------+------------+------------+ + | Bit(s) | Functionality | 0 | 1 | + +========+=========================================+============+============+ + | [0:3] | Unused | | | + +--------+-----------------------------------------+------------+------------+ + | 4 | Enable 3v3 power for digital (inverted) | Enabled | Disabled | + +--------+-----------------------------------------+------------+------------+ + | 5 | Enable 3v3 power for analogue | Disabled | Enabled | + +--------+-----------------------------------------+------------+------------+ + | 6 | PLL Select | CS2100 | Si5351B | + +--------+-----------------------------------------+------------+------------+ + | 7 | Master clock direction | Output | Input | + +--------+-----------------------------------------+------------+------------+ + + +.. note:: + + To use the xCORE application PLL bit 7 should be set to 0. To use one of the external PLL's bit 7 should be set to 1. -Either the locally generated clock (from the PL611) or the recovered low jitter clock (from the CS2100) may be selected to clock the audio stages; the xCORE-200, the ADC/DAC and Digital output stages. Selection is conntrolled via an additional I/O, bit 5 of PORT 8C. LEDs, Buttons and Other IO ++++++++++++++++++++++++++ -An array of 4*4 green LEDs, 3 buttons and a switch are provided for general purpose user interfacing. The LED array is driven by eight signals each controlling one of 4 rows and 4 columns. +All programmable I/O on the board is configured for 3v3. -A standard XMOS xSYS interface is provided to allow host debug of the board via JTAG. +For green LED's and three push buttons are provided for general purpose user interfacing. -|newpage| +The LEDs are connected to PORT 4F and the buttons are connected to bits [0:2] of PORT 4E. Bit 3 of this port is connected to the (currently +unused) ADC interrupt line. + +The board also includes support for an AES11 format Word Clock input via 75 ohm BNC. The software does not support this currently and it is +provided for future expansion. + +All spare IO and Functional IO brought out on headers for easy connection of expansion boards (via 0.1” headers). + +Power ++++++ + +The board is capable of acting as a USB2.0 self or bus powered device. If bus powered, board takes power from ``USB DEVICE`` connector (micro-B receptacle). +If self powered, board takes power from ``EXTERNAL POWER`` input (micro-B receptacle). + +A Power Source Select (marked ``PWR SRC``) is used to select between bus and self-powered configuration. + + +Debug ++++++ + +For convenience the board includes an on-board xTAG4 for debugging via JTAG/xSCOPE. This is accessed via the USB (micro-B) receptacle marked ``DEBUG``. diff --git a/lib_xua/doc/rst/index.rst b/lib_xua/doc/rst/index.rst deleted file mode 100644 index abaf3eee..00000000 --- a/lib_xua/doc/rst/index.rst +++ /dev/null @@ -1,39 +0,0 @@ - -.. include:: ../../../README.rst - - -About This Document -------------------- - -This document describes the structure of the library, its use and resources required. It also covers some implementation detail. - -This document assumes familiarity with the XMOS xCORE architecture, the Universal Serial Bus 2.0 Specification (and related specifications), -the XMOS tool chain and XC language. - - -Host System Requirements ------------------------- - -USB Audio devices built using `lib_xua` have the following host system requirements. - -- Mac OSX version 10.6 or later - -- Windows Vista, 7, 8 or 10 with Thesycon Audio Class 2.0 driver for Windows (Tested against version 3.20). Please contact XMOS for details. - -- Windows Vista, 7, 8 or 10 with built-in USB Audio Class 1.0 driver. - -Older versions of Windows are not guaranteed to operate as expected. Devices are also expected to operate with various Linux distributions including mobile variants. - -.. toctree:: - - Overview - Hardware Platforms - Software Overview - Using lib_xua - Features - Software Detail - Known Issues - - - -.. include:: ../../../CHANGELOG.rst diff --git a/lib_xua/doc/rst/issues.rst b/lib_xua/doc/rst/issues.rst index d0fee06e..f7dd8f01 100644 --- a/lib_xua/doc/rst/issues.rst +++ b/lib_xua/doc/rst/issues.rst @@ -1,20 +1,34 @@ + |appendix| Known Issues ------------- +************ - Quad-SPI DFU will corrupt the factory image with tools version < 14.0.4 due to an issue with libquadflash -- (#14762) When in DSD mode with S/PDIF output enabled, DSD samples are transmitted over S/PDIF if the DSD and S/PDIF channels are shared, this may or may not be desired +- When in DSD mode with S/PDIF output enabled, DSD samples are transmitted over S/PDIF if the DSD and S/PDIF channels are shared, this may or may not be desired (#14762) -- (#14173) I2S input is completely disabled when DSD output is active - any input stream to the host will contain 0 samples +- I2S input is completely disabled when DSD output is active - any input stream to the host will contain 0 samples (#14173) -- (#14780) Operating the design at a sample rate of less than or equal to the SOF rate (i.e. 8kHz at HS, 1kHz at FS) may expose a corner case relating to 0 length packet handling in both the driver and device and should be considered un-supported at this time. +- Operating the design at a sample rate of less than or equal to the SOF rate (i.e. 8kHz at HS, 1kHz at FS) may expose a corner case relating to 0 length packet handling in both the driver and device and should be considered unsupported at this time (#14780) -- (#14883) Before DoP mode is detected a small number of DSD samples will be played out as PCM via I2S +- Before DoP mode is detected a small number of DSD samples will be played out as PCM via I2S (lib_xua #162) -- (#14887) Volume control settings currently affect samples in both DSD and PCM modes. This results in invalid DSD output if volume control not set to 0 +- Volume control settings currently affect samples in both DSD and PCM modes. This results in invalid DSD output if volume control not set to 0 (#14887) - Windows XP volume control very sensitive. The Audio 1.0 driver built into Windows XP (usbaudio.sys) does not properly support master volume AND channel volume controls, leading to a very sensitive control. Descriptors can be easily modified to disable master volume control if required (one byte - bmaControls(0) in Feature Unit descriptors) -- 88.2kHz and 176.4kHz sample frequencies are not exposed in Windows control panels. These are known OS restrictions. +- 88.2kHz and 176.4kHz sample frequencies are not exposed in Windows control panels. These are known OS restrictions. + +- When DFU flash access fails the device NAKS the host indefinitely (sw_usb_audio #54) + +- Host mixer app (xmos_mixer) is currently not provided (lib_xua #279) + +- In synchronous mode there is no nice transition of the reference signal when moving between internal and SOF clocks (lib_xua #275) + +- Binary images exceeding FLASH_MAX_UPGRADE_SIZE fail silently on DFU download (lib_xua #165) + +- UAC 1.0 mode assumes device always has input. A run time exception occurs if this is not the case (lib_xua #58) + +- No support for I2S_CHANS_DAC = 0 and I2S_CHANS_ADC = 0 (lib_xua #260) + diff --git a/lib_xua/doc/rst/lib_xua.rst b/lib_xua/doc/rst/lib_xua.rst new file mode 100644 index 00000000..5bf5bf42 --- /dev/null +++ b/lib_xua/doc/rst/lib_xua.rst @@ -0,0 +1,25 @@ + +.. include:: ../../../README.rst + +About This Document +=================== + +This document describes the structure of ``lib_xua``, its use and resources required. It also covers some implementation detail. + +This document assumes familiarity with the XMOS xCORE architecture, the Universal Serial Bus 2.0 Specification (and related specifications), +the XMOS tool chain and XC language. + +.. toctree:: + + Overview + Software Architecture + Basic Usage + Options + Advanced Usage + Additional Features + Implementation Detail + API + Known Issues + + + diff --git a/lib_xua/doc/rst/opt.rst b/lib_xua/doc/rst/opt.rst new file mode 100644 index 00000000..089553b1 --- /dev/null +++ b/lib_xua/doc/rst/opt.rst @@ -0,0 +1,37 @@ + +.. _sec_options: + +Options +******* + +This section describes key options of ``lib_xua``. These are typically controlled using build time defines. +Where something must be defined, it is recommended this is done in `xua_conf.h` but could also be done in the application Makefile. + +For full details of all options please see ::ref:`sec_api`. + +.. toctree:: + + Strings + Code Location + Channel Counts and Sample Rates + USB Audio Class Support + Synchronisation + I2S/TDM + S/PDIF Transmit + S/PDIF Receive + MIDI + PDM Microphones + Mixer + Direct Stream Digital (DSD) + USB Audio Formats + Other Options + + + + + + + + + + diff --git a/lib_xua/doc/rst/sw_usb_audio_class.rst b/lib_xua/doc/rst/opt_audio_class.rst similarity index 50% rename from lib_xua/doc/rst/sw_usb_audio_class.rst rename to lib_xua/doc/rst/opt_audio_class.rst index ed4279e0..9c16a2f9 100644 --- a/lib_xua/doc/rst/sw_usb_audio_class.rst +++ b/lib_xua/doc/rst/opt_audio_class.rst @@ -1,33 +1,40 @@ -USB Audio Class Version Support -------------------------------- +|newpage| -The XMOS USB Audio framework supports both USB Audio Class 1.0 and Audio Class 2.0. +USB Audio Class Version +======================= -USB Audio Class 2.0 offers many improvements over USB Audio Class 1.0, most notable is the complete support for high-speed operation. This means that Audio Class devices are no longer limited to full-speed operation allowing greater channel counts, sample frequencies and sample bit-depths. Additional improvement, amoungst others, include: +The codebase supports USB Audio Class versions 1.0 and 2.0. + +USB Audio Class 2.0 offers many improvements over USB Audio Class 1.0, most notable is the complete +support for high-speed operation. This means that Audio Class devices are no longer limited to +full-speed operation allowing greater channel counts, sample frequencies and sample bit-depths. +Additional improvements, amongst others, include: - Added support for multiple clock domains, clock description and clock control - Extensive support for interrupts to inform the host about dynamic changes that occur to different entities such as Clocks etc Driver Support -~~~~~~~~~~~~~~ +-------------- Audio Class 1.0 -+++++++++++++++ +^^^^^^^^^^^^^^^ Audio Class 1.0 is fully supported in Apple OSX. Audio Class 1.0 is fully supported in all modern Microsoft Windows operating systems (i.e. Windows XP and later). Audio Class 2.0 -+++++++++++++++ +^^^^^^^^^^^^^^^ -Audio Class 2.0 is fully supported in Apple OSX since version 10.6.4. Audio Class 2.0 is not supported natively by Windows operating systems. It is therefore required that a driver is installed. Documentation of Windows drivers is beyond the scope of this document, please contact XMOS for further details. +Audio Class 2.0 is fully supported in Apple OSX since version 10.6.4. Starting with Windows 10, release 1703, a USB Audio 2.0 driver is shipped with Windows. + +Third party Windows drivers are also available, however, documentation of these is beyond the scope of this document, please contact XMOS for further details. Audio Class 1.0 Mode and Fall-back -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +---------------------------------- -The normal default for XMOS USB Audio applications is to run as a high-speed Audio Class 2.0 +The default for XMOS USB Audio applications is to run as a high-speed Audio Class 2.0 device. However, some products may prefer to run in Audio Class 1.0 mode, this is normally to -allow "driver-less" operation with Windows operating systems. +allow "driver-less" operation with older versions of Windows operating systems. .. note:: @@ -41,7 +48,7 @@ The device will operate in full-speed Audio Class 1.0 mode if one of the followi to the host over a full speed link (and the Audio Class fall back is enabled). -The options to control this behavior are detailed in :ref:`usb_audio_sec_custom_defines_api`. +The options to control this behavior are detailed in :ref:`sec_api_defines`. When running in Audio Class 1.0 mode the following restrictions are applied: @@ -55,3 +62,29 @@ Due to bandwidth limitations of full-speed USB the following sample-frequency re - Sample rate is limited to a maximum of 96kHz if only input *or* output is enabled. + +Related Defines +--------------- + +:ref:`opt_audio_class_defines` descibes the defines that effect audio class selection. + +.. _opt_audio_class_defines: + +.. list-table:: Audio Class defines + :header-rows: 1 + :widths: 20 80 20 + + * - Define + - Description + - Default + * - ``AUDIO_CLASS`` + - Audio Class version (1 or 2) + - N/A (*must* be defined) + * - ``AUDIO_CLASS_FALLBACK`` + - Enable audio class fallback functionalty + - ``0`` (disabled) + +.. note:: + + Enabling USB Audio Class fallback functionality may have USB Compliance implications + diff --git a/lib_xua/doc/rst/opt_audio_formats.rst b/lib_xua/doc/rst/opt_audio_formats.rst new file mode 100644 index 00000000..9be1e17c --- /dev/null +++ b/lib_xua/doc/rst/opt_audio_formats.rst @@ -0,0 +1,135 @@ +|newpage| + +.. _sec_opt_audio_formats: + +Audio Stream Formats +==================== + +The design currently supports up to three different stream formats for playback, selectable at +run time. This is implemented using standard Alternative Settings to the Audio Streaming interfaces. + +An Audio Streaming interface can have Alternate Settings that can be used to change certain characteristics +of the interface and underlying endpoint. A typical use of Alternate Settings is to provide a way to +change the subframe size and/or number of channels on an active Audio Streaming interface. +Whenever an Audio Streaming interface requires an isochronous data endpoint, it must at least provide +the default Alternate Setting (Alternate Setting 0) with zero bandwidth requirements (no isochronous +data endpoint defined) and an additional Alternate Setting that contains the actual isochronous +data endpoint. This zero bandwidth alternative setting 0 is always implemented by the design. + +For further information refer to 3.16.2 of `USB Audio Device Class Definition for Audio Devices `_ + +Customisable parameters for the Alternate Settings provided by the design are as follows.: + + * Audio sample resolution + * Audio sample subslot size + * Audio data format + +.. note:: + + Currently only a single format is supported for the recording stream + +By default the design exposes two sets of Alternative Settings for the playback Audio Streaming interface, one for 16-bit and another for +24-bit playback. When DSD is enabled an additional (32-bit) alternative is exposed. + +Audio Subslot +------------- + +An audio subslot holds a single audio sample. See `USB Device Class Definition for Audio Data Formats +`_ for full details. +This is represented by `bSubslotSize` in the devices descriptor set. + +An audio subslot always contains an integer number of bytes. The specification limits the possible +audio subslot size to 1, 2, 3 or 4 bytes per audio subslot. + +Since the xCORE is a 32-bit machine the value 4 is typically used for `bSubSlot` - this means that +packing/unpacking samples to/from packets is trivial. Other values can, however, be used and the design +supports values 4, 3 and 2. + +Values other than 4 may be used for the following reasons: + + * Bus-bandwidth needs to be efficiently utilised. For example maximising channel-count/sample-rates in + full-speed operation. + + * To support restrictions with certain hosts. For example many Android based hosts support only 16bit + samples in a 2-byte subslot. + +`bSubSlot` size is set using the following defines: + + * When running in high-speed: + + * `HS_STREAM_FORMAT_OUTPUT_1_SUBSLOT_BYTES` + + * `HS_STREAM_FORMAT_OUTPUT_2_SUBSLOT_BYTES` + + * `HS_STREAM_FORMAT_OUTPUT_3_SUBSLOT_BYTES` + + * When running in full-speed: + + * `FS_STREAM_FORMAT_OUTPUT_1_SUBSLOT_BYTES` + + * `FS_STREAM_FORMAT_OUTPUT_2_SUBSLOT_BYTES` + + * `FS_STREAM_FORMAT_OUTPUT_3_SUBSLOT_BYTES` + + +Audio Sample Resolution +----------------------- + +An audio sample is represented using a number of bits (`bBitResolution`) less than or equal to the number +of total bits available in the audio subslot i.e. `bBitResolution` <= `bSubslotSize` * 8). The design +supports values 16, 24 and 32. + +`bBitResolution` is set using the following defines: + + * When operating at high-speed: + + * `HS_STREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS` + + * `HS_STREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS` + + * `HS_STREAM_FORMAT_OUTPUT_3_RESOLUTION_BITS` + + * When operating at full-speed: + + * `FS_STREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS` + + * `FS_STREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS` + + * `FS_STREAM_FORMAT_OUTPUT_3_RESOLUTION_BITS` + + +Audio Format +------------ + +The design supports two audio formats, PCM and, when "Native" DSD is enabled, Direct Stream Digital (DSD). +A DSD capable DAC is required for the latter. + +The USB Audio `Raw Data` format is used to indicate DSD data (2.3.1.7.5 of `USB Device Class +Definition for Audio Data Formats `_). +This use of a RAW/DSD format in an alternative setting is termed by XMOS as *Native DSD* + +The following defines affect both full-speed and high-speed operation: + + * STREAM_FORMAT_OUTPUT_1_DATAFORMAT + + * STREAM_FORMAT_OUTPUT_2_DATAFORMAT + + * STREAM_FORMAT_OUTPUT_3_DATAFORMAT + +The following options are supported: + + * UAC_FORMAT_TYPEI_RAW_DATA + + * UAC_FORMAT_TYPEI_PCM + +.. note:: + + Currently DSD is only supported on the output/playback stream + +.. note:: + + 4 byte slot size with a 32 bit resolution is required for RAW/DSD format + +Native DSD requires driver support and is available in the Thesycon Windows driver via ASIO. + + diff --git a/lib_xua/doc/rst/opt_channels.rst b/lib_xua/doc/rst/opt_channels.rst new file mode 100644 index 00000000..0385518c --- /dev/null +++ b/lib_xua/doc/rst/opt_channels.rst @@ -0,0 +1,73 @@ +|newpage| + +Channel Counts and Sample Rates +=============================== + +The codebase is fully configurable in relation to channel counts and sample rates. +Practical limitations of these are normally based on USB packet size restrictions and I/O +availablity. + +For example, the maximum packet size for high-speed USB is 1024 bytes, limiting the channel count +to 10 channels for a device running at 192kHz with 32bit sample depth. + +The defines in :ref:`opt_channel_defines` set the channel counts exposed to the USB host. + +.. tabularcolumns:: lp{5cm}l +.. _opt_channel_defines: +.. list-table:: Channel count defines + :header-rows: 1 + :widths: 20 80 20 + + * - Define + - Description + - Default + * - ``NUM_USB_CHAN_OUT`` + - Number of output channels the device advertises to the USB host + - N/A (must be defined) + * - ``NUM_USB_CHAN_IN`` + - Number of input channels the device advertises to the USB host + - N/A (must be defined) + +Sample rates ranges are set by the defines in :ref:`opt_channel_sr_defines`. The codebase will +automatically populate the device sample rate list with popular frequencies between the min and +max values. All values are in Hz: + +.. tabularcolumns:: lp{5cm}l +.. _opt_channel_sr_defines: +.. list-table:: Sample rate defines + :header-rows: 1 + :widths: 20 80 20 + + * - Define + - Description + - Default + * - ``MAX_FREQ`` + - Maximum supported sample rate (Hz) + - ``192000`` + * - ``MIN_FREQ`` + - Minimum supported sample rate (Hz) + - ``44100`` + * - ``DEFAULT_FREQ`` + - Starting frequency for the device after boot + - ``MIN_FREQ`` + + +The codebase requires knowledge of the two master clock frequencies that will be present on the +master-clock port(s). One for 44.1kHz, 88.2kHz etc and one for 48kHz, 96kHz etc. These are set +using defines in :ref:`opt_channel_mc_defines`. All values are in Hz. + +.. tabularcolumns:: lp{5cm}l +.. _opt_channel_mc_defines: +.. list-table:: Master clock rate defines + :header-rows: 1 + :widths: 20 80 20 + + * - Define + - Description + - Default + * - ``CLK_441`` + - Master clock defines for 44100 rates (Hz) + - ``(256 * 44100)`` + * - ``MCLK_48`` + - Master clock defines for 48000 rates (Hz) + - ``(256 * 48000)`` diff --git a/lib_xua/doc/rst/opt_dsd.rst b/lib_xua/doc/rst/opt_dsd.rst new file mode 100644 index 00000000..756cec96 --- /dev/null +++ b/lib_xua/doc/rst/opt_dsd.rst @@ -0,0 +1,104 @@ +|newpage| + +Direct Stream Digital (DSD) +=========================== + +Direct Stream Digital (DSD) is used for digitally encoding audio signals on Super Audio CDs (SACD). +It uses pulse-density modulation (PDM) encoding. + +The codebase supports DSD playback from the host via "DSD over PCM" (DoP) and a "Native" implementation +which is, while USB specification based, proprietary to XMOS. + +DSD is enabled with by setting the define in :ref:`opt_dsd_defines` to a non-zero value. + +.. _opt_dsd_defines: + +.. list-table:: DSD defines + :header-rows: 1 + :widths: 20 80 20 + + * - Define + - Description + - Default + * - ``DSD_CHANS_DAC`` + - Number of DSD channels + - ``0`` (Disabled) + +Typically this would be set to ``2`` for stereo output. + +By default both "Native" and DoP functionality are enabled when DSD is enabled. The Native DSD implementation uses +an alternative streaming interface such that the host can inform the device that DSD data is being streamed. +See: ::ref:`sec_opt_audio_formats` for details. + +If only DoP functionality is desired the Native implementation can be disabled with the define in +:ref:`opt_nativedsd_defines`. + +.. _opt_nativedsd_defines: + +.. list-table:: Native DSD defines + :header-rows: 1 + :widths: 20 80 20 + + * - Define + - Description + - Default + * - ``NATIVE_DSD`` + - Enable/Disable "Native" DSD implementation + - ``1`` (Enabled) + + +DSD over PCM (DoP) +------------------ + +DoP support follows the method described in the `DoP Open Standard 1.1 +`_. + +While Native DSD support is available in Windows though a driver, OSX incorporates a USB driver +that only supports PCM, this is also true of the central audio engine, CoreAudio. It is +therefore not possible to use the "Native" scheme defined above using the built in driver of OSX. + +Since the Apple OS only allows a PCM path a method of transporting DSD audio data over PCM frames +has been developed. + +Standard DSD has a sample size of 1 bit and a sample rate of 2.8224MHz - this is 64x the speed of a +compact disc (CD). This equates to the same data-rate as a 16 bit PCM stream at 176.4kHz. + +In order to clearly identify when this PCM stream contains DSD and when it contains PCM some header +bits are added to the sample. A 24-bit PCM stream is therefore used, with the most significant +byte being used for a DSD marker (alternating 0x05 and 0xFA values). + +When enabled, if USB audio design detects a un-interrupted run of these samples (above a defined +threshold) it switches to DSD mode, using the lower 16-bits as DSD sample data. When this check for +DSD headers fails the design falls back to PCM mode. DoP detection and switching is done completely +in the Audio/I2S core (`audio.xc`). All other code handles the audio samples as PCM. + +The design supports higher DSD/DoP rates (i.e. DSD128) by simply raising the underlying PCM sample +rate e.g. from 176.4kHz to 352.8kHz. The marker byte scheme remains exactly the same regardless +of rate. + +.. note:: + + DoP requires bit-perfect transmission - therefore any audio/volume processing will break the stream. + +"Native" vs DoP +--------------- + +Since the DoP specification requires header bytes this eats into the data bandwidth. The "Native" implementation +has no such overhead and can therefore transfer the same DSD rate and half the effective PCM rate of DoP. +Such a property may be desired when upporting DSD128 without exposing a 352.8kHz PCM rate, for example. + +Ports +----- + +The codebase expects 1-bit ports to be defined in the application XN file for the DSD data and +clock lines for example:: + + + + + +.. note:: + + The DSD ports may or may not overlap the I2S ports - the codebase will reconfigure the ports as appropriate + when switching between PCM and DSD modes. + diff --git a/lib_xua/doc/rst/opt_i2s.rst b/lib_xua/doc/rst/opt_i2s.rst new file mode 100644 index 00000000..681626d9 --- /dev/null +++ b/lib_xua/doc/rst/opt_i2s.rst @@ -0,0 +1,49 @@ +|newpage| + +I2S/TDM +======= + +I2S/TDM is typically fundamental to most products and is built into the ``XUA_AudioHub()`` core. + +The defines in :ref:`opt_i2s_defines` effect the I2S implementation. + +.. tabularcolumns:: lp{5cm}l +.. _opt_i2s_defines: +.. list-table:: I2S defines + :header-rows: 1 + :widths: 20 80 20 + + * - Define + - Description + - Default + * - ``I2S_CHANS_DAC`` + - The desired number of output channels via I2S (0 for disabled) + - N/A (Must be defined) + * - ``I2S_CHANS_ADC`` + - The desired number of input channels via I2S (0 for disabled) + - N/A (Must be defined) + * - ``XUA_PCM_FORMAT`` + - Enabled either TDM or I2S mode + - ``XUA_PCM_FORMAT_I2S`` + * - ``CODEC_MASTER`` + - Sets is xCORE is I2S master or slave + - ``0`` (xCORE is master) + +The I2S code expects that the ports required for I2S (master clock, LR-clock, bit-clock and data lines) are be defined in the application XN file in the relevant `Tile``. +For example:: + + + + + + + + + + + +All of the I2S related ports must be 1-bit ports. + +.. note:: + + TDM mode allows 8 channels (rather than 2) to be supplied on each dataline. diff --git a/lib_xua/doc/rst/opt_location.rst b/lib_xua/doc/rst/opt_location.rst new file mode 100644 index 00000000..45a5d1ee --- /dev/null +++ b/lib_xua/doc/rst/opt_location.rst @@ -0,0 +1,44 @@ +|newpage| + +Code Location +============= + +When designing a system there is a choice as to which hardware resources to use for each interface. +In a multi-tile system the codebase needs to be informed as to which tiles to use for these hardware +resources and associated code. + +A series of defines are used to allow the programmer to easily move code between tiles. Arguably the +most important of these are ``AUDIO_IO_TILE`` and ``XUD_TILE``. :ref:`opt_location_defines` shows a +full listing of these ``TILE`` defines. + +.. tabularcolumns:: lp{5cm}l +.. _opt_location_defines: +.. list-table:: Tile defines + :header-rows: 1 + :widths: 20 80 20 + + * - Define + - Description + - Default + * - ``AUDIO_IO_TILE`` + - Tile on which I2S, ADAT Rx, S/PDIF Rx & mixer resides + - ``0`` + * - ``XUD_TILE`` + - Tile on which USB resides, including buffering for all USB interfaces/endppoints + - ``0`` + * - ``MIDI_TILE`` + - Tile on which MIDI resides + - Same as ``AUDIO_IO_TILE`` + * - ``SPDIF_TX_TILE`` + - Tile on which S/PDIF Tx resides + - Same as ``AUDIO_IO_TILE`` + * - ``PDM_TILE`` + - Tile on which PDM microphones resides + - Same as ``AUDIO_IO_TILE`` + * - ``PLL_REF_TILE`` + - Tile on which reference signal to CS2100 resides + - Same as ``AUDIO_IO_TILE`` + +.. note:: + + It should be ensured that the relevant port defines in the application XN file match the code location defines diff --git a/lib_xua/doc/rst/opt_midi.rst b/lib_xua/doc/rst/opt_midi.rst new file mode 100644 index 00000000..8b9fe96a --- /dev/null +++ b/lib_xua/doc/rst/opt_midi.rst @@ -0,0 +1,54 @@ + +|newpage| + +MIDI +==== + +The codebase supports MIDI input/output over USB as per `Universal Serial Bus Device Class Definition for MIDI Devices `_. + +MIDI functionality is enabled with the define in :ref:`opt_midi_defines`. + +.. _opt_midi_defines: + +.. list-table:: MIDI enable define + :header-rows: 1 + :widths: 20 80 20 + + * - Define + - Description + - Default + * - ``MIDI`` + - Enable MIDI functionality + - ``0`` (Disabled) + + +The codebase supports MIDI receive on a 4-bit or 1-bit port, defaulting to using a 1-bit port. +MIDI transmit is supported port of any bit-width. By default the codebase assumes the transmit +and receive I/O is connected to bit[0] of the port. This is configurable for the transmit port. +:ref:`opt_midi_defines` provides information on the configuring these parameters. + +.. _opt_midi_port_defines: + +.. list-table:: MIDI port defines + :header-rows: 1 + :widths: 20 80 20 + + * - Define + - Description + - Default + * - ``MIDI_RX_PORT_WIDTH`` + - Port width of the MIDI rx port (1 or 4bit) + - ``1`` (1-bit port) + * - ``MIDI_SHIFT_TX`` + - MIDI tx bit + - ``0`` (bit[0]) + +The MIDI code expects that the ports for receive and transmit are defined in the application XN file in the relevant Tile. +The expected names for the ports are ``PORT_MIDI_IN`` and ``PORT_MIDI_OUT``, for example:: + + + + + + + diff --git a/lib_xua/doc/rst/opt_mixer.rst b/lib_xua/doc/rst/opt_mixer.rst new file mode 100644 index 00000000..fde57569 --- /dev/null +++ b/lib_xua/doc/rst/opt_mixer.rst @@ -0,0 +1,41 @@ +|newpage| + +Mixer +===== + +The codebase supports audio mixing functionality with highly flexible routing options. + +Essentially the mixer is capable of performing 8 separate mixes with up to 18 inputs at sample rates +up to 96kHz and 2 mixes with up to 18 inputs at higher sample rates. + +Inputs to the mixer can be selected from any device input (USB, S/PDIF, I2S etc) and +outputs from the mixer can be routed to any device output (USB, S/PDIF, I2S etc). + +See :ref:`usb_audio_sec_mixer` for full details of the mixer including control. + +Basic configuration of mixer functionality is achieved with the defines in :ref:`opt_mixer_defines`. + +.. _opt_mixer_defines: + +.. list-table:: Mixer defines + :header-rows: 1 + :widths: 20 80 20 + + * - Define + - Description + - Default + * - ``MIXER`` + - Enable mixer + - ``0`` (Disabled) + * - ``MAX_MIX_COUNT`` + - Number of separate mix outputs to perform + - ``8`` + * - ``MIX_INPUTS`` + - Number of channels input into the mixer + - ``18`` + +.. note:: + + The mixer cores always run on the tile defined by ``AUDIO_IO_TILE`` + + diff --git a/lib_xua/doc/rst/opt_other.rst b/lib_xua/doc/rst/opt_other.rst new file mode 100644 index 00000000..5cfcecad --- /dev/null +++ b/lib_xua/doc/rst/opt_other.rst @@ -0,0 +1,26 @@ +|newpage| + +Other Options +============= + +There are a few other, lesser used, options available. + +.. _opt_other_defines: + +.. list-table:: Other defines + :header-rows: 1 + :widths: 20 80 20 + + * - Define + - Description + - Default + * - ``XUA_USB_EN`` + - Allows the use of the audio subsytem without USB + - ``1`` (enabled) + * - ``INPUT_VOLUME_CONTROL`` + - Enables volume control on input channels, both descriptors and processing + - ``1`` (enabled) + * - ``OUTPUT_VOLUME_CONTROL`` + - Enables volume control on output channels, both descriptors and processing + - ``1`` (enabled) + diff --git a/lib_xua/doc/rst/opt_pdm.rst b/lib_xua/doc/rst/opt_pdm.rst new file mode 100644 index 00000000..4e4dbab5 --- /dev/null +++ b/lib_xua/doc/rst/opt_pdm.rst @@ -0,0 +1,36 @@ +|newpage| + +PDM Microphones +=============== + +The codebase supports input from up to 8 PDM microphones. + +PDM microphone support is provided via ``lib_mic_array``. Settings for PDM microphones are controlled +via the defines in :ref:`opt_pdm_defines`. + +.. _opt_pdm_defines: + +.. list-table:: PDM defines + :header-rows: 1 + :widths: 20 80 20 + + * - Define + - Description + - Default + * - ``XUA_NUM_PDM_MICS`` + - The number of mics to enable (0 for disabled) + - ``0`` (disabled) + * - ``PDM_MIC_INDEX`` + - Defines which input channel the mics map to + - ``0`` + +The codebase expects 1-bit ports to be defined in the application XN file for ``PORT_PDM_CLK`` and ``PORT_PDM_MCLK``. +An 8-bit port is expected for ``PORT_PDM_DATA``. For example:: + + + + + + + + diff --git a/lib_xua/doc/rst/opt_spdif_rx.rst b/lib_xua/doc/rst/opt_spdif_rx.rst new file mode 100644 index 00000000..1000ef20 --- /dev/null +++ b/lib_xua/doc/rst/opt_spdif_rx.rst @@ -0,0 +1,60 @@ +|newpage| + +S/PDIF Receive +============== + +The codebase supports a single, stereo, S/PDIF receiver. This can be input via 75 Ω coaxial or optical fibre. +In order to provide S/PDIF functionality ``lib_xua`` uses ``lib_spdif`` (https://www.github.com/xmos/lib_spdif). + +Basic configuration of S/PDIF receive functionality is achieved with the defines in :ref:`opt_spdif_rx_defines`. + +.. _opt_spdif_rx_defines: + +.. list-table:: S/PDIF rx defines + :header-rows: 1 + :widths: 20 80 20 + + * - Define + - Description + - Default + * - ``XUA_SPDIF_RX_EN`` + - Enable S/PDIF receive + - ``0`` (Disabled) + * - ``SPDIF_RX_INDEX`` + - Defines which channels S/PDIF will be input on + - N/A (must defined) + +.. note:: + + S/PDIF receive always runs on the tile defined by ``AUDIO_IO_TILE`` + +The codebase expects the S/PDIF receive port to be defined in the application XN file as ``PORT_SPDIF_IN``. +This must be a 1-bit port, for example:: + + + +When S/PDIF receive is enabled the codebase expects to drive a synchronisation signal to an external +Cirrus Logic CS2100 device for master-clock generation. + +The programmer should ensure the define in :ref:`opt_spdif_rx_ref_defines` is set appropriately. + +.. _opt_spdif_rx_ref_defines: + +.. list-table:: Reference Clock Location + :header-rows: 1 + :widths: 20 80 20 + + * - Define + - Description + - Default + * - ``PLL_REF_TILE`` + - Tile location of reference to CS2100 device + - ``AUDIO_IO_TILE`` + +The codebase expects this reference signal port to be defined in the application XN file as ``PORT_PLL_REF``. +This may be a port of any bit-width, however, connection to bit[0] is assumed:: + + + +Configuration of the external CS2100 device (typically via I2C) is beyond the scope of this document. + diff --git a/lib_xua/doc/rst/opt_spdif_tx.rst b/lib_xua/doc/rst/opt_spdif_tx.rst new file mode 100644 index 00000000..7d4bf7e0 --- /dev/null +++ b/lib_xua/doc/rst/opt_spdif_tx.rst @@ -0,0 +1,46 @@ +|newpage| + +S/PDIF Transmit +=============== + +The codebase supports a single, stereo, S/PDIF transmitter. This can be output over 75 Ω coaxial or optical fibre. +In order to provide S/PDIF transmit functionality ``lib_xua`` uses ``lib_spdif`` (https://www.github.com/xmos/lib_spdif). + +Basic configuration of S/PDIF transmit functionality is achieved with the defines in :ref:`opt_spdif_tx_defines` + +.. _opt_spdif_tx_defines: + +.. list-table:: S/PDIF tx defines + :header-rows: 1 + :widths: 20 80 20 + + * - Define + - Description + - Default + * - ``XUA_SPDIF_TX_EN`` + - Enable S/PDIF transmit + - ``0`` (Disabled) + * - ``SPDIF_TX_INDEX`` + - Output channel offset to use for S/PDIF transmit + - ``0`` + +In addition, the developer may choose which tile the S/PDIF transmitter runs on, see :ref:`opt_spdif_tx_tile_defines`. + +.. _opt_spdif_tx_tile_defines: + +.. list-table:: S/PDIF tile define + :header-rows: 1 + :widths: 20 80 20 + + * - Define + - Description + - Default + * - ``SPDIF_TX_TILE`` + - Tile that S/PDIF tx is connected to + - ``AUDIO_IO_TILE`` + +The codebase expects the S/PDIF transmit port to be defined in the application XN file as ``PORT_SPDIF_OUT``. +This must be a 1-bit port, for example:: + + + diff --git a/lib_xua/doc/rst/opt_strings.rst b/lib_xua/doc/rst/opt_strings.rst new file mode 100644 index 00000000..dab165b6 --- /dev/null +++ b/lib_xua/doc/rst/opt_strings.rst @@ -0,0 +1,41 @@ + +Strings and ID's +================ + +The codebase includes various strings and ID's that should be customised to match the product requirements. +These are listed in ::ref:`opt_strings_defines`. + +The Vendor ID (VID) should be acquired from the USB Implementers Forum (www.usb.org). Under no circumstances +should the XMOS VID or any other VID be used without express permission. + +The VID and Product ID (PID) pair must be unique to each product, otherwise driver incompatibilities may arise. + +.. tabularcolumns:: lp{5cm}l + +.. _opt_strings_defines: + +.. list-table:: String & ID defines + :header-rows: 1 + :widths: 20 80 20 + + * - Define + - Description + - Default + * - ``VENDOR_STR`` + - Name of vendor/manufacturer, note the is appended to various strings. + - ``"XMOS"`` + * - ``PRODUCT_STR_A2`` + - Name of the product when running in Audio Class 2.0 mode + - ``"XMOS xCORE (UAC2.0)"`` + * - ``PRODUCT_STR_A1`` + - Name of the product when running in Audio Class 1.0 mode + - ``"XMOS xCORE (UAC1.0)"`` + * - ``PID_AUDIO_2`` + - Product ID when running in Audio Class 2.0 mode + - ``0x0002`` + * - ``PID_AUDIO_1`` + - Product ID when running in Audio Class 1.0 mode + - ``0x0003`` + + + diff --git a/lib_xua/doc/rst/opt_sync.rst b/lib_xua/doc/rst/opt_sync.rst new file mode 100644 index 00000000..dce27bab --- /dev/null +++ b/lib_xua/doc/rst/opt_sync.rst @@ -0,0 +1,66 @@ + +|newpage| + +Synchronisation +=============== + +The codebase supports "Synchronous" and "Asynchronous" modes for USB transfer as defined by the +USB specification(s). + +Asynchronous mode (``XUA_SYNCMODE_ASYNC``) has the advantage that the device is clock-master. This means that +a high-quality local master-clock source can be utilised. It also has the benefit that the device may +synchronise it's master clock to an external digital input stream e.g. S/PDIF and thus avoiding sample-rate +conversion. + +The drawback of this mode is that it burdens the host with syncing to the device which some hosts +may not support. This is especially pertinent to embedded hosts, however, most PC's and mobile devices +will indeed support this mode. + +Synchronous mode (``XUA_SYNCMODE_SYNC``) is an option if the target host does not support asynchronous mode +or if it is desirable to synchronise many devices to a single host. It should be noted, however, that input +from digital streams, such as S/PDIF, are not currently supported in this mode. + +.. note:: + + The selection of synchronisation mode is done at build time and cannot be changed dynamically. + +Setting the synchronisation mode of the device is done using the define in :ref:`opt_sync_defines` + +.. _opt_sync_defines: + +.. list-table:: Sync Define + :header-rows: 1 + :widths: 20 80 20 + + * - Define + - Description + - Default + * - ``XUA_SYNCMODE`` + - USB synchronisation mode + - ``XUA_SYNCMODE_ASYNC`` + +When operating in synchronous mode an external Cirrus Logic CS2100 device is required for master clock +generation. The codebase expects to drive a synchronisation signal to this external device + +The programmer should ensure the define in :ref:`opt_sync_ref_defines` is set appropriately. + +.. _opt_sync_ref_defines: + +.. list-table:: Reference clock location + :header-rows: 1 + :widths: 20 80 20 + + * - Define + - Description + - Default + * - ``PLL_REF_TILE`` + - Tile location of reference to CS2100 device + - ``AUDIO_IO_TILE`` + +The codebase expects this reference signal port to be defined in the application XN file as ``PORT_PLL_REF``. +This may be a port of any bit-width, however, connection to bit[0] is assumed:: + + + +Configuration of the external CS2100 device (typically via I2C) is beyond the scope of this document. + diff --git a/lib_xua/doc/rst/overview.rst b/lib_xua/doc/rst/overview.rst index 0c9095e0..8be7bae7 100644 --- a/lib_xua/doc/rst/overview.rst +++ b/lib_xua/doc/rst/overview.rst @@ -1,6 +1,5 @@ -USB Audio Solution Overview ---------------------------- - +Overview +******** .. table:: :class: vertical-borders @@ -48,19 +47,21 @@ USB Audio Solution Overview | **Supported Devices** | +-------------------------------------------------------------------------------------------------------------------------------+ +---------------------------------+---------------------------------------------------------------------------------------------+ - | XMOS Devices | xCORE-200 Series | + | XMOS Devices | xCORE-200 Series | + | +---------------------------------------------------------------------------------------------+ + | | xCORE.AI Series | +---------------------------------+---------------------------------------------------------------------------------------------+ +-------------------------------------------------------------------------------------------------------------------------------+ | **Requirements** | +-------------------------------------------------------------------------------------------------------------------------------+ +---------------------------------+---------------------------------------------------------------------------------------------+ - | Development Tools | xTIMEcomposer Development Tools v14 or later | + | Development Tools | xTIMEcomposer Development Tools v15.1 or later | +---------------------------------+---------------------------------------------------------------------------------------------+ - | USB | xCORE-200 Series device with integrated USB Phy | + | USB | xCORE device with integrated USB phy (external phy not supported) | +---------------------------------+---------------------------------------------------------------------------------------------+ - | Audio | External audio DAC/ADC/CODECs (and required supporting componentry) supporting I2S/TDM | + | Audio | External audio DAC/ADC/CODECs (and required supporting componentry) supporting I2S/TDM | +---------------------------------+---------------------------------------------------------------------------------------------+ - | Boot/Storage | Compatible SPI Flash device (or xCORE-200 device with internal flash) | + | Boot/Storage | Compatible SPI/QSPI Flash device (or xCORE device with internal flash) | +---------------------------------+---------------------------------------------------------------------------------------------+ +-------------------------------------------------------------------------------------------------------------------------------+ | **Licensing and Support** | @@ -73,5 +74,3 @@ USB Audio Solution Overview | Reference code is maintained by XMOS Limited. | +-------------------------------------------------------------------------------------------------------------------------------+ - - diff --git a/lib_xua/doc/rst/sw.rst b/lib_xua/doc/rst/sw.rst index 5201e90a..f3629a65 100644 --- a/lib_xua/doc/rst/sw.rst +++ b/lib_xua/doc/rst/sw.rst @@ -1,89 +1,21 @@ -.. _usb_audio_sec_architecture: +Implementation Detail +********************* -USB Audio Software Overview ---------------------------- +This chapter examines the implementation of the various components that make up ``lib_xua``. It also examines the integration of dependencies and supporting libraries. -This section describes the software architecture of a USB Audio device implemented using `lib_xua`, its dependencies and other supporting libraries. - -`lib_xua` provides fundamental building blocks for producing USB Audio products on XMOS devices. Every system is required to have the components from `lib_xua` listed in :ref:`usb_audio_shared_components`. - -.. _usb_audio_shared_components: - -.. list-table:: Required XUA Components - :header-rows: 1 - :widths: 40 60 - - * - Component - - Description - * - Endpoint 0 - - Provides the logic for Endpoint 0 which handles - enumeration and control of the device including DFU related requests. - * - Endpoint buffer - - Buffers endpoint data packets to and from the host. Manages delivery of audio packets between the endpoint buffer - component and the audio components. It can also handle volume control processing.Note, this currently utlises two cores - * - AudioHub - - Handles audio I/O over I2S and manages audio data - to/from other digital audio I/O components. - -In addition low-level USB I/0 is required and is provided by the external dependency `lib_xud` - -.. list-table:: Additional Components Required - :header-rows: 1 - :widths: 100 60 - - * - Component - - Description - * - XMOS USB Device Driver (XUD) - - Handles the low level USB I/O. - -In addition :ref:`usb_audio_optional_components` shows optional components that can be added/enabled from within `lib_xua` - -.. _usb_audio_optional_components: - -.. list-table:: Optional Components - :header-rows: 1 - :widths: 40 60 - - * - Component - - Description - * - Mixer - - Allows digital mixing of input and output channels. It can also - handle volume control instead of the decoupler. - * - Clockgen - - Drives an external frequency generator (PLL) and manages - changes between internal clocks and external clocks arising - from digital input. - * - MIDI - - Outputs and inputs MIDI over a serial UART interface. - -`lib_xua` also provides optional support for integrating with the following eternal dependencies: - -.. list-table:: Optional Components - :header-rows: 1 - :widths: 40 60 - - * - Component - - Description - * - S/PDIF Transmitter (lib_spdif) - - Outputs samples of an S/PDIF digital audio interface. - * - S/PDIF Receiver (lib_spdif) - - Inputs samples of an S/PDIF digital audio interface (requires the - clockgen component). - * - ADAT Receiver (lib_adat) - - Inputs samples of an ADAT digital audio interface (requires the - clockgen component). - * - PDM Microphones (lib_mic_array) - - Receives PDM data from microphones and performs PDM to PCM conversion - -.. _usb_audio_threads: - -.. figure:: images/threads-crop.* - :width: 100% - - USB Audio Core Diagram - -:ref:`usb_audio_threads` shows how the components interact with each -other in a typical system. The green circles represent cores with arrows indicating inter-core communications. +.. toctree:: + sw_audio + sw_ep0 + sw_xud + sw_clocking + sw_mixer + sw_spdif + sw_spdif_rx + sw_adat_rx + sw_midi + sw_pdm + sw_hid + sw_resource diff --git a/lib_xua/doc/rst/sw_adat.rst b/lib_xua/doc/rst/sw_adat.rst deleted file mode 100644 index 14b535be..00000000 --- a/lib_xua/doc/rst/sw_adat.rst +++ /dev/null @@ -1,54 +0,0 @@ -ADAT Receive ------------- - -The ADAT receive component receives up to eight channels of audio at a sample rate -of 44.1kHz or 48kHz. The API for calling the receiver functions is -described in :ref:`usb_audio_sec_component_api`. - -The component outputs 32 bits words split into nine word frames. The -frames are laid out in the following manner: - - * Control byte - * Channel 0 sample - * Channel 1 sample - * Channel 2 sample - * Channel 3 sample - * Channel 4 sample - * Channel 5 sample - * Channel 6 sample - * Channel 7 sample - -Example of code show how to read the output of the ADAT component is shown below:: - - control = inuint(oChan); - - for(int i = 0; i < 8; i++) - { - sample[i] = inuint(oChan); - } - -Samples are 24-bit values contained in the lower 24 bits of the word. - -The control word comprises four control bits in bits [11..8] and the value 0b00000001 in bits [7..0]. -This control word enables synchronization at a higher level, in that on the channel a single odd -word is always read followed by eight words of data. - -.. Timing Requirements - ~~~~~~~~~~~~~~~~~~~ - -.. The data samples are outputted onto the channel every 2.4 us. The -.. control sample follows 1.7 us after the last data sample, and is -.. followed 2.4 us later by the first data sample. Given that a channel -.. can hold two words of data, when data appears on the channel, it -.. should be input within 4.1 us otherwise the ADAT receiver will block, -.. and data will be lost. Between data samples a window of 4.8 us is -.. available. - -Integration -~~~~~~~~~~~ - -Since the ADAT is a digital stream the devices master clock must synchronised to it. This is -typically achieved with an external fractional-n clock multiplier. - -The ADAT receive function communicates with the clockGen component which passes audio data onto the -audio driver and handles locking to the ADAT clock source if required. diff --git a/lib_xua/doc/rst/sw_adat_rx.rst b/lib_xua/doc/rst/sw_adat_rx.rst new file mode 100644 index 00000000..5472a4e6 --- /dev/null +++ b/lib_xua/doc/rst/sw_adat_rx.rst @@ -0,0 +1,54 @@ +|newpage| + +ADAT Receive +============ + +The ADAT receive component receives up to eight channels of audio at a sample rate +of 44.1kHz or 48kHz. The API for calling the receiver functions is +described in :ref:`usb_audio_sec_component_api`. + +The component outputs 32 bits words split into nine word frames. The +frames are laid out in the following manner: + + * Control byte + * Channel 0 sample + * Channel 1 sample + * Channel 2 sample + * Channel 3 sample + * Channel 4 sample + * Channel 5 sample + * Channel 6 sample + * Channel 7 sample + +An example of how to read the output of the ADAT component is shown below:: + + control = inuint(oChan); + + for(int i = 0; i < 8; i++) + { + sample[i] = inuint(oChan); + } + +Samples are 24-bit values contained in the lower 24 bits of the word. + +The control word comprises four control bits in bits [11..8] and the value 0b00000001 in bits [7..0]. +This control word enables synchronization at a higher level, in that on the channel a single odd +word is always read followed by eight words of data. + + +Usage and Integration +~~~~~~~~~~~~~~~~~~~~~ + +Since the ADAT is a digital stream the device's master clock must synchronised to it. The integration +of ADAT receive is much the same as S/PDIF receive in that the ADAT receive function communicates +with the Clock Gen core. This Clock Gen Core then passes audio data onto the Audio Hub core. +It also handles locking to the ADAT clock source. + +There are some small differences with the S/PDIF integration accounting for the fact that ADAT +typically has 8 channels compared to S/DIF's two. + +The Clock Gen core also handles SMUX II (e.g. 4 channels at 96kHz) and SMUX IV (e.g. 2 channels at +192kHz), populating the sample FIFO as appropriate. SMUX modes are communicated to the Clock Gen +core from Endpoint 0 via the ``c_clk_ctl`` channel. SMUX modes are exposed to the USB host using +Alternative Interfaces, with appropriate channel counts, for the streaming input Endpoint. + diff --git a/lib_xua/doc/rst/sw_audio.rst b/lib_xua/doc/rst/sw_audio.rst index 3258e0ff..a98b7c19 100755 --- a/lib_xua/doc/rst/sw_audio.rst +++ b/lib_xua/doc/rst/sw_audio.rst @@ -1,26 +1,32 @@ +|newpage| + .. _usb_audio_sec_audio: -AudioHub/I2S -............ +Audio Hub +========= -The AudioHub task performs many functions. It receives and transmits samples from/to the decoupler or mixer core over an XC channel. +The Audio Hub task performs many functions. It receives and transmits samples from/to the Decoupler +or Mixer core over a channel. -It also drives several in and out I2S/TDM channels to/from a CODEC, DAC, ADC etc - from now on termed "audio hardware". +It also drives several in and out I2S/TDM channels to/from a CODEC, DAC, ADC etc. From now on these +external devices will be termed "audio hardware". -If the firmware is configured with the xCORE as I2S master the requred clock lines will also be driven out from this task also. +If the firmware is configured with the xCORE as I2S master the required clock lines will also be +driven from this task. It also has the task of forwarding on and receiving samples to/from other +audio related tasks/cores such as S/PDIF tasks, ADAT etc. -It also has the task of formwarding on and reciving samples to/from other audio related tasks such as S/PDIF tasks, ADAT tasks etc. +In master mode, the xCORE generates the I2S "Continuous Serial Clock (SCK)", or "Bit-Clock (BCLK)" +and the "Word Select (WS)" or "left-right clock (LRCLK)" signals. Any CODEC or DAC/ADC combination +that supports I2S and can be used. -The AudioHub task must be connected to external audio hardware that supports I2S (other modes such as "left justified" can be supported with firmware changes). +The LR-clock, bit-clock and data are all derived from the incoming master clock (typically the +output of the external oscillator or PLL). This is not part of the I2S standard but is commonly +included for synchronizing the internal operation of the analog/digital converters. -In master mode, the XMOS device acts as the master generating the I2S "Continous Serial Clock (SCK)" typically called the Bit-Clock (BCLK) and the "Word Select (WS)" line typically called left-right clock (LRCLK) signals. Any CODEC or DAC/ADC combination that supports I2S and can be used. +The Audio Hub task is implemented in the file ``xua_audiohub.xc``. -The LR-clock, bit-clock and data are all derived from the incoming master clock (typically the output of the external oscillator or PLL) -- This is not part of the I2S standard but is commonly included for synchronizing the internal operation of the analog/digital converters. - -The AudioHub task is implemented in the file ``xua_audiohub.xc``. - -:ref:`usb_audio_codec_signals` shows the signals used to communicate audio between the XMOS device and the external audio hardware. +:ref:`usb_audio_codec_signals` shows the signals used to communicate audio between the XMOS device +and the external audio hardware. .. _usb_audio_codec_signals: @@ -83,13 +89,15 @@ with BCLK then being used to clock data in (SDIN) and data out (SDOUT) of the ex - 12.288 - 2 -The master clock must be supplied by an external source e.g. clock generator, fixed oscillators, PLL etc to generate the two frequencies to support -44.1kHz and 48kHz audio frequencies (e.g. 11.2896/22.5792MHz and 12.288/24.576MHzrespectively). This master clock input is then provided to the -external audio hardware and the xCORE device. +For xCORE-200 devices the master clock must be supplied by an external source e.g. clock generator, +fixed oscillators, PLL etc. xCORE.ai devices may use the integrated secondary PLL. +Two master clock frequencies to support 44.1kHz and 48kHz audio frequencies (e.g. 11.2896/22.5792MHz +and 12.288/24.576MHz respectively). This master clock input is then provided to the external audio +hardware and the xCORE device. Port Configuration (xCORE Master) -+++++++++++++++++++++++++++++++++ +--------------------------------- The default software configuration is xCORE is I2S master. That is, the XMOS device provides the BCLK and LRCLK signals to the external audio hardware @@ -122,41 +130,19 @@ The preceding diagram shows the connectivity of ports and clock blocks. input in one input statement. This allows the software to input, process and output 32-bit words, whilst the ports serialize and deserialize to the single I/O pin connected to each port. -xCORE-200 series devices have the ability to divide an extenal clock in a clock-block. +Unlike previous xCORE architectures, xCORE-200 (XS2) and xCORE.ai (XS3) series devices have the ability to divide an external clock in a clock-block. -However, XS1 based devices do not have this functionality. In order achieve the reqired master-clock -to bit-clock/LR-clock divicd on XS1 devices, buffered ports with a transfer width of 32 are also -used for ``p_bclk`` and ``p_lrclk``. The bit clock is generated by performing outputs of a particular pattern to ``p_bclk`` to toggle -the output at the desired rate. The pattern depends on the divide between the master-clock and bit-clock. -The following table shows the required pattern for different values of this divide: - -.. list-table:: Output patterns - :header-rows: 1 - - * - Divide - - Output pattern - - Outputs per sample - * - 2 - - ``0xAAAAAAAA`` - - 2 - * - 4 - - ``0xCCCCCCCC`` - - 4 - * - 8 - - ``0xF0F0F0F0`` - - 8 - -In any case, the bit clock outputs 32 clock cycles per sample. In the special case where the divide is 1 (i.e. the bit clock frequency equals +The bit clock outputs 32 clock cycles per sample. In the special case where the divide is 1 (i.e. the bit clock frequency equals the master clock frequency), the ``p_bclk`` port is set to a special mode where it simply outputs its clock input (i.e. ``p_mclk``). See ``configure_port_clock_output()`` in ``xs1.h`` for details. ``p_lrclk`` is clocked by ``p_bclk``. In I2S mode the port outputs the pattern ``0x7fffffff`` followed by ``0x80000000`` repeatedly. This gives a signal that has a transition one bit-clock -before the data (as required by the I2S standard) and alternates between high and low for the -left and right channels of audio. +before the data (as required by the I2S standard) and alternates between high and low for the left +and right channels of audio. Changing Audio Sample Frequency -+++++++++++++++++++++++++++++++ +------------------------------- .. _usb_audio_sec_chang-audio-sample: @@ -170,7 +156,5 @@ Upon receiving the change of sample frequency request, the audio core stops the I2S/TDM interface and calls the CODEC/port configuration functions. -Once this is complete, the I2S/TDM interface (i.e. the main look in AudioHub) is restarted at the new frequency. - - +Once this is complete, the I2S/TDM interface (i.e. the main loop in AudioHub) is restarted at the new frequency. diff --git a/lib_xua/doc/rst/sw_clocking.rst b/lib_xua/doc/rst/sw_clocking.rst index 6f9a3a90..65880cb5 100755 --- a/lib_xua/doc/rst/sw_clocking.rst +++ b/lib_xua/doc/rst/sw_clocking.rst @@ -1,35 +1,60 @@ + +|newpage| + .. _usb_audio_sec_clock_recovery: -External Clock Recovery (ClockGen) ----------------------------------- +External Clock Recovery (Clock Gen) +=================================== -An application can either provide fixed master clock sources via selectable oscillators, clock -generation IC, etc, to provide the audio master or use an external PLL/Clock Multiplier to -generate a master clock based on reference from the XMOS device. +To provide an audio master clock an application may use selectable oscillators, clock +generation IC or, in the case of xCORE.ai devices, integrated secondary PLL, to generate fixed +master clock frequencies. -Using an external PLL/Clock Multiplier allows the design to lock to an external clock source -from a digital stream (e.g. S/PDIF or ADAT input). +It may also use an external PLL/Clock Multiplier to generate a master clock based on a reference from +the xCORE. -The clock recovery core (clockGen) is responsible for generating the reference frequency -to the Fractional-N Clock Generator. This, in turn, generates the master clock used over the -whole design. +Using an external PLL/Clock Multiplier allows an Asynchronous mode design to lock to an external +clock source from a digital stream (e.g. S/PDIF or ADAT input). The codebase supports the Cirrus +Logic CS2100 device for this purpose. Other devices may be supported via code modification. + +.. note:: + + It is expected that in a future release the secondary PLL in xCORE.ai devices, coupled with + associated software changes, will be capable of replacing the CS2100 part for most designs. + +The Clock Recovery core (Clock Gen) is responsible for generating the reference frequency +to the CS2100 device. This, in turn, generates the master clock used over the whole design. +This core also serves as a smaller buffer between ADAT and S/PDIF receiving cores and the Audio Hub +core. When running in *Internal Clock* mode this core simply generates this clock using a local timer, based on the XMOS reference clock. -When running in an external clock mode (i.e. S/PDIF Clock" or "ADAT Clock" mode) digital -samples are received from the S/PDIF and/or ADAT receive core. +When running in an external clock mode (i.e. S/PDIF Clock" or "ADAT Clock" mode) samples are +received from the S/PDIF and/or ADAT receive core. The external frequency is calculated through +counting samples in a given period. The reference clock to the CS2100 is then generated based on +the reception of these samples. -The external frequency is calculated through counting samples in a given period. The -reference clock to the Fractional-N Clock Multiplier is then generated based on this -external stream. If this stream becomes invalid, the timer event will fire to ensure that -valid master clock generation continues regardless of cable unplugs etc. +If an external stream becomes invalid, the *Internal Clock* timer event will fire to ensure that +valid master clock generation continues regardless of cable unplugs etc. Efforts are made to +ensure the transition between these clocks are relatively seamless. Additionally efforts are also +made to try and keep the jitter on the reference clock as low as possibly, regardless of activity +level of the Clock Gen core. The is achieved though the use of port times to schedule pin toggling +rather than directly outputting to the port. -This core gets clock selection Get/Set commands from Endpoint 0 via the ``c_clk_ctl`` +The Clock Gen core gets clock selection Get/Set commands from Endpoint 0 via the ``c_clk_ctl`` channel. This core also records the validity of external clocks, which is also queried -through the same channel from Endpoint 0. +through the same channel from Endpoint 0. Note, the *Internal Clock* is always reported as being +valid. It should be noted that the device always reports the current device sample rate regardless +of the clock being interrogated. This results in improved user experience for most driver/operating +system combinations +To inform the host of any status change, the Clock Gen core can also cause the Decouple core to +request an interrupt packet on change of clock validity. This functionality is based on the Audio +Class 2.0 status/interrupt endpoint feature. -This core also can cause the decouple core to request an interrupt packet on change of -clock validity. This functionality is based on the Audio Class 2.0 status/interrupt endpoint -feature. +.. note:: + + When running in Synchronous mode external digital input streams are currently not supported. + Such a feature would require sample-rate conversion to covert from the S/PDIF or ADAT clock + domain to the USB host clock domain. As such this core is not used in a Synchronous mode device. diff --git a/lib_xua/doc/rst/sw_detail.rst b/lib_xua/doc/rst/sw_detail.rst deleted file mode 100644 index fc14996a..00000000 --- a/lib_xua/doc/rst/sw_detail.rst +++ /dev/null @@ -1,27 +0,0 @@ - -Implementation Detail ---------------------- - -This section describes the software architecture of a USB Audio device implemented using `lib_xua`, it's dependancies and other supporting libraries. - -This section will now examine the operation of these components in further detail. - -.. toctree:: - - sw_audio - sw_spdif - - -.. - sw_xud - sw_ep0 - sw_audio - sw_mixer - sw_spdif_rx - sw_adat - sw_clocking - sw_midi - sw_pdm - sw_resource -.. - diff --git a/lib_xua/doc/rst/sw_ep0.rst b/lib_xua/doc/rst/sw_ep0.rst index 23d41691..39e90a06 100644 --- a/lib_xua/doc/rst/sw_ep0.rst +++ b/lib_xua/doc/rst/sw_ep0.rst @@ -3,14 +3,14 @@ .. _usb_audio_sec_usb: Endpoint 0: Management and Control -.................................. +================================== All USB devices must support a mandatory control endpoint, Endpoint 0. This controls the management tasks of the USB device. These tasks can be generally split into enumeration, audio configuration and firmware upgrade requests. Enumeration -~~~~~~~~~~~ +----------- When the device is first attached to a host, enumeration occurs. This process involves the host interrogating the device as to its functionality. The device does this by presenting several interfaces to the host via a set of descriptors. @@ -19,7 +19,7 @@ During the enumeration process the host will issue various commands to the devic The endpoint 0 code runs in its own core and follows a similar format to that of the USB Device examples in `lib_xud` (i.e. Example HID Mouse Demo). That is, a call is made to ``USB_GetSetupPacket()`` to receive a command from the host. This populates a ``USB_SetupPacket_t`` structure, which is then parsed. There are many mandatory requests that a USB Device must support as required by the USB Specification. Since these are required for all devices in order to function a -``USB_StandardRequests()`` function is provided (see ``module_usb_device``) which implements all of these requests. This includes the following items: +``USB_StandardRequests()`` function is provided (see ``xud_device.xc``) which implements all of these requests. This includes the following items: - Requests for standard descriptors (Device descriptor, configuration descriptor etc) and string descriptors - USB GET/SET INTERFACE requests @@ -28,8 +28,8 @@ There are many mandatory requests that a USB Device must support as required by For more information and full documentation, including full worked examples of simple devices, please refer to `lib_xud`. -The ``USB_StandardRequests()`` function takes the devices various descriptors as parameters, these are passed from data structures found in the ``descriptors.h`` file. -These data structures are fully customised based on the how the design is configured using various defines (see :ref:`sec_custom_defines_api`). +The ``USB_StandardRequests()`` function takes the devices various descriptors as parameters, these are passed from data structures found in the ``xud_ep0_descriptors.h`` file. +These data structures are fully customised based on the how the design is configured using various defines. The ``USB_StandardRequests()`` functions returns a ``XUD_Result_t``. ``XUD_RESULT_OKAY`` indicates that the request was fully handled without error and no further action is required - The device should move to receiving the next request from the host (via ``USB_GetSetupPacket()``). @@ -38,41 +38,42 @@ The function returns ``XUD_RES_ERR`` if the request was not recognised by the `` The function may also return ``XUD_RES_RST`` if a bus-reset has been issued onto the bus by the host and communicated from XUD to Endpoint 0. -Since the ``USB_StandardRequests()`` function STALLs an unknown request, the endpoint 0 code must parse the ``USB_SetupPacket_t`` structure to handle device specific requests and then calling ``USB_StandardRequests()`` as required. This is described next. +Since the ``USB_StandardRequests()`` function STALLs an unknown request, the endpoint 0 code must first parse the ``USB_SetupPacket_t`` structure to handle device specific requests and then call ``USB_StandardRequests()`` as required. Over-riding Standard Requests -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +----------------------------- -The USB Audio design "over-rides" some of the requests handled by ``USB_StandardRequests()``, for example it uses the SET_INTERFACE request to indicate it if the host is streaming audio to the device. In this case the setup packet is parsed, the relevant action taken, the ``USB_StandardRequests()`` is called to handle the response to the host etc. +The USB Audio design "over-rides" some of the requests handled by ``USB_StandardRequests()``, for example it uses the SET_INTERFACE request to indicate if the host is streaming audio to the device. In this case the setup packet is parsed, the relevant action taken, the ``USB_StandardRequests()`` is still called to handle the response to the host. Class Requests -~~~~~~~~~~~~~~ -Before making the call to ``USB_StandardRequests()`` the setup packet is parsed for Class requests. These are handled in functions such as ``AudioClasRequests_2()``, ``AudioClassRequests_2``, ``DFUDeviceRequests()`` etc depending on the type of request. +-------------- + +Before making the call to ``USB_StandardRequests()`` the setup packet is parsed for Class requests. These are handled in functions such as ``AudioClassRequests_1()``, ``AudioClassRequests_2``, ``DFUDeviceRequests()`` etc depending on the type of request. Any device specific requests are handled - in this case Audio Class, MIDI class, DFU requests etc. Some of the common Audio Class requests and their associated behaviour will now be examined. Audio Requests -++++++++++++++ +^^^^^^^^^^^^^^ When the host issues an audio request (e.g. sample rate or volume change), it sends a command to Endpoint 0. Like all requests this is returned from ``USB_GetSetupPacket()``. After some parsing (namely as Class Request to an Audio Interface) the request is handled by either the ``AudioClassRequests_1()`` or ``AudioClassRequests_2()`` function (based on whether the device is running in Audio Class 1.0 or 2.0 mode). Note, Audio Class 1.0 Sample rate changes are send to the relevant endpoint, rather than the interface - this is handled as a special case in he endpoint 0 request parsing where ``AudioEndpointRequests_1()`` is called. -The ``AudioClassRequests_X()`` functions parses the request further in order to ascertain the correct audio operation to execute. +The ``AudioClassRequests_X()`` functions further parses the request in order to ascertain the correct audio operation to execute. Audio Request: Set Sample Rate -++++++++++++++++++++++++++++++ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The ``AudioClassRequests_2()`` function parses the passed ``USB_SetupPacket_t`` structure for a ``CUR`` request of type ``SAM_FREQ_CNTROL`` to a Clock Unit in the devices topology (as described in the devices descriptors). +The ``AudioClassRequests_2()`` function parses the passed ``USB_SetupPacket_t`` structure for a ``CUR`` request of type ``SAM_FREQ_CONTROL`` to a Clock Unit in the devices topology (as described in the devices descriptors). -The new sample frequency is extracted and passed via channel to the rest of the design - through the buffering code and eventually to the Audio IO/I2S core. The ``AudioClassRequests_2()`` function waits for a handshake to propagate back though the system before signalling to the host that the request has completed successfully. Note, during this time the USB library is NAKing the host essentially holding off further traffic/requests until the sample-rate change is fully complete. +The new sample frequency is extracted and passed via channel to the rest of the design - through the buffering code and eventually to the Audio Hub (I2S) core. The ``AudioClassRequests_2()`` function waits for a handshake to propagate back through the system before signalling to the host that the request has completed successfully. Note, during this time the USB library is NAKing the host essentially holding off further traffic/requests until the sample-rate change is fully complete. .. _usb_audio_sec_audio-requ-volume: Audio Request: Volume Control -+++++++++++++++++++++++++++++ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ When the host requests a volume change, it sends an audio interface request to Endpoint 0. An array is @@ -83,7 +84,7 @@ When changing the volume, Endpoint 0 applies the master volume and channel volume, producing a single volume value for each channel. These are stored in the array. -The volume will either be handled by the ``decoupler`` core or the mixer +The volume will either be handled by the ``decouple`` core or the mixer component (if the mixer component is used). Handling the volume in the mixer gives the decoupler more performance to handle more channels. @@ -97,34 +98,35 @@ the array (ordering between writes and reads is unimportant in this case). Inline assembly is used by the decoupler core to access the array, avoiding the parallel usage checks of XC. -If volume control is implemented in the mixer, Endpoint 0 sends a mixer command to the mixer to change the volume. Mixer commands +If volume control is implemented in the mixer, Endpoint 0 sends a mixer command +to the mixer to change the volume. Mixer commands are described in :ref:`usb_audio_sec_mixer`. Audio Endpoints (Endpoint Buffer and Decoupler) -............................................... +=============================================== Endpoint Buffer -~~~~~~~~~~~~~~~ +--------------- All endpoints other that Endpoint 0 are handled in one core. This -core is implemented in the file ``usb_buffer.xc``. This core is communicates directly with the XUD library. +core is implemented in the file ``ep_buffer.xc``. This core communicates directly with the XUD library. The USB buffer core is also responsible for feedback calculation based on USB Start Of Frame (SOF) notification and reads from the port counter of a port connected to the master clock. -Decoupler -~~~~~~~~~ +Decouple +-------- The decoupler supplies the USB buffering core with buffers to transmit/receive audio data to/from the host. It marshals these buffers into FIFOs. The data from the FIFOs are then sent over XC channels to -other parts of the system as they need it. This core also +other parts of the system as they need it. In asynchronous mode this core also determines the size of each packet of audio sent to the host (thus matching the audio rate to the USB packet rate). The decoupler is implemented in the file ``decouple.xc``. Audio Buffering Scheme -~~~~~~~~~~~~~~~~~~~~~~~ +---------------------- This scheme is executed by co-operation between the buffering core, the decouple core and the XUD library. @@ -132,76 +134,69 @@ core, the decouple core and the XUD library. For data going from the device to the host the following scheme is used: - -#. The decouple core receives samples from the audio core and +#. The Decouple core receives samples from the Audio Hub core and puts them into a FIFO. This FIFO is split into packets when data is entered into it. Packets are stored in a format consisting of their length in bytes followed by the data. -#. When the buffer cores needs a buffer to send to the XUD core - (after sending the previous buffer), the decouple core is +#. When the Endpoint Buffer core needs a buffer to send to the XUD core + (after sending the previous buffer), the Decouple core is signalled (via a shared memory flag). -#. Upon this signal from the buffering core, the decouple core - passes the next packet from the FIFO to the buffer core. It also - signals to the XUD library that the buffer core is able to send a +#. Upon this signal from the Endpoint Buffer core, the Decouple core + passes the next packet from the FIFO to the Endpoint Buffer core. It also + signals to the XUD library that the Endpoint Buffer core is able to send a packet. -#. When the buffer core has sent this buffer, it signals to the - decouple that the buffer has been sent and the decouple core +#. When the Endpoint Buffer core has sent this buffer, it signals to the + Decouple core that the buffer has been sent and the Decouple core moves the read pointer of the FIFO. - For data going from the host to the device the following scheme is used: - -#. The decouple core passes a pointer to the buffering core +#. The Decouple core passes a pointer to the Endpoint Buffer core pointing into a FIFO of data and signals to the XUD library that - the buffering core is ready to receive. + the Endpoint Buffer core is ready to receive. -#. The buffering core then reads a USB packet into the FIFO and - signals to the decoupler that the packet has been read. +#. The Endpoint Buffer core then reads a USB packet into the FIFO and + signals to the Decouple core that the packet has been read. -#. Upon receiving this signal the decoupler core updates the +#. Upon receiving this signal the Decouple core updates the write pointer of the FIFO and provides a new pointer to the - buffering core to fill. - -#. Upon request from the audio core, the decoupler core sends - samples to the audio core by reading samples out of the FIFO. + Endpoint Buffer core to fill. +#. Upon request from the Audio Hub core, the Decouple core sends + samples to the Audio Hub core by reading samples out of the FIFO. Decoupler/Audio Core interaction -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +-------------------------------- -To meet timing requirements of the audio system, the decoupler +To meet timing requirements of the audio system (i.e Audio Hub/Mixer), the Decoupler core must respond to requests from the audio system to send/receive samples immediately. An interrupt handler is set up in the decoupler core to do this. The interrupt handler is implemented in the function ``handle_audio_request``. The audio system sends a word over a channel to the decouple core to -request sample transfer (using the build in outuint function). +request sample transfer (using the build in ``outuint()`` function). The receipt of this word in the channel causes the ``handle_audio_request`` interrupt to fire. -The first operation the interrupt handler does is to send back a word -acknowledging the request (if there was a change of sample frequency +The first operation the interrupt handler does (once it inputs the word that triggered the interrupt) +is to send back a word acknowledging the request (if there was a change of sample frequency a control token would instead be sent---the audio system uses a testct() to inspect for this case). -Sample transfer may now take place. First the audio subsystem transfers -samples destined for the host, then the decouple core sends -samples from the host to device. These transfers always take place +Sample transfer may now take place. First the Decouple core sends samples from host to device then the +audio subsystem transfers samples destined for the host. These transfers always take place in channel count sized chunks (i.e. ``NUM_USB_CHAN_OUT`` and -``NUM_USB_CHAN_IN``). That is, if the device has 10 output channels and -8 input channels, 10 samples are sent from the decouple core and 8 received -every interrupt. +``NUM_USB_CHAN_IN``). That is, if the device has 10 output channels and 8 input channels, +10 samples are sent from the decouple core and 8 received every interrupt. The complete communication scheme is shown in the table below (for non sample frequency change case): - .. table:: Decouple/Audio System Channel Communication +-----------------+-----------------+-----------------------------------------+ @@ -237,55 +232,62 @@ frequency change case): +-----------------+-----------------+-----------------------------------------+ .. note:: - The request and acknowledgement sent to/from Decouple to the Audio System is an "output underflow" sample + The request and acknowledgement sent to/from the Decouple core to the Audio System is an "output underflow" sample value. If in PCM mode it will be 0, in DSD mode it will be DSD silence. This allows the buffering system to output a suitable underflow value without knowing the format of the stream (this is especially advantageous in the DSD over PCM (DoP) case) Asynchronous Feedback -+++++++++++++++++++++ +--------------------- -The device uses a feedback endpoint to report the rate at which +When built to operate in Asynchronous mode the device uses a feedback endpoint to report the rate at which audio is output/input to/from external audio interfaces/devices. This feedback is in accordance with -the *USB 2.0 Specification*. +the *USB 2.0 Specification*. This calculated feedback value is also used to size packets to the host. -This asynchronous clocking scheme means that the device is the clocking master than therefore -means a high-quality local master clock source can be used. +This asynchronous clocking scheme means that the device is the clock master and therefore +a high-quality local master clock or a digital input stream can be used as the clock source. -After each received USB SOF token, the buffering core takes a time-stamp from a port clocked off +After each received USB Start Of Frame (SOF) token, the buffering core takes a time-stamp from a port clocked off the master clock. By subtracting the time-stamp taken at the previous SOF, the number of master clock ticks since the last SOF is calculated. From this the number of samples (as a fixed -point number) between SOFs can be calculated. -This count is aggregated over 128 SOFs and used as a basis for the feedback value. +point number) between SOFs can be calculated. This count is aggregated over 128 SOFs and used as a +basis for the feedback value. -The sending of feedback to the host is also handled in the USB buffering core via an explicit feedback -IN endpoint. If both input and output is enabled then the feedback is implicit based on the audio stream -sent to the host. +The sending of feedback to the host is also handled in the Endpoint Buffer core via an explicit +feedback IN endpoint. + +If both input and output is enabled then the feedback can be implicit based on the audio stream +sent to the host. In practice this an explicit feedback endpoint is normally used due to restrictions +in Microsoft Windows operating systems (see ``UAC_FORCE_FEEDBACK_EP``). USB Rate Control -++++++++++++++++ +---------------- .. _usb_audio_sec_usb-rate-control: -The Audio core must consume data from USB -and provide data to USB at the correct rate for the selected sample -frequency. The *USB 2.0 Specification* states that the maximum -variation on USB packets can be +/- 1 sample per USB frame. USB -frames are sent at 8kHz, so on average for 48kHz each packet -contains six samples per channel. The device uses Asynchronous mode, -so the audio clock may drift and run faster or slower than the -host. Hence, if the audio clock is slightly fast, the device may -occasionally input/output seven samples rather than six. Alternatively, -it may be slightly slow and input/output five samples rather than six. -:ref:`usb_audio_samples_per_packet` shows the allowed number of samples -per packet for each example audio frequency. +The device must consume data from USB host and provide data to USB host at the correct rate for the +selected sample frequency. When running in asynchronous mode the *USB 2.0 Specification* states +that the maximum variation on USB packets can be +/- 1 sample per USB frame (Synchronous mode +mandates no variation other than that required to match a sample rate that doesn't cleanly divide +the USB SOF period e.g. 44.1kHz) -See USB Device Class Definition for Audio Data Formats v2.0 section 2.3.1.1 -for full details. +High-speed USB frames are sent at 8kHz, so on average for 48kHz each packet contains six samples +per channel. + +When running in Asynchronous mode, so the audio clock may drift and run faster or slower than the +host. Hence, if the audio clock is slightly fast, the device may occasionally input/output seven +samples rather than six. Alternatively, it may be slightly slow and input/output five samples rather +than six. :ref:`usb_audio_samples_per_packet` shows the allowed number of samples per packet for +each example audio frequency in Asynchronous mode. + +When running in Synchronous mode the audio clock is synchronised to the USB host SOF clock. Hence, +at 48kHz the device always expects six samples from, and always sends size samples to, the host. + +See USB Device Class Definition for Audio Data Formats v2.0 section 2.3.1.1 for full details. .. _usb_audio_samples_per_packet: -.. table:: Allowed samples per packet +.. table:: Allowed samples per packet in Async mode +-----------------+-------------+-------------+ | Frequency (kHz) | Min Packet | Max Packet | @@ -304,46 +306,11 @@ for full details. +-----------------+-------------+-------------+ -To implement this control, the decoupler core uses the feedback -value calculated in the buffering core. This value is used to -work out the size of the next packet it will insert into the audio -FIFO. +To implement this control, the Decoupler core uses the feedback value calculated in the EP Buffering +core. This value is used to work out the size of the next packet it will insert into the audio FIFO. +.. note:: + In Synchronous mode the same system is used, but the feedback value simply uses a fixed value + rather than one derived from the master clock port. -.. .. _fig_usb_devices: - -.. .. table:: USB interfaces presented to host -.. :class: center -.. -.. +-----------------------+----------------------------------+ -.. | **Mode** | **Interfaces** | -.. +=======================+==================================+ -.. | Application mode | | Audio Class 2/Audio Class 1 | -.. | | | DFU Class 1.1 | -.. | | | MIDI Device Class 1.0 | -.. +-----------------------+----------------------------------+ -.. | DFU mode | DFU Class 1.1 | -.. +-----------------------+----------------------------------+ - -.. The device initially starts in Application mode. - - - - -.. :ref:`usb_audio_sec_dfu` describes how DFU mode is used. The -.. audio device class (1 or 2) is set at compile time---see :ref:`usb_audio_sec_custom_defines_api`. - - - -.. Reset -.. ~~~~~ - -.. On receiving a reset request, three steps occur: - -.. #. Depending on the DFU state, the device may be set into DFU - mode. - -.. #. A XUD function is called to reset the endpoint structure and receive the new bus speed. - -.. _usb_audio_sec_audio-requ-sett: diff --git a/lib_xua/doc/rst/sw_hid.rst b/lib_xua/doc/rst/sw_hid.rst index 89a00ddf..48480c5a 100755 --- a/lib_xua/doc/rst/sw_hid.rst +++ b/lib_xua/doc/rst/sw_hid.rst @@ -1,5 +1,5 @@ Audio Controls via Human Interface Device (HID) ------------------------------------------------- +=============================================== The design supports simple audio controls such as play/pause, volume up/down etc via the USB Human Interface Device Class Specification. @@ -45,4 +45,3 @@ On each HID report request from the host the function ``Vendor_ReadHidButtons(un Since the ``Vendor_ReadHidButtons()`` function is called from the ``buffer`` logical core, care should be taken not to add to much execution time to this function since this could cause issues with servicing other endpoints. -For a full example please see the HID section in :ref:`usb_audio_sec_l1_audio_sw`. diff --git a/lib_xua/doc/rst/sw_iap.rst b/lib_xua/doc/rst/sw_iap.rst deleted file mode 100644 index 01c57cd9..00000000 --- a/lib_xua/doc/rst/sw_iap.rst +++ /dev/null @@ -1,9 +0,0 @@ - -Apple MFi compatibility ------------------------ - -XMOS devices are capable of operating with Apple iPod, iPhone, and iPad devices -that feature USB host support. Information regarding this functionality is -protected by the Made For iPod (MFi) program and associated licensing. - -Please contact XMOS for details and further documentation. diff --git a/lib_xua/doc/rst/sw_midi.rst b/lib_xua/doc/rst/sw_midi.rst index 52ef02e0..dea78378 100755 --- a/lib_xua/doc/rst/sw_midi.rst +++ b/lib_xua/doc/rst/sw_midi.rst @@ -1,7 +1,13 @@ -MIDI ----- +|newpage| + +MIDI +==== + +The MIDI core implements a 31250 baud UART for both input and output. On receiving 32=bit USB MIDI events +from the Endpoint Buffer core, it parses these and translates them to 8-bit MIDI messages which are sent +over UART. Similarly, incoming 8-bit MIDI messages are aggregated into 32-bit USB MIDI events and +passed on to the Endpoint Buffer core. The MIDI core is implemented in the file ``usb_midi.xc``. + +The Endpoint Buffer core implements the two Bulk endpoints (one In and one Out) as well as interacting +with small, shared-memory, FIFOs for each endpoint. -The MIDI driver implements a 31250 baud UART input and output. On receiving 32-bit USB MIDI events -from the ``buffer`` core, it parses these and translates them to 8-bit MIDI messages which are sent -over UART. Similarly, incoming 8-bit MIDI messages are aggregated into 32-bit USB-MIDI events an -passed on to the ``buffer`` core. The MIDI core is implemented in the file ``usb_midi.xc``. diff --git a/lib_xua/doc/rst/sw_mixer.rst b/lib_xua/doc/rst/sw_mixer.rst index 8a6ab14a..3847748a 100755 --- a/lib_xua/doc/rst/sw_mixer.rst +++ b/lib_xua/doc/rst/sw_mixer.rst @@ -3,50 +3,53 @@ Digital Mixer ------------- -The mixer core(s) take outgoing audio from the decoupler core and incoming -audio from the audio driver core. It then applies the volume to each -channel and passes incoming audio on to the decoupler and outgoing -audio to the audio driver. The volume update is achieved using the -built-in 32bit to 64bit signed multiply-accumulate function -(``macs``). The mixer is implemented in the file -``mixer.xc``. +The Mixer core(s) take outgoing audio from the Decouple core and incoming audio from the Audio Hub +core. It then applies the volume to each channel and passes incoming audio on to Decouple and outgoing +audio to Audio Hub. The volume update is achieved using the built-in 32bit to 64bit signed +multiply-accumulate function (``macs``). The mixer is implemented in the file ``mixer.xc``. -The mixer takes two cores and can perform eight mixes with -up to 18 inputs at sample rates up to 96kHz and two mixes with up to 18 -inputs at higher sample rates. The component automatically moves -down to two mixes when switching to a higher rate. +The mixer takes (up to) two cores and can perform eight mixes with up to 18 inputs at sample rates +up to 96kHz and two mixes with up to 18 inputs at higher sample rates. The component automatically +reverts to generating two mixes when running at the higher rate. The mixer can take inputs from either: - * The USB outputs from the host---these samples come from the decoupler core. - * The inputs from the audio interface on the device---these - samples come from the audio driver. + * The USB outputs from the host---these samples come from the Decouple core. + * The inputs from the audio interfaces on the device---these samples come from the Audio Hub core + and includes samples from digital input streams. -Since the sum of these inputs may be more then the 18 possible mix -inputs to each mixer, there is a mapping from all the -possible inputs to the mixer inputs. +Since the sum of these inputs may be more then the 18 possible mix inputs to each mixer, there is a +mapping from all the possible inputs to the mixer inputs. -After the mix occurs, the final outputs are created. There are two -output destinations: +After the mix occurs, the final outputs are created. There are two possible output destinations +for each mix. - * The USB inputs to the host---these samples are sent to the decoupler core. + * The USB inputs to the host---these samples are sent to the Decouple core. - * The outputs to the audio interface on the device---these samples - are sent to the audio driver. + * The outputs to the audio interface on the device---these samples are sent to the Audio Hub + core -For each possible output, a mapping exists to tell the mixer what its -source is. The possible sources are the USB outputs from the host, the -inputs for the audio interface or the outputs from the mixer units. +For each possible output from the device, a mapping exists to inform the mixer what it's source is. +The possible sources are the output from the USB host, the inputs from the Audio Hub core or the +outputs from the mixes. -As mentioned in :ref:`usb_audio_sec_audio-requ-volume`, the mixer can also -handle volume setting. If the mixer is configured to handle volume but -the number of mixes is set to zero (so the component is solely doing -volume setting) then the component will use only one core. +Essentially the mixer/router can be configured such that any device input can be used as an input to +any mix or routed directly to any device output. Additionally, any device output can be derived from +any mixer output or any device input. + +As mentioned in :ref:`usb_audio_sec_audio-requ-volume`, the mixer can also handle processing or +volume controls. If the mixer is configured to handle volume but the number of mixes is set to zero +(such that the core is solely doing volume setting) then the component will use only one core. This +is sometimes a useful configuration for large channel count devices. Control ~~~~~~~ -The mixers can receive the following control commands from the Endpoint 0 core via a channel: +The mixers can receive the control commands from the host via USB Control Requests to Endpoint 0. +The Endpoint 0 core relays these to the Mixer cores(s) via a channel (``c_mix_ctl``). These commands +are described in :ref:`table_mixer_commands`. + +.. _table_mixer_commands: .. list-table:: Mixer Component Commands :header-rows: 1 @@ -79,103 +82,170 @@ Host Control ~~~~~~~~~~~~ The mixer can be controlled from a host PC by sending requests to Endpoint 0. XMOS provides a simple -command line based sample application demonstrating how the mixer can be controlled. +command line based sample application demonstrating how the mixer can be controlled. This is +intended as an example of how you might add mixer control to your own control application. It is not +intended to be exposed to end users. For details, consult the README file in the host_usb_mixer_control directory. +A list of arguments can also be seen with:: -The main requirements of this control are to + $ ./xmos_mixer --help + +The main requirements of this control utility are to * Set the mapping of input channels into the mixer - * Set the coefficients for each mixer output of each input + * Set the coefficients for each mixer output for each input * Set the mapping for physical outputs which can either come directly from the inputs or via the mixer. -There is enough flexibility within this configuration that there will often -be multiple ways of creating the required solution. +.. note:: -Whilst using the XMOS Host control example application, consider setting the -mixer to perform a loop-back from analogue inputs 1 and 2 to analogue -outputs 1 and 2. + The flexibility within this configuration space us such that there is often multiple ways + of producing the desired result. Product developers may only want to expose a subset of this + functionality to their end users. -First consider the inputs to the mixer:: +Whilst using the XMOS Host control example application, consider the example of setting the +mixer to perform a loop-back from analogue inputs 1 & 2 to analogue outputs 1 & 2. - ./xmos_mixer --display-aud-channel-map 0 +.. note:: -displays which channels are mapped to which mixer inputs:: + The command outputs shown are examples; the actual output will depend on the mixer configuration. - ./xmos_mixer --display-aud-channel-map-sources 0 +The following will show the index for each device output along with which channel is currently mapped to it. +In this example the analogue outputs 1 & 2 are 0 & 1 respectively:: -displays which channels could possibly be mapped to mixer inputs. Notice -that analogue inputs 1 and 2 are on mixer inputs 10 and 11. + $ ./xmos_mixer --display-aud-channel-map -Now examine the audio output mapping:: + Audio Output Channel Map + ------------------------ + + 0 (DEVICE OUT - Analogue 1) source is 0 (DAW OUT - Analogue 1) + 1 (DEVICE OUT - Analogue 2) source is 1 (DAW OUT - Analogue 2) + 2 (DEVICE OUT - SPDIF 1) source is 2 (DAW OUT - SPDIF 1) + 3 (DEVICE OUT - SPDIF 2) source is 3 (DAW OUT - SPDIF 2) + $ _ - ./xmos_mixer --display-aud-channel-map 0 +The DAW Output Map can be seen with:: -displays which channels are mapped to which outputs. By default all -of these bypass the mixer. We can also see what all the possible -mappings are:: + $ ./xmos_mixer --display-daw-channel-map - ./xmos_mixer --display-aud-channel-map-sources 0 + DAW Output To Host Channel Map + ------------------------ + + 0 (DEVICE IN - Analogue 1) source is 4 (DEVICE IN - Analogue 1) + 1 (DEVICE IN - Analogue 2) source is 5 (DEVICE IN - Analogue 2) + $ _ -So now map the first two mixer outputs to physical outputs 1 and 2:: +.. note:: - ./xmos_mixer --set-aud-channel-map 0 26 - ./xmos_mixer --set-aud-channel-map 1 27 + In both cases, by default, these bypass the mixer. + +The following command will list the channels which can be mapped to the device outputs from the +Audio Output Channel Map. Note that, in this example, analogue inputs 1 & 2 are source 4 & 5 and +Mix 1 & 2 are source 6 & 7:: + + $ ./xmos_mixer --display-aud-channel-map-sources + + Audio Output Channel Map Source List + ------------------------------------ + + 0 (DAW OUT - Analogue 1) + 1 (DAW OUT - Analogue 2) + 2 (DAW OUT - SPDIF 1) + 3 (DAW OUT - SPDIF 2) + 4 (DEVICE IN - Analogue 1) + 5 (DEVICE IN - Analogue 2) + 6 (MIX - Mix 1) + 7 (MIX - Mix 2) + $ _ + +Using the indices from the previous commands, we will now re-map the first two mixer channels (Mix 1 & Mix 2) to device outputs 1 & 2:: + + $ ./xmos_mixer --set-aud-channel-map 0 6 + $ ./xmos_mixer --set-aud-channel-map 1 7 + $ _ You can confirm the effect of this by re-checking the map:: - ./xmos_mixer --display-aud-channel-map 0 + $ ./xmos_mixer --display-aud-channel-map -This now makes analogue outputs 1 and 2 come from the mixer, rather -than directly from USB. However the mixer is still mapped to pass -the USB channels through to the outputs, so there will still be no -functional change yet. + Audio Output Channel Map + ------------------------ + + 0 (DEVICE OUT - Analogue 1) source is 6 (MIX - Mix 1) + 1 (DEVICE OUT - Analogue 2) source is 7 (MIX - Mix 2) + 2 (DEVICE OUT - SPDIF 1) source is 2 (DAW OUT - SPDIF 1) + 3 (DEVICE OUT - SPDIF 2) source is 3 (DAW OUT - SPDIF 2) + $ _ -The mixer nodes need to be individually set. They can be displayed -with:: +This now derives analogue outputs 1 & 2 from the mixer, rather than directly from USB. However, +since the mixer is mapped, by default, to just pass the USB channels through to the outputs there will be no +functional change. - ./xmos_mixer --display-mixer-nodes 0 -To get the audio from the analogue inputs to outputs 1 and 2, nodes 80 -and 89 need to be set:: +.. note:: - ./xmos_mixer --set-value 0 80 0 - ./xmos_mixer --set-value 0 89 0 + The USB audio reference design has only one unit so the mixer_id argument should always be 0. + +The mixer nodes need to be individually set. The nodes in mixer_id 0 can be displayed +with the following command:: + + $ ./xmos_mixer --display-mixer-nodes 0 + + Mixer Values (0) + ---------------- + + Mixer outputs + 1 2 + DAW - Analogue 1 0:[0000.000] 1:[ -inf ] + DAW - Analogue 2 2:[ -inf ] 3:[0000.000] + DAW - SPDIF 1 4:[ -inf ] 5:[ -inf ] + DAW - SPDIF 2 6:[ -inf ] 7:[ -inf ] + AUD - Analogue 1 8:[ -inf ] 9:[ -inf ] + AUD - Analogue 2 10:[ -inf ] 11:[ -inf ] + $ _ + +With mixer outputs 1 & 2 mapped to device outputs analogue 1 & 2; to get the audio from the analogue inputs to device +outputs mixer_id 0 node 8 and node 11 need to be set to 0db:: + + $ ./xmos_mixer --set-value 0 8 0 + $ ./xmos_mixer --set-value 0 11 0 + $ _ At the same time, the original mixer outputs can be muted:: - ./xmos_mixer --set-value 0 0 -inf - ./xmos_mixer --set-value 0 9 -inf + $ ./xmos_mixer --set-value 0 0 -inf + $ ./xmos_mixer --set-value 0 3 -inf + $ _ -Now audio inputs on analogue 1/2 should be heard on outputs 1/2. +Now audio inputs on analogue 1 and 2 should be heard on outputs 1 and 2 respectively. -As mentioned above, the flexibility of the mixer is such that there -will be multiple ways to create a particular mix. Another option to -create the same routing would be to change the mixer sources such that -mixer 1/2 outputs come from the analogue inputs. +As mentioned above, the flexibility of the mixer is such that there will be multiple ways to create +a particular mix. Another option to create the same routing would be to change the mixer sources +such that mixer outputs 1 and 2 come from the analogue inputs 1 and 2. -To demonstrate this, firstly undo the changes above:: +To demonstrate this, firstly undo the changes above (or simply reset the device):: - ./xmos_mixer --set-value 0 80 -inf - ./xmos_mixer --set-value 0 89 -inf - ./xmos_mixer --set-value 0 0 0 - ./xmos_mixer --set-value 0 9 0 + $ ./xmos_mixer --set-value 0 8 -inf + $ ./xmos_mixer --set-value 0 11 -inf + $ ./xmos_mixer --set-value 0 0 0 + $ ./xmos_mixer --set-value 0 3 0 + $ _ -The mixer should now have the default values. The sources for mixer -1/2 can now be changed:: +The mixer should now have the default values. The sources for mixer 0 output 1 and 2 can now be changed +using indices from the Audio Output Channel Map Source List:: - ./xmos_mixer --set-mixer-source 0 0 10 - ./xmos_mixer --set-mixer-source 0 1 11 - -If you rerun:: - - ./xmos_mixer --display-mixer-nodes 0 - -the first column now has AUD - Analogue 1 and 2 rather than DAW (Digital Audio Workstation i.e. the -host) - Analogue 1 and 2 confirming the new mapping. Again, by playing audio into analogue inputs -1/2 this can be heard looped through to analogue outputs 1/2. + $ ./xmos_mixer --set-mixer-source 0 0 4 + Set mixer(0) input 0 to device input 4 (AUD - Analogue 1) + $ ./xmos_mixer --set-mixer-source 0 1 5 + Set mixer(0) input 1 to device input 5 (AUD - Analogue 2) + $ _ +If you re-run the following command then the first column now has "AUD - Analogue 1 and 2" rather +than "DAW (Digital Audio Workstation i.e. the host) - Analogue 1 and 2" confirming the new mapping. +Again, by playing audio into analogue inputs 1/2 this can be heard looped through to analogue outputs 1/2:: + + $ ./xmos_mixer --display-mixer-nodes 0 diff --git a/lib_xua/doc/rst/sw_pdm.rst b/lib_xua/doc/rst/sw_pdm.rst index 4b8adbe9..91e3a174 100755 --- a/lib_xua/doc/rst/sw_pdm.rst +++ b/lib_xua/doc/rst/sw_pdm.rst @@ -1,11 +1,10 @@ +|newpage| + PDM Microphones ---------------- +=============== -Overview of PDM implemention ----------------------------- - -The design is capable of integrating PDM microphones. The PDM stream from the microphones is converted -to PCM and output to the host via USB. +The XMOS USB Audio Reference Design firmware is capable of integrating with PDM microphones. +The PDM stream from the microphones is converted to PCM and output to the host via USB. Interfacing to the PDM microphones is done using the XMOS microphone array library (``lib_mic_array``). ``lib_mic_array`` is designed to allow interfacing to PDM microphones coupled with efficient decimation @@ -19,9 +18,6 @@ The following components of the library are used: * PDM interface * Four channel decimators -|newpage| - - Up to sixteen PDM microphones can be attached to each high channel count PDM interface (``mic_array_pdm_rx()``). One to four processing tasks, ``mic_array_decimate_to_pcm_4ch()``, each process up to four channels. For 1-4 channels the library requires two logical cores: @@ -40,35 +36,39 @@ for 5-8 channels three logical cores are required, as shown below: Five to eight count PDM interface The left most task, ``mic_array_pdm_rx()``, samples up to 8 microphones and filters the data to provide up to -eight 384 KHz data streams, split in two streams of four channels. The processing thread -decimates the signal to a user chosen sample rate (one of 48, 24, 16, 12 or 8 KHz). +eight 384kHz data streams, split into two streams of four channels. The processing thread +decimates the signal to a user chosen sample rate (one of 48, 24, 16, 12 or 8kHz). More channels can be supported by increasing the number of cores dedicated to the PDM tasks. However, the current -PDM mic integration into USB Audio limits itself to 8. +PDM mic integration into ``lib_xua`` is limited to 8. After the decimation to the output sample-rate various other steps take place e.g. DC offset elimination, gain correction -and compensation etc. Please refer to ``lib_mic_array`` documention for further implementation detail and complete feature set. - +and compensation etc. Please refer to the documentation provided with ``lib_mic_array`` for further +implementation detail and complete feature set. PDM Microphone Hardware Characteristics -+++++++++++++++++++++++++++++++++++++++ +--------------------------------------- -The PDM microphones need a *clock input* and provide the PDM signal on a *data output*. All PDM microphones share the same -clock signal (buffered on the PCB as appropriate), and output onto eight data wires that are connected to a single 8-bit port: +The PDM microphones require a *clock input* and provide the PDM signal on a *data output*. All of +the PDM microphones must share the same clock signal (buffered on the PCB as appropriate), and +output onto eight data wires that are connected to a single 8-bit port: .. _pdm_wire_table: .. list-table:: PDM microphone data and signal wires :class: vertical-borders horizontal-borders - - * - *CLOCK* + :header-rows: 1 + + * - Signal + - Description + * - CLOCK - Clock line, the PDM clock the used by the microphones to drive the data out. - * - *DQ_PDM* + * - DQ_PDM - The data from the PDM microphones on an 8 bit port. The only port that is passed into ``lib_mic_array`` is the 8-bit data port. The library -assumes that the input port is clocked using the PDM clock and requires no knowlege of the +assumes that the input port is clocked using the PDM clock and requires no knowledge of the PDM clock source. The input clock for the microphones can be generated in a multitude of @@ -76,14 +76,14 @@ ways. For example, a 3.072MHz clock can be generated on the board, or the xCORE divide down 12.288 MHz master clock. Or, if clock accuracy is not important, the internal 100 MHz reference can be divided down to provide an approximate clock. -Integration of PDM Microphones into USB Audio -+++++++++++++++++++++++++++++++++++++++++++++ +Usage & Integration +------------------- A PDM microphone wrapper is called from ``main()`` and takes one channel argument connecting it to the rest of the system: ``pcm_pdm_mic(c_pdm_pcm);`` -The implemetation of this function can be found in the file ``pcm_pdm_mics.xc``. +The implementation of this function can be found in the file ``pcm_pdm_mics.xc``. The first job of this function is to configure the ports/clocking for the microphones, this divides the external audio master clock input (on port ``p_mclk``) and outputs the divided clock to the microphones via the ``p_pdm_clk`` port:: @@ -93,7 +93,7 @@ audio master clock input (on port ``p_mclk``) and outputs the divided clock to t configure_in_port(p_pdm_mics, pdmclk); start_clock(pdmclk); -It then runs the various cores required for the PDM interface and PDM to PCM conversion as discussed previously:: +It then runs the various cores required for the PDM interface and PDM to PCM conversion as previously discussed:: par { @@ -107,8 +107,9 @@ The ``pdm_process()`` task includes the main integration code, it takes audio fr it, performs optional local processing and outputs it to the audio driver (TDM/I2S core). This function simply makes a call to ``mic_array_get_next_time_domain_frame()`` in order to get a frame of PCM audio -from the microphones. It then waits for an request for audio samples from the audio/I2S/TDM core via a channel and +from the microphones. It then waits for an request for audio samples from the Audio Hub core via a channel and sends the frame of audio back over this channel. Note, it is assumed that the system shares a global master-clock, therefore no additional buffering or rate-matching/conversion is required. + diff --git a/lib_xua/doc/rst/sw_resource.rst b/lib_xua/doc/rst/sw_resource.rst index 789b496f..dafa5d10 100755 --- a/lib_xua/doc/rst/sw_resource.rst +++ b/lib_xua/doc/rst/sw_resource.rst @@ -1,24 +1,26 @@ +|newpage| + .. _usb_audio_sec_resource_usage: Resource Usage --------------- +============== -The following table details the resource usage of each -component of the reference design software. +The following table details the resource usage of each component of the reference design software. +Note, memory usage is approximate and varies based on device used, compiler settings etc. .. table:: Resource Usage +---------------+---------------+---------------------+-------------------------------------+ | Component | Cores | Memory (KB) | Ports | +===============+===============+=====================+=====================================+ - | XUD library | 1 | 9 (6 code) | ULPI ports | + | XUD library | 1 | 9 (6 code) | USB ports | | | | | | +---------------+---------------+---------------------+-------------------------------------+ | Endpoint 0 | 1 | 17.5 (10.5 code) | none | +---------------+---------------+---------------------+-------------------------------------+ - | USB Buffering | 1 | 22.5 (1 code) | none | + | USB Buffering | 2 | 22.5 (1 code) | 1 x n bit port | +---------------+---------------+---------------------+-------------------------------------+ - | Audio driver | 1 | 8.5 (6 code) | See :ref:`usb_audio_sec_audio` | + | Audio Hub | 1 | 8.5 (6 code) | See :ref:`usb_audio_sec_audio` | +---------------+---------------+---------------------+-------------------------------------+ | S/PDIF Tx | 1 | 3.5 (2 code) | 1 x 1 bit port | +---------------+---------------+---------------------+-------------------------------------+ @@ -26,18 +28,18 @@ component of the reference design software. +---------------+---------------+---------------------+-------------------------------------+ | ADAT Rx | 1 | 3.2 (3.2 code) | 1 x 1 bit port | +---------------+---------------+---------------------+-------------------------------------+ - | Midi | 1 | 6.5 (1.5 code) | 2 x 1 bit ports | + | MIDI | 1 | 6.5 (1.5 code) | 2 x 1 bit ports | +---------------+---------------+---------------------+-------------------------------------+ - | Mixer | 2 | 8.7 (6.5 code) | | + | Mixer | (up to) 2 | 8.7 (6.5 code) | | +---------------+---------------+---------------------+-------------------------------------+ | ClockGen | 1 | 2.5 (2.4 code) | | +---------------+---------------+---------------------+-------------------------------------+ .. note:: - These resource estimates are based on the multichannel reference design with - all options of that design enabled. For fewer channels, the resource - usage is likely to decrease. + These resource estimates are based on the multichannel reference design with + all options of that design enabled. For fewer channels, the resource + usage is likely to decrease. .. note:: @@ -46,6 +48,6 @@ component of the reference design software. .. note:: - The ULPI ports are a fixed set of ports on the L-Series - device. When using these ports, other ports are - unavailable when ULPI is active. See the `XS1-L Hardware Design Checklist `_ for further details. + Unlike other interfaces, since the USB PHY is internal the USB ports are a fixed set of ports + and cannot be modified. See ``lib_xud`` documentation for full details. + diff --git a/lib_xua/doc/rst/sw_spdif.rst b/lib_xua/doc/rst/sw_spdif.rst index bcde1034..12518c56 100755 --- a/lib_xua/doc/rst/sw_spdif.rst +++ b/lib_xua/doc/rst/sw_spdif.rst @@ -1,5 +1,8 @@ + +|newpage| + S/PDIF Transmit -............... +=============== ``lib_xua`` supports the development of devices with S/PDIF transmit throught the use of ``lib_spdif``. The XMOS S/SPDIF transmitter component runs in a single core and supports sample-rates upto 192kHz. @@ -18,7 +21,7 @@ bits) and transmitted in biphase-mark encoding (BMC) with respect to an *externa Note that a minor change to the ``SpdifTransmitPortConfig`` function would enable *internal* master clock generation (e.g. when clock source is already locked to desired audio clock). -.. list-table:: S/PDIF Capabilities +.. list-table:: S/PDIF Capabilities * - **Sample frequencies** - 44.1, 48, 88.2, 96, 176.4, 192 kHz @@ -28,7 +31,7 @@ clock generation (e.g. when clock source is already locked to desired audio cloc - ``lib_spdif`` Clocking -++++++++ +-------- .. only:: latex @@ -51,7 +54,7 @@ This resamples the master clock to its clock domain (oscillator), which introduc A typical jitter-reduction scheme is an external D-type flip-flop clocked from the master clock (as shown in the preceding diagram). Usage -+++++ +----- The interface to the S/PDIF transmitter core is via a normal channel with streaming built-ins (``outuint``, ``inuint``). Data format should be 24-bit left-aligned in a 32-bit word: ``0x12345600`` @@ -79,10 +82,10 @@ The following protocol is used on the channel: * - ``...`` - +This communication is wrapped up in the API functions provided by ``lib_spdif``. - -Output stream structure -+++++++++++++++++++++++ +Output Stream Structure +----------------------- The stream is composed of words with the following structure shown in :ref:`usb_audio_spdif_stream_structure`. The channel status bits are @@ -126,15 +129,15 @@ indicates sampling frequency as shown in :ref:`usb_audio_spdif_sample_bits`. * - Frequency (kHz) - n * - 44.1 - - 0 + - 0x0 * - 48 - - 2 + - 0x2 * - 88.2 - - 8 + - 0x8 * - 96 - - A + - 0xA * - 176.4 - - C + - 0xC * - 192 - - E + - 0xE diff --git a/lib_xua/doc/rst/sw_spdif_rx.rst b/lib_xua/doc/rst/sw_spdif_rx.rst index 56479083..790c4d7c 100644 --- a/lib_xua/doc/rst/sw_spdif_rx.rst +++ b/lib_xua/doc/rst/sw_spdif_rx.rst @@ -1,17 +1,18 @@ +|newpage| + S/PDIF Receive ---------------- +============== -XMOS devices can support S/PDIF receive up to 192kHz. +XMOS devices can support S/PDIF receive up to 192kHz - see ``lib_spdif`` for full specifications. -The S/PDIF receiver module uses a clockblock and a buffered one-bit port. +The S/PDIF receiver module uses a clock-block and a buffered one-bit port. The clock-block is divided of a 100 MHz reference clock. The one bit port is buffered to 4-bits. The receiver code uses this clock to over sample the input data. The receiver outputs audio samples over a *streaming channel end* where data can be input using the -built-in input operator. +built-in input operator. ``lib_spdif`` also provides API functions that wrap up this communication. -The S/PDIF receive function never returns. The 32-bit value from the channel -input comprises: +The S/PDIF receive function never returns. The 32-bit value from the channel input comprises: .. list-table:: S/PDIF RX Word Structure :header-rows: 1 @@ -41,29 +42,34 @@ The tag has one of three values: * - FRAME\_Z - Sample on channel 0 (Left), and the first sample of a frame; can be used if the user bits need to be reconstructed. -See S/PDIF specification for further details on format, user bits etc. +See S/PDIF, IEC 60958-3:2006, specification for further details on format, user bits etc. Usage and Integration -+++++++++++++++++++++ +--------------------- -Since S/PDIF is a digital steam the devices master clock must be syncronised to it. This is typically -done with an external fractional-n multipier. See `Clock Recovery` (:ref:`usb_audio_sec_clock_recovery`) +Since S/PDIF is a digital steam the devices master clock must be synchronised to it. This is typically +done with an external device. See `Clock Recovery` (:ref:`usb_audio_sec_clock_recovery`). -The S/PDIF receive function communicates with the ``clockGen`` component with passes audio data to the -audio driver and handles locking to the S/PDIF clock source if required (see External Clock Recovery). +.. note:: + + Due to the requirement for this clock recovery S/PDIF receive can only be used in Asynchronous + mode. + +The S/PDIF receive function communicates with the Clock Gen core, which in turn passes audio data to the +Audio Hub core. The Clock Gen core also handles locking to the S/PDIF clock source +(see :ref:`usb_audio_sec_clock_recovery`). Ideally the parity of each word/sample received should be checked. This is done using the built in ``crc32`` function (see ``xs1.h``): -.. literalinclude:: sc_usb_audio/module_usb_audio/clocking/clockgen.xc +.. literalinclude:: lib_xua/src/core/clocking/clockgen.xc :start-after: //:badParity :end-before: //: If bad parity is detected the word/sample is ignored, otherwise the tag is inspected for channel (i.e. left or right) and the sample stored. -The following code snippet illustrates how the output of the S/PDIF receive component could be used:: - +The following code snippet illustrates how the output of the S/PDIF receive component could is used:: while(1) { @@ -90,6 +96,5 @@ The following code snippet illustrates how the output of the S/PDIF receive comp } } - - +The Clock Gen core stores samples in a small FIFO before they are communicated to the Audio Hub core. diff --git a/lib_xua/doc/rst/sw_xud.rst b/lib_xua/doc/rst/sw_xud.rst index eb2eb138..618ea70f 100644 --- a/lib_xua/doc/rst/sw_xud.rst +++ b/lib_xua/doc/rst/sw_xud.rst @@ -1,6 +1,8 @@ +|newpage| + XMOS USB Device (XUD) Library -............................. +============================= All low level communication with the USB host is handled by the XMOS USB Device (XUD) library - `lib_xud` diff --git a/lib_xua/doc/rst/using.rst b/lib_xua/doc/rst/using.rst index 0f89e13c..3293c470 100644 --- a/lib_xua/doc/rst/using.rst +++ b/lib_xua/doc/rst/using.rst @@ -1,12 +1,12 @@ -Using lib_xud -------------- -This sections describes the basic usage of `lib_xud`. It provides a guide on how to program the USB Audio Devices using `lib_xud`. +Basic Usage +*********** -Reviewing application note AN00246 is highly recommended at this point. +This sections describes the basic usage of `lib_xua` and provides a guide on how to program USB Audio Devices. -Library structure -~~~~~~~~~~~~~~~~~ + +Library Structure +================= The code is split into several directories. @@ -18,15 +18,17 @@ The code is split into several directories. - MIDI I/O code * - dfu - Device Firmware Upgrade code + * - hid + - Human Interface Device code Note, the midi and dfu directories are potential candidates for separate libs in their own right. -Including in a project -~~~~~~~~~~~~~~~~~~~~~~ +Using in a Project +================== -All `lib_xua` functions can be accessed via the ``xua.h`` header filer:: +All `lib_xua` functions can be accessed via the ``xua.h`` header file:: #include @@ -34,109 +36,51 @@ It is also required to add ``lib_xua`` to the ``USED_MODULES`` field of your app USED_MODULES = .. lib_xua ... +.. _sec_basic_usage_codeless: -Core hardware resources -~~~~~~~~~~~~~~~~~~~~~~~ +"Codeless" Programming Model +============================ -The user must declare and initialise relevant hardware resources (globally) and pass them to the relevant function of `lib_xua`. +Whilst it is possible to code a USB Audio device using the building blocks provided by `lib_xua` +it is realised that this might not be desirable for many classes of customers or products. -As an absolute minimum the following resources are required: +For instance, some users may not have a large software development experience and simply want to +customise some basic settings such as strings, sample-rates, channel-counts etc. +Others may want to fully customise the implementation - adding additional functionality such as +adding DSP or possibly only using a subset of the functions provided - just ``XUA_AudioHub``, +for example. -- A 1-bit port for audio master clock input -- A n-bit port for internal feedback calculation (typically a free, unused port is used e.g. `16B`) -- A clock-block, which will be clocked from the master clock input port +In addition, the large number of supported features can lead to a large number of tasks, hardware +resources, communication channels etc, requiring quite a lot of code to be authored for each product. -Example declaration of these resources might look as follows:: +In order to cater for the former class of users, a "codeless" option is provided. Put simply, a file +``main.xc`` is provided which includes a pre-authored ``main()`` function along with all of the +required hardware resource declarations. Code is generated based on the options provided by the +developer in ``xua_conf.h`` - in port p_mclk_in = PORT_MCLK_IN; - in port p_for_mclk_count = PORT_MCLK_COUNT; /* Extra port for counting master clock ticks */ - clock clk_audio_mclk = on tile[0]: XS1_CLKBLK_5; /* Master clock */ +Using this development model the user simply must include a ``xua_conf.h`` with their settings and +optional implementations of any 'user functions' as desired. This, along with an XN file for their +hardware platform, is all that is required to build a fully featured and functioning product. This +XN file should contain definitions of the ports used for the various ``lib_xua`` functionality, +see ::ref:`sec_options`. -.. note:: +This development model also provides the benefit of a full and verified codebase as a basis for a product. - The `PORT_MCLK_IN` and `PORT_MCLK_COUNT` defintions are derived from the projects XN file +This behaviour described in this section is the default behaviour of `lib_xua`, to disable this please +set ``EXCLUDE_USB_AUDIO_MAIN`` to 1 in the application makefile or ``xua_conf.h`` and see +::ref:`sec_advanced_usage`. +Configuring lib_xua +=================== -The ``XUA_AudioHub()`` function requires an audio master clock input to clock the physical audio I/O. Less obvious is the reasoning for the ``XUA_Buffer()`` -task having the same requirement - it is used for the USB feedback system and packet sizing. +Configuration of the various build time options of ``lib_xua`` is done via the optional header `xua_conf.h`. +To allow the build scripts to locate this file it should reside somewhere in the application `src` directory. -Due to the above, if the ``XUD_AudioHub()`` and ``XUA_Buffer()`` cores must reside on separate tiles a separate master clock input port must be provided to each, for example:: +Such build time options include audio class version, sample rates, channel counts etc. Please see +::ref:`sec_api` for full listings. - /* Master clock for the audio IO tile */ - in port p_mclk_in = PORT_MCLK_IN; - - /* Resources for USB feedback */ - in port p_mclk_in_usb = PORT_MCLK_IN_USB; /* Extra master clock input for the USB tile */ - -Whilst the hardware resources described in this section satisfy the basic requirements for the operation (or build) of `lib_xua` projects typically also needs some additional audio I/O, -I2S or SPDIF for example. - -These should be passed into the various cores as required - see API and Features sections. - -Running the core components -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In their most basic form the core components can be run as follows:: - - par - { - /* Endpoint 0 core from lib_xua */ - XUA_Endpoint0(c_ep_out[0], c_ep_in[0], c_aud_ctl, null, null, null, null); - - /* Buffering cores - handles audio data to/from EP's and gives/gets data to/from the audio I/O core */ - /* Note, this spawns two cores */ - XUA_Buffer(c_ep_out[1], c_ep_in[1], c_sof, c_aud_ctl, p_for_mclk_count, c_aud); - - /* AudioHub/IO core does most of the audio IO i.e. I2S (also serves as a hub for all audio) */ - XUA_AudioHub(c_aud, ...) ; - } - -``XUA_Buffer()`` expects its ``p_for_mclk_count`` argument to be clocked from the audio master clock before being passed it. -The following code satisfies this requirement:: - - { - /* Connect master-clock clock-block to clock-block pin */ - set_clock_src(clk_audio_mclk_usb, p_mclk_in_usb); /* Clock clock-block from mclk pin */ - set_port_clock(p_for_mclk_count, clk_audio_mclk_usb); /* Clock the "count" port from the clock block */ - start_clock(clk_audio_mclk_usb); /* Set the clock off running */ - - XUA_Buffer(c_ep_out[1], c_ep_in[1], c_sof, c_aud_ctl, p_for_mclk_count, c_aud); - - } - -.. note:: Keeping this configuration outside of ``XUA_Buffer()`` does not preclude the possibllty of sharing ``p_mclk_in_usb`` port with additional components - -To produce a fully operating device a call to ``XUD_Main()`` (from ``lib_xud``) must also be made for USB connectivity:: - - /* Low level USB device layer core */ - on tile[1]: XUD_Main(c_ep_out, 2, c_ep_in, 2, c_sof, epTypeTableOut, epTypeTableIn, null, null, -1, XUD_SPEED_HS, XUD_PWR_SELF); - -Additionally the required communication channels must also be declared:: - - /* Channel arrays for lib_xud */ - chan c_ep_out[2]; - chan c_ep_in[2]; - - /* Channel for communicating SOF notifications from XUD to the Buffering cores */ - chan c_sof; - - /* Channel for audio data between buffering cores and AudioHub/IO core */ - chan c_aud; - - /* Channel for communicating control messages from EP0 to the rest of the device (via the buffering cores) */ - chan c_aud_ctl; - - -This section provides enough information to implement a skeleton program for a USB Audio device. When running the xCORE device will present itself as a USB Audio Class device on the bus. - - -Configuring XUA -~~~~~~~~~~~~~~~ - -Configuration of the various build time options of ``lib_xua`` is done via the optional header `xua_conf.h`. Such build time options include audio class version, sample rates, channel counts etc. -Please see the API section for full listings. - -The build system will automatically include the `xua_conf.h` header file as appropriate - the user should continue to include `xua.h` as previously directed. A simple example is shown below:: +The build system will automatically include the `xua_conf.h` header file as appropriate - the developer +should continue to include `xua.h` as previously directed. A simple example is shown below:: #ifndef _XUA_CONF_H_ #define _XUA_CONF_H_ @@ -149,28 +93,10 @@ The build system will automatically include the `xua_conf.h` header file as appr #endif +User Functions +============== -User functions -~~~~~~~~~~~~~~ - -To enable custom functionality, such as configuring external audio hardware, custom functionality on stream start/stop etc various user overridable functions are provided (see API section for full listings). The default implementations are empty. - - -Codeless programming model -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Whilst it is possible to code a USB Audio device using the building blocks provided by `lib_xua` it is realised that this might not be desirable for some classes of customers or product. - -For instance, some users may not have a large software development experience and simply want to customise some basic settings such as strings, sample-rates, channel-counts etc. Others may want to fully customise the implementation - adding additional functionality such as adding DSD or possibly only using a subset of the functions provided - just ``XUA_AudioHub``, for example. - -In addition, the large number of supported features can lead to a large number of tasks, hardware resources, communication channels etc, requiring quite a lot of code to be authored for each product. - -In order to cater for the former class of users, a "codeless" option is provided. Put simply, a file ``main.xc`` is provided which includes a pre-authored ``main()`` function along with all of the required hardware resource declarations. Code is generated based on the options provided in ``xua_conf.h`` - -Using this development model the user simply must include a ``xua_conf.h`` with their settings and optional implementations of any 'user functions' as desired. This, along with an XN file for their hardware platform, is all that is required to build a fully featured and functioning product. - -This model also provides the benefit of a known-good, full codebase as a basis for a product. - -This behaviour described in this section is the default behaviour of `lib_xua`, to disable this please set ``EXCLUDE_USB_AUDIO_MAIN`` to 1 in the application makefile or ``xua_conf.h``. - +To enable custom functionality, such as configuring external audio hardware, bespoke behaviour on +stream start/stop etc, various functions can be overridden by the user. (see ::ref:`sec_api` for +full listings). The default implementations of these functions are empty. diff --git a/lib_xua/doc/rst/using_adv.rst b/lib_xua/doc/rst/using_adv.rst new file mode 100644 index 00000000..e1909fc5 --- /dev/null +++ b/lib_xua/doc/rst/using_adv.rst @@ -0,0 +1,25 @@ +.. _sec_advanced_usage: + +Advanced Usage +************** + +Whilst it is possible to program USB Audio devices using ``lib_xua`` by only setting defines +(see :ref:`sec_basic_usage_codeless`) some developers may want to code a USB Audio device from +scratch using the building blocks provided by `lib_xua`. + +This could be for a number of reasons, adding complex DSP, merging with some other functionality +etc. This section describes these building blocks and their use. + +Reviewing application note AN00246 is highly recommended at this point. + + +.. toctree:: + + Core Hardware Resources + Running the Core Components + I2S/TDM + Mixer + + + + diff --git a/lib_xua/doc/rst/using_adv_core_comp.rst b/lib_xua/doc/rst/using_adv_core_comp.rst new file mode 100644 index 00000000..1d692b35 --- /dev/null +++ b/lib_xua/doc/rst/using_adv_core_comp.rst @@ -0,0 +1,64 @@ +Running the Core Components +=========================== + +In their most basic form the core components can be run as follows:: + + par + { + /* Endpoint 0 core from lib_xua */ + XUA_Endpoint0(c_ep_out[0], c_ep_in[0], c_aud_ctl, ...); + + /* Buffering cores - handles audio data to/from EP's and gives/gets data to/from the audio I/O core */ + /* Note, this spawns two cores */ + XUA_Buffer(c_ep_out[1], c_ep_in[1], c_sof, c_aud_ctl, p_for_mclk_count, c_aud); + + /* AudioHub/IO core does most of the audio IO i.e. I2S (also serves as a hub for all audio) */ + XUA_AudioHub(c_aud, ...) ; + } + +``XUA_Buffer()`` expects its ``p_for_mclk_count`` argument to be clocked from the audio master clock +before receiving it as a parameter. The following code satisfies this requirement:: + + { + /* Connect master-clock clock-block to clock-block pin */ + + /* Clock clock-block from mclk pin */ + set_clock_src(clk_audio_mclk_usb, p_mclk_in_usb); + + /* Clock the "count" port from the clock block */ + set_port_clock(p_for_mclk_count, clk_audio_mclk_usb); + + /* Set the clock off running */ + start_clock(clk_audio_mclk_usb); + + XUA_Buffer(c_ep_out[1], c_ep_in[1], c_sof, c_aud_ctl, p_for_mclk_count, c_aud); + } + +.. note:: Keeping this configuration outside of ``XUA_Buffer()`` means the possibility of sharing the + ``p_mclk_in_usb`` port with additional components is not precluded + +For USB connectivity a call to ``XUD_Main()`` (from ``lib_xud``) must also be made:: + + /* Low level USB device layer core */ + on tile[1]: XUD_Main(c_ep_out, 2, c_ep_in, 2, c_sof, epTypeTableOut, epTypeTableIn, null, null, -1, XUD_SPEED_HS, XUD_PWR_SELF); + +Additionally, the required communication channels must also be declared:: + + /* Channel arrays for lib_xud */ + chan c_ep_out[2]; + chan c_ep_in[2]; + + /* Channel for communicating SOF notifications from XUD to the Buffering cores */ + chan c_sof; + + /* Channel for audio data between buffering cores and AudioHub/IO core */ + chan c_aud; + + /* Channel for communicating control messages from EP0 to the rest of the device (via the buffering cores) */ + chan c_aud_ctl; + + +This section provides enough information to implement a skeleton program for a USB Audio device. When +running the xCORE device will present itself as a USB Audio Class device on the bus. Audio streaming will +be impaired since no physical audio interfaces have yet to be instantiated. + diff --git a/lib_xua/doc/rst/using_adv_core_hw.rst b/lib_xua/doc/rst/using_adv_core_hw.rst new file mode 100644 index 00000000..012fe45c --- /dev/null +++ b/lib_xua/doc/rst/using_adv_core_hw.rst @@ -0,0 +1,43 @@ +Core Hardware Resources +======================= + +The user must declare and initialise relevant hardware resources (globally) and pass them to the +relevant function of `lib_xua`. + +As an absolute minimum the following resources are required: + +- A 1-bit port for audio master clock input +- A clock-block, which will be clocked from the master clock input port + +When using the default asynchronous mode of operation an additional port is required: + +- A n-bit port for internal feedback calculation (typically a free, unused port is used e.g. ``XS1_PORT_16B``) + +Example declaration of these resources might look as follows:: + + in port p_mclk_in = PORT_MCLK_IN; + in port p_for_mclk_count = PORT_MCLK_COUNT; /* Extra port for counting master clock ticks */ + clock clk_audio_mclk = on tile[0]: XS1_CLKBLK_5; /* Master clock */ + +.. note:: + + The ``PORT_MCLK_IN`` and ``PORT_MCLK_COUNT`` definitions are derived from the projects XN file + + +The ``XUA_AudioHub()`` function requires an audio master clock input to clock the physical audio I/O. +Less obvious is the reasoning for the ``XUA_Buffer()`` task having the same requirement when running in +asynchronous mode - it is used for the USB feedback system and packet sizing. + +Due to the above, if the ``XUD_AudioHub()`` and ``XUA_Buffer()`` cores must reside on separate +tiles a separate master clock input port must be provided to each, for example:: + + /* Master clock for the audio IO tile */ + in port p_mclk_in = PORT_MCLK_IN; + + /* Resources for USB feedback */ + in port p_mclk_in_usb = PORT_MCLK_IN_USB; /* Extra master clock input for the USB tile */ + +Whilst the hardware resources described in this section satisfy the basic requirements for the operation (or build) +of `lib_xua`, projects typically also need some additional audio I/O, I2S or S/PDIF for example. + +These should be passed into the various cores as required - see :ref:`sec_api`. diff --git a/lib_xua/doc/rst/using_adv_i2s.rst b/lib_xua/doc/rst/using_adv_i2s.rst new file mode 100644 index 00000000..12d5029c --- /dev/null +++ b/lib_xua/doc/rst/using_adv_i2s.rst @@ -0,0 +1,38 @@ +|newpage| + +I2S/TDM +======= + +I2S/TDM is typically fundamental to most products and is built into the ``XUA_AudioHub()`` core. + +In order to enable I2S on must declare an array of ports for the data-lines (one for each direction):: + + /* Port declarations. Note, the defines come from the XN file */ + buffered out port:32 p_i2s_dac[] = {PORT_I2S_DAC0}; /* I2S Data-line(s) */ + buffered in port:32 p_i2s_adc[] = {PORT_I2S_ADC0}; /* I2S Data-line(s) */ + +Ports for the sample and bit clocks are also required:: + + buffered out port:32 p_lrclk = PORT_I2S_LRCLK; /* I2S Bit-clock */ + buffered out port:32 p_bclk = PORT_I2S_BCLK; /* I2S L/R-clock */ + +.. note:: + + All of these ports must be 1-bit ports, 32-bit buffed. Based on whether the xCORE is bus slave/master the ports must be declared as input/output respectively + +These ports must then be passed to the ``XUA_AudioHub()`` task appropriately. + +I2S functionality also requires two clock-blocks, one for bit and sample clock e.g.:: + + /* Clock-block declarations */ + clock clk_audio_bclk = on tile[0]: XS1_CLKBLK_4; /* Bit clock */ + clock clk_audio_mclk = on tile[0]: XS1_CLKBLK_5; /* Master clock */ + +These hardware resources must be passed into the call to ``XUA_AudioHub()``:: + + /* AudioHub/IO core does most of the audio IO i.e. I2S (also serves + * as a hub for all audio) */ + + on tile[0]: XUA_AudioHub(c_aud, clk_audio_mclk, clk_audio_bclk, p_mclk_in, + p_lrclk, p_bclk, p_i2s_dac, p_i2s_adc); + diff --git a/lib_xua/doc/rst/using_adv_mixer.rst b/lib_xua/doc/rst/using_adv_mixer.rst new file mode 100644 index 00000000..6317b971 --- /dev/null +++ b/lib_xua/doc/rst/using_adv_mixer.rst @@ -0,0 +1,28 @@ +|newpage| + +Mixer +===== + +Since the mixer has no I/O the instantiation is straight forward. Communication wises, the mixer cores are inserted +between the `AudioHub` and Buffering core(s) + +It takes three channel ends as parameters, one for audio to/from the buffering core(s), one for audio to/from the +`AudioHub` core and another one for control requests from the `Endpoint0` core. + +The mixer task will automatically handle the change in mix count based on the current sample frequency (communicated +via the data channels from the buffering task). + +An example of how the mixer task might be called is shown below (some parameter lists are abbreviated) :: + + chan c_aud_0, c_aud_1, c_mix_ctl; + + par + { + XUA_Buffer(..., c_aud_0, ...); + + mixer(c_aud0, c_aud_1, c_mix_ctl); + + XUA_AudioHub(c_aud_1, ...); + + XUA_Endpoint0(..., c_mix_ctl, ...); + } diff --git a/lib_xua/doc/rst/xdoc.conf b/lib_xua/doc/rst/xdoc.conf index fa1fe2cd..4d522c83 100644 --- a/lib_xua/doc/rst/xdoc.conf +++ b/lib_xua/doc/rst/xdoc.conf @@ -1,2 +1,5 @@ -XMOSNEWSTYLE = 1 +XMOSNEWSTYLE = 2 DOXYGEN_DIRS=../../api +SOURCE_INCLUDE_DIRS=../../../lib_xua +SPHINX_MASTER_DOC=lib_xua + diff --git a/lib_xua/host/xmosdfu/libusb/OSX64/libusb.h b/lib_xua/host/xmosdfu/libusb/OSX64/libusb.h index b1b1a18c..193bc643 100644 --- a/lib_xua/host/xmosdfu/libusb/OSX64/libusb.h +++ b/lib_xua/host/xmosdfu/libusb/OSX64/libusb.h @@ -1277,7 +1277,7 @@ enum libusb_capability { * still have to call additional libusb functions such as * \ref libusb_detach_kernel_driver(). */ LIBUSB_CAP_HAS_HID_ACCESS = 0x0100, - /** The library supports detaching of the default USB driver, using + /** The library supports detaching of the default USB driver, using * \ref libusb_detach_kernel_driver(), if one is set by the OS kernel */ LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER = 0x0101 }; diff --git a/lib_xua/host/xmosdfu/libusb/Win32/libusb.h b/lib_xua/host/xmosdfu/libusb/Win32/libusb.h index 430136b2..26bf91bd 100644 --- a/lib_xua/host/xmosdfu/libusb/Win32/libusb.h +++ b/lib_xua/host/xmosdfu/libusb/Win32/libusb.h @@ -1273,7 +1273,7 @@ enum libusb_capability { * still have to call additional libusb functions such as * \ref libusb_detach_kernel_driver(). */ LIBUSB_CAP_HAS_HID_ACCESS = 0x0100, - /** The library supports detaching of the default USB driver, using + /** The library supports detaching of the default USB driver, using * \ref libusb_detach_kernel_driver(), if one is set by the OS kernel */ LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER = 0x0101 }; diff --git a/lib_xua/module_build_info b/lib_xua/module_build_info index 39315c72..b95b65f6 100644 --- a/lib_xua/module_build_info +++ b/lib_xua/module_build_info @@ -1,16 +1,27 @@ -VERSION = 1.3.0 +VERSION = 3.4.0 -DEPENDENT_MODULES = lib_logging(>=3.0.0) \ - lib_xassert(>=4.0.0) \ - lib_xud(>=1.0.0) \ - lib_spdif(>=4.0.0) \ - lib_mic_array(>=4.0.0) +DEBUG ?= 0 + +ifeq ($(DEBUG),1) +DEBUG_FLAGS = -g -DXASSERT_ENABLE_ASSERTIONS=1 -DXASSERT_ENABLE_DEBUG=1 -DXASSERT_ENABLE_LINE_NUMBERS=1 +else +DEBUG_FLAGS = -DXASSERT_ENABLE_ASSERTIONS=0 -DXASSERT_ENABLE_DEBUG=0 -DXASSERT_ENABLE_LINE_NUMBERS=0 +endif + +DEPENDENT_MODULES = lib_locks(>=2.1.0) \ + lib_logging(>=3.1.1) \ + lib_mic_array(>=4.5.0) \ + lib_spdif(>=4.2.1) \ + lib_xassert(>=4.1.0) \ + lib_xud(>=2.2.3) \ + lib_adat(>=1.0.0) MODULE_XCC_FLAGS = $(XCC_FLAGS) \ -O3 \ -DREF_CLK_FREQ=100 \ -fasm-linenum \ - -fcomment-asm + -fcomment-asm \ + $(DEBUG_FLAGS) # Core XCC_FLAGS_xua_endpoint0.c = $(MODULE_XCC_FLAGS) -Os -mno-dual-issue @@ -40,7 +51,6 @@ INCLUDE_DIRS = $(EXPORT_INCLUDE_DIRS) \ src/core/pdm_mics \ src/core/ports \ src/core/support \ - src/core/support/powersave \ src/core/user \ src/core/user/audiostream \ src/core/user/hid \ @@ -58,7 +68,6 @@ SOURCE_DIRS = src/core \ src/core/pdm_mics \ src/core/ports \ src/core/support \ - src/core/support/powersave \ src/core/user/audiostream \ src/core/user/hostactive \ src/core/xuduser \ diff --git a/lib_xua/src/core/audiohub/audiohub_adat.h b/lib_xua/src/core/audiohub/audiohub_adat.h index dd51e3ec..90a4d21c 100644 --- a/lib_xua/src/core/audiohub/audiohub_adat.h +++ b/lib_xua/src/core/audiohub/audiohub_adat.h @@ -1,4 +1,4 @@ -// Copyright 2018-2021 XMOS LIMITED. +// Copyright 2018-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. unsigned adatCounter = 0; @@ -11,7 +11,7 @@ static inline void TransferAdatTxSamples(chanend c_adat_out, const unsigned samp /* Do some re-arranging for SMUX.. */ unsafe { - unsigned * unsafe samplesFromHostAdat = &samplesFromHost[ADAT_TX_INDEX]; + unsigned * unsafe samplesFromHostAdat = (unsigned * unsafe) &samplesFromHost[ADAT_TX_INDEX]; /* Note, when smux == 1 this loop just does a straight 1:1 copy */ //if(smux != 1) @@ -38,7 +38,7 @@ static inline void TransferAdatTxSamples(chanend c_adat_out, const unsigned samp inuint(c_adat_out); /* Send buffer pointer over to ADAT core */ - volatile unsigned * unsafe samplePtr = &adatSamples; + volatile unsigned * unsafe samplePtr = (unsigned * unsafe) &adatSamples; outuint(c_adat_out, (unsigned) samplePtr); } #else diff --git a/lib_xua/src/core/audiohub/audiohub_dsd.h b/lib_xua/src/core/audiohub/audiohub_dsd.h index f101160b..80af207b 100644 --- a/lib_xua/src/core/audiohub/audiohub_dsd.h +++ b/lib_xua/src/core/audiohub/audiohub_dsd.h @@ -1,4 +1,4 @@ -// Copyright 2018-2021 XMOS LIMITED. +// Copyright 2018-2023 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #if (DSD_CHANS_DAC != 0) @@ -28,7 +28,7 @@ static inline void DoDsdNative(unsigned samplesOut[], unsigned &dsdSample_l, uns asm volatile("out res[%0], %1"::"r"(p_dsd_dac[1]),"r"(dsdSample_r)); } -/* This function performs the DOP loop and collects 16b of DSD per loop +/* This function performs the DOP loop and collects 16b of DSD per loop and outputs a 32b word into the port buffer every other cycle. */ static inline void DoDsdDop(int &everyOther, unsigned samplesOut[], unsigned &dsdSample_l, unsigned &dsdSample_r, unsigned divide) { @@ -38,7 +38,7 @@ static inline void DoDsdDop(int &everyOther, unsigned samplesOut[], unsigned &ds dsdSample_r = ((samplesOut[1] & 0xffff00) << 8); everyOther = 1; } - else + else { everyOther = 0; dsdSample_l = dsdSample_l | ((samplesOut[0] & 0xffff00) >> 8); @@ -52,7 +52,7 @@ static inline void DoDsdDop(int &everyOther, unsigned samplesOut[], unsigned &ds /* When DSD is enabled and streaming is standard PCM, this function checks for a series of DoP markers in the upper byte. If found it will exit deliver() with the command to restart in DoP mode. When in DoP mode, this function will check for a single absence of the DoP marker and exit deliver() with the command - to restart in I2S mode. */ + to restart in I2S/PCM mode. */ static inline int DoDsdDopCheck(unsigned &dsdMode, int &dsdCount, unsigned curSamFreq, unsigned samplesOut[], unsigned &dsdMarker) { /* Check for DSD - note we only move into DoP mode if valid DoP Freq */ @@ -77,7 +77,7 @@ static inline int DoDsdDopCheck(unsigned &dsdMode, int &dsdCount, unsigned curSa dsdMarker = DSD_MARKER_2; } } - else if(dsdMode == DSD_MODE_DOP) + else if(dsdMode == DSD_MODE_DOP) { /* If we are running in DOP mode, check if we need to come out */ if((DSD_MASK(samplesOut[0]) != DSD_MARKER_1) && (DSD_MASK(samplesOut[1]) != DSD_MARKER_1)) diff --git a/lib_xua/src/core/audiohub/audiohub_initport.xc b/lib_xua/src/core/audiohub/audiohub_initport.xc index 116ae38a..9d4619ef 100644 --- a/lib_xua/src/core/audiohub/audiohub_initport.xc +++ b/lib_xua/src/core/audiohub/audiohub_initport.xc @@ -1,4 +1,4 @@ -// Copyright 2018-2021 XMOS LIMITED. +// Copyright 2018-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #include "xua.h" @@ -38,7 +38,6 @@ void InitPorts_master(unsigned divide, buffered _XUA_CLK_DIR port:32 p_lrclk, bu } #endif -#pragma xta endpoint "divide_1" unsigned tmp; #ifdef N_BITS_I2S tmp = partout_timestamped(p_lrclk, N_BITS_I2S, 0); @@ -59,14 +58,18 @@ void InitPorts_master(unsigned divide, buffered _XUA_CLK_DIR port:32 p_lrclk, bu #endif } #endif + unsigned lrClkVal = 0x7FFFFFFF; + if(XUA_PCM_FORMAT == XUA_PCM_FORMAT_TDM) + { + lrclkVal = 0x80000000; + } #ifdef N_BITS_I2S - partout_timed(p_lrclk, N_BITS_I2S, 0x7FFFFFFF, tmp); + partout_timed(p_lrclk, N_BITS_I2S, lrClkVal, tmp); #else - p_lrclk @ tmp <: 0x7FFFFFFF; + p_lrclk @ tmp <: lrClkVal; #endif - #if (I2S_CHANS_ADC != 0) for(int i = 0; i < I2S_WIRES_ADC; i++) { @@ -88,9 +91,7 @@ void InitPorts_master(unsigned divide, buffered _XUA_CLK_DIR port:32 p_lrclk, bu } #endif } - -#else - +#else void InitPorts_slave(unsigned divide, buffered _XUA_CLK_DIR port:32 p_lrclk, buffered _XUA_CLK_DIR port:32 p_bclk, buffered out port:32 (&?p_i2s_dac)[I2S_WIRES_DAC], buffered in port:32 (&?p_i2s_adc)[I2S_WIRES_ADC]) { #if (I2S_CHANS_ADC != 0 || I2S_CHANS_DAC != 0) @@ -101,7 +102,7 @@ void InitPorts_slave(unsigned divide, buffered _XUA_CLK_DIR port:32 p_lrclk, buf p_lrclk when pinseq(1) :> void; p_lrclk when pinseq(0) :> void; p_lrclk when pinseq(1) :> void; -#if I2S_MODE_TDM +#if (XUA_PCM_FORMAT == XUA_PCM_FORMAT_TDM) p_lrclk when pinseq(0) :> void; p_lrclk when pinseq(1) :> void @ tmp; #else diff --git a/lib_xua/src/core/audiohub/xua_audiohub.xc b/lib_xua/src/core/audiohub/xua_audiohub.xc index 8062b59b..9a023838 100755 --- a/lib_xua/src/core/audiohub/xua_audiohub.xc +++ b/lib_xua/src/core/audiohub/xua_audiohub.xc @@ -1,4 +1,4 @@ -// Copyright 2011-2021 XMOS LIMITED. +// Copyright 2011-2023 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. /** * @file xua_audiohub.xc @@ -25,7 +25,7 @@ #if (XUA_SPDIF_TX_EN) #include "spdif.h" #endif -#ifdef ADAT_TX +#if (XUA_ADAT_TX_EN) #include "adat_tx.h" #ifndef ADAT_TX_USE_SHARED_BUFF #error Designed for ADAT tx shared buffer mode ONLY @@ -34,7 +34,7 @@ #if (XUA_NUM_PDM_MICS > 0) #include "xua_pdm_mic.h" -#endif +#endif #if (AUD_TO_USB_RATIO > 1) #include "src.h" @@ -45,12 +45,12 @@ #define MAX(x,y) ((x)>(y) ? (x) : (y)) -static unsigned samplesOut[MAX(NUM_USB_CHAN_OUT, I2S_CHANS_DAC)]; +unsigned samplesOut[MAX(NUM_USB_CHAN_OUT, I2S_CHANS_DAC)]; /* Two buffers for ADC data to allow for DAC and ADC I2S ports being offset */ -#define IN_CHAN_COUNT (I2S_CHANS_ADC + XUA_NUM_PDM_MICS + (8*ADAT_RX) + (2*SPDIF_RX)) +#define IN_CHAN_COUNT (I2S_CHANS_ADC + XUA_NUM_PDM_MICS + (8*XUA_ADAT_RX_EN) + (2*XUA_SPDIF_RX_EN)) -static unsigned samplesIn[2][MAX(NUM_USB_CHAN_IN, IN_CHAN_COUNT)]; +unsigned samplesIn[2][MAX(NUM_USB_CHAN_IN, IN_CHAN_COUNT)]; #ifdef XTA_TIMING_AUDIO #pragma xta command "add exclusion received_command" @@ -65,11 +65,11 @@ static unsigned samplesIn[2][MAX(NUM_USB_CHAN_IN, IN_CHAN_COUNT)]; #pragma xta command "set required - 2000 ns" #endif -#ifdef ADAT_TX +#if (XUA_ADAT_TX_EN) extern buffered out port:32 p_adat_tx; #endif -#if defined(ADAT_TX) +#if (XUA_ADAT_TX_EN) extern clock clk_mst_spd; #endif @@ -78,7 +78,7 @@ void InitPorts_slave #else void InitPorts_master #endif -(unsigned divide, buffered _XUA_CLK_DIR port:32 p_lrclk, buffered _XUA_CLK_DIR port:32 p_bclk, buffered out port:32 (&?p_i2s_dac)[I2S_WIRES_DAC], +(unsigned divide, buffered _XUA_CLK_DIR port:32 p_lrclk, buffered _XUA_CLK_DIR port:32 p_bclk, buffered out port:32 (&?p_i2s_dac)[I2S_WIRES_DAC], buffered in port:32 (&?p_i2s_adc)[I2S_WIRES_ADC]); @@ -88,73 +88,10 @@ unsigned dsdMode = DSD_MODE_OFF; #include "audiohub_dsd.h" #endif -#ifdef ADAT_TX +#if (XUA_ADAT_TX_EN) #include "audiohub_adat.h" #endif - -#pragma unsafe arrays -static inline unsigned DoSampleTransfer(chanend ?c_out, const int readBuffNo, const unsigned underflowWord) -{ - if(XUA_USB_EN) - { - outuint(c_out, underflowWord); - - /* Check for sample freq change (or other command) or new samples from mixer*/ - if(testct(c_out)) - { - unsigned command = inct(c_out); -#ifndef CODEC_MASTER - if(dsdMode == DSD_MODE_OFF) - { -#if (I2S_CHANS_ADC != 0 || I2S_CHANS_DAC != 0) - /* Set clocks low */ - p_lrclk <: 0; - p_bclk <: 0; -#endif - } - else - { -#if(DSD_CHANS_DAC != 0) - /* DSD Clock might not be shared with lrclk or bclk... */ - p_dsd_clk <: 0; -#endif - } -#endif -#if (DSD_CHANS_DAC > 0) - if(dsdMode == DSD_MODE_DOP) - dsdMode = DSD_MODE_OFF; -#endif -#pragma xta endpoint "received_command" - return command; - } - else - { -#if NUM_USB_CHAN_OUT > 0 -#pragma loop unroll - for(int i = 0; i < NUM_USB_CHAN_OUT; i++) - { - int tmp = inuint(c_out); - samplesOut[i] = tmp; - } -#else - inuint(c_out); -#endif - UserBufferManagement(samplesOut, samplesIn[readBuffNo]); - -#if NUM_USB_CHAN_IN > 0 -#pragma loop unroll - for(int i = 0; i < NUM_USB_CHAN_IN; i++) - { - outuint(c_out, samplesIn[readBuffNo][i]); - } -#endif - } - } - else - UserBufferManagement(samplesOut, samplesIn[readBuffNo]); - - return 0; -} +#include "xua_audiohub_st.h" static inline int HandleSampleClock(int frameCount, buffered _XUA_CLK_DIR port:32 p_lrclk) { @@ -170,10 +107,10 @@ static inline int HandleSampleClock(int frameCount, buffered _XUA_CLK_DIR port:3 p_lrclk :> lrval; #endif - if(I2S_MODE_TDM) + if(XUA_PCM_FORMAT == XUA_PCM_FORMAT_TDM) { /* Only check for the rising edge of frame sync being in the right place because falling edge timing not specified */ - if (frameCount == 1) + if (frameCount == 1) { lrval &= 0xc0000000; // Mask off last two (MSB) frame clock bits which are the most recently sampled syncError += (lrval != 0x80000000); // We need MSB = 1 and MSB-1 = 0 to signify rising edge @@ -187,37 +124,32 @@ static inline int HandleSampleClock(int frameCount, buffered _XUA_CLK_DIR port:3 else { if(frameCount == 0) - #ifdef N_BITS_I2S { + #ifdef N_BITS_I2S if ((lrval & lrval_mask) != 0x80000000) { syncError = 1; } - } #else - { syncError += (lrval != 0x80000000); } #endif else #ifdef N_BITS_I2S - { if ((lrval | (~lrval_mask)) != 0x7FFFFFFF) { syncError = 1; } - } #else - { syncError += (lrval != 0x7FFFFFFF); - } #endif + } } - + return syncError; #else - if(I2S_MODE_TDM) + if(XUA_PCM_FORMAT == XUA_PCM_FORMAT_TDM) { if(frameCount == (I2S_CHANS_PER_FRAME-1)) p_lrclk <: 0x80000000; @@ -240,7 +172,7 @@ static inline int HandleSampleClock(int frameCount, buffered _XUA_CLK_DIR port:3 #endif } - + return 0; #endif @@ -248,18 +180,18 @@ static inline int HandleSampleClock(int frameCount, buffered _XUA_CLK_DIR port:3 #pragma unsafe arrays unsigned static AudioHub_MainLoop(chanend ?c_out, chanend ?c_spd_out -#ifdef ADAT_TX +#if (XUA_ADAT_TX_EN) , chanend c_adat_out , unsigned adatSmuxMode #endif , unsigned divide, unsigned curSamFreq -#if( (SPDIF_RX==1) || (ADAT_RX == 1)) +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) , chanend c_dig_rx #endif #if (XUA_NUM_PDM_MICS > 0) , chanend c_pdm_pcm #endif - , buffered _XUA_CLK_DIR port:32 ?p_lrclk, + , buffered _XUA_CLK_DIR port:32 ?p_lrclk, buffered _XUA_CLK_DIR port:32 ?p_bclk, buffered out port:32 (&?p_i2s_dac)[I2S_WIRES_DAC], buffered in port:32 (&?p_i2s_adc)[I2S_WIRES_ADC] @@ -278,7 +210,7 @@ unsigned static AudioHub_MainLoop(chanend ?c_out, chanend ?c_spd_out #endif unsigned underflowWord = 0; -#ifdef ADAT_TX +#if (XUA_ADAT_TX_EN) adatCounter = 0; #endif @@ -336,7 +268,7 @@ unsigned static AudioHub_MainLoop(chanend ?c_out, chanend ?c_spd_out // Reinitialise user state before entering the main loop UserBufferManagementInit(); -#ifdef ADAT_TX +#if (XUA_ADAT_TX_EN) unsafe{ //TransferAdatTxSamples(c_adat_out, samplesOut, adatSmuxMode, 0); volatile unsigned * unsafe samplePtr = &samplesOut[ADAT_TX_INDEX]; @@ -362,14 +294,14 @@ unsigned static AudioHub_MainLoop(chanend ?c_out, chanend ?c_spd_out InitPorts_master(divide, p_lrclk, p_bclk, p_i2s_dac, p_i2s_adc); #endif } - + /* Note we always expect syncError to be 0 when we are master */ while(!syncError) { #if (DSD_CHANS_DAC != 0) && (NUM_USB_CHAN_OUT > 0) - if(dsdMode == DSD_MODE_NATIVE) + if(dsdMode == DSD_MODE_NATIVE) DoDsdNative(samplesOut, dsdSample_l, dsdSample_r, divide); - else if(dsdMode == DSD_MODE_DOP) + else if(dsdMode == DSD_MODE_DOP) DoDsdDop(everyOther, samplesOut, dsdSample_l, dsdSample_r, divide); else #endif @@ -461,24 +393,24 @@ unsigned static AudioHub_MainLoop(chanend ?c_out, chanend ?c_spd_out } #endif // (I2S_CHANS_DAC != 0) -#ifdef ADAT_TX +#if (XUA_ADAT_TX_EN) TransferAdatTxSamples(c_adat_out, samplesOut, adatSmuxMode, 1); #endif if(frameCount == 0) { -#if (SPDIF_RX == 1) || (ADAT_RX == 1) +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) /* Sync with clockgen */ inuint(c_dig_rx); /* Note, digi-data we just store in samplesIn[readBuffNo] - we only double buffer the I2S input data */ #endif -#if (SPDIF_RX == 1) +#if (XUA_SPDIF_RX_EN) asm("ldw %0, dp[g_digData]" :"=r"(samplesIn[readBuffNo][SPDIF_RX_INDEX + 0])); asm("ldw %0, dp[g_digData+4]":"=r"(samplesIn[readBuffNo][SPDIF_RX_INDEX + 1])); #endif -#if (ADAT_RX == 1) +#if (XUA_ADAT_RX_EN) asm("ldw %0, dp[g_digData+8]" :"=r"(samplesIn[readBuffNo][ADAT_RX_INDEX])); asm("ldw %0, dp[g_digData+12]":"=r"(samplesIn[readBuffNo][ADAT_RX_INDEX + 1])); asm("ldw %0, dp[g_digData+16]":"=r"(samplesIn[readBuffNo][ADAT_RX_INDEX + 2])); @@ -489,14 +421,13 @@ unsigned static AudioHub_MainLoop(chanend ?c_out, chanend ?c_spd_out asm("ldw %0, dp[g_digData+36]":"=r"(samplesIn[readBuffNo][ADAT_RX_INDEX + 7])); #endif -#if (SPDIF_RX == 1) || (ADAT_RX == 1) +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) /* Request digital data (with prefill) */ outuint(c_dig_rx, 0); #endif #if (XUA_SPDIF_TX_EN) && (NUM_USB_CHAN_OUT > 0) - outuint(c_spd_out, samplesOut[SPDIF_TX_INDEX]); /* Forward sample to S/PDIF Tx thread */ - unsigned sample = samplesOut[SPDIF_TX_INDEX + 1]; - outuint(c_spd_out, sample); /* Forward sample to S/PDIF Tx thread */ + outuint(c_spd_out, samplesOut[SPDIF_TX_INDEX]); /* Forward samples to S/PDIF Tx thread */ + outuint(c_spd_out, samplesOut[SPDIF_TX_INDEX + 1]); #endif #if (XUA_NUM_PDM_MICS > 0) @@ -521,7 +452,7 @@ unsigned static AudioHub_MainLoop(chanend ?c_out, chanend ?c_spd_out #endif } - frameCount++; + frameCount++; #if (I2S_CHANS_ADC != 0) index = 0; @@ -614,7 +545,7 @@ unsigned static AudioHub_MainLoop(chanend ?c_out, chanend ?c_spd_out } #endif -#if I2S_MODE_TDM +#if (XUA_PCM_FORMAT == XUA_PCM_FORMAT_TDM) /* Increase frameCount by 2 since we have output two channels (per data line) */ frameCount+=1; if(frameCount == I2S_CHANS_PER_FRAME) @@ -724,12 +655,12 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk, in port p_mclk_in, buffered _XUA_CLK_DIR port:32 ?p_lrclk, buffered _XUA_CLK_DIR port:32 ?p_bclk, - buffered out port:32 (&?p_i2s_dac)[I2S_WIRES_DAC], + buffered out port:32 (&?p_i2s_dac)[I2S_WIRES_DAC], buffered in port:32 (&?p_i2s_adc)[I2S_WIRES_ADC] #if (XUA_SPDIF_TX_EN) //&& (SPDIF_TX_TILE != AUDIO_IO_TILE) , chanend c_spdif_out #endif -#if ((ADAT_RX == 1) || (SPDIF_RX == 1)) +#if (XUA_ADAT_RX_EN || XUA_SPDIF_RX_EN) , chanend c_dig_rx #endif #if (XUD_TILE != 0) && (AUDIO_IO_TILE == 0) && (XUA_DFU_EN == 1) @@ -740,12 +671,11 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk, #endif ) { -#ifdef ADAT_TX +#if (XUA_ADAT_TX_EN) chan c_adat_out; unsigned adatSmuxMode = 0; unsigned adatMultiple = 0; #endif - unsigned curSamFreq = DEFAULT_FREQ * AUD_TO_USB_RATIO; unsigned curSamRes_DAC = STREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS; /* Default to something reasonable */ unsigned curSamRes_ADC = STREAM_FORMAT_INPUT_1_RESOLUTION_BITS; /* Default to something reasonable - note, currently this never changes*/ @@ -769,7 +699,7 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk, } #endif -#ifdef ADAT_TX +#if (XUA_ADAT_TX_EN) /* Share SPDIF clk blk */ configure_clock_src(clk_mst_spd, p_mclk_in); configure_out_port_no_ready(p_adat_tx, clk_mst_spd, 0); @@ -778,26 +708,26 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk, start_clock(clk_mst_spd); #endif #endif - + /* Perform required CODEC/ADC/DAC initialisation */ AudioHwInit(); while(1) { /* Calculate what master clock we should be using */ - if ((MCLK_441 % curSamFreq) == 0) + if (((MCLK_441) % curSamFreq) == 0) { mClk = MCLK_441; -#ifdef ADAT_TX +#if (XUA_ADAT_TX_EN) /* Calculate ADAT SMUX mode (1, 2, 4) */ adatSmuxMode = curSamFreq / 44100; adatMultiple = mClk / 44100; #endif } - else if ((MCLK_48 % curSamFreq) == 0) + else if (((MCLK_48) % curSamFreq) == 0) { mClk = MCLK_48; -#ifdef ADAT_TX +#if (XUA_ADAT_TX_EN) /* Calculate ADAT SMUX mode (1, 2, 4) */ adatSmuxMode = curSamFreq / 48000; adatMultiple = mClk / 48000; @@ -807,7 +737,7 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk, /* Calculate master clock to bit clock (or DSD clock) divide for current sample freq * e.g. 11.289600 / (176400 * 64) = 1 */ { -#if I2S_MODE_TDM +#if (XUA_PCM_FORMAT == XUA_PCM_FORMAT_TDM) /* I2S has 32 bits per sample. *8 as 8 channels */ unsigned numBits = 256; @@ -836,7 +766,7 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk, numBits = 32; } #endif - divide = mClk / ( curSamFreq * numBits); + divide = mClk / (curSamFreq * numBits); //Do some checks xassert((divide > 0) && "Error: divider is 0, BCLK rate unachievable"); @@ -849,12 +779,11 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk, } - #if (DSD_CHANS_DAC > 0) if(dsdMode) { - /* Configure audio ports */ - ConfigAudioPortsWrapper( + /* Configure audio ports */ + ConfigAudioPortsWrapper( #if (I2S_CHANS_DAC != 0) || (DSD_CHANS_DAC != 0) p_dsd_dac, DSD_CHANS_DAC, @@ -867,12 +796,11 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk, null, p_dsd_clk, #endif - divide, curSamFreq, dsdMode); + p_mclk_in, clk_audio_bclk, divide, curSamFreq); } else #endif { - ConfigAudioPortsWrapper( #if (I2S_CHANS_DAC != 0) p_i2s_dac, @@ -891,9 +819,8 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk, p_bclk, #endif #endif - divide, curSamFreq, dsdMode); -} - + p_mclk_in, clk_audio_bclk, divide, curSamFreq); + } { unsigned curFreq = curSamFreq; @@ -939,7 +866,7 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk, par { -#ifdef ADAT_TX +#if (XUA_ADAT_TX_EN) { set_thread_fast_mode_on(); adat_tx_port(c_adat_out, p_adat_tx); @@ -958,7 +885,7 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk, c_pdm_in <: curSamFreq / AUD_TO_MICS_RATIO; #endif -#ifdef ADAT_TX +#if (XUA_ADAT_TX_EN) // Configure ADAT parameters ... // // adat_oversampling = 256 for MCLK = 12M288 or 11M2896 @@ -978,12 +905,12 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk, #else , null #endif -#ifdef ADAT_TX +#if (XUA_ADAT_TX_EN) , c_adat_out , adatSmuxMode #endif , divide, curSamFreq -#if (ADAT_RX == 1) || (SPDIF_RX == 1) +#if (XUA_ADAT_RX_EN || XUA_SPDIF_RX_EN) , c_dig_rx #endif #if (XUA_NUM_PDM_MICS > 0) @@ -1043,7 +970,7 @@ void XUA_AudioHub(chanend ?c_aud, clock ?clk_audio_mclk, clock ?clk_audio_bclk, c_pdm_in <: 0; #endif -#ifdef ADAT_TX +#if (XUA_ADAT_TX_EN) #ifdef ADAT_TX_USE_SHARED_BUFF /* Take out-standing handshake from ADAT core */ inuint(c_adat_out); diff --git a/lib_xua/src/core/audiohub/xua_audiohub_st.h b/lib_xua/src/core/audiohub/xua_audiohub_st.h new file mode 100644 index 00000000..e265de77 --- /dev/null +++ b/lib_xua/src/core/audiohub/xua_audiohub_st.h @@ -0,0 +1,66 @@ +// Copyright 2011-2023 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +#pragma unsafe arrays +static inline unsigned DoSampleTransfer(chanend ?c_out, const int readBuffNo, const unsigned underflowWord) +{ + if(XUA_USB_EN) + { + outuint(c_out, underflowWord); + + /* Check for sample freq change (or other command) or new samples from mixer*/ + if(testct(c_out)) + { + unsigned command = inct(c_out); +#ifndef CODEC_MASTER + if(dsdMode == DSD_MODE_OFF) + { +#if (I2S_CHANS_ADC != 0 || I2S_CHANS_DAC != 0) + /* Set clocks low */ + p_lrclk <: 0; + p_bclk <: 0; +#endif + } + else + { +#if(DSD_CHANS_DAC != 0) + /* DSD Clock might not be shared with lrclk or bclk... */ + p_dsd_clk <: 0; +#endif + } +#endif +#if (DSD_CHANS_DAC > 0) + if(dsdMode == DSD_MODE_DOP) + dsdMode = DSD_MODE_OFF; +#endif + return command; + } + else + { +#if NUM_USB_CHAN_OUT > 0 +#pragma loop unroll + for(int i = 0; i < NUM_USB_CHAN_OUT; i++) + { + int tmp = inuint(c_out); + samplesOut[i] = tmp; + } +#else + inuint(c_out); +#endif + UserBufferManagement(samplesOut, samplesIn[readBuffNo]); + +#if NUM_USB_CHAN_IN > 0 +#pragma loop unroll + for(int i = 0; i < NUM_USB_CHAN_IN; i++) + { + outuint(c_out, samplesIn[readBuffNo][i]); + } +#endif + } + } + else + UserBufferManagement(samplesOut, samplesIn[readBuffNo]); + + return 0; +} + diff --git a/lib_xua/src/core/buffer/decouple/decouple.xc b/lib_xua/src/core/buffer/decouple/decouple.xc index f22f9582..c276abda 100644 --- a/lib_xua/src/core/buffer/decouple/decouple.xc +++ b/lib_xua/src/core/buffer/decouple/decouple.xc @@ -1,7 +1,10 @@ -// Copyright 2011-2021 XMOS LIMITED. +// Copyright 2011-2023 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #include "xua.h" +#define XASSERT_UNIT DECOUPLE +#include "xassert.h" + #if XUA_USB_EN #include #include "xc_ptr.h" @@ -14,12 +17,11 @@ #include "usbaudio20.h" /* Defines from the USB Audio 2.0 Specifications */ #endif -#if( 0 < HID_CONTROLS ) +#if (HID_CONTROLS) #include "user_hid.h" #endif #define MAX(x,y) ((x)>(y) ? (x) : (y)) - /* TODO use SLOTSIZE to potentially save memory */ /* Note we could improve on this, for one subslot is set to 4 */ /* The *4 is conversion to bytes, note we're assuming a slotsize of 4 here whic is potentially as waste */ @@ -41,7 +43,7 @@ /*** BUFFER SIZES ***/ -#define BUFFER_PACKET_COUNT 3 /* How many packets too allow for in buffer - minimum is 3! */ +#define BUFFER_PACKET_COUNT (4) /* How many packets too allow for in buffer - minimum is 4 */ #define BUFF_SIZE_OUT_HS MAX_DEVICE_AUD_PACKET_SIZE_OUT_HS * BUFFER_PACKET_COUNT #define BUFF_SIZE_OUT_FS MAX_DEVICE_AUD_PACKET_SIZE_OUT_FS * BUFFER_PACKET_COUNT @@ -53,25 +55,41 @@ #define BUFF_SIZE_IN MAX(BUFF_SIZE_IN_HS, BUFF_SIZE_IN_FS) #define OUT_BUFFER_PREFILL (MAX(MAX_DEVICE_AUD_PACKET_SIZE_OUT_HS, MAX_DEVICE_AUD_PACKET_SIZE_OUT_FS)) -#define IN_BUFFER_PREFILL (MAX(MAX_DEVICE_AUD_PACKET_SIZE_IN_HS, MAX_DEVICE_AUD_PACKET_SIZE_IN_FS)*2) +#define IN_BUFFER_PREFILL (MAX(MAX_DEVICE_AUD_PACKET_SIZE_IN_HS, MAX_DEVICE_AUD_PACKET_SIZE_IN_FS)*2) /* Volume and mute tables */ -#if !defined(OUT_VOLUME_IN_MIXER) && (OUTPUT_VOLUME_CONTROL == 1) +#if (OUT_VOLUME_IN_MIXER == 0) && (OUTPUT_VOLUME_CONTROL == 1) unsigned int multOut[NUM_USB_CHAN_OUT + 1]; -static xc_ptr p_multOut; +unsafe +{ + unsigned int volatile * unsafe multOutPtr = multOut; +} #endif -#if !defined(IN_VOLUME_IN_MIXER) && (INPUT_VOLUME_CONTROL == 1) +#if (IN_VOLUME_IN_MIXER == 0) && (INPUT_VOLUME_CONTROL == 1) unsigned int multIn[NUM_USB_CHAN_IN + 1]; -static xc_ptr p_multIn; +unsafe +{ + unsigned int volatile * unsafe multInPtr = multIn; +} #endif -/* Number of channels to/from the USB bus - initialised to HS Audio 2.0 */ -#if (AUDIO_CLASS == 1) -unsigned g_numUsbChan_In = NUM_USB_CHAN_IN_FS; -unsigned g_numUsbChan_Out = NUM_USB_CHAN_OUT_FS; +/* Default to something sensible but the following are setup at stream start (unless UAC1 only..) */ +#if (AUDIO_CLASS == 2) +int g_numUsbChan_In = NUM_USB_CHAN_IN; /* Number of channels to/from the USB bus - initialised to HS for UAC2.0 */ +int g_numUsbChan_Out = NUM_USB_CHAN_OUT; +int g_curSubSlot_Out = HS_STREAM_FORMAT_OUTPUT_1_SUBSLOT_BYTES; +int g_curSubSlot_In = HS_STREAM_FORMAT_INPUT_1_SUBSLOT_BYTES; +int sampsToWrite = DEFAULT_FREQ/8000; /* HS assumed here. Expect to be junked during a overflow before stream start */ +int totalSampsToWrite = DEFAULT_FREQ/8000; +int g_maxPacketSize = MAX_DEVICE_AUD_PACKET_SIZE_IN_HS; /* IN packet size. Init to something sensible, but expect to be re-set before stream start */ #else -unsigned g_numUsbChan_In = NUM_USB_CHAN_IN; -unsigned g_numUsbChan_Out = NUM_USB_CHAN_OUT; +int g_numUsbChan_In = NUM_USB_CHAN_IN_FS; /* Number of channels to/from the USB bus - initialised to FS for UAC1.0 */ +int g_numUsbChan_Out = NUM_USB_CHAN_OUT_FS; +int g_curSubSlot_Out = FS_STREAM_FORMAT_OUTPUT_1_SUBSLOT_BYTES; +int g_curSubSlot_In = FS_STREAM_FORMAT_INPUT_1_SUBSLOT_BYTES; +int sampsToWrite = DEFAULT_FREQ/1000; /* FS assumed here. Expect to be junked during a overflow before stream start */ +int totalSampsToWrite = DEFAULT_FREQ/1000; +int g_maxPacketSize = MAX_DEVICE_AUD_PACKET_SIZE_IN_FS; /* IN packet size. Init to something sensible, but expect to be re-set before stream start */ #endif /* Circular audio buffers */ @@ -89,13 +107,12 @@ XUD_ep aud_to_host_usb_ep = 0; /* Shared global audio buffering variables */ unsigned g_aud_from_host_buffer; -unsigned g_aud_to_host_buffer; unsigned g_aud_to_host_flag = 0; int buffer_aud_ctl_chan = 0; unsigned g_aud_from_host_flag = 0; unsigned g_aud_from_host_info; unsigned g_freqChange_flag = 0; -unsigned g_freqChange_sampFreq; +unsigned g_freqChange_sampFreq = DEFAULT_FREQ; /* Global vars for sharing stream format change between buffer and decouple (save a channel) */ unsigned g_formatChange_SubSlot; @@ -115,14 +132,8 @@ xc_ptr aud_to_host_fifo_end; xc_ptr g_aud_to_host_wrptr; xc_ptr g_aud_to_host_dptr; xc_ptr g_aud_to_host_rdptr; -xc_ptr g_aud_to_host_zeros; -#if (AUDIO_CLASS == 2) -int sampsToWrite = DEFAULT_FREQ/8000; /* HS assumed here. Expect to be junked during a overflow before stream start */ -int totalSampsToWrite = DEFAULT_FREQ/8000; -#else -int sampsToWrite = DEFAULT_FREQ/1000; /* HS assumed here. Expect to be junked during a overflow before stream start */ -int totalSampsToWrite = DEFAULT_FREQ/1000; -#endif +int g_aud_to_host_fill_level; + int aud_data_remaining_to_device = 0; /* Audio over/under flow flags */ @@ -139,28 +150,77 @@ unsigned unpackData = 0; unsigned packState = 0; unsigned packData = 0; +static inline void SendSamples4(chanend c_mix_out) +{ + /* Doing this checking allows us to unroll */ + if(g_numUsbChan_Out == NUM_USB_CHAN_OUT) + { + /* Buffering not underflow condition send out some samples...*/ +#pragma loop unroll + for(int i = 0; i < NUM_USB_CHAN_OUT; i++) + { + int sample; + int mult; + int h; + unsigned l; -/* Default to something sensible but the following are setup at stream start (unless UAC1 only..) */ -#if (AUDIO_CLASS == 2) -unsigned g_curSubSlot_Out = HS_STREAM_FORMAT_OUTPUT_1_SUBSLOT_BYTES; -unsigned g_curSubSlot_In = HS_STREAM_FORMAT_INPUT_1_SUBSLOT_BYTES; -#else -unsigned g_curSubSlot_Out = FS_STREAM_FORMAT_OUTPUT_1_SUBSLOT_BYTES; -unsigned g_curSubSlot_In = FS_STREAM_FORMAT_INPUT_1_SUBSLOT_BYTES; -#endif + read_via_xc_ptr(sample, g_aud_from_host_rdptr); + g_aud_from_host_rdptr+=4; -/* IN packet size. Init to something sensible, but expect to be re-set before stream start */ -#if (AUDIO_CLASS==2) -int g_maxPacketSize = MAX_DEVICE_AUD_PACKET_SIZE_IN_HS; -#else -int g_maxPacketSize = MAX_DEVICE_AUD_PACKET_SIZE_IN_FS; +#if (OUTPUT_VOLUME_CONTROL == 1) && (!OUT_VOLUME_IN_MIXER) + unsafe + { + mult = multOutPtr[i]; + } + {h, l} = macs(mult, sample, 0, 0); + h <<= 3; +#if (STREAM_FORMAT_OUTPUT_RESOLUTION_32BIT_USED == 1) + h |= (l >>29) & 0x7; // Note: This step is not required if we assume sample depth is 24bit (rather than 32bit) + // Note: We need all 32bits for Native DSD #endif + outuint(c_mix_out, h); +#else + outuint(c_mix_out, sample); +#endif + } + } + else + { +#pragma loop unroll + for(int i = 0; i < NUM_USB_CHAN_OUT_FS; i++) + { + int sample; + int mult; + int h; + unsigned l; + + read_via_xc_ptr(sample, g_aud_from_host_rdptr); + g_aud_from_host_rdptr+=4; + +#if (OUTPUT_VOLUME_CONTROL == 1) && (!OUT_VOLUME_IN_MIXER) + unsafe + { + mult = multOutPtr[i]; + } + {h, l} = macs(mult, sample, 0, 0); + h <<= 3; +#if (STREAM_FORMAT_OUTPUT_RESOLUTION_32BIT_USED == 1) + h |= (l >>29) & 0x7; // Note: This step is not required if we assume sample depth is 24bit (rather than 32bit) + // Note: We need all 32bits for Native DSD +#endif + outuint(c_mix_out, h); +#else + outuint(c_mix_out, sample); +#endif + } + } +} + #pragma select handler #pragma unsafe arrays void handle_audio_request(chanend c_mix_out) { - int space_left; #if(defined XUA_USB_DESCRIPTOR_OVERWRITE_RATE_RES) g_curSubSlot_Out = get_usb_to_device_bit_res() >> 3; g_curSubSlot_In = get_device_to_usb_bit_res() >> 3; @@ -218,8 +278,11 @@ __builtin_unreachable(); g_aud_from_host_rdptr+=2; sample <<= 16; -#if (OUTPUT_VOLUME_CONTROL == 1) && !defined(OUT_VOLUME_IN_MIXER) - asm volatile("ldw %0, %1[%2]":"=r"(mult):"r"(p_multOut),"r"(i)); +#if (OUTPUT_VOLUME_CONTROL == 1) && (!OUT_VOLUME_IN_MIXER) + unsafe + { + mult = multOutPtr[i]; + } {h, l} = macs(mult, sample, 0, 0); /* Note, in 2 byte subslot mode - ignore lower result of macs */ h <<= 3; @@ -235,41 +298,17 @@ __builtin_unreachable(); __builtin_unreachable(); #endif /* Buffering not underflow condition send out some samples...*/ - for(int i = 0; i < g_numUsbChan_Out; i++) - { -#pragma xta endpoint "mixer_request" - int sample; - int mult; - int h; - unsigned l; - - read_via_xc_ptr(sample, g_aud_from_host_rdptr); - g_aud_from_host_rdptr+=4; - -#if (OUTPUT_VOLUME_CONTROL == 1) && !defined(OUT_VOLUME_IN_MIXER) - asm volatile("ldw %0, %1[%2]":"=r"(mult):"r"(p_multOut),"r"(i)); - {h, l} = macs(mult, sample, 0, 0); - h <<= 3; -#if (STREAM_FORMAT_OUTPUT_RESOLUTION_32BIT_USED == 1) - h |= (l >>29)& 0x7; // Note: This step is not required if we assume sample depth is 24bit (rather than 32bit) - // Note: We need all 32bits for Native DSD -#endif - outuint(c_mix_out, h); -#else - outuint(c_mix_out, sample); -#endif - } - + SendSamples4(c_mix_out); break; case 3: #if (STREAM_FORMAT_OUTPUT_SUBSLOT_3_USED == 0) __builtin_unreachable(); #endif - /* Buffering not underflow condition send out some samples...*/ + /* Note, in this case the unpacking of data is more of an overhead than the loop overhead + * so we do not currently make attempts to unroll */ for(int i = 0; i < g_numUsbChan_Out; i++) { -#pragma xta endpoint "mixer_request" int sample; int mult; int h; @@ -301,19 +340,20 @@ __builtin_unreachable(); } unpackState++; -#if (OUTPUT_VOLUME_CONTROL == 1) && !defined(OUT_VOLUME_IN_MIXER) - asm volatile("ldw %0, %1[%2]":"=r"(mult):"r"(p_multOut),"r"(i)); +#if (OUTPUT_VOLUME_CONTROL == 1) && (!OUT_VOLUME_IN_MIXER) + unsafe + { + mult = multOutPtr[i]; + } {h, l} = macs(mult, sample, 0, 0); h <<= 3; outuint(c_mix_out, h); #else outuint(c_mix_out, sample); - #endif } break; - default: __builtin_unreachable(); break; @@ -332,6 +372,9 @@ __builtin_unreachable(); #endif { + int dPtr; + GET_SHARED_GLOBAL(dPtr, g_aud_to_host_dptr); + /* Store samples from mixer into sample buffer */ switch(g_curSubSlot_In) { @@ -344,22 +387,25 @@ __builtin_unreachable(); /* Receive sample */ int sample = inuint(c_mix_out); #if (INPUT_VOLUME_CONTROL == 1) -#if !defined(IN_VOLUME_IN_MIXER) +#if (!IN_VOLUME_IN_MIXER) /* Apply volume */ int mult; int h; unsigned l; - asm volatile("ldw %0, %1[%2]":"=r"(mult):"r"(p_multIn),"r"(i)); + unsafe + { + mult = multInPtr[i]; + } {h, l} = macs(mult, sample, 0, 0); sample = h << 3; /* Note, in 2 byte sub slot - ignore lower bits of macs */ -#elif defined(IN_VOLUME_IN_MIXER) && defined(IN_VOLUME_AFTER_MIX) +#elif (IN_VOLUME_IN_MIXER) && defined(IN_VOLUME_AFTER_MIX) sample = sample << 3; #endif #endif - write_short_via_xc_ptr(g_aud_to_host_dptr, sample>>16); - g_aud_to_host_dptr+=2; + write_short_via_xc_ptr(dPtr, sample>>16); + dPtr+=2; } break; @@ -368,36 +414,35 @@ __builtin_unreachable(); #if (STREAM_FORMAT_INPUT_SUBSLOT_4_USED == 0) __builtin_unreachable(); #endif - unsigned ptr = g_aud_to_host_dptr; for(int i = 0; i < g_numUsbChan_In; i++) { /* Receive sample */ int sample = inuint(c_mix_out); #if(INPUT_VOLUME_CONTROL == 1) -#if !defined(IN_VOLUME_IN_MIXER) +#if (!IN_VOLUME_IN_MIXER) /* Apply volume */ int mult; int h; unsigned l; - asm volatile("ldw %0, %1[%2]":"=r"(mult):"r"(p_multIn),"r"(i)); + unsafe + { + mult = multInPtr[i]; + } {h, l} = macs(mult, sample, 0, 0); sample = h << 3; #if (STREAM_FORMAT_INPUT_RESOLUTION_32BIT_USED == 1) sample |= (l >> 29) & 0x7; // Note, this step is not required if we assume sample depth is 24 (rather than 32) #endif -#elif defined(IN_VOLUME_IN_MIXER) && defined(IN_VOLUME_AFTER_MIX) +#elif (IN_VOLUME_IN_MIXER) && (IN_VOLUME_AFTER_MIX) sample = sample << 3; #endif #endif /* Write into fifo */ - write_via_xc_ptr(ptr, sample); - ptr+=4; + write_via_xc_ptr(dPtr, sample); + dPtr+=4; } - /* Update global pointer */ - g_aud_to_host_dptr = ptr; - break; } @@ -409,12 +454,15 @@ __builtin_unreachable(); { /* Receive sample */ int sample = inuint(c_mix_out); -#if (INPUT_VOLUME_CONTROL) && !defined(IN_VOLUME_IN_MIXER) +#if (INPUT_VOLUME_CONTROL) && (!IN_VOLUME_IN_MIXER) /* Apply volume */ int mult; int h; unsigned l; - asm volatile("ldw %0, %1[%2]":"=r"(mult):"r"(p_multIn),"r"(i)); + unsafe + { + mult = multInPtr[i]; + } {h, l} = macs(mult, sample, 0, 0); sample = h << 3; #endif @@ -426,21 +474,21 @@ __builtin_unreachable(); break; case 1: packData = (packData >> 8) | ((sample & 0xff00)<<16); - write_via_xc_ptr(g_aud_to_host_dptr, packData); - g_aud_to_host_dptr+=4; - write_via_xc_ptr(g_aud_to_host_dptr, sample>>16); + write_via_xc_ptr(dPtr, packData); + dPtr+=4; + write_via_xc_ptr(dPtr, sample>>16); packData = sample; break; case 2: packData = (packData>>16) | ((sample & 0xffff00) << 8); - write_via_xc_ptr(g_aud_to_host_dptr, packData); - g_aud_to_host_dptr+=4; + write_via_xc_ptr(dPtr, packData); + dPtr+=4; packData = sample; break; case 3: packData = (packData >> 24) | (sample & 0xffffff00); - write_via_xc_ptr(g_aud_to_host_dptr, packData); - g_aud_to_host_dptr+=4; + write_via_xc_ptr(dPtr, packData); + dPtr+=4; break; } packState++; @@ -452,6 +500,8 @@ __builtin_unreachable(); break; } + SET_SHARED_GLOBAL(g_aud_to_host_dptr, dPtr); + /* Input any remaining channels - past this thread we always operate on max channel count */ for(int i = 0; i < NUM_USB_CHAN_IN - g_numUsbChan_In; i++) { @@ -466,27 +516,35 @@ __builtin_unreachable(); /* Total samps to write could start at 0 (i.e. no MCLK) so need to check for < 0) */ if (sampsToWrite <= 0) { - int speed; + int speed, wrPtr; packState = 0; /* Write last packet length into FIFO */ - unsigned datasize = totalSampsToWrite * g_curSubSlot_In * g_numUsbChan_In; + int datasize = totalSampsToWrite * g_curSubSlot_In * g_numUsbChan_In; - write_via_xc_ptr(g_aud_to_host_wrptr, datasize); + GET_SHARED_GLOBAL(wrPtr, g_aud_to_host_wrptr); + write_via_xc_ptr(wrPtr, datasize); /* Round up to nearest word - note, not needed for slotsize == 4! */ datasize = (datasize+3) & (~0x3); + assert(datasize >= 0); + assert(datasize <= g_maxPacketSize); /* Move wr ptr on by old packet length */ - g_aud_to_host_wrptr += 4+datasize; + wrPtr += 4+datasize; + int fillLevel; + GET_SHARED_GLOBAL(fillLevel, g_aud_to_host_fill_level); + fillLevel += 4+datasize; + assert(fillLevel <= BUFF_SIZE_IN); /* Do wrap */ - if (g_aud_to_host_wrptr >= aud_to_host_fifo_end) + if (wrPtr >= aud_to_host_fifo_end) { - g_aud_to_host_wrptr = aud_to_host_fifo_start; + wrPtr = aud_to_host_fifo_start; } - g_aud_to_host_dptr = g_aud_to_host_wrptr + 4; + SET_SHARED_GLOBAL(g_aud_to_host_wrptr, wrPtr); + SET_SHARED_GLOBAL(g_aud_to_host_dptr, wrPtr + 4); /* Now calculate new packet length... * First get feedback val (ideally this would be syncronised) @@ -506,47 +564,53 @@ __builtin_unreachable(); totalSampsToWrite = 0; } - /* Calc slots left in fifo */ - space_left = g_aud_to_host_rdptr - g_aud_to_host_wrptr; - - /* Mod and special case */ - if ((space_left <= 0) && (g_aud_to_host_rdptr == aud_to_host_fifo_start)) - { - space_left = aud_to_host_fifo_end - g_aud_to_host_wrptr; - } - - if((space_left < (totalSampsToWrite * g_numUsbChan_In * g_curSubSlot_In + 4))) + /* Must allow space for at least one sample per channel, as these are written at the beginning of + * the interrupt handler even if totalSampsToWrite is zero (will be overwritten by a later packet). */ + int spaceRequired = MAX(totalSampsToWrite, 1) * g_numUsbChan_In * g_curSubSlot_In + 4; + if (spaceRequired > BUFF_SIZE_IN - fillLevel) { /* In pipe has filled its buffer - we need to overflow * Accept the packet, and throw away the oldest in the buffer */ - /* Keep throwing away packets until buffer is at a nice level.. */ + unsigned sampFreq; + GET_SHARED_GLOBAL(sampFreq, g_freqChange_sampFreq); + int min, mid, max; + GetADCCounts(sampFreq, min, mid, max); + const int max_pkt_size = ((max * g_curSubSlot_In * g_numUsbChan_In + 3) & ~0x3) + 4; + int rdPtr; + GET_SHARED_GLOBAL(rdPtr, g_aud_to_host_rdptr); + + /* Keep throwing away packets until buffer contains two packets */ do { - unsigned rdPtr; + int wrPtr; + GET_SHARED_GLOBAL(wrPtr, g_aud_to_host_wrptr); /* Read length of packet in buffer at read pointer */ - unsigned datalength; - - GET_SHARED_GLOBAL(rdPtr, g_aud_to_host_rdptr); + int datalength; asm volatile("ldw %0, %1[0]":"=r"(datalength):"r"(rdPtr)); /* Round up datalength */ datalength = ((datalength+3) & ~0x3) + 4; + assert(datalength >= 4); + assert(fillLevel >= datalength); /* Move read pointer on by length */ + fillLevel -= datalength; rdPtr += datalength; if (rdPtr >= aud_to_host_fifo_end) { rdPtr = aud_to_host_fifo_start; } - space_left += datalength; - SET_SHARED_GLOBAL(g_aud_to_host_rdptr, rdPtr); + assert(rdPtr < aud_to_host_fifo_end && msg("rdPtr must be within buffer")); - } while(space_left < (BUFF_SIZE_IN/2)); + } while (fillLevel > 2 * max_pkt_size); + + SET_SHARED_GLOBAL(g_aud_to_host_rdptr, rdPtr); } + SET_SHARED_GLOBAL(g_aud_to_host_fill_level, fillLevel); sampsToWrite = totalSampsToWrite; } } @@ -585,10 +649,12 @@ __builtin_unreachable(); } } +#if (NUM_USB_CHAN_IN > 0) /* Mark Endpoint (IN) ready with an appropriately sized zero buffer */ -static inline void SetupZerosSendBuffer(XUD_ep aud_to_host_usb_ep, unsigned sampFreq, unsigned slotSize) +static inline void SetupZerosSendBuffer(XUD_ep aud_to_host_usb_ep, unsigned sampFreq, unsigned slotSize, + xc_ptr aud_to_host_zeros) { - int min, mid, max, p; + int min, mid, max; GetADCCounts(sampFreq, min, mid, max); /* Set IN stream packet size to something sensible. We expect the buffer to @@ -598,7 +664,7 @@ static inline void SetupZerosSendBuffer(XUD_ep aud_to_host_usb_ep, unsigned samp mid *= g_numUsbChan_In * slotSize; - asm volatile("stw %0, %1[0]"::"r"(mid),"r"(g_aud_to_host_zeros)); + asm volatile("stw %0, %1[0]"::"r"(mid),"r"(aud_to_host_zeros)); #if XUA_DEBUG_BUFFER printstr("SetupZerosSendBuffer\n"); @@ -613,12 +679,9 @@ static inline void SetupZerosSendBuffer(XUD_ep aud_to_host_usb_ep, unsigned samp /* Mark EP ready with the zero buffer. Note this will simply update the packet size * if it is already ready */ - /* g_aud_to_host_buffer is already set to g_aud_to_host_zeros */ - - GET_SHARED_GLOBAL(p, g_aud_to_host_buffer); - - XUD_SetReady_InPtr(aud_to_host_usb_ep, p+4, mid); + XUD_SetReady_InPtr(aud_to_host_usb_ep, aud_to_host_zeros+4, mid); } +#endif #pragma unsafe arrays void XUA_Buffer_Decouple(chanend c_mix_out @@ -638,13 +701,6 @@ void XUA_Buffer_Decouple(chanend c_mix_out int t = array_to_xc_ptr(outAudioBuff); -#if !defined(OUT_VOLUME_IN_MIXER) && (OUTPUT_VOLUME_CONTROL == 1) - p_multOut = array_to_xc_ptr(multOut); -#endif -#if !defined(IN_VOLUME_IN_MIXER) && (INPUT_VOLUME_CONTROL == 1) - p_multIn = array_to_xc_ptr(multIn); -#endif - aud_from_host_fifo_start = t; aud_from_host_fifo_end = aud_from_host_fifo_start + BUFF_SIZE_OUT; g_aud_from_host_wrptr = aud_from_host_fifo_start; @@ -652,31 +708,33 @@ void XUA_Buffer_Decouple(chanend c_mix_out t = array_to_xc_ptr(audioBuffIn); + int aud_to_host_buffer; aud_to_host_fifo_start = t; aud_to_host_fifo_end = aud_to_host_fifo_start + BUFF_SIZE_IN; - g_aud_to_host_wrptr = aud_to_host_fifo_start; - g_aud_to_host_rdptr = aud_to_host_fifo_start; - g_aud_to_host_dptr = aud_to_host_fifo_start + 4; + SET_SHARED_GLOBAL(g_aud_to_host_wrptr, aud_to_host_fifo_start); + SET_SHARED_GLOBAL(g_aud_to_host_rdptr, aud_to_host_fifo_start); + SET_SHARED_GLOBAL(g_aud_to_host_dptr, aud_to_host_fifo_start + 4); + SET_SHARED_GLOBAL(g_aud_to_host_fill_level, 0); /* Setup pointer to In stream 0 buffer. Note, length will be innited to 0 * However, this should be over-written on first stream start (assuming host properly sends a SetInterface() before streaming. In any case we will send 0 length packets, which is reasonable behaviour */ t = array_to_xc_ptr(inZeroBuff); - g_aud_to_host_zeros = t; + xc_ptr aud_to_host_zeros = t; /* Init vol mult tables */ -#if !defined(OUT_VOLUME_IN_MIXER) && (OUTPUT_VOLUME_CONTROL == 1) +#if (OUT_VOLUME_IN_MIXER == 0) && (OUTPUT_VOLUME_CONTROL == 1) for (int i = 0; i < NUM_USB_CHAN_OUT + 1; i++) - { - asm volatile("stw %0, %1[%2]"::"r"(MAX_VOL),"r"(p_multOut),"r"(i)); + unsafe{ + multOutPtr[i] = MAX_VOLUME_MULT; } #endif -#if !defined(IN_VOLUME_IN_MIXER) && (INPUT_VOLUME_CONTROL == 1) +#if (IN_VOLUME_IN_MIXER == 0) && (INPUT_VOLUME_CONTROL == 1) for (int i = 0; i < NUM_USB_CHAN_IN + 1; i++) - { - asm volatile("stw %0, %1[%2]"::"r"(MAX_VOL),"r"(p_multIn),"r"(i)); + unsafe{ + multInPtr[i] = MAX_VOLUME_MULT; } #endif @@ -715,8 +773,7 @@ void XUA_Buffer_Decouple(chanend c_mix_out #if (AUDIO_CLASS == 1) /* For UAC1 we know we only run at FS */ /* Set buffer back to zeros buffer */ - SET_SHARED_GLOBAL(g_aud_to_host_buffer, g_aud_to_host_zeros); - SetupZerosSendBuffer(aud_to_host_usb_ep, sampFreq, g_curSubSlot_In); + SetupZerosSendBuffer(aud_to_host_usb_ep, sampFreq, g_curSubSlot_In, aud_to_host_zeros); #endif #endif @@ -748,28 +805,34 @@ void XUA_Buffer_Decouple(chanend c_mix_out outct(c_mix_out, SET_SAMPLE_FREQ); outuint(c_mix_out, sampFreq); - inUnderflow = 1; - SET_SHARED_GLOBAL(g_aud_to_host_rdptr, aud_to_host_fifo_start); - SET_SHARED_GLOBAL(g_aud_to_host_wrptr, aud_to_host_fifo_start); - SET_SHARED_GLOBAL(g_aud_to_host_dptr,aud_to_host_fifo_start+4); - - /* Set buffer to send back to zeros buffer */ - SET_SHARED_GLOBAL(g_aud_to_host_buffer, g_aud_to_host_zeros); - - /* Update size of zeros buffer (and sampsToWrite) */ - SetupZerosSendBuffer(aud_to_host_usb_ep, sampFreq, g_curSubSlot_In); - - /* Reset OUT buffer state */ - outUnderflow = 1; - SET_SHARED_GLOBAL(g_aud_from_host_rdptr, aud_from_host_fifo_start); - SET_SHARED_GLOBAL(g_aud_from_host_wrptr, aud_from_host_fifo_start); - SET_SHARED_GLOBAL(aud_data_remaining_to_device, 0); - - if(outOverflow) + if(sampFreq != AUDIO_STOP_FOR_DFU) { - /* If we were previously in overflow we wont have marked as ready */ - XUD_SetReady_OutPtr(aud_from_host_usb_ep, aud_from_host_fifo_start+4); - outOverflow = 0; + inUnderflow = 1; + SET_SHARED_GLOBAL(g_aud_to_host_rdptr, aud_to_host_fifo_start); + SET_SHARED_GLOBAL(g_aud_to_host_wrptr, aud_to_host_fifo_start); + SET_SHARED_GLOBAL(g_aud_to_host_dptr,aud_to_host_fifo_start+4); + SET_SHARED_GLOBAL(g_aud_to_host_fill_level, 0); + + /* Set buffer to send back to zeros buffer */ + aud_to_host_buffer = aud_to_host_zeros; + +#if (NUM_USB_CHAN_IN > 0) + /* Update size of zeros buffer (and sampsToWrite) */ + SetupZerosSendBuffer(aud_to_host_usb_ep, sampFreq, g_curSubSlot_In, aud_to_host_zeros); +#endif + + /* Reset OUT buffer state */ + outUnderflow = 1; + SET_SHARED_GLOBAL(g_aud_from_host_rdptr, aud_from_host_fifo_start); + SET_SHARED_GLOBAL(g_aud_from_host_wrptr, aud_from_host_fifo_start); + SET_SHARED_GLOBAL(aud_data_remaining_to_device, 0); + + if(outOverflow) + { + /* If we were previously in overflow we wont have marked as ready */ + XUD_SetReady_OutPtr(aud_from_host_usb_ep, aud_from_host_fifo_start+4); + outOverflow = 0; + } } /* Wait for handshake back and pass back up */ @@ -780,7 +843,8 @@ void XUA_Buffer_Decouple(chanend c_mix_out ENABLE_INTERRUPTS(); - speedRem = 0; + if(sampFreq != AUDIO_STOP_FOR_DFU) + speedRem = 0; continue; } #if (AUDIO_CLASS == 2) @@ -804,12 +868,15 @@ void XUA_Buffer_Decouple(chanend c_mix_out SET_SHARED_GLOBAL(g_aud_to_host_rdptr, aud_to_host_fifo_start); SET_SHARED_GLOBAL(g_aud_to_host_wrptr,aud_to_host_fifo_start); SET_SHARED_GLOBAL(g_aud_to_host_dptr,aud_to_host_fifo_start+4); + SET_SHARED_GLOBAL(g_aud_to_host_fill_level, 0); /* Set buffer back to zeros buffer */ - SET_SHARED_GLOBAL(g_aud_to_host_buffer, g_aud_to_host_zeros); + aud_to_host_buffer = aud_to_host_zeros; +#if (NUM_USB_CHAN_IN > 0) /* Update size of zeros buffer (and sampsToWrite) */ - SetupZerosSendBuffer(aud_to_host_usb_ep, sampFreq, g_curSubSlot_In); + SetupZerosSendBuffer(aud_to_host_usb_ep, sampFreq, g_curSubSlot_In, aud_to_host_zeros); +#endif GET_SHARED_GLOBAL(usbSpeed, g_curUsbSpeed); if (usbSpeed == XUD_SPEED_HS) @@ -972,28 +1039,26 @@ void XUA_Buffer_Decouple(chanend c_mix_out /* Reset flag */ SET_SHARED_GLOBAL(g_aud_to_host_flag, 0); + DISABLE_INTERRUPTS(); + if (inUnderflow) { - int aud_to_host_wrptr; - int aud_to_host_rdptr; - int fill_level; - GET_SHARED_GLOBAL(aud_to_host_wrptr, g_aud_to_host_wrptr); - GET_SHARED_GLOBAL(aud_to_host_rdptr, g_aud_to_host_rdptr); + int fillLevel; + GET_SHARED_GLOBAL(fillLevel, g_aud_to_host_fill_level); + assert(fillLevel >= 0); + assert(fillLevel <= BUFF_SIZE_IN); /* Check if we have come out of underflow */ - fill_level = aud_to_host_wrptr - aud_to_host_rdptr; - - if (fill_level < 0) - fill_level += BUFF_SIZE_IN; - - if (fill_level >= IN_BUFFER_PREFILL) + if (fillLevel >= IN_BUFFER_PREFILL) { + int aud_to_host_rdptr; + GET_SHARED_GLOBAL(aud_to_host_rdptr, g_aud_to_host_rdptr); inUnderflow = 0; - SET_SHARED_GLOBAL(g_aud_to_host_buffer, aud_to_host_rdptr); + aud_to_host_buffer = aud_to_host_rdptr; } else { - SET_SHARED_GLOBAL(g_aud_to_host_buffer, g_aud_to_host_zeros); + aud_to_host_buffer = aud_to_host_zeros; } } else @@ -1002,37 +1067,49 @@ void XUA_Buffer_Decouple(chanend c_mix_out int datalength; int aud_to_host_wrptr; int aud_to_host_rdptr; + int fillLevel; GET_SHARED_GLOBAL(aud_to_host_wrptr, g_aud_to_host_wrptr); GET_SHARED_GLOBAL(aud_to_host_rdptr, g_aud_to_host_rdptr); + GET_SHARED_GLOBAL(fillLevel, g_aud_to_host_fill_level); /* Read datalength and round to nearest word */ read_via_xc_ptr(datalength, aud_to_host_rdptr); - aud_to_host_rdptr = aud_to_host_rdptr + ((datalength+3)&~0x3) + 4; + datalength = ((datalength + 3) & ~0x3) + 4; + assert(datalength >= 4); + assert(fillLevel >= datalength); + + aud_to_host_rdptr += datalength; + fillLevel -= datalength; + if (aud_to_host_rdptr >= aud_to_host_fifo_end) { aud_to_host_rdptr = aud_to_host_fifo_start; } SET_SHARED_GLOBAL(g_aud_to_host_rdptr, aud_to_host_rdptr); + SET_SHARED_GLOBAL(g_aud_to_host_fill_level, fillLevel); /* Check for read pointer hitting write pointer - underflow */ - if (aud_to_host_rdptr != aud_to_host_wrptr) + if (fillLevel != 0) { - SET_SHARED_GLOBAL(g_aud_to_host_buffer, aud_to_host_rdptr); + aud_to_host_buffer = aud_to_host_rdptr; } else { + assert(aud_to_host_rdptr == aud_to_host_wrptr); inUnderflow = 1; - SET_SHARED_GLOBAL(g_aud_to_host_buffer, g_aud_to_host_zeros); + aud_to_host_buffer = aud_to_host_zeros; } } /* Request to send packet */ { - int p, len; - GET_SHARED_GLOBAL(p, g_aud_to_host_buffer); - asm volatile("ldw %0, %1[0]":"=r"(len):"r"(p)); - XUD_SetReady_InPtr(aud_to_host_usb_ep, p+4, len); + int len; + asm volatile("ldw %0, %1[0]":"=r"(len):"r"(aud_to_host_buffer)); + XUD_SetReady_InPtr(aud_to_host_usb_ep, aud_to_host_buffer+4, len); } + + ENABLE_INTERRUPTS(); + continue; } } diff --git a/lib_xua/src/core/buffer/decouple/interrupt.h b/lib_xua/src/core/buffer/decouple/interrupt.h index 1aec8cd9..894ebd49 100644 --- a/lib_xua/src/core/buffer/decouple/interrupt.h +++ b/lib_xua/src/core/buffer/decouple/interrupt.h @@ -72,13 +72,8 @@ //int ksp_enter, ksp_exit, r11_store; -#if defined(__XS2A__) || defined(__XS3A__) #define ISSUE_MODE_SINGLE ".issue_mode single\n" #define ISSUE_MODE_DUAL ".issue_mode dual\n" -#else -#define ISSUE_MODE_SINGLE -#define ISSUE_MODE_DUAL -#endif #define do_interrupt_handler(f,args) \ asm(ISSUE_MODE_SINGLE\ diff --git a/lib_xua/src/core/buffer/ep/ep_buffer.xc b/lib_xua/src/core/buffer/ep/ep_buffer.xc index 50f475bd..0fc7a13e 100644 --- a/lib_xua/src/core/buffer/ep/ep_buffer.xc +++ b/lib_xua/src/core/buffer/ep/ep_buffer.xc @@ -1,28 +1,20 @@ -// Copyright 2011-2021 XMOS LIMITED. +// Copyright 2011-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #include "xua.h" #if XUA_USB_EN #include -#include +#include - -#ifdef MIDI -#include "usb_midi.h" -#endif -#ifdef IAP -#include "iap.h" -#ifdef IAP_EA_NATIVE_TRANS -#include "iap2_ea_nativetransport.h" -#endif -#endif #include "xc_ptr.h" #include "xua_commands.h" #include "xud.h" #include "testct_byref.h" #if( 0 < HID_CONTROLS ) +#include "xua_hid_report.h" #include "user_hid.h" -unsigned char g_hidData[HID_DATA_BYTES] = {0}; +#include "xua_hid.h" +unsigned char g_hidData[HID_MAX_DATA_BYTES] = {0U}; #endif void GetADCCounts(unsigned samFreq, int &min, int &mid, int &max); @@ -44,7 +36,7 @@ unsigned g_speed = (AUDIO_CLASS == 2) ? (DEFAULT_FREQ/8000) << 16 : (DEFAULT_FRE unsigned g_freqChange = 0; unsigned feedbackValid = 0; -#if defined (SPDIF_RX) || defined (ADAT_RX) +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) /* When digital Rx enabled we enable an interrupt EP to inform host about changes in clock validity */ /* Interrupt EP report data */ unsigned char g_intData[8] = @@ -89,46 +81,36 @@ unsigned int fb_clocks[4]; #define FB_TOLERANCE 0x100 void XUA_Buffer( - register chanend c_aud_out, + register chanend c_aud_out, #if (NUM_USB_CHAN_IN > 0) - register chanend c_aud_in, + register chanend c_aud_in, #endif #if (NUM_USB_CHAN_IN == 0) || defined (UAC_FORCE_FEEDBACK_EP) - chanend c_aud_fb, + chanend c_aud_fb, #endif #ifdef MIDI - chanend c_midi_from_host, - chanend c_midi_to_host, - chanend c_midi, + chanend c_midi_from_host, + chanend c_midi_to_host, + chanend c_midi, #endif -#ifdef IAP - chanend c_iap_from_host, - chanend c_iap_to_host, -#ifdef IAP_INT_EP - chanend c_iap_to_host_int, +#if (XUA_SPDIF_RX_EN) || (XUA_ADAT_RX_EN) + chanend ?c_ep_int, + chanend ?c_clk_int, #endif - chanend c_iap, -#ifdef IAP_EA_NATIVE_TRANS - chanend c_iap_ea_native_out, - chanend c_iap_ea_native_in, - chanend c_iap_ea_native_ctrl, - chanend c_iap_ea_native_data, + chanend c_sof, + chanend c_aud_ctl, + in port p_off_mclk +#if (HID_CONTROLS ) + , chanend c_hid #endif + , chanend c_aud +#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) + , client interface pll_ref_if i_pll_ref #endif -#if (SPDIF_RX) || (ADAT_RX) - chanend ?c_ep_int, - chanend ?c_clk_int, -#endif - chanend c_sof, - chanend c_aud_ctl, - in port p_off_mclk -#if( 0 < HID_CONTROLS ) - , chanend c_hid -#endif - , chanend c_aud ) { #ifdef CHAN_BUFF_CTRL +#warning Using channel to control buffering - this may reduce performance but improve power consumption chan c_buff_ctrl; #endif @@ -146,21 +128,7 @@ void XUA_Buffer( c_midi_to_host, /* MIDI In */ // 4 c_midi, #endif -#ifdef IAP - c_iap_from_host, /* iAP Out */ - c_iap_to_host, /* iAP In */ -#ifdef IAP_INT_EP - c_iap_to_host_int, /* iAP Interrupt In */ -#endif - c_iap, -#ifdef IAP_EA_NATIVE_TRANS - c_iap_ea_native_out, - c_iap_ea_native_in, - c_EANativeTransport_ctrl, - c_ea_data, -#endif -#endif -#if (SPDIF_RX) || (ADAT_RX) +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) /* Audio Interrupt - only used for interrupts on external clock change */ c_ep_int, c_clk_int, @@ -171,6 +139,9 @@ void XUA_Buffer( #endif #ifdef CHAN_BUFF_CTRL , c_buff_ctrl +#endif +#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) + , i_pll_ref #endif ); @@ -194,46 +165,35 @@ unsafe{volatile unsigned * unsafe masterClockFreq_ptr;} * @param c_aud_fb chanend for feeback to xud * @return void */ -void XUA_Buffer_Ep(register chanend c_aud_out, +void XUA_Buffer_Ep(register chanend c_aud_out, #if (NUM_USB_CHAN_IN > 0) - register chanend c_aud_in, + register chanend c_aud_in, #endif #if (NUM_USB_CHAN_IN == 0) || defined (UAC_FORCE_FEEDBACK_EP) chanend c_aud_fb, #endif #ifdef MIDI - chanend c_midi_from_host, - chanend c_midi_to_host, - chanend c_midi, + chanend c_midi_from_host, + chanend c_midi_to_host, + chanend c_midi, #endif -#ifdef IAP - chanend c_iap_from_host, - chanend c_iap_to_host, -#ifdef IAP_INT_EP - chanend c_iap_to_host_int, +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + chanend ?c_ep_int, + chanend ?c_clk_int, #endif - chanend c_iap, -#ifdef IAP_EA_NATIVE_TRANS - chanend c_iap_ea_native_out, - chanend c_iap_ea_native_in, - chanend c_iap_ea_native_ctrl, - chanend c_iap_ea_native_data, -#endif -#endif -#if (SPDIF_RX) || (ADAT_RX) - chanend ?c_ep_int, - chanend ?c_clk_int, -#endif - chanend c_sof, - chanend c_aud_ctl, - in port p_off_mclk -#if( 0 < HID_CONTROLS ) - , chanend c_hid + chanend c_sof, + chanend c_aud_ctl, + in port p_off_mclk +#if(HID_CONTROLS) + , chanend c_hid #endif #ifdef CHAN_BUFF_CTRL - , chanend c_buff_ctrl + , chanend c_buff_ctrl #endif - ) +#if XUA_SYNCMODE == XUA_SYNCMODE_SYNC + , client interface pll_ref_if i_pll_ref +#endif + ) { XUD_ep ep_aud_out = XUD_InitEp(c_aud_out); @@ -260,7 +220,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out, XUD_ep ep_iap_ea_native_in = XUD_InitEp(c_iap_ea_native_in); #endif #endif -#if (SPDIF_RX) || (ADAT_RX) +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) XUD_ep ep_int = XUD_InitEp(c_ep_int); #endif @@ -270,8 +230,11 @@ void XUA_Buffer_Ep(register chanend c_aud_out, unsigned u_tmp; unsigned sampleFreq = DEFAULT_FREQ; unsigned masterClockFreq = DEFAULT_MCLK_FREQ; - unsigned lastClock = 0; +#if (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC) + unsigned lastClock = 0; + unsigned freqChange = 0; +#endif unsafe{masterClockFreq_ptr = &masterClockFreq;} unsigned clocks = 0; @@ -285,7 +248,6 @@ void XUA_Buffer_Ep(register chanend c_aud_out, unsigned bufferIn = 1; #endif unsigned sofCount = 0; - unsigned freqChange = 0; unsigned mod_from_last_time = 0; #ifdef FB_TOLERANCE_TEST @@ -371,7 +333,14 @@ void XUA_Buffer_Ep(register chanend c_aud_out, #endif #if( 0 < HID_CONTROLS ) - XUD_SetReady_In(ep_hid, g_hidData, 1); + + while (!hidIsReportDescriptorPrepared()) + ; + + UserHIDInit(); + unsigned hid_ready_flag = 0U; + unsigned hid_ready_id = 0U; + #endif #if (AUDIO_CLASS == 1) @@ -381,6 +350,21 @@ void XUA_Buffer_Ep(register chanend c_aud_out, #endif #endif +#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) +#ifndef LOCAL_CLOCK_INCREMENT +#define LOCAL_CLOCK_INCREMENT (100000) /* 500Hz */ +#endif +#ifndef LOCAL_CLOCK_MARGIN +#define LOCAL_CLOCK_MARGIN (1000) +#endif + timer t_sofCheck; + unsigned timeLastEdge; + unsigned timeNextEdge; + t_sofCheck :> timeLastEdge; + timeNextEdge + LOCAL_CLOCK_INCREMENT; + i_pll_ref.toggle(); +#endif + while(1) { XUD_Result_t result; @@ -389,7 +373,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out, /* Wait for response from XUD and service relevant EP */ select { -#if (SPDIF_RX) || (ADAT_RX) +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) /* Clocking thread wants to produce an interrupt... */ case inuint_byref(c_clk_int, u_tmp): chkct(c_clk_int, XS1_CT_END); @@ -437,9 +421,9 @@ void XUA_Buffer_Ep(register chanend c_aud_out, if(receivedSampleFreq != AUDIO_STOP_FOR_DFU) { sampleFreq = receivedSampleFreq; - #ifdef FB_TOLERANCE_TEST +#ifdef FB_TOLERANCE_TEST expected_fb = ((sampleFreq * 0x2000) / frameTime); - #endif +#endif /* Reset FB */ /* Note, Endpoint 0 will hold off host for a sufficient period to allow our feedback * to stabilise (i.e. sofCount == 128 to fire) */ @@ -524,6 +508,13 @@ void XUA_Buffer_Ep(register chanend c_aud_out, } break; } +#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) + case t_sofCheck when timerafter(timeNextEdge) :> void: + i_pll_ref.toggle(); + timeLastEdge = timeNextEdge; + timeNextEdge += LOCAL_CLOCK_INCREMENT; + break; +#endif #define MASK_16_13 (7) /* Bits that should not be transmitted as part of feedback */ #define MASK_16_10 (127) /* For Audio 1.0 we use a mask 1 bit longer than expected to avoid Windows LSB issues */ @@ -531,6 +522,30 @@ void XUA_Buffer_Ep(register chanend c_aud_out, /* SOF notification from XUD_Manager() */ case inuint_byref(c_sof, u_tmp): +#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) + /* This really could (should) be done in decouple. However, for a quick demo this is okay + * Decouple expects a 16:16 number in fixed point stored in the global g_speed */ + unsigned usbSpeed; + int framesPerSec; + GET_SHARED_GLOBAL(usbSpeed, g_curUsbSpeed); + static int sofCount = 0; + + framesPerSec = (usbSpeed == XUD_SPEED_HS) ? 8000 : 1000; + + clocks = ((int64_t) sampleFreq << 16) / framesPerSec; + + asm volatile("stw %0, dp[g_speed]"::"r"(clocks)); + + sofCount += 1000; + if (sofCount == framesPerSec) + { + /* Port is accessed via interface to allow flexibilty with location */ + i_pll_ref.toggle(); + t_sofCheck :> timeLastEdge; + sofCount = 0; + timeNextEdge = timeLastEdge + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN; + } +#elif (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC) /* NOTE our feedback will be wrong for a couple of SOF's after a SF change due to * lastClock being incorrect */ @@ -555,7 +570,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out, { unsigned usb_speed; GET_SHARED_GLOBAL(usb_speed, g_curUsbSpeed); - + #if FB_USE_REF_CLOCK unsigned long long feedbackMul = 64ULL; @@ -631,7 +646,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out, clockcounter = 0; } #else - + /* Assuming 48kHz from a 24.576 master clock (0.0407uS period) * MCLK ticks per SOF = 125uS / 0.0407 = 3072 MCLK ticks per SOF. * expected Feedback is 48000/8000 = 6 samples. so 0x60000 in 16:16 format. @@ -700,6 +715,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out, #endif sofCount++; } +#endif break; #if (NUM_USB_CHAN_IN > 0) @@ -708,12 +724,12 @@ void XUA_Buffer_Ep(register chanend c_aud_out, { /* Inform stream that buffer sent */ SET_SHARED_GLOBAL0(g_aud_to_host_flag, bufferIn+1); + break; } - break; #endif #if (NUM_USB_CHAN_OUT > 0) -#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) +#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) && (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC) /* Feedback Pipe */ case XUD_SetData_Select(c_aud_fb, ep_aud_fb, result): { @@ -729,8 +745,8 @@ void XUA_Buffer_Ep(register chanend c_aud_out, { XUD_SetReady_In(ep_aud_fb, (fb_clocks, unsigned char[]), 3); } + break; } - break; #endif /* Received Audio packet HOST -> DEVICE. Datalength written to length */ case XUD_GetData_Select(c_aud_out, ep_aud_out, length, result): @@ -741,164 +757,166 @@ void XUA_Buffer_Ep(register chanend c_aud_out, /* Sync with decouple thread */ SET_SHARED_GLOBAL0(g_aud_from_host_flag, 1); - } break; + } #endif #ifdef MIDI - case XUD_GetData_Select(c_midi_from_host, ep_midi_from_host, length, result): + case XUD_GetData_Select(c_midi_from_host, ep_midi_from_host, length, result): - if((result == XUD_RES_OKAY) && (length > 0)) - { - /* Get buffer data from host - MIDI OUT from host always into a single buffer */ - midi_data_remaining_to_device = length; - - midi_from_host_rdptr = midi_from_host_buffer; - - if (midi_data_remaining_to_device) + if((result == XUD_RES_OKAY) && (length > 0)) { - read_via_xc_ptr(datum, midi_from_host_rdptr); - outuint(c_midi, datum); - midi_from_host_rdptr += 4; - midi_data_remaining_to_device -= 4; + /* Get buffer data from host - MIDI OUT from host always into a single buffer */ + midi_data_remaining_to_device = length; + + midi_from_host_rdptr = midi_from_host_buffer; + + if (midi_data_remaining_to_device) + { + read_via_xc_ptr(datum, midi_from_host_rdptr); + outuint(c_midi, datum); + midi_from_host_rdptr += 4; + midi_data_remaining_to_device -= 4; + } } - } - break; + break; - /* MIDI IN to host */ - case XUD_SetData_Select(c_midi_to_host, ep_midi_to_host, result): + /* MIDI IN to host */ + case XUD_SetData_Select(c_midi_to_host, ep_midi_to_host, result): - /* The buffer has been sent to the host, so we can ack the midi thread */ - if (midi_data_collected_from_device != 0) - { - /* Swap the collecting and sending buffer */ - swap(midi_to_host_buffer_being_collected, midi_to_host_buffer_being_sent); + /* The buffer has been sent to the host, so we can ack the midi thread */ + if (midi_data_collected_from_device != 0) + { + /* Swap the collecting and sending buffer */ + swap(midi_to_host_buffer_being_collected, midi_to_host_buffer_being_sent); - /* Request to send packet */ - XUD_SetReady_InPtr(ep_midi_to_host, midi_to_host_buffer_being_sent, midi_data_collected_from_device); + /* Request to send packet */ + XUD_SetReady_InPtr(ep_midi_to_host, midi_to_host_buffer_being_sent, midi_data_collected_from_device); - /* Mark as waiting for host to poll us */ - midi_waiting_on_send_to_host = 1; - /* Reset the collected data count */ - midi_data_collected_from_device = 0; - } - else - { - midi_waiting_on_send_to_host = 0; - } - break; + /* Mark as waiting for host to poll us */ + midi_waiting_on_send_to_host = 1; + /* Reset the collected data count */ + midi_data_collected_from_device = 0; + } + else + { + midi_waiting_on_send_to_host = 0; + } + break; #endif #ifdef IAP - /* IAP OUT from host. Datalength writen to tmp */ - case XUD_GetData_Select(c_iap_from_host, ep_iap_from_host, length, result): + /* IAP OUT from host. Datalength writen to tmp */ + case XUD_GetData_Select(c_iap_from_host, ep_iap_from_host, length, result): - if((result == XUD_RES_OKAY) && (length > 0)) - { - iap_data_remaining_to_device = length; - - if(iap_data_remaining_to_device) + if((result == XUD_RES_OKAY) && (length > 0)) { - // Send length first so iAP thread knows how much data to expect - // Don't expect ack from this to make it simpler - outuint(c_iap, iap_data_remaining_to_device); + iap_data_remaining_to_device = length; - /* Send out first byte in buffer */ - datum_iap = iap_from_host_buffer[0]; - outuint(c_iap, datum_iap); + if(iap_data_remaining_to_device) + { + // Send length first so iAP thread knows how much data to expect + // Don't expect ack from this to make it simpler + outuint(c_iap, iap_data_remaining_to_device); - /* Set read ptr to next byte in buffer */ - iap_from_host_rdptr = 1; - iap_data_remaining_to_device -= 1; + /* Send out first byte in buffer */ + datum_iap = iap_from_host_buffer[0]; + outuint(c_iap, datum_iap); + + /* Set read ptr to next byte in buffer */ + iap_from_host_rdptr = 1; + iap_data_remaining_to_device -= 1; + } } - } - break; + break; - /* IAP IN to host */ - case XUD_SetData_Select(c_iap_to_host, ep_iap_to_host, result): + /* IAP IN to host */ + case XUD_SetData_Select(c_iap_to_host, ep_iap_to_host, result): - if(result == XUD_RES_RST) - { - XUD_ResetEndpoint(ep_iap_to_host, null); + if(result == XUD_RES_RST) + { + XUD_ResetEndpoint(ep_iap_to_host, null); #ifdef IAP_INT_EP - XUD_ResetEndpoint(ep_iap_to_host_int, null); + XUD_ResetEndpoint(ep_iap_to_host_int, null); #endif - iap_send_reset(c_iap); - iap_draining_chan = 1; // Drain c_iap until a reset is sent back - iap_data_collected_from_device = 0; - iap_data_remaining_to_device = -1; - iap_expected_data_length = 0; - iap_from_host_rdptr = 0; - } - else - { - /* Send out an iAP packet to host, ACK last msg from iAP to let it know we can move on..*/ - iap_send_ack(c_iap); - } - break; /* IAP IN to host */ + iap_send_reset(c_iap); + iap_draining_chan = 1; // Drain c_iap until a reset is sent back + iap_data_collected_from_device = 0; + iap_data_remaining_to_device = -1; + iap_expected_data_length = 0; + iap_from_host_rdptr = 0; + } + else + { + /* Send out an iAP packet to host, ACK last msg from iAP to let it know we can move on..*/ + iap_send_ack(c_iap); + } + break; /* IAP IN to host */ #ifdef IAP_INT_EP - case XUD_SetData_Select(c_iap_to_host_int, ep_iap_to_host_int, result): + case XUD_SetData_Select(c_iap_to_host_int, ep_iap_to_host_int, result): - /* Do nothing.. */ - /* Note, could get a reset notification here, but deal with it in the case above */ - break; + /* Do nothing.. */ + /* Note, could get a reset notification here, but deal with it in the case above */ + break; #endif #ifdef IAP_EA_NATIVE_TRANS - /* iAP EA Native Transport OUT from host */ - case XUD_GetData_Select(c_iap_ea_native_out, ep_iap_ea_native_out, iap_ea_native_rx_length, result): - if ((result == XUD_RES_OKAY) && iap_ea_native_rx_length > 0) - { - // Notify EA Protocol user code we have iOS app data from XUD - iAP2_EANativeTransport_writeToChan_start(c_iap_ea_native_data, EA_NATIVE_SEND_DATA); - } - break; + /* iAP EA Native Transport OUT from host */ + case XUD_GetData_Select(c_iap_ea_native_out, ep_iap_ea_native_out, iap_ea_native_rx_length, result): + if ((result == XUD_RES_OKAY) && iap_ea_native_rx_length > 0) + { + // Notify EA Protocol user code we have iOS app data from XUD + iAP2_EANativeTransport_writeToChan_start(c_iap_ea_native_data, EA_NATIVE_SEND_DATA); + } + break; - /* iAP EA Native Transport IN to host */ - case XUD_SetData_Select(c_iap_ea_native_in, ep_iap_ea_native_in, result): - switch (result) - { - case XUD_RES_RST: - XUD_ResetEndpoint(ep_iap_ea_native_in, null); - // Notify user code of USB reset to allow any state to be cleared - iAP2_EANativeTransport_writeToChan_start(c_iap_ea_native_data, EA_NATIVE_SEND_CONTROL); - // Set up the control flag to send to EA Protocol user code when it responds - iap_ea_native_control_flag = EA_NATIVE_RESET; - iap_ea_native_control_to_send = 1; - break; + /* iAP EA Native Transport IN to host */ + case XUD_SetData_Select(c_iap_ea_native_in, ep_iap_ea_native_in, result): + switch (result) + { + case XUD_RES_RST: + XUD_ResetEndpoint(ep_iap_ea_native_in, null); + // Notify user code of USB reset to allow any state to be cleared + iAP2_EANativeTransport_writeToChan_start(c_iap_ea_native_data, EA_NATIVE_SEND_CONTROL); + // Set up the control flag to send to EA Protocol user code when it responds + iap_ea_native_control_flag = EA_NATIVE_RESET; + iap_ea_native_control_to_send = 1; + break; - case XUD_RES_OKAY: // EA Protocol user data successfully passed to XUD - // Notify user code - iAP2_EANativeTransport_writeToChan_start(c_iap_ea_native_data, EA_NATIVE_SEND_CONTROL); - // Set up the control flag to send to EA Protocol user code when it responds - iap_ea_native_control_flag = EA_NATIVE_DATA_SENT; - iap_ea_native_control_to_send = 1; - break; - } - break; - //:: + case XUD_RES_OKAY: // EA Protocol user data successfully passed to XUD + // Notify user code + iAP2_EANativeTransport_writeToChan_start(c_iap_ea_native_data, EA_NATIVE_SEND_CONTROL); + // Set up the control flag to send to EA Protocol user code when it responds + iap_ea_native_control_flag = EA_NATIVE_DATA_SENT; + iap_ea_native_control_to_send = 1; + break; + } + break; + //:: #endif #endif #if( 0 < HID_CONTROLS ) - /* HID Report Data */ + /* HID Report Data */ case XUD_SetData_Select(c_hid, ep_hid, result): - { - g_hidData[0]=0; - UserHIDGetData(g_hidData); - XUD_SetReady_In(ep_hid, g_hidData, 1); - } - break; + hid_ready_flag = 0U; + unsigned reportTime; + timer tmr; + tmr :> reportTime; + hidCaptureReportTime(hid_ready_id, reportTime); + hidCalcNextReportTime(hid_ready_id); + hidClearChangePending(hid_ready_id); + break; #endif #ifdef MIDI - /* Received word from MIDI thread - Check for ACK or Data */ + /* Received word from MIDI thread - Check for ACK or Data */ case midi_get_ack_or_data(c_midi, is_ack, datum): if (is_ack) { /* An ack from the midi/uart thread means it has accepted some data we sent it - * we are okay to send another word */ + * we are okay to send another word */ if (midi_data_remaining_to_device <= 0) { /* We have read an entire packet - Mark ready to receive another */ @@ -963,7 +981,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out, if (is_ack_iap) { /* An ack from the iap/uart thread means it has accepted some data we sent it - * we are okay to send another word */ + * we are okay to send another word */ if (iap_data_remaining_to_device == 0) { /* We have read an entire packet - Mark ready to receive another */ @@ -996,7 +1014,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out, } else { - // Too many events from device - drop + // Too many events from device - drop } /* Once we have the whole message, sent it to host */ @@ -1034,7 +1052,7 @@ void XUA_Buffer_Ep(register chanend c_aud_out, } break; -# if IAP_EA_NATIVE_TRANS + # if IAP_EA_NATIVE_TRANS /* Change of EA Native Transport interface setting */ case inuint_byref(c_iap_ea_native_ctrl, iap_ea_native_interface_alt_setting): /* Handshake */ @@ -1092,14 +1110,33 @@ void XUA_Buffer_Ep(register chanend c_aud_out, break; } break; - //:: -#endif +#endif // if IAP_EA_NATIVE_TRANS +#endif // ifdef IAP + + default: +#if ( 0 < HID_CONTROLS ) + if (!hid_ready_flag) + { + for (unsigned id = hidIsReportIdInUse(); id < hidGetReportIdLimit(); id++) + { + if ( hidIsChangePending(id) || !HidIsSetIdleSilenced(id) ) + { + int hidDataLength = (int) UserHIDGetData(id, g_hidData); + XUD_SetReady_In(ep_hid, g_hidData, hidDataLength); + + hid_ready_id = id; + hid_ready_flag = 1U; + break; + } + } + } #endif + break; + //:: } - } } #endif /* XUA_USB_EN */ diff --git a/lib_xua/src/core/clocking/clockgen.xc b/lib_xua/src/core/clocking/clockgen.xc index eb294057..ec5f1c77 100644 --- a/lib_xua/src/core/clocking/clockgen.xc +++ b/lib_xua/src/core/clocking/clockgen.xc @@ -1,4 +1,4 @@ -// Copyright 2011-2021 XMOS LIMITED. +// Copyright 2011-2023 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #include @@ -7,19 +7,20 @@ #include "xua.h" #include "xua_commands.h" +#include "xua_clocking.h" -#if (SPDIF_RX) +#if (XUA_SPDIF_RX_EN) #include "spdif.h" #endif -#define LOCAL_CLOCK_INCREMENT 166667 -#define LOCAL_CLOCK_MARGIN 1666 +#define LOCAL_CLOCK_INCREMENT (166667) +#define LOCAL_CLOCK_MARGIN (1666) -#define MAX_SAMPLES 64 /* Must be power of 2 */ +#define MAX_SAMPLES (64) /* Must be power of 2 */ #define MAX_SPDIF_SAMPLES (2 * MAX_SAMPLES) /* Must be power of 2 */ #define MAX_ADAT_SAMPLES (8 * MAX_SAMPLES) /* Must be power of 2 */ -#define SPDIF_FRAME_ERRORS_THRESH 40 +#define SPDIF_FRAME_ERRORS_THRESH (40) unsigned g_digData[10]; @@ -38,8 +39,49 @@ static int clockValid[NUM_CLOCKS]; /* Store current val static int clockInt[NUM_CLOCKS]; /* Interupt flag for clocks */ static int clockId[NUM_CLOCKS]; +[[distributable]] +void PllRefPinTask(server interface pll_ref_if i_pll_ref, out port p_pll_ref) +{ + static unsigned pinVal= 0; + static unsigned short pinTime = 0; -#if (SPDIF_RX) || (ADAT_RX) + while(1) + { + select + { + case i_pll_ref.toggle(): + pinVal = ~pinVal; + p_pll_ref <: pinVal; + break; + + case i_pll_ref.init(): + p_pll_ref <: pinVal @ pinTime; + pinTime += (unsigned short)(LOCAL_CLOCK_INCREMENT - (LOCAL_CLOCK_INCREMENT/2)); + p_pll_ref @ pinTime <: pinVal; + break; + + case i_pll_ref.toggle_timed(int relative): + + if (!relative) + { + pinTime += (short) LOCAL_CLOCK_INCREMENT; + pinVal = !pinVal; + p_pll_ref @ pinTime <: pinVal; + } + else + { + p_pll_ref <: pinVal @ pinTime; + pinTime += (short) LOCAL_CLOCK_INCREMENT; + pinVal = !pinVal; + p_pll_ref @ pinTime <: pinVal; + } + break; + } + } +} + + +#if (XUA_SPDIF_RX_EN) || (XUA_ADAT_RX_EN) static int abs(int x) { if (x < 0) return -x; @@ -74,7 +116,7 @@ static void outInterrupt(chanend c_interruptControl, int value) void VendorClockValidity(int valid); #endif -#if (SPDIF_RX) || (ADAT_RX) +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) static inline void setClockValidity(chanend c_interruptControl, int clkIndex, int valid, int currentClkMode) { if (clockValid[clkIndex] != valid) @@ -83,13 +125,13 @@ static inline void setClockValidity(chanend c_interruptControl, int clkIndex, in outInterrupt(c_interruptControl, clockId[clkIndex]); #ifdef CLOCK_VALIDITY_CALL -#if ADAT_RX +#if (XUA_ADAT_RX_EN) if (currentClkMode == CLOCK_ADAT && clkIndex == CLOCK_ADAT_INDEX) { VendorClockValidity(valid); } #endif -#if SPDIF_RX +#if (XUA_SPDIF_RX_EN) if (currentClkMode == CLOCK_SPDIF && clkIndex == CLOCK_SPDIF_INDEX) { VendorClockValidity(valid); @@ -177,7 +219,7 @@ static inline int validSamples(Counter &counter, int clockIndex) } #endif -#ifdef SPDIF_RX +#if (XUA_SPDIF_RX_EN) //:badParity /* Returns 1 for bad parity, else 0 */ static inline int badParity(unsigned x) @@ -199,12 +241,10 @@ extern int samples_to_host_inputs_buff[NUM_USB_CHAN_IN]; int VendorAudCoreReqs(unsigned cmd, chanend c); #pragma unsafe arrays -void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, out port p, chanend c_dig_rx, chanend c_clk_ctl, chanend c_clk_int) +void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, client interface pll_ref_if i_pll_ref, chanend c_dig_rx, chanend c_clk_ctl, chanend c_clk_int) { timer t_local; unsigned timeNextEdge, timeLastEdge, timeNextClockDetection; - unsigned pinVal = 0; - unsigned short pinTime; unsigned clkMode = CLOCK_INTERNAL; /* Current clocking mode in operation */ unsigned tmp; @@ -216,11 +256,11 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, out port p, ch unsigned levelTime; #endif -#if (SPDIF_RX) || (ADAT_RX) +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) timer t_external; #endif -#if SPDIF_RX +#if (XUA_SPDIF_RX_EN) /* S/PDIF buffer state */ int spdifSamples[MAX_SPDIF_SAMPLES]; /* S/PDIF sample buffer */ int spdifWr = 0; /* Write index */ @@ -234,7 +274,7 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, out port p, ch unsigned spdifLeft = 0; #endif -#if ADAT_RX +#if (XUA_ADAT_RX_EN) /* ADAT buffer state */ int adatSamples[MAX_ADAT_SAMPLES]; int adatWr = 0; @@ -256,7 +296,7 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, out port p, ch } /* Init clock unit state */ -#if SPDIF_RX +#if (XUA_SPDIF_RX_EN) clockFreq[CLOCK_SPDIF_INDEX] = 0; clockValid[CLOCK_SPDIF_INDEX] = 0; clockInt[CLOCK_SPDIF_INDEX] = 0; @@ -266,13 +306,13 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, out port p, ch clockId[CLOCK_INTERNAL_INDEX] = ID_CLKSRC_INT; clockValid[CLOCK_INTERNAL_INDEX] = 0; clockInt[CLOCK_INTERNAL_INDEX] = 0; -#if ADAT_RX +#if (XUA_ADAT_RX_EN) clockFreq[CLOCK_ADAT_INDEX] = 0; clockInt[CLOCK_ADAT_INDEX] = 0; clockValid[CLOCK_ADAT_INDEX] = 0; clockId[CLOCK_ADAT_INDEX] = ID_CLKSRC_ADAT; #endif -#if SPDIF_RX +#if (XUA_SPDIF_RX_EN) spdifCounters.receivedSamples = 0; spdifCounters.samples = 0; spdifCounters.savedSamples = 0; @@ -281,7 +321,7 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, out port p, ch spdifCounters.samplesPerTick = 0; #endif -#if ADAT_RX +#if (XUA_ADAT_RX_EN) adatCounters.receivedSamples = 0; adatCounters.samples = 0; adatCounters.savedSamples = 0; @@ -301,15 +341,13 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, out port p, ch levelTime+= LEVEL_UPDATE_RATE; #endif -#if (SPDIF_RX) || (ADAT_RX) +#if (XUA_SPDIF_RX_EN) || (XUA_ADAT_RX_EN) /* Fill channel */ outuint(c_dig_rx, 1); #endif /* Initial ref clock output and get timestamp */ - p <: pinVal @ pinTime; - pinTime += (unsigned short)(LOCAL_CLOCK_INCREMENT - (LOCAL_CLOCK_INCREMENT/2)); - p @ pinTime <: pinVal; + i_pll_ref.init(); while(1) { @@ -380,12 +418,12 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, out port p, ch case CLOCK_INTERNAL: VendorClockValidity(1); break; -#if ADAT_RX +#if (XUA_ADAT_RX_EN) case CLOCK_ADAT: VendorClockValidity(clockValid[CLOCK_ADAT_INDEX]); break; #endif -#if SPDIF_RX +#if (XUA_SPDIF_RX_EN) case CLOCK_SPDIF: VendorClockValidity(clockValid[CLOCK_SPDIF_INDEX]); break; @@ -411,7 +449,7 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, out port p, ch case SET_SMUX: smux = inuint(c_clk_ctl); -#if ADAT_RX +#if (XUA_ADAT_RX_EN) adatRd = 0; /* Reset adat FIFO */ adatWr = 0; adatSamps = 0; @@ -432,11 +470,8 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, out port p, ch /* Generate local clock from timer */ case t_local when timerafter(timeNextEdge) :> void: - /* Setup next local clock edge */ - pinTime += (short) LOCAL_CLOCK_INCREMENT; - pinVal = !pinVal; - p @ pinTime <: pinVal; + i_pll_ref.toggle_timed(0); /* Record time of edge */ timeLastEdge = timeNextEdge; @@ -445,20 +480,14 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, out port p, ch timeNextClockDetection = timeNextEdge + (LOCAL_CLOCK_INCREMENT/2); timeNextEdge += LOCAL_CLOCK_INCREMENT; - /* If we are in an external clock mode and this fire, then clock invalid */ - -#if SPDIF_RX - // if(clkMode == CLOCK_SPDIF) - { - /* We must have lost valid S/PDIF stream, reset counters, so we dont produce a double edge */ - spdifCounters.receivedSamples = 0; - } + /* If we are in an external clock mode and this fire, then clock invalid + * reset counters in case we are moved to digital clock - we want a well timed + * first edge */ +#if (XUA_SPDIF_RX_EN) + spdifCounters.receivedSamples = 0; #endif -#if ADAT_RX - //if(clkMode == CLOCK_ADAT) - { - adatCounters.receivedSamples = 0; - } +#if (XUA_ADAT_RX_EN) + adatCounters.receivedSamples = 0; #endif #ifdef CLOCK_VALIDITY_CALL @@ -471,18 +500,18 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, out port p, ch break; -#if (SPDIF_RX) || (ADAT_RX) +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) case t_external when timerafter(timeNextClockDetection) :> void: timeNextClockDetection += (LOCAL_CLOCK_INCREMENT); -#if SPDIF_RX +#if (XUA_SPDIF_RX_EN) tmp = spdifCounters.samplesPerTick; /* Returns 1 if valid clock found */ tmp = validSamples(spdifCounters, CLOCK_SPDIF_INDEX); setClockValidity(c_clk_int, CLOCK_SPDIF_INDEX, tmp, clkMode); #endif -#if ADAT_RX +#if (XUA_ADAT_RX_EN) tmp = validSamples(adatCounters, CLOCK_ADAT_INDEX); setClockValidity(c_clk_int, CLOCK_ADAT_INDEX, tmp, clkMode); #endif @@ -491,7 +520,7 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, out port p, ch #endif -#if SPDIF_RX +#if (XUA_SPDIF_RX_EN) /* Receive sample from S/PDIF RX thread (steaming chan) */ case c_spdif_rx :> tmp: @@ -566,10 +595,7 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, out port p, ch timeNextEdge = spdifReceivedTime + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN; /* Toggle edge */ - p <: pinVal @ pinTime; - pinTime += (short) LOCAL_CLOCK_INCREMENT; - pinVal = !pinVal; - p @ pinTime <: pinVal; + i_pll_ref.toggle_timed(1); /* Reset counters */ spdifCounters.receivedSamples = 0; @@ -578,7 +604,7 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, out port p, ch } break; #endif -#if ADAT_RX +#if (XUA_ADAT_RX_EN) /* receive sample from ADAT rx thread (streaming channel with CT_END) */ case inuint_byref(c_adat_rx, tmp): /* record time of sample */ @@ -665,7 +691,7 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, out port p, ch /* Inspect for if we need to produce an edge */ if ((adatCounters.receivedSamples >= adatCounters.samplesPerTick)) { - /* Check edge is about right... S/PDIF may have changed freq... */ + /* Check edge is about right... ADAT may have changed freq... */ if (timeafter(adatReceivedTime, (timeLastEdge + LOCAL_CLOCK_INCREMENT - LOCAL_CLOCK_MARGIN))) { /* Record edge time */ @@ -675,10 +701,7 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, out port p, ch timeNextEdge = adatReceivedTime + LOCAL_CLOCK_INCREMENT + LOCAL_CLOCK_MARGIN; /* Toggle edge */ - p <: pinVal @ pinTime; - pinTime += LOCAL_CLOCK_INCREMENT; - pinVal = !pinVal; - p @ pinTime <: pinVal; + i_pll_ref.toggle_timed(1); /* Reset counters */ adatCounters.receivedSamples = 0; @@ -694,10 +717,10 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, out port p, ch #endif -#if (SPDIF_RX) || (ADAT_RX) - /* Mixer requests data */ +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + /* AudioHub requests data */ case inuint_byref(c_dig_rx, tmp): -#if SPDIF_RX +#if (XUA_SPDIF_RX_EN) if(spdifUnderflow) { /* S/PDIF underflowing, send out zero samples */ @@ -735,7 +758,7 @@ void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, out port p, ch } #endif -#if ADAT_RX +#if (XUA_ADAT_RX_EN) if (adatUnderflow) { /* ADAT underflowing, send out zero samples */ diff --git a/lib_xua/src/core/endpoint0/chanstringgen.py b/lib_xua/src/core/endpoint0/chanstringgen.py index de74c9d0..8ce17575 100644 --- a/lib_xua/src/core/endpoint0/chanstringgen.py +++ b/lib_xua/src/core/endpoint0/chanstringgen.py @@ -1,4 +1,4 @@ -# Copyright 2015-2021 XMOS LIMITED. +# Copyright 2015-2022 XMOS LIMITED. # This Software is subject to the terms of the XMOS Public Licence: Version 1. @@ -6,7 +6,7 @@ def genstrings(outputChanCount, chanString, portString, structureString, adc_dac for i in range(1,outputChanCount): - print "#if (NUM_USB_CHAN_{c} > {i}-1)\n\ + print("#if (NUM_USB_CHAN_{c} > {i}-1)\n\ .{s}ChanStr_{i} = \"\"\n\ #if ({i} < I2S_CHANS_{adcdac}+1)\n\ \"Analogue {i}\"\n\ @@ -21,38 +21,38 @@ def genstrings(outputChanCount, chanString, portString, structureString, adc_dac \"SPDIF 2\"\n\ #endif\n\ #endif\n\ - #if (({i} < ADAT_{p}_INDEX+8+1) && ({i} > ADAT_{p}_INDEX)) && defined(ADAT_{p})\n\ + #if (({i} < ADAT_{p}_INDEX+8+1) && ({i} > ADAT_{p}_INDEX)) && (XUA_ADAT_{p}_EN)\n\ #if (({i} < SPDIF_{p}_INDEX+2+1) && ({i} > SPDIF_{p}_INDEX)) && (XUA_SPDIF_{p}_EN) || ({i} < I2S_CHANS_{adcdac}+1)\n\ \"/\"\n\ #endif\n\ - #if({i} - ADAT_TX_INDEX == 1)\n\ + #if({i} - ADAT_{p}_INDEX == 1)\n\ \"ADAT 1\"\n\ - #elif({i} - ADAT_TX_INDEX == 2)\n\ + #elif({i} - ADAT_{p}_INDEX == 2)\n\ \"ADAT 2\"\n\ - #elif({i} - ADAT_TX_INDEX == 3)\n\ + #elif({i} - ADAT_{p}_INDEX == 3)\n\ \"ADAT 3\"\n\ - #elif({i} - ADAT_TX_INDEX == 4)\n\ + #elif({i} - ADAT_{p}_INDEX == 4)\n\ \"ADAT 4\"\n\ - #elif({i} - ADAT_TX_INDEX == 5)\n\ + #elif({i} - ADAT_{p}_INDEX == 5)\n\ \"ADAT 5\"\n\ - #elif({i} - ADAT_TX_INDEX == 6)\n\ + #elif({i} - ADAT_{p}_INDEX == 6)\n\ \"ADAT 6\"\n\ - #elif({i} - ADAT_TX_INDEX == 7)\n\ + #elif({i} - ADAT_{p}_INDEX == 7)\n\ \"ADAT 7\"\n\ - #elif({i} - ADAT_TX_INDEX == 8)\n\ + #elif({i} - ADAT_{p}_INDEX == 8)\n\ \"ADAT 8\"\n\ #endif\n\ #endif\n\ - ,\n#endif\n".format(i=i, c=chanString, p=portString, s=structureString, adcdac=adc_dac); + ,\n#endif\n".format(i=i, c=chanString, p=portString, s=structureString, adcdac=adc_dac)); return; -print "/* AUTOGENERATED using chanstringgen.py */\n" -print "/* Not very nice looking but the standard preprocessor is not very powerful\n and we save some memory over doing this all at runtime */" +print("/* AUTOGENERATED using chanstringgen.py */\n") +print("/* Not very nice looking but the standard preprocessor is not very powerful\n and we save some memory over doing this all at runtime */") -print "/* Output Strings */\n\n" +print("/* Output Strings */\n\n") genstrings(33, "OUT", "TX", "output", "DAC"); -print "/* Input Strings */\n\n" +print("/* Input Strings */\n\n") genstrings(33, "IN", "RX", "input", "ADC"); diff --git a/lib_xua/src/core/endpoint0/chanstrings.h b/lib_xua/src/core/endpoint0/chanstrings.h index 68be3481..71bc276c 100644 --- a/lib_xua/src/core/endpoint0/chanstrings.h +++ b/lib_xua/src/core/endpoint0/chanstrings.h @@ -1,4 +1,4 @@ -// Copyright 2015-2021 XMOS LIMITED. +// Copyright 2015-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. /* AUTOGENERATED using chanstringgen.py */ @@ -22,7 +22,7 @@ "SPDIF 2" #endif #endif - #if ((1 < ADAT_TX_INDEX+8+1) && (1 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((1 < ADAT_TX_INDEX+8+1) && (1 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((1 < SPDIF_TX_INDEX+2+1) && (1 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (1 < I2S_CHANS_DAC+1) "/" #endif @@ -62,7 +62,7 @@ "SPDIF 2" #endif #endif - #if ((2 < ADAT_TX_INDEX+8+1) && (2 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((2 < ADAT_TX_INDEX+8+1) && (2 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((2 < SPDIF_TX_INDEX+2+1) && (2 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (2 < I2S_CHANS_DAC+1) "/" #endif @@ -102,7 +102,7 @@ "SPDIF 2" #endif #endif - #if ((3 < ADAT_TX_INDEX+8+1) && (3 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((3 < ADAT_TX_INDEX+8+1) && (3 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((3 < SPDIF_TX_INDEX+2+1) && (3 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (3 < I2S_CHANS_DAC+1) "/" #endif @@ -142,7 +142,7 @@ "SPDIF 2" #endif #endif - #if ((4 < ADAT_TX_INDEX+8+1) && (4 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((4 < ADAT_TX_INDEX+8+1) && (4 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((4 < SPDIF_TX_INDEX+2+1) && (4 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (4 < I2S_CHANS_DAC+1) "/" #endif @@ -182,7 +182,7 @@ "SPDIF 2" #endif #endif - #if ((5 < ADAT_TX_INDEX+8+1) && (5 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((5 < ADAT_TX_INDEX+8+1) && (5 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((5 < SPDIF_TX_INDEX+2+1) && (5 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (5 < I2S_CHANS_DAC+1) "/" #endif @@ -222,7 +222,7 @@ "SPDIF 2" #endif #endif - #if ((6 < ADAT_TX_INDEX+8+1) && (6 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((6 < ADAT_TX_INDEX+8+1) && (6 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((6 < SPDIF_TX_INDEX+2+1) && (6 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (6 < I2S_CHANS_DAC+1) "/" #endif @@ -262,7 +262,7 @@ "SPDIF 2" #endif #endif - #if ((7 < ADAT_TX_INDEX+8+1) && (7 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((7 < ADAT_TX_INDEX+8+1) && (7 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((7 < SPDIF_TX_INDEX+2+1) && (7 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (7 < I2S_CHANS_DAC+1) "/" #endif @@ -302,7 +302,7 @@ "SPDIF 2" #endif #endif - #if ((8 < ADAT_TX_INDEX+8+1) && (8 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((8 < ADAT_TX_INDEX+8+1) && (8 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((8 < SPDIF_TX_INDEX+2+1) && (8 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (8 < I2S_CHANS_DAC+1) "/" #endif @@ -342,7 +342,7 @@ "SPDIF 2" #endif #endif - #if ((9 < ADAT_TX_INDEX+8+1) && (9 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((9 < ADAT_TX_INDEX+8+1) && (9 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((9 < SPDIF_TX_INDEX+2+1) && (9 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (9 < I2S_CHANS_DAC+1) "/" #endif @@ -382,7 +382,7 @@ "SPDIF 2" #endif #endif - #if ((10 < ADAT_TX_INDEX+8+1) && (10 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((10 < ADAT_TX_INDEX+8+1) && (10 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((10 < SPDIF_TX_INDEX+2+1) && (10 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (10 < I2S_CHANS_DAC+1) "/" #endif @@ -422,7 +422,7 @@ "SPDIF 2" #endif #endif - #if ((11 < ADAT_TX_INDEX+8+1) && (11 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((11 < ADAT_TX_INDEX+8+1) && (11 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((11 < SPDIF_TX_INDEX+2+1) && (11 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (11 < I2S_CHANS_DAC+1) "/" #endif @@ -462,7 +462,7 @@ "SPDIF 2" #endif #endif - #if ((12 < ADAT_TX_INDEX+8+1) && (12 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((12 < ADAT_TX_INDEX+8+1) && (12 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((12 < SPDIF_TX_INDEX+2+1) && (12 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (12 < I2S_CHANS_DAC+1) "/" #endif @@ -502,7 +502,7 @@ "SPDIF 2" #endif #endif - #if ((13 < ADAT_TX_INDEX+8+1) && (13 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((13 < ADAT_TX_INDEX+8+1) && (13 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((13 < SPDIF_TX_INDEX+2+1) && (13 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (13 < I2S_CHANS_DAC+1) "/" #endif @@ -542,7 +542,7 @@ "SPDIF 2" #endif #endif - #if ((14 < ADAT_TX_INDEX+8+1) && (14 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((14 < ADAT_TX_INDEX+8+1) && (14 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((14 < SPDIF_TX_INDEX+2+1) && (14 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (14 < I2S_CHANS_DAC+1) "/" #endif @@ -582,7 +582,7 @@ "SPDIF 2" #endif #endif - #if ((15 < ADAT_TX_INDEX+8+1) && (15 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((15 < ADAT_TX_INDEX+8+1) && (15 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((15 < SPDIF_TX_INDEX+2+1) && (15 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (15 < I2S_CHANS_DAC+1) "/" #endif @@ -622,7 +622,7 @@ "SPDIF 2" #endif #endif - #if ((16 < ADAT_TX_INDEX+8+1) && (16 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((16 < ADAT_TX_INDEX+8+1) && (16 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((16 < SPDIF_TX_INDEX+2+1) && (16 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (16 < I2S_CHANS_DAC+1) "/" #endif @@ -662,7 +662,7 @@ "SPDIF 2" #endif #endif - #if ((17 < ADAT_TX_INDEX+8+1) && (17 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((17 < ADAT_TX_INDEX+8+1) && (17 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((17 < SPDIF_TX_INDEX+2+1) && (17 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (17 < I2S_CHANS_DAC+1) "/" #endif @@ -702,7 +702,7 @@ "SPDIF 2" #endif #endif - #if ((18 < ADAT_TX_INDEX+8+1) && (18 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((18 < ADAT_TX_INDEX+8+1) && (18 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((18 < SPDIF_TX_INDEX+2+1) && (18 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (18 < I2S_CHANS_DAC+1) "/" #endif @@ -742,7 +742,7 @@ "SPDIF 2" #endif #endif - #if ((19 < ADAT_TX_INDEX+8+1) && (19 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((19 < ADAT_TX_INDEX+8+1) && (19 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((19 < SPDIF_TX_INDEX+2+1) && (19 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (19 < I2S_CHANS_DAC+1) "/" #endif @@ -782,7 +782,7 @@ "SPDIF 2" #endif #endif - #if ((20 < ADAT_TX_INDEX+8+1) && (20 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((20 < ADAT_TX_INDEX+8+1) && (20 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((20 < SPDIF_TX_INDEX+2+1) && (20 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (20 < I2S_CHANS_DAC+1) "/" #endif @@ -822,7 +822,7 @@ "SPDIF 2" #endif #endif - #if ((21 < ADAT_TX_INDEX+8+1) && (21 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((21 < ADAT_TX_INDEX+8+1) && (21 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((21 < SPDIF_TX_INDEX+2+1) && (21 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (21 < I2S_CHANS_DAC+1) "/" #endif @@ -862,7 +862,7 @@ "SPDIF 2" #endif #endif - #if ((22 < ADAT_TX_INDEX+8+1) && (22 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((22 < ADAT_TX_INDEX+8+1) && (22 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((22 < SPDIF_TX_INDEX+2+1) && (22 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (22 < I2S_CHANS_DAC+1) "/" #endif @@ -902,7 +902,7 @@ "SPDIF 2" #endif #endif - #if ((23 < ADAT_TX_INDEX+8+1) && (23 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((23 < ADAT_TX_INDEX+8+1) && (23 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((23 < SPDIF_TX_INDEX+2+1) && (23 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (23 < I2S_CHANS_DAC+1) "/" #endif @@ -942,7 +942,7 @@ "SPDIF 2" #endif #endif - #if ((24 < ADAT_TX_INDEX+8+1) && (24 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((24 < ADAT_TX_INDEX+8+1) && (24 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((24 < SPDIF_TX_INDEX+2+1) && (24 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (24 < I2S_CHANS_DAC+1) "/" #endif @@ -982,7 +982,7 @@ "SPDIF 2" #endif #endif - #if ((25 < ADAT_TX_INDEX+8+1) && (25 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((25 < ADAT_TX_INDEX+8+1) && (25 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((25 < SPDIF_TX_INDEX+2+1) && (25 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (25 < I2S_CHANS_DAC+1) "/" #endif @@ -1022,7 +1022,7 @@ "SPDIF 2" #endif #endif - #if ((26 < ADAT_TX_INDEX+8+1) && (26 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((26 < ADAT_TX_INDEX+8+1) && (26 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((26 < SPDIF_TX_INDEX+2+1) && (26 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (26 < I2S_CHANS_DAC+1) "/" #endif @@ -1062,7 +1062,7 @@ "SPDIF 2" #endif #endif - #if ((27 < ADAT_TX_INDEX+8+1) && (27 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((27 < ADAT_TX_INDEX+8+1) && (27 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((27 < SPDIF_TX_INDEX+2+1) && (27 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (27 < I2S_CHANS_DAC+1) "/" #endif @@ -1102,7 +1102,7 @@ "SPDIF 2" #endif #endif - #if ((28 < ADAT_TX_INDEX+8+1) && (28 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((28 < ADAT_TX_INDEX+8+1) && (28 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((28 < SPDIF_TX_INDEX+2+1) && (28 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (28 < I2S_CHANS_DAC+1) "/" #endif @@ -1142,7 +1142,7 @@ "SPDIF 2" #endif #endif - #if ((29 < ADAT_TX_INDEX+8+1) && (29 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((29 < ADAT_TX_INDEX+8+1) && (29 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((29 < SPDIF_TX_INDEX+2+1) && (29 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (29 < I2S_CHANS_DAC+1) "/" #endif @@ -1182,7 +1182,7 @@ "SPDIF 2" #endif #endif - #if ((30 < ADAT_TX_INDEX+8+1) && (30 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((30 < ADAT_TX_INDEX+8+1) && (30 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((30 < SPDIF_TX_INDEX+2+1) && (30 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (30 < I2S_CHANS_DAC+1) "/" #endif @@ -1222,7 +1222,7 @@ "SPDIF 2" #endif #endif - #if ((31 < ADAT_TX_INDEX+8+1) && (31 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((31 < ADAT_TX_INDEX+8+1) && (31 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((31 < SPDIF_TX_INDEX+2+1) && (31 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (31 < I2S_CHANS_DAC+1) "/" #endif @@ -1262,7 +1262,7 @@ "SPDIF 2" #endif #endif - #if ((32 < ADAT_TX_INDEX+8+1) && (32 > ADAT_TX_INDEX)) && defined(ADAT_TX) + #if ((32 < ADAT_TX_INDEX+8+1) && (32 > ADAT_TX_INDEX)) && (XUA_ADAT_TX_EN) #if ((32 < SPDIF_TX_INDEX+2+1) && (32 > SPDIF_TX_INDEX)) && (XUA_SPDIF_TX_EN) || (32 < I2S_CHANS_DAC+1) "/" #endif @@ -1305,7 +1305,7 @@ "SPDIF 2" #endif #endif - #if ((1 < ADAT_RX_INDEX+8+1) && (1 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((1 < ADAT_RX_INDEX+8+1) && (1 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((1 < SPDIF_RX_INDEX+2+1) && (1 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (1 < I2S_CHANS_ADC+1) "/" #endif @@ -1345,7 +1345,7 @@ "SPDIF 2" #endif #endif - #if ((2 < ADAT_RX_INDEX+8+1) && (2 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((2 < ADAT_RX_INDEX+8+1) && (2 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((2 < SPDIF_RX_INDEX+2+1) && (2 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (2 < I2S_CHANS_ADC+1) "/" #endif @@ -1385,7 +1385,7 @@ "SPDIF 2" #endif #endif - #if ((3 < ADAT_RX_INDEX+8+1) && (3 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((3 < ADAT_RX_INDEX+8+1) && (3 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((3 < SPDIF_RX_INDEX+2+1) && (3 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (3 < I2S_CHANS_ADC+1) "/" #endif @@ -1425,7 +1425,7 @@ "SPDIF 2" #endif #endif - #if ((4 < ADAT_RX_INDEX+8+1) && (4 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((4 < ADAT_RX_INDEX+8+1) && (4 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((4 < SPDIF_RX_INDEX+2+1) && (4 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (4 < I2S_CHANS_ADC+1) "/" #endif @@ -1465,7 +1465,7 @@ "SPDIF 2" #endif #endif - #if ((5 < ADAT_RX_INDEX+8+1) && (5 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((5 < ADAT_RX_INDEX+8+1) && (5 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((5 < SPDIF_RX_INDEX+2+1) && (5 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (5 < I2S_CHANS_ADC+1) "/" #endif @@ -1505,7 +1505,7 @@ "SPDIF 2" #endif #endif - #if ((6 < ADAT_RX_INDEX+8+1) && (6 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((6 < ADAT_RX_INDEX+8+1) && (6 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((6 < SPDIF_RX_INDEX+2+1) && (6 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (6 < I2S_CHANS_ADC+1) "/" #endif @@ -1545,7 +1545,7 @@ "SPDIF 2" #endif #endif - #if ((7 < ADAT_RX_INDEX+8+1) && (7 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((7 < ADAT_RX_INDEX+8+1) && (7 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((7 < SPDIF_RX_INDEX+2+1) && (7 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (7 < I2S_CHANS_ADC+1) "/" #endif @@ -1585,7 +1585,7 @@ "SPDIF 2" #endif #endif - #if ((8 < ADAT_RX_INDEX+8+1) && (8 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((8 < ADAT_RX_INDEX+8+1) && (8 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((8 < SPDIF_RX_INDEX+2+1) && (8 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (8 < I2S_CHANS_ADC+1) "/" #endif @@ -1625,7 +1625,7 @@ "SPDIF 2" #endif #endif - #if ((9 < ADAT_RX_INDEX+8+1) && (9 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((9 < ADAT_RX_INDEX+8+1) && (9 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((9 < SPDIF_RX_INDEX+2+1) && (9 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (9 < I2S_CHANS_ADC+1) "/" #endif @@ -1665,7 +1665,7 @@ "SPDIF 2" #endif #endif - #if ((10 < ADAT_RX_INDEX+8+1) && (10 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((10 < ADAT_RX_INDEX+8+1) && (10 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((10 < SPDIF_RX_INDEX+2+1) && (10 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (10 < I2S_CHANS_ADC+1) "/" #endif @@ -1705,7 +1705,7 @@ "SPDIF 2" #endif #endif - #if ((11 < ADAT_RX_INDEX+8+1) && (11 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((11 < ADAT_RX_INDEX+8+1) && (11 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((11 < SPDIF_RX_INDEX+2+1) && (11 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (11 < I2S_CHANS_ADC+1) "/" #endif @@ -1745,7 +1745,7 @@ "SPDIF 2" #endif #endif - #if ((12 < ADAT_RX_INDEX+8+1) && (12 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((12 < ADAT_RX_INDEX+8+1) && (12 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((12 < SPDIF_RX_INDEX+2+1) && (12 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (12 < I2S_CHANS_ADC+1) "/" #endif @@ -1785,7 +1785,7 @@ "SPDIF 2" #endif #endif - #if ((13 < ADAT_RX_INDEX+8+1) && (13 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((13 < ADAT_RX_INDEX+8+1) && (13 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((13 < SPDIF_RX_INDEX+2+1) && (13 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (13 < I2S_CHANS_ADC+1) "/" #endif @@ -1825,7 +1825,7 @@ "SPDIF 2" #endif #endif - #if ((14 < ADAT_RX_INDEX+8+1) && (14 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((14 < ADAT_RX_INDEX+8+1) && (14 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((14 < SPDIF_RX_INDEX+2+1) && (14 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (14 < I2S_CHANS_ADC+1) "/" #endif @@ -1865,7 +1865,7 @@ "SPDIF 2" #endif #endif - #if ((15 < ADAT_RX_INDEX+8+1) && (15 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((15 < ADAT_RX_INDEX+8+1) && (15 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((15 < SPDIF_RX_INDEX+2+1) && (15 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (15 < I2S_CHANS_ADC+1) "/" #endif @@ -1905,7 +1905,7 @@ "SPDIF 2" #endif #endif - #if ((16 < ADAT_RX_INDEX+8+1) && (16 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((16 < ADAT_RX_INDEX+8+1) && (16 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((16 < SPDIF_RX_INDEX+2+1) && (16 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (16 < I2S_CHANS_ADC+1) "/" #endif @@ -1945,7 +1945,7 @@ "SPDIF 2" #endif #endif - #if ((17 < ADAT_RX_INDEX+8+1) && (17 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((17 < ADAT_RX_INDEX+8+1) && (17 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((17 < SPDIF_RX_INDEX+2+1) && (17 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (17 < I2S_CHANS_ADC+1) "/" #endif @@ -1985,7 +1985,7 @@ "SPDIF 2" #endif #endif - #if ((18 < ADAT_RX_INDEX+8+1) && (18 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((18 < ADAT_RX_INDEX+8+1) && (18 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((18 < SPDIF_RX_INDEX+2+1) && (18 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (18 < I2S_CHANS_ADC+1) "/" #endif @@ -2025,7 +2025,7 @@ "SPDIF 2" #endif #endif - #if ((19 < ADAT_RX_INDEX+8+1) && (19 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((19 < ADAT_RX_INDEX+8+1) && (19 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((19 < SPDIF_RX_INDEX+2+1) && (19 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (19 < I2S_CHANS_ADC+1) "/" #endif @@ -2065,7 +2065,7 @@ "SPDIF 2" #endif #endif - #if ((20 < ADAT_RX_INDEX+8+1) && (20 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((20 < ADAT_RX_INDEX+8+1) && (20 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((20 < SPDIF_RX_INDEX+2+1) && (20 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (20 < I2S_CHANS_ADC+1) "/" #endif @@ -2105,7 +2105,7 @@ "SPDIF 2" #endif #endif - #if ((21 < ADAT_RX_INDEX+8+1) && (21 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((21 < ADAT_RX_INDEX+8+1) && (21 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((21 < SPDIF_RX_INDEX+2+1) && (21 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (21 < I2S_CHANS_ADC+1) "/" #endif @@ -2145,7 +2145,7 @@ "SPDIF 2" #endif #endif - #if ((22 < ADAT_RX_INDEX+8+1) && (22 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((22 < ADAT_RX_INDEX+8+1) && (22 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((22 < SPDIF_RX_INDEX+2+1) && (22 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (22 < I2S_CHANS_ADC+1) "/" #endif @@ -2185,7 +2185,7 @@ "SPDIF 2" #endif #endif - #if ((23 < ADAT_RX_INDEX+8+1) && (23 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((23 < ADAT_RX_INDEX+8+1) && (23 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((23 < SPDIF_RX_INDEX+2+1) && (23 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (23 < I2S_CHANS_ADC+1) "/" #endif @@ -2225,7 +2225,7 @@ "SPDIF 2" #endif #endif - #if ((24 < ADAT_RX_INDEX+8+1) && (24 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((24 < ADAT_RX_INDEX+8+1) && (24 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((24 < SPDIF_RX_INDEX+2+1) && (24 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (24 < I2S_CHANS_ADC+1) "/" #endif @@ -2265,7 +2265,7 @@ "SPDIF 2" #endif #endif - #if ((25 < ADAT_RX_INDEX+8+1) && (25 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((25 < ADAT_RX_INDEX+8+1) && (25 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((25 < SPDIF_RX_INDEX+2+1) && (25 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (25 < I2S_CHANS_ADC+1) "/" #endif @@ -2305,7 +2305,7 @@ "SPDIF 2" #endif #endif - #if ((26 < ADAT_RX_INDEX+8+1) && (26 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((26 < ADAT_RX_INDEX+8+1) && (26 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((26 < SPDIF_RX_INDEX+2+1) && (26 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (26 < I2S_CHANS_ADC+1) "/" #endif @@ -2345,7 +2345,7 @@ "SPDIF 2" #endif #endif - #if ((27 < ADAT_RX_INDEX+8+1) && (27 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((27 < ADAT_RX_INDEX+8+1) && (27 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((27 < SPDIF_RX_INDEX+2+1) && (27 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (27 < I2S_CHANS_ADC+1) "/" #endif @@ -2385,7 +2385,7 @@ "SPDIF 2" #endif #endif - #if ((28 < ADAT_RX_INDEX+8+1) && (28 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((28 < ADAT_RX_INDEX+8+1) && (28 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((28 < SPDIF_RX_INDEX+2+1) && (28 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (28 < I2S_CHANS_ADC+1) "/" #endif @@ -2425,7 +2425,7 @@ "SPDIF 2" #endif #endif - #if ((29 < ADAT_RX_INDEX+8+1) && (29 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((29 < ADAT_RX_INDEX+8+1) && (29 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((29 < SPDIF_RX_INDEX+2+1) && (29 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (29 < I2S_CHANS_ADC+1) "/" #endif @@ -2465,7 +2465,7 @@ "SPDIF 2" #endif #endif - #if ((30 < ADAT_RX_INDEX+8+1) && (30 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((30 < ADAT_RX_INDEX+8+1) && (30 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((30 < SPDIF_RX_INDEX+2+1) && (30 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (30 < I2S_CHANS_ADC+1) "/" #endif @@ -2505,7 +2505,7 @@ "SPDIF 2" #endif #endif - #if ((31 < ADAT_RX_INDEX+8+1) && (31 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((31 < ADAT_RX_INDEX+8+1) && (31 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((31 < SPDIF_RX_INDEX+2+1) && (31 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (31 < I2S_CHANS_ADC+1) "/" #endif @@ -2545,7 +2545,7 @@ "SPDIF 2" #endif #endif - #if ((32 < ADAT_RX_INDEX+8+1) && (32 > ADAT_RX_INDEX)) && defined(ADAT_RX) + #if ((32 < ADAT_RX_INDEX+8+1) && (32 > ADAT_RX_INDEX)) && (XUA_ADAT_RX_EN) #if ((32 < SPDIF_RX_INDEX+2+1) && (32 > SPDIF_RX_INDEX)) && (XUA_SPDIF_RX_EN) || (32 < I2S_CHANS_ADC+1) "/" #endif diff --git a/lib_xua/src/core/endpoint0/descriptor_defs.h b/lib_xua/src/core/endpoint0/descriptor_defs.h index b4dc692b..42567878 100644 --- a/lib_xua/src/core/endpoint0/descriptor_defs.h +++ b/lib_xua/src/core/endpoint0/descriptor_defs.h @@ -66,7 +66,7 @@ enum USBInterfaceNumber INTERFACE_COUNT /* End marker */ }; -#if( 0 < HID_CONTROLS ) +#ifndef ENDPOINT_INT_INTERVAL_IN_HID #define ENDPOINT_INT_INTERVAL_IN_HID 0x08 #endif diff --git a/lib_xua/src/core/endpoint0/vendorrequests.c b/lib_xua/src/core/endpoint0/vendorrequests.c index 66a1ac09..e5384b9c 100644 --- a/lib_xua/src/core/endpoint0/vendorrequests.c +++ b/lib_xua/src/core/endpoint0/vendorrequests.c @@ -7,12 +7,12 @@ #include "vendorrequests.h" int VendorAudioRequests(XUD_ep ep0_out, XUD_ep ep0_in, unsigned char bRequest, unsigned char cs, unsigned char cn, - unsigned short unitId, unsigned char direction, chanend c_audioControl, + unsigned short unitId, unsigned char direction, NULLABLE_RESOURCE(chanend, c_audioControl), NULLABLE_RESOURCE(chanend, c_mix_ctl), NULLABLE_RESOURCE(chanend, c_clk_ctL)) __attribute__ ((weak)); int VendorAudioRequests(XUD_ep ep0_out, XUD_ep ep0_in, unsigned char bRequest, unsigned char cs, unsigned char cn, - unsigned short unitId, unsigned char direction, chanend c_audioControl, + unsigned short unitId, unsigned char direction, NULLABLE_RESOURCE(chanend, c_audioControl), NULLABLE_RESOURCE(chanend, c_mix_ctl), NULLABLE_RESOURCE(chanend, c_clk_ctL)) { diff --git a/lib_xua/src/core/endpoint0/vendorrequests.h b/lib_xua/src/core/endpoint0/vendorrequests.h index c241011d..d00a5873 100644 --- a/lib_xua/src/core/endpoint0/vendorrequests.h +++ b/lib_xua/src/core/endpoint0/vendorrequests.h @@ -33,7 +33,7 @@ #endif int VendorAudioRequests(XUD_ep ep0_out, XUD_ep ep0_in, unsigned char bRequest, unsigned char cs, unsigned char cn, - unsigned short unitId, unsigned char direction, chanend c_audioControl, + unsigned short unitId, unsigned char direction, NULLABLE_RESOURCE(chanend, c_audioControl), NULLABLE_RESOURCE(chanend, c_mix_ctl), NULLABLE_RESOURCE(chanend, c_clk_ctL)); diff --git a/lib_xua/src/core/endpoint0/xua_endpoint0.c b/lib_xua/src/core/endpoint0/xua_endpoint0.c index 40064918..a21e605a 100755 --- a/lib_xua/src/core/endpoint0/xua_endpoint0.c +++ b/lib_xua/src/core/endpoint0/xua_endpoint0.c @@ -1,4 +1,4 @@ -// Copyright 2011-2021 XMOS LIMITED. +// Copyright 2011-2023 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. /** * @brief Implements endpoint zero for an USB Audio 1.0/2.0 device @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include "xua.h" #if XUA_USB_EN @@ -23,18 +25,24 @@ #include "vendorrequests.h" #include "xc_ptr.h" #include "xua_ep0_uacreqs.h" + #if( 0 < HID_CONTROLS ) #include "hid.h" +#include "xua_hid.h" +#include "xua_hid_report.h" #endif + #if DSD_CHANS_DAC > 0 #include "dsd_support.h" #endif + #define DEBUG_UNIT XUA_EP0 + #ifndef DEBUG_PRINT_ENABLE_XUA_EP0 #define DEBUG_PRINT_ENABLE_XUA_EP0 0 #endif // DEBUG_PRINT_ENABLE_XUA_EP0 -#include "debug_print.h" +#include "debug_print.h" #include "xua_usb_params_funcs.h" #ifndef __XC__ @@ -51,7 +59,7 @@ #if ((AUDIO_CLASS == 1) || (AUDIO_CLASS_FALLBACK)) && defined(DFU) #warning DFU will not be enabled in AUDIO 1.0 mode due to Windows requesting driver #endif -#endif +#endif // FORCE_UAC1_DFU /* MIDI not supported in Audio 1.0 mode */ #if ((AUDIO_CLASS == 1) || (AUDIO_CLASS_FALLBACK)) && defined(MIDI) @@ -85,9 +93,6 @@ extern void device_reboot(void); #endif -#if( 0 < HID_CONTROLS ) -#include "xua_hid.h" -#endif #ifndef MIN #define MIN(a,b) (((a)<(b))?(a):(b)) #endif @@ -101,13 +106,17 @@ unsigned int mutesOut[NUM_USB_CHAN_OUT + 1]; int volsIn[NUM_USB_CHAN_IN + 1]; unsigned int mutesIn[NUM_USB_CHAN_IN + 1]; -#ifdef MIXER -unsigned char mixer1Crossbar[18]; -short mixer1Weights[18*8]; +#if (MIXER) +short mixer1Weights[MIX_INPUTS * MAX_MIX_COUNT]; -unsigned char channelMap[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT]; +//unsigned char channelMap[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT]; +/* Mapping of channels to output audio interfaces */ unsigned char channelMapAud[NUM_USB_CHAN_OUT]; + +/* Mapping of channels to USB host */ unsigned char channelMapUsb[NUM_USB_CHAN_IN]; + +/* Mapping of channels to Mixer(s) */ unsigned char mixSel[MAX_MIX_COUNT][MIX_INPUTS]; #endif @@ -125,7 +134,11 @@ unsigned g_curStreamAlt_Out = 0; unsigned g_curStreamAlt_In = 0; /* Global variable for current USB bus speed (i.e. FS/HS) */ -XUD_BusSpeed_t g_curUsbSpeed = 0; +#if (AUDIO_CLASS == 2) +XUD_BusSpeed_t g_curUsbSpeed = XUD_SPEED_HS; +#else +XUD_BusSpeed_t g_curUsbSpeed = XUD_SPEED_FS; +#endif /* Global variables for current USB Vendor and Product strings */ char g_vendor_str[XUA_MAX_STR_LEN] = VENDOR_STR; @@ -253,9 +266,47 @@ void XUA_Endpoint0_setVendorId(unsigned short vid) { #endif // AUDIO_CLASS == 1} } +#if (MIXER) +void InitLocalMixerState() +{ + for (int i = 0; i < MIX_INPUTS * MAX_MIX_COUNT; i++) + { + mixer1Weights[i] = 0x8001; //-inf + } + + /* Configure default connections */ + for (int i = 0; i < MAX_MIX_COUNT; i++) + { + mixer1Weights[(i * MAX_MIX_COUNT) + i] = 0; + } + +#if NUM_USB_CHAN_OUT > 0 + /* Setup up audio output channel mapping */ + for(int i = 0; i < NUM_USB_CHAN_OUT; i++) + { + channelMapAud[i] = i; + } +#endif + +#if NUM_USB_CHAN_IN > 0 + for(int i = 0; i < NUM_USB_CHAN_IN; i++) + { + channelMapUsb[i] = i + NUM_USB_CHAN_OUT; + } +#endif + + /* Init mixer inputs */ + for(int j = 0; j < MAX_MIX_COUNT; j++) + for(int i = 0; i < MIX_INPUTS; i++) + { + mixSel[j][i] = i; + } +} +#endif + void concatenateAndCopyStrings(char* string1, char* string2, char* string_buffer) { - debug_printf("concatenateAndCopyStrings() for \"%s\" and \"%s\"\n", string1, string2); - + debug_printf("concatenateAndCopyStrings() for \"%s\" and \"%s\"\n", string1, string2); + memset(string_buffer, '\0', strlen(string_buffer)); uint32_t remaining_buffer_size = MIN(strlen(string1), XUA_MAX_STR_LEN-1); @@ -279,10 +330,10 @@ void XUA_Endpoint0_setStrTable() { concatenateAndCopyStrings(g_vendor_str, " Clock Selector", g_strTable.clockSelectorStr); concatenateAndCopyStrings(g_vendor_str, " Internal Clock", g_strTable.internalClockSourceStr); #endif -#if SPDIF_RX +#if (XUA_SPDIF_RX_EN) concatenateAndCopyStrings(g_vendor_str, " S/PDIF Clock", g_strTable.spdifClockSourceStr); #endif -#if ADAT_RX +#if (XUA_ADAT_RX_EN) concatenateAndCopyStrings(g_vendor_str, " ADAT Clock", g_strTable.adatClockSourceStr); #endif #if (XUA_DFU_EN == 1) @@ -309,7 +360,7 @@ void XUA_Endpoint0_setStrTable() { concatenateAndCopyStrings(g_product_str, "", g_strTable.usbInputTermStr_Audio2); concatenateAndCopyStrings(g_product_str, "", g_strTable.usbOutputTermStr_Audio2); #endif - + // update Serial strings concatenateAndCopyStrings(g_serial_str, "", g_strTable.serialStr); } @@ -391,7 +442,7 @@ void XUA_Endpoint0_setBcdDevice(unsigned short bcd) { #endif // AUDIO_CLASS == 1} } -void XUA_Endpoint0_init(chanend c_ep0_out, chanend c_ep0_in, chanend c_audioControl, +void XUA_Endpoint0_init(chanend c_ep0_out, chanend c_ep0_in, NULLABLE_RESOURCE(chanend, c_audioControl), chanend c_mix_ctl, chanend c_clk_ctl, chanend c_EANativeTransport_ctrl, CLIENT_INTERFACE(i_dfu, dfuInterface) VENDOR_REQUESTS_PARAMS_DEC_) { ep0_out = XUD_InitEp(c_ep0_out); @@ -399,75 +450,11 @@ void XUA_Endpoint0_init(chanend c_ep0_out, chanend c_ep0_in, chanend c_audioCont XUA_Endpoint0_setStrTable(); -#if 0 - /* Dont need to init globals.. */ - /* Init tables for volumes (+ 1 for master) */ - for(int i = 0; i < NUM_USB_CHAN_OUT + 1; i++) - { - volsOut[i] = 0; - mutesOut[i] = 0; - } - - for(int i = 0; i < NUM_USB_CHAN_IN + 1; i++) - { - volsIn[i] = 0; - mutesIn[i] = 0; - } -#endif VendorRequests_Init(VENDOR_REQUESTS_PARAMS); -#ifdef MIXER +#if (MIXER) /* Set up mixer default state */ - for (int i = 0; i < 18*8; i++) - { - mixer1Weights[i] = 0x8001; //-inf - } - - /* Configure default connections */ - mixer1Weights[0] = 0; - mixer1Weights[9] = 0; - mixer1Weights[18] = 0; - mixer1Weights[27] = 0; - mixer1Weights[36] = 0; - mixer1Weights[45] = 0; - mixer1Weights[54] = 0; - mixer1Weights[63] = 0; - -#if NUM_USB_CHAN_OUT > 0 - /* Setup up audio output channel mapping */ - for(int i = 0; i < NUM_USB_CHAN_OUT; i++) - { - channelMapAud[i] = i; - } -#endif - -#if NUM_USB_CHAN_IN > 0 - for(int i = 0; i < NUM_USB_CHAN_IN; i++) - { - channelMapUsb[i] = i + NUM_USB_CHAN_OUT; - } -#endif - - /* Set up channel mapping default */ - for (int i = 0; i < NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN; i++) - { - channelMap[i] = i; - } - -#if MAX_MIX_COUNT > 0 - /* Mixer outputs mapping defaults */ - for (int i = 0; i < MAX_MIX_COUNT; i++) - { - channelMap[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + i] = i; - } -#endif - - /* Init mixer inputs */ - for(int j = 0; j < MAX_MIX_COUNT; j++) - for(int i = 0; i < MIX_INPUTS; i++) - { - mixSel[j][i] = i; - } + InitLocalMixerState(); #endif #ifdef VENDOR_AUDIO_REQS @@ -478,6 +465,8 @@ void XUA_Endpoint0_init(chanend c_ep0_out, chanend c_ep0_in, chanend c_audioCont /* Check if device has started in DFU mode */ if (DFUReportResetState(null)) { + assert(((unsigned)c_audioControl != 0) && msg("DFU not supported when c_audioControl is null")); + /* Stop audio */ outuint(c_audioControl, SET_SAMPLE_FREQ); outuint(c_audioControl, AUDIO_STOP_FOR_DFU); @@ -486,41 +475,64 @@ void XUA_Endpoint0_init(chanend c_ep0_out, chanend c_ep0_in, chanend c_audioCont } #endif -#ifdef XUA_USB_DESCRIPTOR_OVERWRITE_RATE_RES //change USB descriptor frequencies and bit resolution values here +#ifdef XUA_USB_DESCRIPTOR_OVERWRITE_RATE_RES //change USB descriptor frequencies and bit resolution values here - cfgDesc_Audio1[USB_AS_IN_INTERFACE_DESCRIPTOR_OFFSET_SUB_FRAME] = get_device_to_usb_bit_res() >> 3; //sub frame rate = bit rate /8 - cfgDesc_Audio1[USB_AS_IN_INTERFACE_DESCRIPTOR_OFFSET_SUB_FRAME + 1] = (get_device_to_usb_bit_res() & 0xff); //bit resolution + const int num_of_usb_descriptor_freq = 3; //This should be =3 according to the comments "using a value of <=2 or > 7 for num_freqs_a1 causes enumeration issues on Windows" in xua_ep0_descriptors.h - cfgDesc_Audio1[USB_AS_OUT_INTERFACE_DESCRIPTOR_OFFSET_SUB_FRAME] = get_usb_to_device_bit_res() >> 3; //sub frame rate = bit rate /8 - cfgDesc_Audio1[USB_AS_OUT_INTERFACE_DESCRIPTOR_OFFSET_SUB_FRAME + 1] = (get_usb_to_device_bit_res() & 0xff); //bit resolution +#if( 0 < NUM_USB_CHAN_IN ) - const unsigned num_of_usb_descriptor_freq=3; //This should be =3 according to the comments "using a value of <=2 or > 7 for num_freqs_a1 causes enumeration issues on Windows" in xua_ep0_descriptors.h - int i=0; - for(i=0;i> 3; //sub frame rate = bit rate /8 + cfgDesc_Audio1[USB_AS_IN_INTERFACE_DESCRIPTOR_OFFSET_SUB_FRAME + 1] = (get_device_to_usb_bit_res() & 0xff); //bit resolution + + for(int i=0;i> 8; cfgDesc_Audio1[USB_AS_IN_INTERFACE_DESCRIPTOR_OFFSET_FREQ + 3*i + 2] = (get_device_to_usb_rate() & 0xff0000)>> 16; } + cfgDesc_Audio1[USB_AS_IN_EP_DESCRIPTOR_OFFSET_MAXPACKETSIZE] = ((get_device_to_usb_bit_res() >> 3) * MAX_PACKET_SIZE_MULT_IN_FS) & 0xff; //max packet size + cfgDesc_Audio1[USB_AS_IN_EP_DESCRIPTOR_OFFSET_MAXPACKETSIZE + 1] = (((get_device_to_usb_bit_res() >> 3) * MAX_PACKET_SIZE_MULT_IN_FS) & 0xff00) >> 8; //max packet size - for(i=0;i> 3; //sub frame rate = bit rate /8 + cfgDesc_Audio1[USB_AS_OUT_INTERFACE_DESCRIPTOR_OFFSET_SUB_FRAME + 1] = (get_usb_to_device_bit_res() & 0xff); //bit resolution + + for(int i=0;i> 8; cfgDesc_Audio1[USB_AS_OUT_INTERFACE_DESCRIPTOR_OFFSET_FREQ + 3*i + 2] = (get_usb_to_device_rate() & 0xff0000)>> 16; } - cfgDesc_Audio1[USB_AS_IN_EP_DESCRIPTOR_OFFSET_MAXPACKETSIZE] = ((get_device_to_usb_bit_res() >> 3) * MAX_PACKET_SIZE_MULT_IN_FS) & 0xff; //max packet size - cfgDesc_Audio1[USB_AS_IN_EP_DESCRIPTOR_OFFSET_MAXPACKETSIZE + 1] = (((get_device_to_usb_bit_res() >> 3) * MAX_PACKET_SIZE_MULT_IN_FS) & 0xff00) >> 8; //max packet size + cfgDesc_Audio1[USB_AS_OUT_EP_DESCRIPTOR_OFFSET_MAXPACKETSIZE] = ((get_usb_to_device_bit_res() >> 3) * MAX_PACKET_SIZE_MULT_OUT_FS) & 0xff; //max packet size + cfgDesc_Audio1[USB_AS_OUT_EP_DESCRIPTOR_OFFSET_MAXPACKETSIZE + 1] = (((get_usb_to_device_bit_res() >> 3) * MAX_PACKET_SIZE_MULT_OUT_FS) & 0xff00) >> 8; //max packet size +#endif // NUM_USB_CHAN_OUT - cfgDesc_Audio1[USB_AS_OUT_EP_DESCRIPTOR_OFFSET_MAXPACKETSIZE] = ((get_usb_to_device_bit_res() >> 3) * MAX_PACKET_SIZE_MULT_OUT_FS) & 0xff; //max packet size - cfgDesc_Audio1[USB_AS_OUT_EP_DESCRIPTOR_OFFSET_MAXPACKETSIZE + 1] = (((get_usb_to_device_bit_res() >> 3) * MAX_PACKET_SIZE_MULT_OUT_FS) & 0xff00) >> 8; //max packet size +#endif // XUA_USB_DESCRIPTOR_OVERWRITE_RATE_RES +#if( 0 < HID_CONTROLS ) + hidReportInit(); + hidPrepareReportDescriptor(); + + size_t hidReportDescriptorLength = hidGetReportDescriptorLength(); + unsigned char hidReportDescriptorLengthLo = hidReportDescriptorLength & 0xFF; + unsigned char hidReportDescriptorLengthHi = (hidReportDescriptorLength & 0xFF00) >> 8; + +#if( AUDIO_CLASS == 1 ) + cfgDesc_Audio1[USB_HID_DESCRIPTOR_OFFSET + HID_DESCRIPTOR_LENGTH_FIELD_OFFSET ] = hidReportDescriptorLengthLo; + cfgDesc_Audio1[USB_HID_DESCRIPTOR_OFFSET + HID_DESCRIPTOR_LENGTH_FIELD_OFFSET + 1] = hidReportDescriptorLengthHi; #endif + hidDescriptor[HID_DESCRIPTOR_LENGTH_FIELD_OFFSET ] = hidReportDescriptorLengthLo; + hidDescriptor[HID_DESCRIPTOR_LENGTH_FIELD_OFFSET + 1] = hidReportDescriptorLengthHi; +#endif // 0 < HID_CONTROLS + } -void XUA_Endpoint0_loop(XUD_Result_t result, USB_SetupPacket_t sp, chanend c_ep0_out, chanend c_ep0_in, chanend c_audioControl, +void XUA_Endpoint0_loop(XUD_Result_t result, USB_SetupPacket_t sp, chanend c_ep0_out, chanend c_ep0_in, NULLABLE_RESOURCE(chanend, c_audioControl), chanend c_mix_ctl, chanend c_clk_ctl, chanend c_EANativeTransport_ctrl, CLIENT_INTERFACE(i_dfu, dfuInterface) VENDOR_REQUESTS_PARAMS_DEC_) { if (result == XUD_RES_OKAY) @@ -547,6 +559,7 @@ void XUA_Endpoint0_loop(XUD_Result_t result, USB_SetupPacket_t sp, chanend c_ep0 /* Only send change if we need to */ if((sp.wValue > 0) && (g_curStreamAlt_Out != sp.wValue)) { + assert((c_audioControl != null) && msg("Format change not supported when c_audioControl is null")); g_curStreamAlt_Out = sp.wValue; /* Send format of data onto buffering */ @@ -582,6 +595,7 @@ void XUA_Endpoint0_loop(XUD_Result_t result, USB_SetupPacket_t sp, chanend c_ep0 /* Only send change if we need to */ if((sp.wValue > 0) && (g_curStreamAlt_In != sp.wValue)) { + assert((c_audioControl != null) && msg("Format change not supported when c_audioControl is null")); g_curStreamAlt_In = sp.wValue; /* Send format of data onto buffering */ @@ -729,15 +743,22 @@ void XUA_Endpoint0_loop(XUD_Result_t result, USB_SetupPacket_t sp, chanend c_ep0 switch (descriptorType) { case HID_HID: - /* Return HID Descriptor */ - result = XUD_DoGetRequest(ep0_out, ep0_in, hidDescriptor, - sizeof(hidDescriptor), sp.wLength); + { + /* Return HID Descriptor */ + result = XUD_DoGetRequest(ep0_out, ep0_in, hidDescriptor, + sizeof(hidDescriptor), sp.wLength); + } break; case HID_REPORT: - /* Return HID report descriptor */ - result = XUD_DoGetRequest(ep0_out, ep0_in, hidReportDescriptor, - sizeof(hidReportDescriptor), sp.wLength); - break; + { + /* Return HID report descriptor */ + unsigned char* hidReportDescriptorPtr; + hidReportDescriptorPtr = hidGetReportDescriptor(); + size_t hidReportDescriptorLength = hidGetReportDescriptorLength(); + result = XUD_DoGetRequest(ep0_out, ep0_in, hidReportDescriptorPtr, + hidReportDescriptorLength, sp.wLength); + } + break; } } break; @@ -819,6 +840,7 @@ void XUA_Endpoint0_loop(XUD_Result_t result, USB_SetupPacket_t sp, chanend c_ep0 if ((DFU_IF == INTERFACE_NUMBER_DFU) && (sp.bRequest != XMOS_DFU_SAVESTATE) && (sp.bRequest != XMOS_DFU_RESTORESTATE)) { + assert((c_audioControl != null) && msg("DFU not supported when c_audioControl is null")); // Stop audio outuint(c_audioControl, SET_SAMPLE_FREQ); outuint(c_audioControl, AUDIO_STOP_FOR_DFU); @@ -1061,7 +1083,7 @@ void XUA_Endpoint0_loop(XUD_Result_t result, USB_SetupPacket_t sp, chanend c_ep0 } /* Endpoint 0 function. Handles all requests to the device */ -void XUA_Endpoint0(chanend c_ep0_out, chanend c_ep0_in, chanend c_audioControl, +void XUA_Endpoint0(chanend c_ep0_out, chanend c_ep0_in, NULLABLE_RESOURCE(chanend, c_audioControl), chanend c_mix_ctl, chanend c_clk_ctl, chanend c_EANativeTransport_ctrl, CLIENT_INTERFACE(i_dfu, dfuInterface) VENDOR_REQUESTS_PARAMS_DEC_) { USB_SetupPacket_t sp; diff --git a/lib_xua/src/core/endpoint0/xua_ep0_descriptors.h b/lib_xua/src/core/endpoint0/xua_ep0_descriptors.h index 47991bf9..91fd3b94 100644 --- a/lib_xua/src/core/endpoint0/xua_ep0_descriptors.h +++ b/lib_xua/src/core/endpoint0/xua_ep0_descriptors.h @@ -1,4 +1,4 @@ -// Copyright 2011-2021 XMOS LIMITED. +// Copyright 2011-2023 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. /** * @file xua_ep0_descriptors.h @@ -10,16 +10,13 @@ #define _DEVICE_DESCRIPTORS_ #include -#include "xua.h" /* Device specific define */ +#include "xua.h" /* Device specific define */ #include "descriptor_defs.h" #include "usbaudio20.h" /* Defines from the USB Audio 2.0 Specifications */ #include "usbaudiocommon.h" #include "xud_device.h" #include "xua_hid_descriptor.h" - -#ifdef IAP_EA_NATIVE_TRANS -#include "iap2.h" /* Defines iAP EA Native Transport protocol name */ -#endif +#include "xud.h" #define APPEND_VENDOR_STR(x) VENDOR_STR" "#x @@ -46,14 +43,25 @@ // The value below must match the length of XUA_DESCR_EMPTY_STRING. #define XUA_MAX_STR_LEN (32) -#define ISO_EP_ATTRIBUTES_ASYNCH 0x05 //ISO, ASYNCH, DATA EP -#define ISO_EP_ATTRIBUTES_ADAPTIVE 0x09 //ISO, ADAPTIVE, DATA EP -#define ISO_EP_IMPL_ATTRIBUTES_ASYNCH 0x25 //ISO, ASYNCH, IMPLICIT FB DATA EP -#define ISO_EP_IMPL_ATTRIBUTES_ADAPTIVE 0x29 //ISO, ADAPTIVE, IMPLICIT FB DATA EP +#define ISO_EP_ATTRIBUTES_ASYNC ((USB_ENDPOINT_TRANSTYPE_ISO << USB_ENDPOINT_TRANSTYPE_SHIFT)\ + | (USB_ENDPOINT_SYNCTYPE_ASYNC << USB_ENDPOINT_SYNCTYPE_SHIFT)\ + | (USB_ENDPOINT_USAGETYPE_DATA << USB_ENDPOINT_USAGETYPE_SHIFT)) -#if (defined(XUA_ADAPTIVE) && (XUA_ADAPTIVE == 0)) -#undef XUA_ADAPTIVE -#endif +#define ISO_EP_ATTRIBUTES_ADAPTIVE ((USB_ENDPOINT_TRANSTYPE_ISO << USB_ENDPOINT_TRANSTYPE_SHIFT)\ + | (USB_ENDPOINT_SYNCTYPE_ADAPT << USB_ENDPOINT_SYNCTYPE_SHIFT)\ + | (USB_ENDPOINT_USAGETYPE_DATA << USB_ENDPOINT_USAGETYPE_SHIFT)) + +#define ISO_EP_ATTRIBUTES_SYNC ((USB_ENDPOINT_TRANSTYPE_ISO << USB_ENDPOINT_TRANSTYPE_SHIFT)\ + | (USB_ENDPOINT_SYNCTYPE_SYNC << USB_ENDPOINT_SYNCTYPE_SHIFT)\ + | (USB_ENDPOINT_USAGETYPE_DATA << USB_ENDPOINT_USAGETYPE_SHIFT)) + +#define ISO_EP_IMPL_ATTRIBUTES_ASYNC ((USB_ENDPOINT_TRANSTYPE_ISO << USB_ENDPOINT_TRANSTYPE_SHIFT)\ + | (USB_ENDPOINT_SYNCTYPE_ASYNC << USB_ENDPOINT_SYNCTYPE_SHIFT)\ + | (USB_ENDPOINT_USAGETYPE_IMPLICIT << USB_ENDPOINT_USAGETYPE_SHIFT)) + +#define ISO_EP_IMPL_ATTRIBUTES_ADAPT ((USB_ENDPOINT_TRANSTYPE_ISO << USB_ENDPOINT_TRANSTYPE_SHIFT)\ + | (USB_ENDPOINT_SYNCTYPE_ADAPT << USB_ENDPOINT_SYNCTYPE_SHIFT)\ + | (USB_ENDPOINT_USAGETYPE_IMPLICIT << USB_ENDPOINT_USAGETYPE_SHIFT)) #if __STDC__ typedef struct @@ -81,10 +89,10 @@ typedef struct #if (AUDIO_CLASS == 2) STR_TABLE_ENTRY(clockSelectorStr); /* iClockSel */ STR_TABLE_ENTRY(internalClockSourceStr); /* iClockSource for internal clock */ -#if SPDIF_RX +#if XUA_SPDIF_RX_EN STR_TABLE_ENTRY(spdifClockSourceStr); /* iClockSource for external S/PDIF clock */ #endif -#if ADAT_RX +#if (XUA_ADAT_RX_EN) STR_TABLE_ENTRY(adatClockSourceStr); /* iClockSource for external S/PDIF clock */ #endif #endif // AUDIO_CLASS == 2 @@ -300,28 +308,28 @@ typedef struct #error NUM_USB_CHAN > 32 #endif -#if defined(MIXER) && (MAX_MIX_COUNT > 0) +#if (MIXER) && (MAX_MIX_COUNT > 0) STR_TABLE_ENTRY(mixOutStr_1); #endif -#if defined(MIXER) && (MAX_MIX_COUNT > 1) +#if (MIXER) && (MAX_MIX_COUNT > 1) STR_TABLE_ENTRY(mixOutStr_2); #endif -#if defined(MIXER) && (MAX_MIX_COUNT > 2) +#if (MIXER) && (MAX_MIX_COUNT > 2) STR_TABLE_ENTRY(mixOutStr_3); #endif -#if defined(MIXER) && (MAX_MIX_COUNT > 3) +#if (MIXER) && (MAX_MIX_COUNT > 3) STR_TABLE_ENTRY(mixOutStr_4); #endif -#if defined(MIXER) && (MAX_MIX_COUNT > 4) +#if (MIXER) && (MAX_MIX_COUNT > 4) STR_TABLE_ENTRY(mixOutStr_5); #endif -#if defined(MIXER) && (MAX_MIX_COUNT > 5) +#if (MIXER) && (MAX_MIX_COUNT > 5) STR_TABLE_ENTRY(mixOutStr_6); #endif -#if defined(MIXER) && (MAX_MIX_COUNT > 6) +#if (MIXER) && (MAX_MIX_COUNT > 6) STR_TABLE_ENTRY(mixOutStr_7); #endif -#if defined(MIXER) && (MAX_MIX_COUNT > 7) +#if (MIXER) && (MAX_MIX_COUNT > 7) STR_TABLE_ENTRY(mixOutStr_8); #endif #ifdef IAP @@ -355,10 +363,10 @@ StringDescTable_t g_strTable = #if (AUDIO_CLASS == 2) .clockSelectorStr = XUA_CLOCK_SELECTOR_EMPTY_STRING, .internalClockSourceStr = XUA_INTERNAL_CLOCK_SELECTOR_EMPTY_STRING, -#if SPDIF_RX +#if (XUA_SPDIF_RX_EN) .spdifClockSourceStr = XUA_SPDIF_CLOCK_SOURCE_EMPTY_STRING, #endif -#if ADAT_RX +#if (XUA_ADAT_RX_EN) .adatClockSourceStr = XUA_ADAT_CLOCK_SOURCE_EMPTY_STRING, #endif #endif // AUDIO_CLASS == 2 @@ -383,31 +391,31 @@ StringDescTable_t g_strTable = #error NUM_USB_CHAN_IN > 32 #endif -#if defined(MIXER) && (MAX_MIX_COUNT > 0) +#if (MIXER) && (MAX_MIX_COUNT > 0) .mixOutStr_1 = "Mix 1", #endif -#if defined(MIXER) && (MAX_MIX_COUNT > 1) +#if (MIXER) && (MAX_MIX_COUNT > 1) .mixOutStr_2 = "Mix 2", #endif -#if defined(MIXER) && (MAX_MIX_COUNT > 2) +#if (MIXER) && (MAX_MIX_COUNT > 2) .mixOutStr_3 = "Mix 3", #endif -#if defined(MIXER) && (MAX_MIX_COUNT > 3) +#if (MIXER) && (MAX_MIX_COUNT > 3) .mixOutStr_4 = "Mix 4", #endif -#if defined(MIXER) && (MAX_MIX_COUNT > 4) +#if (MIXER) && (MAX_MIX_COUNT > 4) .mixOutStr_5 = "Mix 5", #endif -#if defined(MIXER) && (MAX_MIX_COUNT > 5) +#if (MIXER) && (MAX_MIX_COUNT > 5) .mixOutStr_6 = "Mix 6", #endif -#if defined(MIXER) && (MAX_MIX_COUNT > 6) +#if (MIXER) && (MAX_MIX_COUNT > 6) .mixOutStr_7 = "Mix 7", #endif -#if defined(MIXER) && (MAX_MIX_COUNT > 7) +#if (MIXER) && (MAX_MIX_COUNT > 7) .mixOutStr_8 = "Mix 8", #endif -#if defined(MIXER) && (MAX_MIX_COUNT > 8) +#if (MIXER) && (MAX_MIX_COUNT > 8) #error #endif #ifdef IAP @@ -550,7 +558,7 @@ unsigned char devQualDesc_Null[] = }; -#if defined(MIXER) && !defined(AUDIO_PATH_XUS) && (MAX_MIX_COUNT > 0) +#if (MIXER) && !defined(AUDIO_PATH_XUS) && (MAX_MIX_COUNT > 0) //#warning Extension units on the audio path are required for mixer. Enabling them now. #define AUDIO_PATH_XUS #endif @@ -567,7 +575,7 @@ unsigned char devQualDesc_Null[] = #define DFU_LENGTH (0) #endif -#ifdef MIXER +#if (MIXER) #define MIX_BMCONTROLS_LEN_TMP ((MAX_MIX_COUNT * MIX_INPUTS) / 8) #if ((MAX_MIX_COUNT * MIX_INPUTS)%8)==0 @@ -580,33 +588,6 @@ unsigned char devQualDesc_Null[] = #define MIXER_LENGTH (0) #endif -#if( 0 < HID_CONTROLS ) -unsigned char hidReportDescriptor[] = -{ - 0x05, 0x01, /* Usage Page (Generic Desktop) */ - 0x09, 0x06, /* Usage (Keyboard) */ - 0xa1, 0x01, /* Collection (Application) */ - 0x75, 0x01, /* Report Size (1) */ - 0x95, 0x04, /* Report Count (4) */ - 0x15, 0x00, /* Logical Minimum (0) */ - 0x25, 0x00, /* Logical Maximum (0) */ - 0x81, 0x01, /* Input (Cnst, Ary, Abs, No Wrap, Lin, Pref, No Nul) */ - 0x95, 0x01, /* Report Count (1) */ - 0x25, 0x01, /* Logical Maximum (1) */ - 0x05, 0x0C, /* Usage Page (Consumer) */ - 0x0a, 0x21, 0x02, /* Usage (AC Search) */ - 0x81, 0x02, /* Input (Data, Var, Abs, No Wrap, Lin, Pref, No Nul) */ - 0x0a, 0x26, 0x02, /* Usage (AC Stop) */ - 0x81, 0x02, /* Input (Data, Var, Abs, No Wrap, Lin, Pref, No Nul) */ - 0x95, 0x02, /* Report Count (2) */ - 0x05, 0x07, /* Usage Page (Key Codes) */ - 0x19, 0x72, /* Usage Minimum (Keyboard F23) */ - 0x29, 0x73, /* Usage Maximum (Keyboard F24) */ - 0x81, 0x02, /* Input (Data, Var, Abs, No Wrap, Lin, Pref, No Nul) */ - 0xc0 /* End collection (Application) */ -}; -#endif - /* Max packet sizes: * Samples per channel. e.g (192000+7999/8000) = 24 * Must allow 1 sample extra per chan (24 + 1) = 25 @@ -675,17 +656,17 @@ typedef struct /* Class Specific Audio Control Interface Header Descriptor */ UAC_Descriptor_Interface_AC_t Audio_ClassControlInterface; USB_Descriptor_Audio_ClockSource_t Audio_ClockSource; -#if SPDIF_RX +#if (XUA_SPDIF_RX_EN) USB_Descriptor_Audio_ClockSource_t Audio_ClockSource_SPDIF; #endif -#if ADAT_RX +#if (XUA_ADAT_RX_EN) USB_Descriptor_Audio_ClockSource_t Audio_ClockSource_ADAT; #endif USB_Descriptor_Audio_ClockSelector_t Audio_ClockSelector; #if (NUM_USB_CHAN_OUT > 0) /* Output path */ USB_Descriptor_Audio_InputTerminal_t Audio_Out_InputTerminal; -#if defined(MIXER) && (MAX_MIX_COUNT > 0) +#if (MIXER) && (MAX_MIX_COUNT > 0) USB_Descriptor_Audio_ExtensionUnit_t Audio_Out_ExtensionUnit; #endif #if(OUTPUT_VOLUME_CONTROL == 1) @@ -696,7 +677,7 @@ typedef struct #if (NUM_USB_CHAN_IN > 0) /* Input path */ USB_Descriptor_Audio_InputTerminal_t Audio_In_InputTerminal; -#if defined(MIXER) && (MAX_MIX_COUNT > 0) +#if (MIXER) && (MAX_MIX_COUNT > 0) USB_Descriptor_Audio_ExtensionUnit_t Audio_In_ExtensionUnit; #endif #if(INPUT_VOLUME_CONTROL == 1) @@ -704,13 +685,13 @@ typedef struct #endif USB_Descriptor_Audio_OutputTerminal_t Audio_In_OutputTerminal; #endif -#if defined(MIXER) && (MAX_MIX_COUNT > 0) +#if (MIXER) && (MAX_MIX_COUNT > 0) USB_Descriptor_Audio_ExtensionUnit2_t Audio_Mix_ExtensionUnit; // Currently no struct for mixer unit // USB_Descriptor_Audio_MixerUnit_t Audio_MixerUnit; unsigned char configDesc_MixerUnit[MIXER_LENGTH]; #endif -#if (SPDIF_RX) || (ADAT_RX) +#if (XUA_SPDIF_RX_EN) || (XUA_ADAT_RX_EN) /* Interrupt EP */ USB_Descriptor_Endpoint_t Audio_Int_Endpoint; #endif @@ -734,7 +715,7 @@ typedef struct USB_Descriptor_Audio_Format_Type1_t Audio_Out_Format; USB_Descriptor_Endpoint_t Audio_Out_Endpoint; USB_Descriptor_Audio_Class_AS_Endpoint_t Audio_Out_ClassEndpoint; -#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) +#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) && (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC) USB_Descriptor_Endpoint_t Audio_Out_Fb_Endpoint; #endif #if (OUTPUT_FORMAT_COUNT > 1) @@ -743,7 +724,7 @@ typedef struct USB_Descriptor_Audio_Format_Type1_t Audio_Out_Format_2; USB_Descriptor_Endpoint_t Audio_Out_Endpoint_2; USB_Descriptor_Audio_Class_AS_Endpoint_t Audio_Out_ClassEndpoint_2; -#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) +#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) && (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC) USB_Descriptor_Endpoint_t Audio_Out_Fb_Endpoint_2; #endif #endif // OUTPUT_FORMAT_COUNT > 1 @@ -753,7 +734,7 @@ typedef struct USB_Descriptor_Audio_Format_Type1_t Audio_Out_Format_3; USB_Descriptor_Endpoint_t Audio_Out_Endpoint_3; USB_Descriptor_Audio_Class_AS_Endpoint_t Audio_Out_ClassEndpoint_3; -#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) +#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) && (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC) USB_Descriptor_Endpoint_t Audio_Out_Fb_Endpoint_3; #endif #endif // OUTPUT_FORMAT_COUNT > 2 @@ -825,12 +806,12 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2= .bNumInterfaces = INTERFACE_COUNT, .bConfigurationValue = 0x01, .iConfiguration = 0x00, -#ifdef SELF_POWERED +#if (XUA_POWERMODE == XUA_POWERMODE_SELF) .bmAttributes = 192, #else .bmAttributes = 128, #endif - .bMaxPower = BMAX_POWER, + .bMaxPower = _XUA_BMAX_POWER, }, .Audio_InterfaceAssociation = @@ -852,7 +833,7 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2= .bDescriptorType = USB_DESCTYPE_INTERFACE, .bInterfaceNumber = INTERFACE_NUMBER_AUDIO_CONTROL, .bAlternateSetting = 0x00, /* Must be 0 */ -#if (SPDIF_RX) || (ADAT_RX) +#if (XUA_SPDIF_RX_EN) || (XUA_ADAT_RX_EN) .bNumEndpoints = 0x01, /* 0 or 1 if optional interrupt endpoint is present */ #else .bNumEndpoints = 0x00, @@ -899,7 +880,7 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2= .iClockSource = offsetof(StringDescTable_t, internalClockSourceStr)/sizeof(char *), }, -#if SPDIF_RX +#if XUA_SPDIF_RX_EN /* Clock Source Descriptor (4.7.2.1) */ .Audio_ClockSource_SPDIF = { @@ -923,7 +904,7 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2= }, #endif -#if ADAT_RX +#if (XUA_ADAT_RX_EN) /* Clock Source Descriptor (4.7.2.1) */ .Audio_ClockSource_ADAT = { @@ -957,11 +938,11 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2= .bClockID = ID_CLKSEL, .bNrPins = NUM_CLOCKS, .baCSourceId[0] = ID_CLKSRC_INT, /* baCSourceID */ -#if SPDIF_RX +#if (XUA_SPDIF_RX_EN) ID_CLKSRC_SPDIF, /* baCSourceID */ #endif -#if ADAT_RX +#if (XUA_ADAT_RX_EN) ID_CLKSRC_ADAT, /* baCSourceID */ #endif .bmControl = 0x03, @@ -1187,7 +1168,7 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2= UAC_CS_DESCTYPE_INTERFACE, /* 1 bDescriptorType: CS_INTERFACE */ UAC_CS_AC_INTERFACE_SUBTYPE_FEATURE_UNIT, /* 2 bDescriptorSubType: FEATURE_UNIT */ FU_USBIN, /* 3 bUnitID */ -#if defined(MIXER) && (MAX_MIX_COUNT > 0) +#if (MIXER) && (MAX_MIX_COUNT > 0) ID_XU_IN, /* 4 bSourceID */ #else ID_IT_AUD, /* 4 bSourceID */ @@ -1311,7 +1292,7 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2= .bSourceID = FU_USBIN, /* 7 bSourceID Connect to analog input feature unit*/ #else - .bSourceID = ID_IT_USB,/* 7 bSourceID Connect to analog input term */ + .bSourceID = ID_IT_AUD,/* 7 bSourceID Connect to analog input term */ #endif .bCSourceID = ID_CLKSEL, .bmControls = 0x0000, @@ -1319,7 +1300,7 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2= }, #endif /* (NUM_USB_CHAN_IN > 0) */ -#if defined(MIXER) && (MAX_MIX_COUNT > 0) +#if (MIXER) && (MAX_MIX_COUNT > 0) /* Extension Unit Descriptor (4.7.2.12) */ .Audio_Mix_ExtensionUnit = { @@ -1411,9 +1392,9 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2= 0x00, /* bmControls */ 0 /* Mixer unit string descriptor index */ }, -#endif /* defined(MIXER) && (MAX_MIX_COUNT > 0) */ +#endif /* (MIXER) && (MAX_MIX_COUNT > 0) */ -#if (SPDIF_RX) || (ADAT_RX) +#if (XUA_SPDIF_RX_EN) || (XUA_ADAT_RX_EN) /* Standard AS Interrupt Endpoint Descriptor (4.8.2.1): */ .Audio_Int_Endpoint = { @@ -1451,7 +1432,7 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2= USB_DESCTYPE_INTERFACE, /* 1 bDescriptorType: INTERFACE */ INTERFACE_NUMBER_AUDIO_OUTPUT, /* 2 bInterfaceNumber: Number of interface */ 1, /* 3 bAlternateSetting */ -#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) +#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) && (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC) 2, /* 4 bNumEndpoints */ #else 1, /* 4 bNumEndpoints */ @@ -1494,14 +1475,18 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2= .bLength = sizeof(USB_Descriptor_Endpoint_t), .bDescriptorType = USB_DESCTYPE_ENDPOINT, .bEndpointAddress = ENDPOINT_ADDRESS_OUT_AUDIO, -#ifdef XUA_ADAPTIVE +#if (XUA_SYNCMODE == XUA_SYNCMODE_ADAPT) .bmAttributes = ISO_EP_ATTRIBUTES_ADAPTIVE, -#else +#elif (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC) #if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) - .bmAttributes = ISO_EP_ATTRIBUTES_ASYNCH, /* Iso, async, data endpoint */ + .bmAttributes = ISO_EP_ATTRIBUTES_ASYNC, /* Iso, async, data endpoint */ #else - .bmAttributes = ISO_EP_IMPL_ATTRIBUTES_ASYNCH, /* Feedback data endpoint */ + .bmAttributes = ISO_EP_IMPL_ATTRIBUTES_ASYNC, /* Feedback data endpoint */ #endif +#elif (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) + .bmAttributes = ISO_EP_ATTRIBUTES_SYNC, +#else + #error "Bad XUA_SYNCMODE" #endif .wMaxPacketSize = HS_STREAM_FORMAT_OUTPUT_1_MAXPACKETSIZE, .bInterval = 1, @@ -1519,7 +1504,7 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2= 0x0008, /* 6:7 bLockDelay */ }, -#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) +#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) && (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC) .Audio_Out_Fb_Endpoint = { .bLength = 0x07, @@ -1538,7 +1523,7 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2= USB_DESCTYPE_INTERFACE, /* 1 bDescriptorType: INTERFACE */ INTERFACE_NUMBER_AUDIO_OUTPUT, /* 2 bInterfaceNumber: Number of interface */ 2, /* 3 bAlternateSetting */ -#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) +#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) && (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC) 2, /* 4 bNumEndpoints */ #else 1, /* 4 bNumEndpoints */ @@ -1580,14 +1565,18 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2= .bLength = sizeof(USB_Descriptor_Endpoint_t), .bDescriptorType = USB_DESCTYPE_ENDPOINT, .bEndpointAddress = ENDPOINT_ADDRESS_OUT_AUDIO, -#ifdef XUA_ADAPTIVE +#if (XUA_SYNCMODE == XUA_SYNCMODE_ADAPT) .bmAttributes = ISO_EP_ATTRIBUTES_ADAPTIVE, +#elif (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC) + #if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) + .bmAttributes = ISO_EP_ATTRIBUTES_ASYNC, /* Iso, Async, data endpoint */ + #else + .bmAttributes = ISO_EP_IMPL_ATTRIBUTES_ASYNC, /* Feedback data endpoint */ + #endif +#elif (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) + .bmAttributes = ISO_EP_ATTRIBUTES_SYNC, /* Iso, Sync, data endpoint */ #else - #if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) - .bmAttributes = ISO_EP_ATTRIBUTES_ASYNCH, /* Iso, async, data endpoint */ - #else - .bmAttributes = ISO_EP_IMPL_ATTRIBUTES_ASYNCH, /* Feedback data endpoint */ - #endif + #error "Bad XUA_SYNCMODE" #endif .wMaxPacketSize = HS_STREAM_FORMAT_OUTPUT_2_MAXPACKETSIZE, .bInterval = 1, @@ -1605,7 +1594,7 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2= 0x0008, /* 6:7 bLockDelay */ }, -#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) +#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) && (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC) .Audio_Out_Fb_Endpoint_2 = { 0x07, /* 0 bLength: 7 */ @@ -1625,7 +1614,7 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2= USB_DESCTYPE_INTERFACE, /* 1 bDescriptorType: INTERFACE */ INTERFACE_NUMBER_AUDIO_OUTPUT, /* 2 bInterfaceNumber: Number of interface */ 3, /* 3 bAlternateSetting */ -#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) +#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) && (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC) 2, /* 4 bNumEndpoints */ #else 1, /* 4 bNumEndpoints */ @@ -1668,14 +1657,18 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2= .bLength = 0x07, .bDescriptorType = USB_DESCTYPE_ENDPOINT, .bEndpointAddress = ENDPOINT_ADDRESS_OUT_AUDIO, -#ifdef XUA_ADAPTIVE - .bmAttributes = ISO_EP_ATTRIBUTES_ADAPTIVE, +#if (XUA_SYNCMODE == XUA_SYNCMODE_ADAPT) + .bmAttributes = ISO_EP_ATTRIBUTES_ADAPTIVE, +#elif (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC) + #if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) + .bmAttributes = ISO_EP_ATTRIBUTES_ASYNC, /* Iso, Async, data endpoint */ + #else + .bmAttributes = ISO_EP_IMPL_ATTRIBUTES_ASYNC, /* Feedback data endpoint */ + #endif +#elif (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) + .bmAttributes = ISO_EP_ATTRIBUTES_SYNC, /* Iso, Sync, data endpoint */ #else - #if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) - .bmAttributes = ISO_EP_ATTRIBUTES_ASYNCH, /* Iso, async, data endpoint */ - #else - .bmAttributes = ISO_EP_IMPL_ATTRIBUTES_ASYNCH, /* Feedback data endpoint */ - #endif + #error "Bad XUA_SYNCMODE" #endif .wMaxPacketSize = HS_STREAM_FORMAT_OUTPUT_3_MAXPACKETSIZE, .bInterval = 1, @@ -1693,7 +1686,7 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2= .wLockDelay = 0x0008, }, -#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) +#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) && (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC) .Audio_Out_Fb_Endpoint_3 = { .bLength = 0x07, @@ -1769,14 +1762,18 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2= .bLength = 0x07, .bDescriptorType = USB_DESCTYPE_ENDPOINT, .bEndpointAddress = ENDPOINT_ADDRESS_IN_AUDIO, -#ifdef XUA_ADAPTIVE - .bmAttributes = ISO_EP_ATTRIBUTES_ADAPTIVE, +#if (XUA_SYNCMODE == XUA_SYNCMODE_ADAPT) + .bmAttributes = ISO_EP_ATTRIBUTES_ADAPTIVE, +#elif (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC) + #if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) + .bmAttributes = ISO_EP_ATTRIBUTES_ASYNC, /* Iso, Async, data endpoint */ + #else + .bmAttributes = ISO_EP_IMPL_ATTRIBUTES_ASYNC, /* Feedback data endpoint */ + #endif +#elif (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) + .bmAttributes = ISO_EP_ATTRIBUTES_SYNC, /* Iso, Sync, data endpoint */ #else - #if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) - .bmAttributes = ISO_EP_ATTRIBUTES_ASYNCH, /* Iso, async, data endpoint */ - #else - .bmAttributes = ISO_EP_IMPL_ATTRIBUTES_ASYNCH, /* Feedback data endpoint */ - #endif + #error "Bad XUA_SYNCMODE" #endif .wMaxPacketSize = HS_STREAM_FORMAT_INPUT_1_MAXPACKETSIZE, .bInterval = 0x01, @@ -1841,14 +1838,18 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2= .bLength = 0x07, .bDescriptorType = USB_DESCTYPE_ENDPOINT, .bEndpointAddress = ENDPOINT_ADDRESS_IN_AUDIO, -#ifdef XUA_ADAPTIVE - .bmAttributes = ISO_EP_ATTRIBUTES_ADAPTIVE, +#if (XUA_SYNCMODE == XUA_SYNCMODE_ADAPT) + .bmAttributes = ISO_EP_ATTRIBUTES_ADAPTIVE, +#elif (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC) + #if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) + .bmAttributes = ISO_EP_ATTRIBUTES_ASYNC, /* Iso, Async, data endpoint */ + #else + .bmAttributes = ISO_EP_IMPL_ATTRIBUTES_ASYNC, /* Feedback data endpoint */ + #endif +#elif (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) + .bmAttributes = ISO_EP_ATTRIBUTES_SYNC, /* Iso, Sync, data endpoint */ #else - #if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) - .bmAttributes = ISO_EP_ATTRIBUTES_ASYNCH, /* Iso, async, data endpoint */ - #else - .bmAttributes = ISO_EP_IMPL_ATTRIBUTES_ASYNCH, /* Feedback data endpoint */ - #endif + #error "Bad XUA_SYNCMODE" #endif .wMaxPacketSize = HS_STREAM_FORMAT_INPUT_2_MAXPACKETSIZE, .bInterval = 0x01, @@ -1914,14 +1915,18 @@ USB_Config_Descriptor_Audio2_t cfgDesc_Audio2= .bLength = 0x07, .bDescriptorType = USB_DESCTYPE_ENDPOINT, .bEndpointAddress = ENDPOINT_ADDRESS_IN_AUDIO, -#ifdef XUA_ADAPTIVE - .bmAttributes = ISO_EP_ATTRIBUTES_ADAPTIVE, +#if (XUA_SYNCMODE == XUA_SYNCMODE_ADAPT) + .bmAttributes = ISO_EP_ATTRIBUTES_ADAPTIVE, +#elif (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC) + #if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) + .bmAttributes = ISO_EP_ATTRIBUTES_ASYNC, /* Iso, Async, data endpoint */ + #else + .bmAttributes = ISO_EP_IMPL_ATTRIBUTES_ASYNC, /* Feedback data endpoint */ + #endif +#elif (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) + .bmAttributes = ISO_EP_ATTRIBUTES_SYNC, /* Iso, Sync, data endpoint */ #else - #if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) - .bmAttributes = ISO_EP_ATTRIBUTES_ASYNCH, /* Iso, async, data endpoint */ - #else - .bmAttributes = ISO_EP_IMPL_ATTRIBUTES_ASYNCH, /* Feedback data endpoint */ - #endif + #error "Bad XUA_SYNCMODE" #endif .wMaxPacketSize = HS_STREAM_FORMAT_INPUT_3_MAXPACKETSIZE, .bInterval = 0x01, @@ -2234,12 +2239,12 @@ unsigned char cfgDesc_Null[] = 0x01, /* 4 bNumInterface: Number of interfaces*/ 0x01, /* 5 bConfigurationValue */ 0x00, /* 6 iConfiguration */ -#ifdef SELF_POWERED +#if (XUA_POWERMODE == XUA_POWERMODE_SELF) 192, /* 7 bmAttributes */ #else 128, #endif - BMAX_POWER, /* 8 bMaxPower */ + _XUA_BMAX_POWER, /* 8 bMaxPower */ 0x09, /* 0 bLength : Size of this descriptor, in bytes. (field size 1 bytes) */ 0x04, /* 1 bDescriptorType : INTERFACE descriptor. (field size 1 bytes) */ @@ -2352,15 +2357,16 @@ const unsigned num_freqs_a1 = MAX(3, (0 /* Note, this is different that INTERFACE_COUNT since we dont support items such as MIDI, iAP etc in UAC1 mode */ #define NUM_INTERFACES_A1 (1 + INPUT_INTERFACES_A1 + OUTPUT_INTERFACES_A1 + NUM_CONTROL_USB_INTERFACES + DFU_INTERFACES_A1 + HID_INTERFACES_A1) -#if ((NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP)) +#if ((NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP)) && (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC) #define CFG_TOTAL_LENGTH_A1 (18 + AC_TOTAL_LENGTH + (INPUT_INTERFACES_A1 * (49 + num_freqs_a1 * 3)) + (OUTPUT_INTERFACES_A1 * (58 + num_freqs_a1 * 3)) + CONTROL_INTERFACE_BYTES + DFU_INTERFACE_BYTES + HID_INTERFACE_BYTES) #else #define CFG_TOTAL_LENGTH_A1 (18 + AC_TOTAL_LENGTH + (INPUT_INTERFACES_A1 * (49 + num_freqs_a1 * 3)) + (OUTPUT_INTERFACES_A1 * (49 + num_freqs_a1 * 3)) + CONTROL_INTERFACE_BYTES + DFU_INTERFACE_BYTES + HID_INTERFACE_BYTES) #endif +#define INTERFACE_DESCRIPTOR_BYTES (9) + #ifdef XUA_USB_DESCRIPTOR_OVERWRITE_RATE_RES #define AS_INTERFACE_BYTES (7) - #define INTERFACE_DESCRIPTOR_BYTES (9) #define AS_FORMAT_TYPE_BYTES (17) #define USB_AS_IN_INTERFACE_DESCRIPTOR_OFFSET_SUB_FRAME (18 + AC_TOTAL_LENGTH + (OUTPUT_INTERFACES_A1 * (49 + num_freqs_a1 * 3)) + (2*INTERFACE_DESCRIPTOR_BYTES) + (AS_INTERFACE_BYTES) + 5) #define USB_AS_OUT_INTERFACE_DESCRIPTOR_OFFSET_SUB_FRAME (18 + AC_TOTAL_LENGTH + (2*INTERFACE_DESCRIPTOR_BYTES) + (AS_INTERFACE_BYTES) + 5) @@ -2373,6 +2379,10 @@ const unsigned num_freqs_a1 = MAX(3, (0 #endif +#if( 0 < HID_CONTROLS ) + #define USB_HID_DESCRIPTOR_OFFSET (18 + AC_TOTAL_LENGTH + (INPUT_INTERFACES_A1 * (49 + num_freqs_a1 * 3)) + (OUTPUT_INTERFACES_A1 * (49 + num_freqs_a1 * 3)) + CONTROL_INTERFACE_BYTES + DFU_INTERFACE_BYTES + INTERFACE_DESCRIPTOR_BYTES) +#endif + #define CHARIFY_SR(x) (x & 0xff),((x & 0xff00)>> 8),((x & 0xff0000)>> 16) #if (MIN_FREQ_FS < 12000) && (MAX_FREQ_FS > 48000) @@ -2389,12 +2399,12 @@ unsigned char cfgDesc_Audio1[] = NUM_INTERFACES_A1, /* numInterfaces - we dont support MIDI in audio 1.0 mode*/ 0x01, /* ID of this configuration */ 0x00, /* Unused */ -#ifdef SELF_POWERED +#if (XUA_POWERMODE == XUA_POWERMODE_SELF) 192, /* 7 bmAttributes */ #else 128, /* 7 bmAttributes */ #endif - BMAX_POWER, /* 8 bMaxPower */ + _XUA_BMAX_POWER, /* 8 bMaxPower */ /* Standard AC interface descriptor */ 0x09, @@ -2579,7 +2589,7 @@ unsigned char cfgDesc_Audio1[] = 0x04, /* INTERFACE */ 0x01, /* bInterfaceNumber */ 0x01, /* bAlternateSetting */ -#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) +#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) && (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC) 0x02, /* bNumEndpoints 2: audio EP and feedback EP */ #else 0x01, /* bNumEndpoints */ @@ -2662,27 +2672,31 @@ unsigned char cfgDesc_Audio1[] = 0x09, 0x05, /* ENDPOINT */ ENDPOINT_ADDRESS_OUT_AUDIO, /* endpointAddress - D7, direction (0 OUT, 1 IN). D6..4 reserved (0). D3..0 endpoint no. */ -#ifdef XUA_ADAPTIVE +#if (XUA_SYNCMODE == XUA_SYNCMODE_ADAPT) ISO_EP_ATTRIBUTES_ADAPTIVE, -#else +#elif (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC) #if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) - ISO_EP_ATTRIBUTES_ASYNCH, /* Iso, async, data endpoint */ + ISO_EP_ATTRIBUTES_ASYNC, /* Iso, async, data endpoint */ #else - ISO_EP_IMPL_ATTRIBUTES_ASYNCH, /* Feedback data endpoint */ + ISO_EP_IMPL_ATTRIBUTES_ASYNC, /* Feedback data endpoint */ #endif +#elif (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) + ISO_EP_ATTRIBUTES_SYNC, /* Iso, sync, data endpoint */ +#else +#error "Unsupported XUA_SYNCMODE" #endif (FS_STREAM_FORMAT_OUTPUT_1_MAXPACKETSIZE&0xff), /* 4 wMaxPacketSize (Typically 294 bytes)*/ (FS_STREAM_FORMAT_OUTPUT_1_MAXPACKETSIZE&0xff00)>>8, /* 5 wMaxPacketSize */ 0x01, /* bInterval */ 0x00, /* bRefresh */ -#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) +#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) && (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC) ENDPOINT_ADDRESS_IN_FEEDBACK, /* bSynchAdddress - address of EP used to communicate sync info */ -#else /* Bi-directional in/out device */ -#ifdef XUA_ADAPTIVE - 0, /* OUT */ -#else - ENDPOINT_ADDRESS_IN_AUDIO, -#endif +#else /* Bi-directional in/out device */ + #if (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC) + ENDPOINT_ADDRESS_IN_AUDIO, + #else + 0, /* Unused */ + #endif #endif /* CS_Endpoint Descriptor ?? */ @@ -2691,13 +2705,13 @@ unsigned char cfgDesc_Audio1[] = 0x01, /* subtype - GENERAL */ 0x01, /* attributes. D[0]: sample freq ctrl. */ 0x02, /* bLockDelayUnits */ -#ifdef XUA_ADAPTIVE +#if (XUA_SYNCMODE == XUA_SYNCMODE_ADAPT) 0x08, 0x00, /* bLockDelay */ #else 0x00, 0x00, /* Not used */ #endif -#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) +#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) && (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC) /* Feedback EP */ 0x09, 0x05, /* bDescriptorType: ENDPOINT */ @@ -2806,14 +2820,18 @@ unsigned char cfgDesc_Audio1[] = 0x09, 0x05, /* ENDPOINT */ ENDPOINT_ADDRESS_IN_AUDIO, /* EndpointAddress */ -#ifdef XUA_ADAPTIVE +#if (XUA_SYNCMODE == XUA_SYNCMODE_ADAPT) ISO_EP_ATTRIBUTES_ADAPTIVE, -#else +#elif (XUA_SYNCMODE == XUA_SYNCMODE_ASYNC) #if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) - ISO_EP_ATTRIBUTES_ASYNCH, /* Iso, async, data endpoint */ + ISO_EP_ATTRIBUTES_ASYNC, /* Iso, async, data endpoint */ #else - ISO_EP_IMPL_ATTRIBUTES_ASYNCH, /* Feedback data endpoint */ + ISO_EP_IMPL_ATTRIBUTES_ASYNC, /* Feedback data endpoint */ #endif +#elif (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) + ISO_EP_ATTRIBUTES_SYNC, /* Iso, sync, data endpoint */ +#else +#error "Unsupported XUA_SYNCMODE" #endif FS_STREAM_FORMAT_INPUT_1_MAXPACKETSIZE&0xff, /* 4 wMaxPacketSize (Typically 294 bytes)*/ (FS_STREAM_FORMAT_INPUT_1_MAXPACKETSIZE&0xff00)>>8, /* 5 wMaxPacketSize */ @@ -2826,13 +2844,13 @@ unsigned char cfgDesc_Audio1[] = 0x25, /* CS_ENDPOINT */ 0x01, /* Subtype - GENERAL */ 0x01, /* Attributes. D[0]: sample freq ctrl. */ -#ifdef XUA_ADAPTIVE +#if (XUA_SYNCMODE == XUA_SYNCMODE_ADAPT) 0x02, /* Lock Delay units PCM samples*/ 0x08, 0x00, /* No lock delay */ #else 0x00, /* Undefined */ 0x00, 0x00, /* Not used */ -#endif // XUA_ADAPTIVE +#endif #endif // NUM_USB_CHAN_IN > 0 #if (XUA_DFU_EN == 1) && (FORCE_UAC1_DFU == 1) diff --git a/lib_xua/src/core/endpoint0/xua_ep0_uacreqs.xc b/lib_xua/src/core/endpoint0/xua_ep0_uacreqs.xc index 89169336..5ef46010 100644 --- a/lib_xua/src/core/endpoint0/xua_ep0_uacreqs.xc +++ b/lib_xua/src/core/endpoint0/xua_ep0_uacreqs.xc @@ -1,4 +1,4 @@ -// Copyright 2011-2021 XMOS LIMITED. +// Copyright 2011-2023 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. /** * @brief Implements relevant requests from the USB Audio 2.0 Specification @@ -14,29 +14,29 @@ #include "usbaudio10.h" #include "dbcalc.h" #include "xua_commands.h" -#include "xc_ptr.h" -#ifdef MIXER -#include "mixer.h" -#endif #define CS_XU_MIXSEL (0x06) +/* From decouple.xc */ +#if (OUT_VOLUME_IN_MIXER == 0) && (OUTPUT_VOLUME_CONTROL == 1) extern unsigned int multOut[NUM_USB_CHAN_OUT + 1]; +#endif +#if (IN_VOLUME_IN_MIXER == 0) && (INPUT_VOLUME_CONTROL == 1) extern unsigned int multIn[NUM_USB_CHAN_IN + 1]; +#endif extern int interfaceAlt[]; -/* Global volume and mute tables */ +/* Global volume and mute tables - from xua_endpoint0.c */ extern int volsOut[]; extern unsigned int mutesOut[]; extern int volsIn[]; extern unsigned int mutesIn[]; -/* Mixer settings */ -#ifdef MIXER -extern unsigned char mixer1Crossbar[]; -extern short mixer1Weights[]; +#if (MIXER) +/* Mixer weights */ +extern short mixer1Weights[MIX_INPUTS * MAX_MIX_COUNT]; /* Device channel mapping */ extern unsigned char channelMapAud[NUM_USB_CHAN_OUT]; @@ -105,20 +105,6 @@ void FeedbackStabilityDelay() t when timerafter(time + delay):> void; } -#if 0 -/* Original feedback implementation */ -unsafe -{ - unsigned * unsafe curSamFreqMultiplier = &g_curSamFreqMultiplier; - -static void setG_curSamFreqMultiplier(unsigned x) -{ - // asm(" stw %0, dp[g_curSamFreqMultiplier]" :: "r"(x)); - *curSamFreqMultiplier = x; -} -} -#endif - #if (OUTPUT_VOLUME_CONTROL == 1) || (INPUT_VOLUME_CONTROL == 1) static unsigned longMul(unsigned a, unsigned b, int prec) { @@ -133,16 +119,9 @@ static unsigned longMul(unsigned a, unsigned b, int prec) } /* Update master volume i.e. i.e update weights for all channels */ -static void updateMasterVol( int unitID, chanend ?c_mix_ctl) +static void updateMasterVol(int unitID, chanend ?c_mix_ctl) { - int x; -#ifndef OUT_VOLUME_IN_MIXER - xc_ptr p_multOut = array_to_xc_ptr(multOut); -#endif -#ifndef IN_VOLUME_IN_MIXER - xc_ptr p_multIn = array_to_xc_ptr(multIn); -#endif - switch( unitID) + switch(unitID) { case FU_USBOUT: { @@ -154,18 +133,24 @@ static void updateMasterVol( int unitID, chanend ?c_mix_ctl) /* 0x8000 is a special value representing -inf (i.e. mute) */ unsigned vol = volsOut[i] == 0x8000 ? 0 : db_to_mult(volsOut[i], 8, 29); - x = longMul(master_vol, vol, 29) * !mutesOut[0] * !mutesOut[i]; + int x = longMul(master_vol, vol, 29) * !mutesOut[0] * !mutesOut[i]; -#ifdef OUT_VOLUME_IN_MIXER +#if (OUT_VOLUME_IN_MIXER) if (!isnull(c_mix_ctl)) { + outct(c_mix_ctl, XS1_CT_END); + inct(c_mix_ctl); outuint(c_mix_ctl, SET_MIX_OUT_VOL); outuint(c_mix_ctl, i-1); outuint(c_mix_ctl, x); outct(c_mix_ctl, XS1_CT_END); } -#else - asm("stw %0, %1[%2]"::"r"(x),"r"(p_multOut),"r"(i-1)); +#else + unsafe + { + unsigned int * unsafe multOutPtr = multOut; + multOutPtr[i-1] = x; + } #endif } } @@ -180,18 +165,24 @@ static void updateMasterVol( int unitID, chanend ?c_mix_ctl) /* 0x8000 is a special value representing -inf (i.e. mute) */ unsigned vol = volsIn[i] == 0x8000 ? 0 : db_to_mult(volsIn[i], 8, 29); - x = longMul(master_vol, vol, 29) * !mutesIn[0] * !mutesIn[i]; + int x = longMul(master_vol, vol, 29) * !mutesIn[0] * !mutesIn[i]; -#ifdef IN_VOLUME_IN_MIXER +#if (IN_VOLUME_IN_MIXER) if (!isnull(c_mix_ctl)) { + outct(c_mix_ctl, XS1_CT_END); + inct(c_mix_ctl); outuint(c_mix_ctl, SET_MIX_IN_VOL); outuint(c_mix_ctl, i-1); outuint(c_mix_ctl, x); outct(c_mix_ctl, XS1_CT_END); } #else - asm("stw %0, %1[%2]"::"r"(x),"r"(p_multIn),"r"(i-1)); + unsafe + { + unsigned int * unsafe multInPtr = multIn; + multInPtr[i-1] = x; + } #endif } } @@ -205,12 +196,6 @@ static void updateMasterVol( int unitID, chanend ?c_mix_ctl) static void updateVol(int unitID, int channel, chanend ?c_mix_ctl) { int x; -#ifndef OUT_VOLUME_IN_MIXER - xc_ptr p_multOut = array_to_xc_ptr(multOut); -#endif -#ifndef IN_VOLUME_IN_MIXER - xc_ptr p_multIn = array_to_xc_ptr(multIn); -#endif /* Check for master volume update */ if (channel == 0) { @@ -229,16 +214,22 @@ static void updateVol(int unitID, int channel, chanend ?c_mix_ctl) x = longMul(master_vol, vol, 29) * !mutesOut[0] * !mutesOut[channel]; -#ifdef OUT_VOLUME_IN_MIXER +#if (OUT_VOLUME_IN_MIXER) if (!isnull(c_mix_ctl)) { + outct(c_mix_ctl, XS1_CT_END); + inct(c_mix_ctl); outuint(c_mix_ctl, SET_MIX_OUT_VOL); outuint(c_mix_ctl, channel-1); outuint(c_mix_ctl, x); outct(c_mix_ctl, XS1_CT_END); } #else - asm("stw %0, %1[%2]"::"r"(x),"r"(p_multOut),"r"(channel-1)); + unsafe + { + unsigned int * unsafe multOutPtr = multOut; + multOutPtr[channel-1] = x; + } #endif break; } @@ -247,20 +238,26 @@ static void updateVol(int unitID, int channel, chanend ?c_mix_ctl) /* Calc multipliers with 29 fractional bits from a db value with 8 fractional bits */ /* 0x8000 is a special value representing -inf (i.e. mute) */ unsigned master_vol = volsIn[0] == 0x8000 ? 0 : db_to_mult(volsIn[0], 8, 29); - unsigned vol = volsIn[channel] == 0x8000 ? 0 : db_to_mult(volsIn[channel], 8, 29); + unsigned vol = volsIn[channel] == 0x8000 ? 0 : db_to_mult(volsIn[channel], 8, 29); x = longMul(master_vol, vol, 29) * !mutesIn[0] * !mutesIn[channel]; -#ifdef IN_VOLUME_IN_MIXER +#if (IN_VOLUME_IN_MIXER) if (!isnull(c_mix_ctl)) { + outct(c_mix_ctl, XS1_CT_END); + inct(c_mix_ctl); outuint(c_mix_ctl, SET_MIX_IN_VOL); outuint(c_mix_ctl, channel-1); outuint(c_mix_ctl, x); outct(c_mix_ctl, XS1_CT_END); } #else - asm("stw %0, %1[%2]"::"r"(x),"r"(p_multIn),"r"(channel-1)); + unsafe + { + unsigned int * unsafe multInPtr = multIn; + multInPtr[channel-1] = x; + } #endif break; } @@ -269,6 +266,38 @@ static void updateVol(int unitID, int channel, chanend ?c_mix_ctl) } #endif +void UpdateMixerOutputRouting(chanend c_mix_ctl, unsigned map, unsigned dst, unsigned src) +{ + outct(c_mix_ctl, XS1_CT_END); + inct(c_mix_ctl); + outuint(c_mix_ctl, map); + outuint(c_mix_ctl, dst); + outuint(c_mix_ctl, src); + outct(c_mix_ctl, XS1_CT_END); +} + +void UpdateMixMap(chanend c_mix_ctl, int mix, int input, int src) +{ + outct(c_mix_ctl, XS1_CT_END); + inct(c_mix_ctl); + outuint(c_mix_ctl, SET_MIX_MAP); + outuint(c_mix_ctl, mix); /* Mix bus */ + outuint(c_mix_ctl, input); /* Mixer input (cn) */ + outuint(c_mix_ctl, src); /* Source (mixSel[cn]) */ + outct(c_mix_ctl, XS1_CT_END); +} + +void UpdateMixerWeight(chanend c_mix_ctl, int mix, int index, unsigned mult) +{ + outct(c_mix_ctl, XS1_CT_END); + inct(c_mix_ctl); + outuint(c_mix_ctl, SET_MIX_MULT); + outuint(c_mix_ctl, mix); + outuint(c_mix_ctl, index); + outuint(c_mix_ctl, mult); + outct(c_mix_ctl, XS1_CT_END); +} + /* Handles the audio class specific requests * returns: XUD_RES_OKAY if request dealt with successfully without error, * XUD_RES_RST for device reset @@ -285,7 +314,6 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c /* Inspect request, NOTE: these are class specific requests */ switch( sp.bRequest ) { - /* CUR Request*/ case CUR: { @@ -341,7 +369,7 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c setG_curSamFreqMultiplier(g_curSamFreq/(newMasterClock/512)); #endif -#if ADAT_RX +#if (XUA_ADAT_RX_EN) /* Configure ADAT SMUX based on sample rate */ outuint(c_clk_ctl, SET_SMUX); if(g_curSamFreq < 88200) @@ -374,7 +402,7 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c } #endif /* MAX_FREQ != MIN_FREQ */ /* Send 0 Length as status stage */ - XUD_DoSetRequestStatus(ep0_in); + return XUD_DoSetRequestStatus(ep0_in); } /* Direction: Device-to-host: Send Current Sample Freq */ else @@ -540,7 +568,7 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c if ((sp.wValue & 0xff) <= NUM_USB_CHAN_OUT) { volsOut[ sp.wValue&0xff ] = (buffer, unsigned char[])[0] | (((int) (signed char) (buffer, unsigned char[])[1]) << 8); - updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl ); + updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl); return XUD_DoSetRequestStatus(ep0_in); } } @@ -549,7 +577,7 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c if ((sp.wValue & 0xff) <= NUM_USB_CHAN_IN) { volsIn[ sp.wValue&0xff ] = (buffer, unsigned char[])[0] | (((int) (signed char) (buffer, unsigned char[])[1]) << 8); - updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl ); + updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl); return XUD_DoSetRequestStatus(ep0_in); } } @@ -635,85 +663,76 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c break; /* FU_USBIN */ #endif -#if defined(MIXER) && (MAX_MIX_COUNT > 0) +#if ((MIXER) && (MAX_MIX_COUNT > 0)) case ID_XU_OUT: - { - if(sp.bmRequestType.Direction == USB_BM_REQTYPE_DIRECTION_H2D) /* Direction: Host-to-device */ { - unsigned volume = 0; - int c = sp.wValue & 0xff; + int dst = sp.wValue & 0xff; - - if((result = XUD_GetBuffer(ep0_out, (buffer, unsigned char[]), datalength)) != XUD_RES_OKAY) + if(sp.bmRequestType.Direction == USB_BM_REQTYPE_DIRECTION_H2D) /* Direction: Host-to-device */ { - return result; - } - - channelMapAud[c] = (buffer, unsigned char[])[0] | (buffer, unsigned char[])[1] << 8; - - if (!isnull(c_mix_ctl)) - { - if (c < NUM_USB_CHAN_OUT) + if((result = XUD_GetBuffer(ep0_out, (buffer, unsigned char[]), datalength)) != XUD_RES_OKAY) { - outuint(c_mix_ctl, SET_SAMPLES_TO_DEVICE_MAP); - outuint(c_mix_ctl, c); - outuint(c_mix_ctl, channelMapAud[c]); - outct(c_mix_ctl, XS1_CT_END); - /* Send 0 Length as status stage */ - return XUD_DoSetRequestStatus(ep0_in); + return result; } + + if (dst < NUM_USB_CHAN_OUT) + { + channelMapAud[dst] = (buffer, unsigned char[])[0] | (buffer, unsigned char[])[1] << 8; + + if (!isnull(c_mix_ctl)) + { + UpdateMixerOutputRouting(c_mix_ctl, SET_SAMPLES_TO_DEVICE_MAP, dst, channelMapAud[dst]); + } + } + + /* Send 0 Length as status stage */ + return XUD_DoSetRequestStatus(ep0_in); + } + else + { + (buffer, unsigned char[])[0] = channelMapAud[dst]; + (buffer, unsigned char[])[1] = 0; + return XUD_DoGetRequest(ep0_out, ep0_in, (buffer, unsigned char[]), sp.wLength, sp.wLength); } - } - else - { - (buffer, unsigned char[])[0] = channelMapAud[sp.wValue & 0xff]; - (buffer, unsigned char[])[1] = 0; - - return XUD_DoGetRequest(ep0_out, ep0_in, (buffer, unsigned char[]), sp.wLength, sp.wLength); - } - - } break; case ID_XU_IN: - if(sp.bmRequestType.Direction == USB_BM_REQTYPE_DIRECTION_H2D) /* Direction: Host-to-device */ { - unsigned volume = 0; - int c = sp.wValue & 0xff; + int dst = sp.wValue & 0xff; - if((result = XUD_GetBuffer(ep0_out, (buffer, unsigned char[]), datalength)) != XUD_RES_OKAY) + if(sp.bmRequestType.Direction == USB_BM_REQTYPE_DIRECTION_H2D) /* Direction: Host-to-device */ { - return result; - } - - channelMapUsb[c] = (buffer, unsigned char[])[0] | (buffer, unsigned char[])[1] << 8; - - if (c < NUM_USB_CHAN_IN) - { - if (!isnull(c_mix_ctl)) + if((result = XUD_GetBuffer(ep0_out, (buffer, unsigned char[]), datalength)) != XUD_RES_OKAY) { - outuint(c_mix_ctl, SET_SAMPLES_TO_HOST_MAP); - outuint(c_mix_ctl, c); - outuint(c_mix_ctl, channelMapUsb[c]); - outct(c_mix_ctl, XS1_CT_END); - return XUD_DoSetRequestStatus(ep0_in); + return result; } + + if (dst < NUM_USB_CHAN_IN) + { + channelMapUsb[dst] = (buffer, unsigned char[])[0] | (buffer, unsigned char[])[1] << 8; + + if (!isnull(c_mix_ctl)) + { + UpdateMixerOutputRouting(c_mix_ctl, SET_SAMPLES_TO_HOST_MAP, dst, channelMapUsb[dst]); + } + } + return XUD_DoSetRequestStatus(ep0_in); + } + else + { + /* Direction: Device-to-host */ + (buffer, unsigned char[])[0] = channelMapUsb[dst]; + (buffer, unsigned char[])[1] = 0; + return XUD_DoGetRequest(ep0_out, ep0_in, (buffer, unsigned char[]), sp.wLength, sp.wLength); } - } - else - { - /* Direction: Device-to-host */ - (buffer, unsigned char[])[0] = channelMapUsb[sp.wValue & 0xff]; - (buffer, unsigned char[])[1] = 0; - return XUD_DoGetRequest(ep0_out, ep0_in, (buffer, unsigned char[]), sp.wLength, sp.wLength); } break; case ID_XU_MIXSEL: { int cs = sp.wValue >> 8; /* Control Selector */ - int cn = sp.wValue & 0xff; /* Channel number */ + int cn = sp.wValue & 0xff; /* Channel Number */ /* Check for Get or Set */ if(sp.bmRequestType.Direction == USB_BM_REQTYPE_DIRECTION_H2D) @@ -726,21 +745,19 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c if(datalength > 0) { - /* cn bounds check for safety..*/ + /* CN bounds check for safety..*/ if(cn < MIX_INPUTS) { //if(cs == CS_XU_MIXSEL) /* cs now contains mix number */ if(cs < (MAX_MIX_COUNT + 1)) { + int source = (buffer, unsigned char[])[0]; + /* Check for "off" - update local state */ - if((buffer, unsigned char[])[0] == 0xFF) + if(source == 0xFF) { - mixSel[cs][cn] = (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT); - } - else - { - mixSel[cs][cn] = (buffer, unsigned char[])[0]; + source = (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT); } if(cs == 0) @@ -748,21 +765,17 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c /* Update all mix maps */ for (int i = 0; i < MAX_MIX_COUNT; i++) { - outuint(c_mix_ctl, SET_MIX_MAP); - outuint(c_mix_ctl, i); /* Mix bus */ - outuint(c_mix_ctl, cn); /* Mixer input */ - outuint(c_mix_ctl, (int) mixSel[cn]); /* Source */ - outct(c_mix_ctl, XS1_CT_END); + /* i : Mix bus */ + /* cn: Mixer input */ + mixSel[i][cn] = source; + UpdateMixMap(c_mix_ctl, i, cn, mixSel[i][cn]); } } else { /* Update relevant mix map */ - outuint(c_mix_ctl, SET_MIX_MAP); /* Command */ - outuint(c_mix_ctl, (cs-1)); /* Mix bus */ - outuint(c_mix_ctl, cn); /* Mixer input */ - outuint(c_mix_ctl, (int) mixSel[cs][cn]); /* Source */ - outct(c_mix_ctl, XS1_CT_END); /* Wait for handshake back */ + mixSel[cs-1][cn] = source; + UpdateMixMap(c_mix_ctl, cs-1, cn, mixSel[cs-1][cn]); } return XUD_DoSetRequestStatus(ep0_in); @@ -783,7 +796,7 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c if((cs > 0) && (cs < (MAX_MIX_COUNT+1))) { (buffer, unsigned char[])[0] = mixSel[cs-1][cn]; - return XUD_DoGetRequest(ep0_out, ep0_in, (buffer, unsigned char[]), 1, 1 ); + return XUD_DoGetRequest(ep0_out, ep0_in, (buffer, unsigned char[]), 1, 1); } } } @@ -791,49 +804,53 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c } case ID_MIXER_1: - - if(sp.bmRequestType.Direction == USB_BM_REQTYPE_DIRECTION_H2D) /* Direction: Host-to-device */ { - unsigned volume = 0; + int cs = sp.wValue >> 8; /* Control Selector - currently unused */ + int cn = sp.wValue & 0xff; /* Channel number - used for mixer node index */ - /* Expect OUT here with mute */ - if((result = XUD_GetBuffer(ep0_out, (buffer, unsigned char[]), datalength)) != XUD_RES_OKAY) + if(sp.bmRequestType.Direction == USB_BM_REQTYPE_DIRECTION_H2D) /* Direction: Host-to-device */ { - return result; - } + unsigned weightMult = 0; - mixer1Weights[sp.wValue & 0xff] = (buffer, unsigned char[])[0] | (buffer, unsigned char[])[1] << 8; + /* Expect OUT here with weight */ + if((result = XUD_GetBuffer(ep0_out, (buffer, unsigned char[]), datalength)) != XUD_RES_OKAY) + { + return result; + } - if (mixer1Weights[sp.wValue & 0xff] == 0x8000) - { - volume = 0; + if(cn < sizeof(mixer1Weights)/sizeof(mixer1Weights[0])) + { + mixer1Weights[cn] = (buffer, unsigned char[])[0] | (buffer, unsigned char[])[1] << 8; + + if (mixer1Weights[cn] != 0x8000) + { + weightMult = db_to_mult(mixer1Weights[cn], XUA_MIXER_DB_FRAC_BITS, XUA_MIXER_MULT_FRAC_BITS); + } + + if (!isnull(c_mix_ctl)) + { + UpdateMixerWeight(c_mix_ctl, (cn) % 8, (cn) / 8, weightMult); + } + } + + /* Send 0 Length as status stage */ + return XUD_DoSetRequestStatus(ep0_in); } else { - volume = db_to_mult(mixer1Weights[sp.wValue & 0xff], 8, 25); - } - if (!isnull(c_mix_ctl)) - { - outuint(c_mix_ctl, SET_MIX_MULT); - outuint(c_mix_ctl, (sp.wValue & 0xff) % 8); - outuint(c_mix_ctl, (sp.wValue & 0xff) / 8); - outuint(c_mix_ctl, volume); - outct(c_mix_ctl, XS1_CT_END); - } + short weight = 0x8000; - /* Send 0 Length as status stage */ - return XUD_DoSetRequestStatus(ep0_in); - } - else - { - short weight = mixer1Weights[sp.wValue & 0xff]; - (buffer, unsigned char[])[0] = weight & 0xff; - (buffer, unsigned char[])[1] = (weight >> 8) & 0xff; + if(cn < sizeof(mixer1Weights)/sizeof(mixer1Weights[0])) + { + weight = mixer1Weights[cn]; + } - return XUD_DoGetRequest(ep0_out, ep0_in, (buffer, unsigned char[]), sp.wLength, sp.wLength); + storeShort((buffer, unsigned char[]), 0, weight); + + return XUD_DoGetRequest(ep0_out, ep0_in, (buffer, unsigned char[]), sp.wLength, sp.wLength); + } } break; - #endif default: /* We dont have a unit with this ID! */ @@ -922,7 +939,6 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c num_freqs++; } #endif - storeShort((buffer, unsigned char[]), 0, num_freqs); return XUD_DoGetRequest(ep0_out, ep0_in, (buffer, unsigned char[]), i, sp.wLength); @@ -960,7 +976,7 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c } break; -#ifdef MIXER +#if (MIXER) /* Mixer Unit */ case ID_MIXER_1: storeShort((buffer, unsigned char[]), 0, 1); @@ -970,7 +986,6 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c return XUD_DoGetRequest(ep0_out, ep0_in, (buffer, unsigned char[]), sp.wLength, sp.wLength); break; #endif - default: /* Unknown Unit ID in Range Request selector for FU */ break; @@ -980,7 +995,7 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c break; /* case: RANGE */ } -#if defined (MIXER) && (MAX_MIX_COUNT > 0) +#if ((MIXER) && (MAX_MIX_COUNT > 0)) case MEM: /* Memory Requests (5.2.7.1) */ unitID = sp.wIndex >> 8; @@ -1006,6 +1021,8 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c { if (!isnull(c_mix_ctl)) { + outct(c_mix_ctl, XS1_CT_END); + inct(c_mix_ctl); outuint(c_mix_ctl, GET_STREAM_LEVELS); outuint(c_mix_ctl, i); outct(c_mix_ctl, XS1_CT_END); @@ -1021,6 +1038,8 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c { if (!isnull(c_mix_ctl)) { + outct(c_mix_ctl, XS1_CT_END); + inct(c_mix_ctl); outuint(c_mix_ctl, GET_INPUT_LEVELS); outuint(c_mix_ctl, (i - NUM_USB_CHAN_OUT)); outct(c_mix_ctl, XS1_CT_END); @@ -1043,6 +1062,8 @@ int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, c { if (!isnull(c_mix_ctl)) { + outct(c_mix_ctl, XS1_CT_END); + inct(c_mix_ctl); outuint(c_mix_ctl, GET_OUTPUT_LEVELS); outuint(c_mix_ctl, i); outct(c_mix_ctl, XS1_CT_END); @@ -1095,7 +1116,7 @@ int AudioEndpointRequests_1(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp /* Check Control Selector */ unsigned short controlSelector = sp.wValue>>8; - if((result != XUD_GetBuffer(ep0_out, (buffer, unsigned char[]), length)) != XUD_RES_OKAY) + if((result = XUD_GetBuffer(ep0_out, (buffer, unsigned char[]), length)) != XUD_RES_OKAY) { return result; } @@ -1110,13 +1131,10 @@ int AudioEndpointRequests_1(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp if(newSampleRate != g_curSamFreq) { - int curSamFreq44100Family; - int curSamFreq48000Family; - /* Windows Audio Class driver has a nice habbit of sending invalid SF's (e.g. 48001Hz) * when under stress. Lets double check it here and ignore if not valid. */ - curSamFreq48000Family = MCLK_48 % newSampleRate == 0; - curSamFreq44100Family = MCLK_441 % newSampleRate == 0; + int curSamFreq48000Family = MCLK_48 % newSampleRate == 0; + int curSamFreq44100Family = MCLK_441 % newSampleRate == 0; if(curSamFreq48000Family || curSamFreq44100Family) { @@ -1131,7 +1149,7 @@ int AudioEndpointRequests_1(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp /* Allow time for the change - feedback to stabilise */ FeedbackStabilityDelay(); - } + } } return XUD_SetBuffer(ep0_in, (buffer, unsigned char[]), 0); } @@ -1193,12 +1211,12 @@ XUD_Result_t AudioClassRequests_1(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket { case FU_USBOUT: volsOut[ sp.wValue & 0xff ] = buffer[0] | (((int) (signed char) buffer[1]) << 8); - updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl ); + updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl); return XUD_DoSetRequestStatus(ep0_in); case FU_USBIN: volsIn[ sp.wValue & 0xff ] = buffer[0] | (((int) (signed char) buffer[1]) << 8); - updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl ); + updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl); return XUD_DoSetRequestStatus(ep0_in); } } @@ -1212,12 +1230,12 @@ XUD_Result_t AudioClassRequests_1(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket { case FU_USBOUT: mutesOut[ sp.wValue & 0xff ] = buffer[0]; - updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl ); + updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl); return XUD_DoSetRequestStatus(ep0_in); case FU_USBIN: mutesIn[ sp.wValue & 0xff ] = buffer[0]; - updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl ); + updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl); return XUD_DoSetRequestStatus(ep0_in); } } @@ -1280,9 +1298,12 @@ XUD_Result_t AudioClassRequests_1(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket buffer[0] = (VOLUME_RES_MIXER & 0xff); buffer[1] = (VOLUME_RES_MIXER >> 8); return XUD_DoGetRequest(ep0_out, ep0_in, buffer, 2, sp.wLength); + + default: + break; } #endif return XUD_RES_ERR; } #endif -#endif /* NO_USB */ +#endif /* XUA_USB_EN */ diff --git a/lib_xua/src/core/main.xc b/lib_xua/src/core/main.xc index 421793f2..17c418e1 100755 --- a/lib_xua/src/core/main.xc +++ b/lib_xua/src/core/main.xc @@ -1,4 +1,4 @@ -// Copyright 2012-2021 XMOS LIMITED. +// Copyright 2012-2023 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #include "xua.h" /* Device specific defines */ @@ -24,29 +24,20 @@ #endif #include "uac_hwresources.h" -#ifdef MIDI -#include "usb_midi.h" -#endif #ifdef IAP #include "i2c_shared.h" #include "iap.h" #endif -#ifdef MIXER -#include "mixer.h" +#if (XUA_SPDIF_RX_EN) +#include "spdif.h" #endif -#if (SPDIF_RX == 1) -#include "SpdifReceive.h" -#endif - -#if ADAT_RX +#if (XUA_ADAT_RX_EN) #include "adat_rx.h" #endif -#include "clocking.h" - #if (XUA_NUM_PDM_MICS > 0) #include "xua_pdm_mic.h" #endif @@ -134,29 +125,29 @@ on tile[AUDIO_IO_TILE] : buffered out port:32 p_bclk = PORT_I2S_BCLK; on tile[AUDIO_IO_TILE] : in port p_mclk_in = PORT_MCLK_IN; -#if XUA_USB_EN +#if XUA_USB_EN on tile[XUD_TILE] : in port p_for_mclk_count = PORT_MCLK_COUNT; #endif -#if (XUA_SPDIF_TX_EN == 1) +#if (XUA_SPDIF_TX_EN) on tile[SPDIF_TX_TILE] : buffered out port:32 p_spdif_tx = PORT_SPDIF_OUT; #endif -#ifdef ADAT_TX +#if (XUA_ADAT_TX_EN) on stdcore[AUDIO_IO_TILE] : buffered out port:32 p_adat_tx = PORT_ADAT_OUT; #endif -#if ADAT_RX +#if (XUA_ADAT_RX_EN) on stdcore[XUD_TILE] : buffered in port:32 p_adat_rx = PORT_ADAT_IN; #endif -#if (SPDIF_RX == 1) -on tile[XUD_TILE] : buffered in port:4 p_spdif_rx = PORT_SPDIF_IN; +#if (XUA_SPDIF_RX_EN) +on tile[XUD_TILE] : in port p_spdif_rx = PORT_SPDIF_IN; #endif -#if (SPDIF_RX == 1) || (ADAT_RX) +#if (XUA_SPDIF_RX_EN) || (XUA_ADAT_RX_EN) || (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) /* Reference to external clock multiplier */ -on tile[AUDIO_IO_TILE] : out port p_pll_clk = PORT_PLL_REF; +on tile[PLL_REF_TILE] : out port p_pll_ref = PORT_PLL_REF; #endif #ifdef MIDI @@ -178,32 +169,24 @@ clock clk_pdm = on tile[PDM_TILE]: on tile[MIDI_TILE] : clock clk_midi = CLKBLK_MIDI; #endif -#if XUA_SPDIF_TX_EN || defined(ADAT_TX) +#if (XUA_SPDIF_TX_EN || XUA_ADAT_TX_EN) on tile[SPDIF_TX_TILE] : clock clk_mst_spd = CLKBLK_SPDIF_TX; #endif -#if (SPDIF_RX == 1) +#if (XUA_SPDIF_RX_EN) on tile[XUD_TILE] : clock clk_spd_rx = CLKBLK_SPDIF_RX; #endif #if (XUA_NUM_PDM_MICS > 0) -in port p_pdm_clk = PORT_PDM_CLK; +in port p_pdm_clk = PORT_PDM_CLK; -in buffered port:32 p_pdm_mics = PORT_PDM_DATA; +in buffered port:32 p_pdm_mics = PORT_PDM_DATA; #if (PDM_TILE != AUDIO_IO_TILE) /* If Mics and I2S are not the same tile we need a separate MCLK port */ -in port p_pdm_mclk = PORT_PDM_MCLK; +in port p_pdm_mclk = PORT_PDM_MCLK; #endif #endif - - -#if(XUD_SERIES_SUPPORT == XUD_L_SERIES) && (ADAT_RX) -/* Cannot use default clock (CLKBLK_REF) for ADAT RX since it is tied to the -60MHz USB clock on G/L series parts. */ -on tile[XUD_TILE] : clock clk_adat_rx = CLKBLK_ADAT_RX; -#endif - on tile[AUDIO_IO_TILE] : clock clk_audio_mclk = CLKBLK_MCLK; /* Master clock */ #if(AUDIO_IO_TILE != XUD_TILE) && XUA_USB_EN @@ -212,17 +195,7 @@ on tile[XUD_TILE] : clock clk_audio_mclk_usb = CLKBLK_MCLK; on tile[XUD_TILE] : in port p_mclk_in_usb = PORT_MCLK_IN_USB; #endif -on tile[AUDIO_IO_TILE] : clock clk_audio_bclk = CLKBLK_I2S_BIT; /* Bit clock */ - - -/* L/G Series needs a port to use for USB reset */ -#if (XUD_SERIES_SUPPORT != XUD_U_SERIES) && defined(PORT_USB_RESET) -/* This define is checked since it could be on a shift reg or similar */ -on tile[XUD_TILE] : out port p_usb_rst = PORT_USB_RESET; -#else -/* Reset port not required for U series due to built in Phy */ -#define p_usb_rst null -#endif +on tile[AUDIO_IO_TILE] : clock clk_audio_bclk = CLKBLK_I2S_BIT; /* Bit clock */ #ifdef IAP /* I2C ports - in a struct for use with module_i2c_shared & module_i2c_simple/module_i2c_single_port */ @@ -254,8 +227,8 @@ XUD_EpType epTypeTableIn[ENDPOINT_COUNT_IN] = { XUD_EPTYPE_CTL | XUD_STATUS_ENAB #if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP) XUD_EPTYPE_ISO, /* Async feedback endpoint */ #endif -#if (SPDIF_RX == 1) || (ADAT_RX) - XUD_EPTYPE_BUL, +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + XUD_EPTYPE_INT, #endif #ifdef MIDI XUD_EPTYPE_BUL, @@ -298,20 +271,17 @@ void xscope_user_init() /* Core USB Audio functions - must be called on the Tile connected to the USB Phy */ void usb_audio_core(chanend c_mix_out #ifdef MIDI -, chanend c_midi + , chanend c_midi #endif -#ifdef IAP -, chanend c_iap -#ifdef IAP_EA_NATIVE_TRANS -, chanend c_ea_data +#if (MIXER) + , chanend c_mix_ctl #endif + , chanend ?c_clk_int + , chanend ?c_clk_ctl + , client interface i_dfu ?dfuInterface +#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) + , client interface pll_ref_if i_pll_ref #endif -#ifdef MIXER -, chanend c_mix_ctl -#endif -, chanend ?c_clk_int -, chanend ?c_clk_ctl -, client interface i_dfu ?dfuInterface VENDOR_REQUESTS_PARAMS_DEC_ ) { @@ -319,12 +289,8 @@ VENDOR_REQUESTS_PARAMS_DEC_ chan c_xud_out[ENDPOINT_COUNT_OUT]; /* Endpoint channels for XUD */ chan c_xud_in[ENDPOINT_COUNT_IN]; chan c_aud_ctl; -#ifdef CHAN_BUFF_CTRL -#warning Using channel to control buffering - this may reduce performance but improve power consumption - chan c_buff_ctrl; -#endif -#ifndef MIXER +#if (!MIXER) #define c_mix_ctl null #endif @@ -344,13 +310,13 @@ VENDOR_REQUESTS_PARAMS_DEC_ /* Run UAC2.0 at high-speed, UAC1.0 at full-speed */ unsigned usbSpeed = (AUDIO_CLASS == 2) ? XUD_SPEED_HS : XUD_SPEED_FS; - /* USB Interface Core */ + unsigned xudPwrCfg = (XUA_POWERMODE == XUA_POWERMODE_SELF) ? XUD_PWR_SELF : XUD_PWR_BUS; + + /* USB interface core */ XUD_Main(c_xud_out, ENDPOINT_COUNT_OUT, c_xud_in, ENDPOINT_COUNT_IN, - c_sof, epTypeTableOut, epTypeTableIn, p_usb_rst, - null, 0, usbSpeed, XUD_PWR_CFG); + c_sof, epTypeTableOut, epTypeTableIn, usbSpeed, xudPwrCfg); } - /* USB Packet buffering Core */ { unsigned x; thread_speed(); @@ -367,7 +333,7 @@ VENDOR_REQUESTS_PARAMS_DEC_ asm("ldw %0, dp[clk_audio_mclk]":"=r"(x)); asm("setclk res[%0], %1"::"r"(p_for_mclk_count), "r"(x)); #endif - //:buffer + /* Endpoint & audio buffering cores */ XUA_Buffer(c_xud_out[ENDPOINT_NUMBER_OUT_AUDIO],/* Audio Out*/ #if (NUM_USB_CHAN_IN > 0) @@ -381,33 +347,19 @@ VENDOR_REQUESTS_PARAMS_DEC_ c_xud_in[ENDPOINT_NUMBER_IN_MIDI], /* MIDI In */ // 4 c_midi, #endif -#ifdef IAP - c_xud_out[ENDPOINT_NUMBER_OUT_IAP], /* iAP Out */ - c_xud_in[ENDPOINT_NUMBER_IN_IAP], /* iAP In */ -#ifdef IAP_INT_EP - c_xud_in[ENDPOINT_NUMBER_IN_IAP_INT], /* iAP Interrupt In */ -#endif - c_iap, -#ifdef IAP_EA_NATIVE_TRANS - c_xud_out[ENDPOINT_NUMBER_OUT_IAP_EA_NATIVE_TRANS], - c_xud_in[ENDPOINT_NUMBER_IN_IAP_EA_NATIVE_TRANS], - c_EANativeTransport_ctrl, - c_ea_data, -#endif -#endif -#if (SPDIF_RX) || (ADAT_RX) +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) /* Audio Interrupt - only used for interrupts on external clock change */ c_xud_in[ENDPOINT_NUMBER_IN_INTERRUPT], c_clk_int, #endif c_sof, c_aud_ctl, p_for_mclk_count -#if( 0 < HID_CONTROLS ) +#if (HID_CONTROLS) , c_xud_in[ENDPOINT_NUMBER_IN_HID] -#endif -#ifdef CHAN_BUFF_CTRL - , c_buff_ctrl #endif , c_mix_out +#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) + , i_pll_ref +#endif ); //: } @@ -445,11 +397,11 @@ void SpdifTxWrapper(chanend c_spdif_tx) } #endif -void usb_audio_io(chanend ?c_aud_in, chanend ?c_adc, +void usb_audio_io(chanend ?c_aud_in, #if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE) chanend c_spdif_tx, #endif -#ifdef MIXER +#if (MIXER) chanend c_mix_ctl, #endif streaming chanend ?c_spdif_rx, @@ -465,13 +417,16 @@ void usb_audio_io(chanend ?c_aud_in, chanend ?c_adc, #endif , chanend c_pdm_pcm #endif +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + , client interface pll_ref_if i_pll_ref +#endif ) { -#ifdef MIXER +#if (MIXER) chan c_mix_out; #endif -#if (SPDIF_RX) || (ADAT_RX) +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) chan c_dig_rx; #else #define c_dig_rx null @@ -491,7 +446,7 @@ void usb_audio_io(chanend ?c_aud_in, chanend ?c_adc, par { -#ifdef MIXER +#if (MIXER) /* Mixer cores(s) */ { thread_speed(); @@ -506,10 +461,10 @@ void usb_audio_io(chanend ?c_aud_in, chanend ?c_adc, } #endif - /* Audio I/O Core (pars additional S/PDIF TX Core) */ + /* Audio I/O core (pars additional S/PDIF TX Core) */ { thread_speed(); -#ifdef MIXER +#if (MIXER) #define AUDIO_CHANNEL c_mix_out #else #define AUDIO_CHANNEL c_aud_in @@ -518,7 +473,7 @@ void usb_audio_io(chanend ?c_aud_in, chanend ?c_adc, #if (XUA_SPDIF_TX_EN) //&& (SPDIF_TX_TILE != AUDIO_IO_TILE) , c_spdif_tx #endif -#if (SPDIF_RX) ||(ADAT_RX) +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) , c_dig_rx #endif #if (XUD_TILE != 0) && (AUDIO_IO_TILE == 0) && (XUA_DFU_EN == 1) @@ -534,11 +489,13 @@ void usb_audio_io(chanend ?c_aud_in, chanend ?c_adc, xua_pdm_mic(c_ds_output, p_pdm_mics); #endif -#if (SPDIF_RX) || (ADAT_RX) +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) { + /* ClockGen must currently run on same tile as AudioHub due to shared memory buffer + * However, due to the use of an interface the pll reference signal port can be on another tile + */ thread_speed(); - - clockGen(c_spdif_rx, c_adat_rx, p_pll_clk, c_dig_rx, c_clk_ctl, c_clk_int); + clockGen(c_spdif_rx, c_adat_rx, i_pll_ref, c_dig_rx, c_clk_ctl, c_clk_int); } #endif @@ -573,23 +530,18 @@ int main() chan c_ea_data; #endif #endif -#ifdef SU1_ADC_ENABLE - chan c_adc; -#else -#define c_adc null -#endif -#ifdef MIXER +#if (MIXER) chan c_mix_ctl; #endif -#if SPDIF_RX +#if (XUA_SPDIF_RX_EN) streaming chan c_spdif_rx; #else #define c_spdif_rx null #endif -#if ADAT_RX +#if (XUA_ADAT_RX_EN) chan c_adat_rx; #else #define c_adat_rx null @@ -599,8 +551,7 @@ int main() chan c_spdif_tx; #endif - -#if ((SPDIF_RX) || (ADAT_RX)) +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) chan c_clk_ctl; chan c_clk_int; #else @@ -622,12 +573,19 @@ int main() #endif #endif +#if ((XUA_SYNCMODE == XUA_SYNCMODE_SYNC) || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + interface pll_ref_if i_pll_ref; +#endif + USER_MAIN_DECLARATIONS par { USER_MAIN_CORES +#if ((XUA_SYNCMODE == XUA_SYNCMODE_SYNC) || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + on tile[PLL_REF_TILE]: PllRefPinTask(i_pll_ref, p_pll_ref); +#endif on tile[XUD_TILE]: par { @@ -639,6 +597,7 @@ int main() #endif #endif #if XUA_USB_EN + /* Core USB audio task, buffering, USB etc */ usb_audio_core(c_mix_out #ifdef MIDI , c_midi @@ -649,23 +608,27 @@ int main() , c_ea_data #endif #endif -#ifdef MIXER +#if (MIXER) , c_mix_ctl #endif , c_clk_int, c_clk_ctl, dfuInterface +#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) + , i_pll_ref +#endif VENDOR_REQUESTS_PARAMS_ ); #endif /* XUA_USB_EN */ } - on tile[AUDIO_IO_TILE]: + on tile[AUDIO_IO_TILE]: { - usb_audio_io(c_mix_out, c_adc + /* Audio I/O task, includes mixing etc */ + usb_audio_io(c_mix_out #if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE) , c_spdif_tx #endif -#ifdef MIXER +#if (MIXER) , c_mix_ctl #endif , c_spdif_rx, c_adat_rx, c_clk_ctl, c_clk_int @@ -677,9 +640,13 @@ int main() , c_ds_output #endif , c_pdm_pcm +#endif +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + , i_pll_ref #endif ); } + //: #if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE) on tile[SPDIF_TX_TILE]: @@ -702,7 +669,7 @@ int main() on tile[MIDI_TILE]: { thread_speed(); - usb_midi(p_midi_rx, p_midi_tx, clk_midi, c_midi, 0, null, null, null, null); + usb_midi(p_midi_rx, p_midi_tx, clk_midi, c_midi, 0); } #endif #if defined(IAP) @@ -714,24 +681,19 @@ int main() #endif #endif -#if SPDIF_RX +#if (XUA_SPDIF_RX_EN) on tile[XUD_TILE]: { thread_speed(); - SpdifReceive(p_spdif_rx, c_spdif_rx, 1, clk_spd_rx); + spdif_rx(c_spdif_rx,p_spdif_rx,clk_spd_rx,192000); } #endif -#if (ADAT_RX) +#if (XUA_ADAT_RX_EN) on stdcore[XUD_TILE] : { set_thread_fast_mode_on(); -#if(XUD_SERIES_SUPPORT == XUD_L_SERIES) - /* Can't use REF clock on L-series as this is usb clock */ - set_port_clock(p_adat_rx, clk_adat_rx); - start_clock(clk_adat_rx); -#endif while (1) { adatReceiver48000(p_adat_rx, c_adat_rx); @@ -744,17 +706,17 @@ int main() #if XUA_USB_EN #if (XUD_TILE != 0 ) && (AUDIO_IO_TILE != 0) && (XUA_DFU_EN == 1) /* Run flash code on its own - hope it gets combined */ -//#warning Running DFU flash code on its own + //#warning Running DFU flash code on its own on stdcore[0]: DFUHandler(dfuInterface, null); #endif #endif #ifndef PDM_RECORD -#if (XUA_NUM_PDM_MICS > 0) +#if (XUA_NUM_PDM_MICS > 0) #if (PDM_TILE != AUDIO_IO_TILE) /* PDM Mics running on a separate to AudioHub */ on stdcore[PDM_TILE]: - { + { xua_pdm_mic_config(p_pdm_mclk, p_pdm_clk, p_pdm_mics, clk_pdm); xua_pdm_mic(c_ds_output, p_pdm_mics); } @@ -767,11 +729,6 @@ int main() #endif /*MIC_PROCESSING_USE_INTERFACE*/ #endif /*XUA_NUM_PDM_MICS > 0*/ #endif /*PDM_RECORD*/ - - -#ifdef SU1_ADC_ENABLE - xs1_su_adc_service(c_adc); -#endif } return 0; diff --git a/lib_xua/src/core/mixer/fastmix.S b/lib_xua/src/core/mixer/fastmix.S index c8828a38..9ed41075 100644 --- a/lib_xua/src/core/mixer/fastmix.S +++ b/lib_xua/src/core/mixer/fastmix.S @@ -1,16 +1,21 @@ -// Copyright 2018-2021 XMOS LIMITED. +// Copyright 2018-2023 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. -//#include "devicedefines.h" -#define MAX_MIX_COUNT 8 -#define MIX_INPUTS 18 +#include "xua.h" +#ifndef MAX_MIX_COUNT +#error +#endif -#if defined(__XS2A__) || defined(__XS3A__) +#ifndef MIX_INPUTS +#error +#endif + +#if (MAX_MIX_COUNT > 0) #define DOMIX_TOP(i) \ .cc_top doMix##i.function,doMix##i; \ - .align 4 ;\ + .align 16 ;\ .globl doMix##i ;\ .type doMix##i, @function ;\ .globl doMix##i##.nstackwords ;\ @@ -50,52 +55,6 @@ doMix##i##: ;\ .size doMix##i, .-doMix##i; \ .cc_bottom doMix##i##.function; -#else - - -#define DOMIX_TOP(i) \ -.cc_top doMix##i.function,doMix##i; \ - .align 4 ;\ -.globl doMix##i ;\ -.type doMix##i, @function ;\ -.globl doMix##i##.nstackwords ;\ -.globl doMix##i##.maxthreads ; \ -.globl doMix##i##.maxtimers ; \ -.globl doMix##i##.maxchanends ; \ -.globl doMix##i##.maxsync ;\ -.linkset doMix##i##.locnoside, 1; \ -.linkset doMix##i##.locnochandec, 1;\ -.linkset doMix##i##.nstackwords, 0 ;\ -.linkset doMix##i##.maxchanends, 0 ;\ -.linkset doMix##i##.maxtimers, 0 ;\ -.linkset doMix##i##.maxthreads, 1; \ -doMix##i##: ;\ - set cp, r0; \ - set dp, r1; \ - lsub r0, r1, r0, r0, r0;\ - .label_##i##: - -#define DOMIX_BOT(i) \ - ldap r11, _dp; \ - set dp, r11;\ - ldap r11, _cp;\ - set cp, r11;\ -\ - mov r0, r1;\ - ldc r2, 0x19;\ - sext r0, r2;\ - eq r0, r0, r1;\ - bf r0, .L20; \ -\ - shl r0, r1, 0x7;\ - retsp 0x0;\ -\ -\ -.size doMix##i, .-doMix##i; \ -.cc_bottom doMix##i##.function; - -#endif - #define N MIX_INPUTS #define BODY(i) \ ldw r2,cp[i]; \ @@ -115,8 +74,6 @@ doMix##i##: ;\ retsp 0x0; \ - - #if(MAX_MIX_COUNT > 0) DOMIX_TOP(0) #include "repeat.h" @@ -176,7 +133,7 @@ DOMIX_BOT(7) #undef BODY #define N MAX_MIX_COUNT .cc_top setPtr.function,setPtr; -.align 4 ; +.align 16 ; .globl setPtr; .type setPtr, @function .globl setPtr.nstackwords; @@ -225,5 +182,5 @@ setPtr_go: #undef N #undef BODY - +#endif diff --git a/lib_xua/src/core/mixer/mixer.xc b/lib_xua/src/core/mixer/mixer.xc index 7f8b6e05..afa2017a 100644 --- a/lib_xua/src/core/mixer/mixer.xc +++ b/lib_xua/src/core/mixer/mixer.xc @@ -1,29 +1,39 @@ -// Copyright 2011-2021 XMOS LIMITED. +// Copyright 2011-2023 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. - +#define XASSERT_UNIT MIXER +#include "xassert.h" #include -#include #include "xua.h" -#include "mixer.h" -#include "xc_ptr.h" #include "xua_commands.h" #include "dbcalc.h" -#ifdef MIXER +/* FAST_MIXER has a bit of a nasty implentation but is more efficient */ +#ifndef FAST_MIXER +#define FAST_MIXER (1) +#endif -/* FAST_MIXER has a bit of a nasty implentation but is more effcient */ -#define FAST_MIXER 1 +#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS) || !FAST_MIXER +#include "xc_ptr.h" +#endif +#if (MIXER) -//#ifdef OUT_VOLUME_IN_MIXER +#if (OUT_VOLUME_IN_MIXER) static unsigned int multOut_array[NUM_USB_CHAN_OUT + 1]; -static xc_ptr multOut; -//#endif -//#ifdef IN_VOLUME_IN_MIXER +unsafe +{ + unsigned int volatile * unsafe multOut = multOut_array; +} +#endif + +#if (IN_VOLUME_IN_MIXER) static unsigned int multIn_array[NUM_USB_CHAN_IN + 1]; -static xc_ptr multIn; -//#endif +unsafe +{ + unsigned int volatile * unsafe multIn = multIn_array; +} +#endif #if defined (LEVEL_METER_LEDS) || defined (LEVEL_METER_HOST) static unsigned abs(int x) @@ -39,35 +49,38 @@ static unsigned abs(int x) } #endif +static const int SOURCE_COUNT = NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT + 1; + static int samples_array[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT + 1]; /* One larger for an "off" channel for mixer sources" */ -xc_ptr samples; +static int samples_to_host_map_array[NUM_USB_CHAN_IN]; +static int samples_to_device_map_array[NUM_USB_CHAN_OUT]; unsafe { - static int volatile * const unsafe ptr_samples = samples_array; + int volatile * const unsafe ptr_samples = samples_array; + int volatile * const unsafe samples_to_host_map = samples_to_host_map_array; + int volatile * const unsafe samples_to_device_map = samples_to_device_map_array; } -int savedsamples2[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT]; - -int samples_to_host_map_array[NUM_USB_CHAN_IN]; -xc_ptr samples_to_host_map; - -int samples_to_device_map_array[NUM_USB_CHAN_OUT]; -xc_ptr samples_to_device_map; - -#if MAX_MIX_COUNT > 0 -int mix_mult_array[MAX_MIX_COUNT][MIX_INPUTS]; -xc_ptr mix_mult; -#define write_word_to_mix_mult(x,y,val) write_via_xc_ptr_indexed(mix_mult,((x)*MIX_INPUTS)+(y), val) -#define mix_mult_slice(x) (mix_mult + x * MIX_INPUTS * sizeof(int)) -#ifndef FAST_MIXER -int mix_map_array[MAX_MIX_COUNT][MIX_INPUTS]; -xc_ptr mix_map; -#define write_word_to_mix_map(x,y,val) write_via_xc_ptr_indexed(mix_map,((x)*MIX_INPUTS)+(y), val) -#define mix_map_slice(x) (mix_map + x * MIX_INPUTS * sizeof(int)) -#endif +#if (MAX_MIX_COUNT > 0) +int mix_mult_array[MAX_MIX_COUNT * MIX_INPUTS]; +#if (FAST_MIXER == 0) +int mix_map_array[MAX_MIX_COUNT * MIX_INPUTS]; #endif +unsafe +{ + int volatile * const unsafe mix_mult = mix_mult_array; +#if (FAST_MIXER == 0) + int volatile * const unsafe mix_map = mix_map_array; +#endif +} + +#define slice(a, i) (a + i * MIX_INPUTS) + +#endif + +#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS) /* Arrays for level data */ int samples_to_host_inputs[NUM_USB_CHAN_IN]; /* Audio transmitted to host i.e. device inputs */ xc_ptr samples_to_host_inputs_ptr; @@ -81,19 +94,6 @@ static int samples_from_host_streams[NUM_USB_CHAN_OUT]; /* Peak samples for audi static int samples_mixer_outputs[MAX_MIX_COUNT]; /* Peak samples out of the mixer */ xc_ptr samples_mixer_outputs_ptr; -#if 0 -#pragma xta command "add exclusion mixer1_rate_change" -#pragma xta command "analyse path mixer1_req mixer1_req" -#pragma xta command "set required - 10400 ns" /* 96kHz */ -#endif - -#if 0 -#pragma xta command "add exclusion mixer2_rate_change" -#pragma xta command "analyse path mixer2_req mixer2_req" -#pragma xta command "set required - 10400 ns" /* 96kHz */ -#endif - -#if defined (LEVEL_METER_LEDS) || defined (LEVEL_METER_HOST) static inline void ComputeMixerLevel(int sample, int i) { int x; @@ -112,42 +112,40 @@ static inline void ComputeMixerLevel(int sample, int i) } } #endif -#ifdef FAST_MIXER + +#if (FAST_MIXER) void setPtr(int src, int dst, int mix); -int doMix0(xc_ptr samples, xc_ptr mult); -int doMix1(xc_ptr samples, xc_ptr mult); -int doMix2(xc_ptr samples, xc_ptr mult); -int doMix3(xc_ptr samples, xc_ptr mult); -int doMix4(xc_ptr samples, xc_ptr mult); -int doMix5(xc_ptr samples, xc_ptr mult); -int doMix6(xc_ptr samples, xc_ptr mult); -int doMix7(xc_ptr samples, xc_ptr mult); -int doMix8(xc_ptr samples, xc_ptr mult); +int doMix0(volatile int * const unsafe samples, volatile int * const unsafe mult); +int doMix1(volatile int * const unsafe samples, volatile int * const unsafe mult); +int doMix2(volatile int * const unsafe samples, volatile int * const unsafe mult); +int doMix3(volatile int * const unsafe samples, volatile int * const unsafe mult); +int doMix4(volatile int * const unsafe samples, volatile int * const unsafe mult); +int doMix5(volatile int * const unsafe samples, volatile int * const unsafe mult); +int doMix6(volatile int * const unsafe samples, volatile int * const unsafe mult); +int doMix7(volatile int * const unsafe samples, volatile int * const unsafe mult); #else -/* DO NOT inline, causes 10.4.2 tools to add extra loads in loop */ -/* At 18 x 12dB we could get 64 x bigger */ #pragma unsafe arrays -static inline int doMix(xc_ptr samples, xc_ptr ptr, xc_ptr mult) +static inline int doMix(volatile int * unsafe samples, volatile int * unsafe const mixMap, volatile int * const unsafe mult) { int h=0; int l=0; -/* By breaking up the loop we keep things in the encoding for ldw (0-11) */ #pragma loop unroll for (int i=0; i>7); } -#endif return h<<7; } #endif #pragma unsafe arrays -static inline void GiveSamplesToHost(chanend c, xc_ptr ptr, xc_ptr multIn) +static inline void GiveSamplesToHost(chanend c, volatile int * unsafe hostMap) { -#if defined(IN_VOLUME_IN_MIXER) && defined(IN_VOLUME_AFTER_MIX) +#if (IN_VOLUME_IN_MIXER && IN_VOLUME_AFTER_MIX) int mult; int h; unsigned l; @@ -174,23 +171,26 @@ static inline void GiveSamplesToHost(chanend c, xc_ptr ptr, xc_ptr multIn) for (int i=0; i 0 - read_via_xc_ptr_indexed(index,ptr,i); -#else - index = i + NUM_USB_CHAN_OUT; -#endif +#if (MAX_MIX_COUNT > 0) unsafe { - //read_via_xc_ptr_indexed(sample,samples,index); - sample = ptr_samples[index]; + sample = ptr_samples[hostMap[i]]; } +#else + unsafe + { + sample = ptr_samples[i + NUM_USB_CHAN_OUT]; + } +#endif -#if defined(IN_VOLUME_IN_MIXER) && defined(IN_VOLUME_AFTER_MIX) +#if (IN_VOLUME_IN_MIXER && IN_VOLUME_AFTER_MIX) #warning IN Vols in mixer, AFTER mix & map - //asm("ldw %0, %1[%2]":"=r"(mult):"r"(multIn),"r"(i)); - read_via_xc_ptr_indexed(mult, multIn, i); + + unsafe + { + mult = multIn[i]; + } {h, l} = macs(mult, sample, 0, 0); //h <<= 3 done on other side */ @@ -213,7 +213,7 @@ static inline void GetSamplesFromHost(chanend c) for (int i=0; i>29)& 0x7; // Note: This step is not required if we assume sample depth is 24bit (rather than 32bit) - // Note: We need all 32bits for Native DSD + // Note: We need all 32bits for Native DSD +#endif + sample = h; #endif - write_via_xc_ptr_indexed(multOut, index, val); - write_via_xc_ptr_indexed(samples_array, i, h); -#else ptr_samples[i] = sample; -#endif } } #endif } #pragma unsafe arrays -static inline void GiveSamplesToDevice(chanend c, xc_ptr ptr, xc_ptr multOut) +static inline void GiveSamplesToDevice(chanend c, volatile int * unsafe deviceMap) { - { -#pragma loop unroll - for (int i=0; i 0 - /* If mixer turned on sort out the channel mapping */ - - /* Read pointer to sample from the map */ - read_via_xc_ptr_indexed(index, ptr, i); - - /* Read the actual sample value */ - read_via_xc_ptr_indexed(sample, samples, index); +#if (NUM_USB_CHAN_OUT == 0) + outuint(c, 0); #else - unsafe - { - /* Read the actual sample value */ - sample = ptr_samples[i]; - } +#pragma loop unroll + for (int i=0; i 0) + /* If mixer turned on sort out the channel mapping */ + unsafe + { + /* Read index to sample from the map then Read the actual sample value */ + sample = ptr_samples[deviceMap[i]]; + } +#else + unsafe + { + /* Read the actual sample value */ + sample = ptr_samples[i]; + } #endif -#if defined(OUT_VOLUME_IN_MIXER) && defined(OUT_VOLUME_AFTER_MIX) - /* Do volume control processing */ +#if (OUT_VOLUME_IN_MIXER && OUT_VOLUME_AFTER_MIX) + /* Do volume control processing */ #warning OUT Vols in mixer, AFTER mix & map - read_via_xc_ptr_indexed(mult, multOut, i); - {h, l} = macs(mult, sample, 0, 0); - h<<=3; // Shift used to be done in audio thread but now done here incase of 32bit support -#error + unsafe + { + mult = multOut[i]; + } + + {h, l} = macs(mult, sample, 0, 0); + h<<=3; // Shift used to be done in audio thread but now done here incase of 32bit support #if (STREAM_FORMAT_OUTPUT_RESOLUTION_32BIT_USED == 1) - h |= (l >>29)& 0x7; // Note: This step is not required if we assume sample depth is 24bit (rather than 32bit) + h |= (l >>29)& 0x7; // Note: This step is not required if we assume sample depth is 24bit (rather than 32bit) // Note: We need all 32bits for Native DSD #endif - outuint(c, h); + outuint(c, h); #else - outuint(c, sample); + outuint(c, sample); #endif - } } +#endif } #pragma unsafe arrays static inline void GetSamplesFromDevice(chanend c) { -#if defined(IN_VOLUME_IN_MIXER) && !defined(IN_VOLUME_AFTER_MIX) +#if (IN_VOLUME_IN_MIXER && IN_VOLUME_AFTER_MIX) int mult; int h; unsigned l; #endif #pragma loop unroll - for (int i=0;i 96000); @@ -355,44 +359,80 @@ static void mixer1(chanend c_host, chanend c_mix_ctl, chanend c_mixer2) #if (MAX_MIX_COUNT > 0) int mixed; #endif +#if (MAX_MIX_COUNT > 0) || (IN_VOLUME_IN_MIXER) || (OUT_VOLUME_IN_MIXER) || defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS) unsigned cmd; + unsigned char ct; +#endif unsigned request = 0; while (1) { -#pragma xta endpoint "mixer1_req" /* Request from audio()/mixer2() */ request = inuint(c_mixer2); /* Forward on Request for data to decouple thread */ outuint(c_host, request); -#if (MAX_MIX_COUNT > 0) +#if (MAX_MIX_COUNT > 0) /* Sync */ outuint(c_mixer2, 0); #endif - /* Between request to decouple and response ~ 400nS latency for interrupt to fire */ + +#if (MAX_MIX_COUNT > 0) || (IN_VOLUME_IN_MIXER) || (OUT_VOLUME_IN_MIXER) || defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS) select { - case inuint_byref(c_mix_ctl, cmd): + /* Check if EP0 intends to send us a control command */ + case inct_byref(c_mix_ctl, ct): { int mix, index, val; + + /* Handshake back to tell EP0 we are ready for an update */ + outct(c_mix_ctl, XS1_CT_END); + + /* Receive command from EP0 */ + cmd = inuint(c_mix_ctl); + + /* Interpret control command */ switch (cmd) { -#if MAX_MIX_COUNT > 0 +#if (MAX_MIX_COUNT > 0) case SET_SAMPLES_TO_HOST_MAP: - index = inuint(c_mix_ctl); - val = inuint(c_mix_ctl); - inct(c_mix_ctl); - write_via_xc_ptr_indexed(samples_to_host_map, index, val); + { + int dst = inuint(c_mix_ctl); + int src = inuint(c_mix_ctl); + inct(c_mix_ctl); + + assert((dst < NUM_USB_CHAN_IN) && msg("Host map destination out of range")); + assert((src < SOURCE_COUNT) && msg("Host map source out of range")); + + if((dst < NUM_USB_CHAN_IN) && (src < SOURCE_COUNT)) + { + unsafe + { + samples_to_host_map[dst] = src; + } + } + } break; case SET_SAMPLES_TO_DEVICE_MAP: - index = inuint(c_mix_ctl); - val = inuint(c_mix_ctl); - inct(c_mix_ctl); - write_via_xc_ptr_indexed(samples_to_device_map,index,val); + { + int dst = inuint(c_mix_ctl); + int src = inuint(c_mix_ctl); + inct(c_mix_ctl); + + assert((dst < NUM_USB_CHAN_OUT) && msg("Device map destination out of range")); + assert((src < SOURCE_COUNT) && msg("Device map source out of range")); + + if((dst < NUM_USB_CHAN_OUT) && (src < SOURCE_COUNT)) + { + unsafe + { + samples_to_device_map[dst] = src; + } + } + } break; case SET_MIX_MULT: @@ -401,40 +441,80 @@ static void mixer1(chanend c_host, chanend c_mix_ctl, chanend c_mixer2) val = inuint(c_mix_ctl); inct(c_mix_ctl); - write_word_to_mix_mult(mix, index, val); + assert((mix < MAX_MIX_COUNT) && msg("Mix mult mix out of range")); + assert((index < MIX_INPUTS) && msg("Mix mult index out of range")); + + if((index < MIX_INPUTS) && (mix < MAX_MIX_COUNT)) + { + unsafe + { + mix_mult[(mix * MIX_INPUTS) + index] = val; + } + } break; case SET_MIX_MAP: - mix = inuint(c_mix_ctl); - index = inuint(c_mix_ctl); /* mixer input */ - val = inuint(c_mix_ctl); /* source */ - inct(c_mix_ctl); -#ifdef FAST_MIXER - setPtr(index, val, mix); + { + unsigned mix = inuint(c_mix_ctl); + unsigned input = inuint(c_mix_ctl); /* mixer input */ + unsigned source = inuint(c_mix_ctl); /* source */ + inct(c_mix_ctl); + + assert((mix < MAX_MIX_COUNT) && msg("Mix map mix out of range")); + assert((input < MIX_INPUTS) && msg("Mix map index out of range")); + assert((source < SOURCE_COUNT) && msg("Mix map source out of range")); + + if((input < MIX_INPUTS) && (mix < MAX_MIX_COUNT) && (source < SOURCE_COUNT)) + { +#if (FAST_MIXER) + setPtr(input, source, mix); #else - write_word_to_mix_map(mix, index, val); + unsafe + { + mix_map[(mix * MIX_INPUTS) + input] = source; + } #endif + } + } break; #endif /* if MAX_MIX_COUNT > 0 */ -#ifdef IN_VOLUME_IN_MIXER + +#if (IN_VOLUME_IN_MIXER) case SET_MIX_IN_VOL: index = inuint(c_mix_ctl); val = inuint(c_mix_ctl); inct(c_mix_ctl); - write_via_xc_ptr_indexed(multIn, index, val); + assert((index < (NUM_USB_CHAN_IN + 1)) && msg("In volume index out of range")); + + if(index < NUM_USB_CHAN_IN + 1) + { + unsafe + { + multIn[index] = val; + } + } break; #endif -#ifdef OUT_VOLUME_IN_MIXER +#if (OUT_VOLUME_IN_MIXER) case SET_MIX_OUT_VOL: index = inuint(c_mix_ctl); val = inuint(c_mix_ctl); inct(c_mix_ctl); - write_via_xc_ptr_indexed(multOut, index, val); + assert((index < (NUM_USB_CHAN_OUT + 1)) && msg("Out volume index out of range")); + + if(index < NUM_USB_CHAN_OUT + 1) + { + unsafe + { + multOut[index] = val; + } + } break; #endif +#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS) /* Peak samples of stream from host to device (via USB) */ case GET_STREAM_LEVELS: index = inuint(c_mix_ctl); @@ -443,42 +523,6 @@ static void mixer1(chanend c_host, chanend c_mix_ctl, chanend c_mixer2) outct(c_mix_ctl, XS1_CT_END); samples_from_host_streams[index] = 0; break; - - case GET_INPUT_LEVELS: - index = inuint(c_mix_ctl); - chkct(c_mix_ctl, XS1_CT_END); -#ifdef LEVEL_METER_LEDS - /* Level LEDS process reseting samples_to_host_inputs - * Other side makes sure we don't miss a peak */ - //val = samples_to_host_inputs_buff[index]; - //samples_to_host_inputs_buff[index] = 0; - /* Access funcs used to avoid disjointness check */ - read_via_xc_ptr_indexed(val, samples_to_host_inputs_buff_ptr, index); - write_via_xc_ptr_indexed(samples_to_host_inputs_buff_ptr, index, 0); -#else - /* We dont have a level LEDs process, so reset ourselves */ - //val = samples_to_host_inputs[index]; - //samples_to_host_inputs[index] = 0; - /* Access funcs used to avoid disjointness check */ - read_via_xc_ptr_indexed(val, samples_to_host_inputs_ptr, index); - write_via_xc_ptr_indexed(samples_to_host_inputs_ptr, index, 0); -#endif - outuint(c_mix_ctl, val); - outct(c_mix_ctl, XS1_CT_END); - break; - -#if (MAX_MIX_COUNT > 0) - /* Peak samples of the mixer outputs */ - case GET_OUTPUT_LEVELS: - index = inuint(c_mix_ctl); - chkct(c_mix_ctl, XS1_CT_END); - read_via_xc_ptr_indexed(val, samples_mixer_outputs, index); - write_via_xc_ptr_indexed(samples_mixer_outputs, index, 0); - //val = samples_mixer_outputs[index]; - //samples_mixer_outputs[index] = 0; - outuint(c_mix_ctl, val); - outct(c_mix_ctl, XS1_CT_END); - break; #endif } break; @@ -486,14 +530,13 @@ static void mixer1(chanend c_host, chanend c_mix_ctl, chanend c_mixer2) default: /* Select default */ break; - } - + } // select +#endif /* Get response from decouple */ if(testct(c_host)) { int sampFreq; -#pragma xta endpoint "mixer1_rate_change" unsigned command = inct(c_host); switch(command) @@ -522,9 +565,8 @@ static void mixer1(chanend c_host, chanend c_mix_ctl, chanend c_mixer2) #pragma loop unroll /* Reset the mix values back to 0 */ - for (int i=0;i 0 +#if (MAX_MIX_COUNT > 0) GetSamplesFromHost(c_host); - GiveSamplesToHost(c_host, samples_to_host_map, multIn); + GiveSamplesToHost(c_host, samples_to_host_map); /* Sync with mixer 2 (once it has swapped samples with audiohub) */ outuint(c_mixer2, 0); inuint(c_mixer2); - - /* Do the mixing */ -#ifdef FAST_MIXER - mixed = doMix0(samples, mix_mult_slice(0)); + + /* Do the mixing */ + unsafe + { +#if (FAST_MIXER) + mixed = doMix0(ptr_samples, slice(mix_mult, 0)); #else - mixed = doMix(samples, mix_map_slice(0),mix_mult_slice(0)); + mixed = doMix(ptr_samples, slice(mix_map, 0), slice(mix_mult, 0)); #endif - write_via_xc_ptr_indexed(samples_array, (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 0), mixed); + ptr_samples[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 0] = mixed; + } #if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS) ComputeMixerLevel(mixed, 0); @@ -562,40 +607,46 @@ static void mixer1(chanend c_host, chanend c_mix_ctl, chanend c_mixer2) #endif { -#if MAX_MIX_COUNT > 2 -#ifdef FAST_MIXER - mixed = doMix2(samples, mix_mult_slice(2)); +#if (MAX_MIX_COUNT > 2) + unsafe + { +#if (FAST_MIXER) + mixed = doMix2(ptr_samples, slice(mix_mult, 2)); #else - mixed = doMix(samples, mix_map_slice(2),mix_mult_slice(2)); + mixed = doMix(ptr_samples, slice(mix_map, 2), slice(mix_mult, 2)); #endif - write_via_xc_ptr_indexed(samples_array, (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 2), mixed); - + ptr_samples[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 2] = mixed; + } #if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS) ComputeMixerLevel(mixed, 2); #endif #endif -#if MAX_MIX_COUNT > 4 -#ifdef FAST_MIXER - mixed = doMix4(samples, mix_mult_slice(4)); +#if (MAX_MIX_COUNT > 4) + unsafe + { +#if (FAST_MIXER) + mixed = doMix4(ptr_samples, slice(mix_mult, 4)); #else - mixed = doMix(samples, mix_map_slice(4),mix_mult_slice(4)); + mixed = doMix(ptr_samples, slice(mix_map, 4), slice(mix_mult, 4)); #endif - write_via_xc_ptr_indexed(samples_array, (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 4), mixed); - + ptr_samples[XUA_MIXER_OFFSET_MIX + 4] = mixed; + } #if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS) ComputeMixerLevel(mixed, 4); #endif #endif -#if MAX_MIX_COUNT > 6 -#ifdef FAST_MIXER - mixed = doMix6(samples, mix_mult_slice(6)); +#if (MAX_MIX_COUNT > 6) + unsafe + { +#if (FAST_MIXER) + mixed = doMix6(ptr_samples, slice(mix_mult, 6)); #else - mixed = doMix(samples, mix_map_slice(6),mix_mult_slice(6)); + mixed = doMix(ptr_samples, slice(mix_map, 6), slice(mix_mult, 6)); #endif - write_via_xc_ptr_indexed(samples, (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 6), mixed); - + ptr_samples[XUA_MIXER_OFFSET_MIX + 6] = mixed; + } #if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS) ComputeMixerLevel(mixed, 6); #endif @@ -603,13 +654,10 @@ static void mixer1(chanend c_host, chanend c_mix_ctl, chanend c_mixer2) } #else /* IF MAX_MIX_COUNT > 0 */ /* No mixes, this thread runs on its own doing just volume */ -#if(NUM_USB_CHAN_OUT == 0) - outuint(c_mixer2, 0); -#endif - GiveSamplesToDevice(c_mixer2, samples_to_device_map, multOut); + GiveSamplesToDevice(c_mixer2, samples_to_device_map); GetSamplesFromDevice(c_mixer2); GetSamplesFromHost(c_host); - GiveSamplesToHost(c_host, samples_to_host_map, multIn); + GiveSamplesToHost(c_host, samples_to_host_map); #endif } } @@ -626,7 +674,6 @@ static void mixer2(chanend c_mixer1, chanend c_audio) while (1) { -#pragma xta endpoint "mixer2_req" request = inuint(c_audio); /* Forward the request on */ @@ -638,7 +685,6 @@ static void mixer2(chanend c_mixer1, chanend c_audio) if(testct(c_mixer1)) { int sampFreq; -#pragma xta endpoint "mixer2_rate_change" unsigned command = inct(c_mixer1); switch(command) @@ -665,8 +711,8 @@ static void mixer2(chanend c_mixer1, chanend c_audio) } for (int i=0;i 1 -#ifdef FAST_MIXER - mixed = doMix1(samples, mix_mult_slice(1)); +#if (MAX_MIX_COUNT > 1) + unsafe + { +#if (FAST_MIXER) + mixed = doMix1(ptr_samples, slice(mix_mult, 1)); #else - mixed = doMix(samples, mix_map_slice(1),mix_mult_slice(1)); + mixed = doMix(ptr_samples, slice(mix_map, 1), slice(mix_mult, 1)); #endif - - write_via_xc_ptr_indexed(samples, (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 1), mixed); - + ptr_samples[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 1] = mixed; + } #if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS) ComputeMixerLevel(mixed, 1); #endif #endif - #if (MAX_FREQ > 96000) /* Fewer mixes when running higher than 96kHz */ if (!mixer2_mix2_flag) #endif { -#if MAX_MIX_COUNT > 3 -#ifdef FAST_MIXER - mixed = doMix3(samples, mix_mult_slice(3)); +#if (MAX_MIX_COUNT > 3) + unsafe + { +#if (FAST_MIXER) + mixed = doMix3(ptr_samples, slice(mix_mult, 3)); #else - mixed = doMix(samples, mix_map_slice(3),mix_mult_slice(3)); + mixed = doMix(ptr_samples, slice(mix_map, 3), slice(mix_mult, 3)); #endif - - write_via_xc_ptr_indexed(samples, (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 3), mixed); - + ptr_samples[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 3] = mixed; + } #if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS) ComputeMixerLevel(mixed, 3); #endif #endif -#if MAX_MIX_COUNT > 5 -#ifdef FAST_MIXER - mixed = doMix5(samples, mix_mult_slice(5)); +#if (MAX_MIX_COUNT > 5) + unsafe + { +#if (FAST_MIXER) + mixed = doMix5(ptr_samples, slice(mix_mult, 5)); #else - mixed = doMix(samples, mix_map_slice(5),mix_mult_slice(5)); + mixed = doMix(ptr_samples, slice(mix_map, 5), slice(mix_mult, 5)); #endif - write_via_xc_ptr_indexed(samples, NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 5, mixed); + ptr_samples[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 5] = mixed; + } #if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS) ComputeMixerLevel(mixed, 5); #endif #endif -#if MAX_MIX_COUNT > 7 -#ifdef FAST_MIXER - mixed = doMix7(samples, mix_mult_slice(7)); +#if (MAX_MIX_COUNT > 7) + unsafe + { +#if (FAST_MIXER) + mixed = doMix7(ptr_samples, slice(mix_mult, 7)); #else - mixed = doMix(samples, mix_map_slice(7),mix_mult_slice(7)); + mixed = doMix(ptr_samples, slice(mix_map, 7), slice(mix_mult, 7)); #endif - - write_via_xc_ptr_indexed(samples, NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 7, mixed); + ptr_samples[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 7] = mixed; + } #if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS) ComputeMixerLevel(mixed, 7); #endif @@ -753,68 +805,52 @@ void mixer(chanend c_mix_in, chanend c_mix_out, chanend c_mix_ctl) #if (MAX_MIX_COUNT > 0) chan c; #endif - multOut = array_to_xc_ptr((multOut_array,unsigned[])); - multIn = array_to_xc_ptr((multIn_array,unsigned[])); - - samples = array_to_xc_ptr((samples_array,unsigned[])); - samples_to_host_map = array_to_xc_ptr((samples_to_host_map_array,unsigned[])); - samples_to_device_map = array_to_xc_ptr((samples_to_device_map_array,unsigned[])); +#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS) samples_to_host_inputs_ptr = array_to_xc_ptr((samples_to_host_inputs, unsigned[])); #ifdef LEVEL_METER_LEDS samples_to_host_inputs_buff_ptr = array_to_xc_ptr((samples_to_host_inputs, unsigned[])); #endif samples_mixer_outputs_ptr = array_to_xc_ptr((samples_mixer_outputs, unsigned[])); - -#if MAX_MIX_COUNT >0 - mix_mult = array_to_xc_ptr((mix_mult_array,unsigned[])); -#ifndef FAST_MIXER - mix_map = array_to_xc_ptr((mix_map_array,unsigned[])); -#endif #endif for (int i=0;i 96000 ? 2 : MAX_MIX_COUNT; - for (int i=0;i 0 +#if (MAX_MIX_COUNT> 0) for (int i=0;i @@ -6,21 +6,20 @@ #include #undef __ASSEMBLER__ #include "audioports.h" -#include #include "xua.h" /* Note since DSD ports could be reused for I2S ports we do all the setup manually in C */ -#if DSD_CHANS_DAC > 0 +#if (DSD_CHANS_DAC > 0) port p_dsd_dac[DSD_CHANS_DAC] = { PORT_DSD_DAC0, #endif -#if DSD_CHANS_DAC > 1 +#if (DSD_CHANS_DAC > 1) PORT_DSD_DAC1, #endif -#if DSD_CHANS_DAC > 2 +#if (DSD_CHANS_DAC > 2) #error > 2 DSD chans currently not supported #endif -#if DSD_CHANS_DAC > 0 +#if (DSD_CHANS_DAC > 0) }; port p_dsd_clk = PORT_DSD_CLK; #endif @@ -46,7 +45,7 @@ void ConfigAudioPortsWrapper( port p_lrclk, port p_bclk, #endif -unsigned int divide, unsigned curSamFreq, unsigned int dsdMode) + port p_mclk_in, clock clk_audio_bclk, unsigned int divide, unsigned curSamFreq) { ConfigAudioPorts( #if (I2S_CHANS_DAC != 0) || (DSD_CHANS_DAC != 0) @@ -61,6 +60,6 @@ unsigned int divide, unsigned curSamFreq, unsigned int dsdMode) p_lrclk, p_bclk, #endif - divide, curSamFreq); + p_mclk_in, clk_audio_bclk, divide, curSamFreq); } diff --git a/lib_xua/src/core/ports/audioports.h b/lib_xua/src/core/ports/audioports.h index 5f01fd9c..d37e86e2 100644 --- a/lib_xua/src/core/ports/audioports.h +++ b/lib_xua/src/core/ports/audioports.h @@ -1,9 +1,12 @@ -// Copyright 2011-2021 XMOS LIMITED. +// Copyright 2011-2023 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #ifndef _AUDIOPORTS_H_ #define _AUDIOPORTS_H_ #include +#ifdef __STDC__ +typedef unsigned clock; +#endif #include "xua.h" #ifdef __XC__ @@ -28,7 +31,7 @@ void ConfigAudioPorts( in port p_bclk, #endif #endif - unsigned int divide, unsigned int curSamFreq); + in port p_mclk_in, clock clk_audio_bclk, unsigned int divide, unsigned int curSamFreq); #else void ConfigAudioPorts( @@ -51,7 +54,7 @@ void ConfigAudioPorts( port p_bclk, #endif #endif - unsigned int divide, unsigned int curSamFreq); + port p_mclk_in, clock clk_audio_bclk, unsigned int divide, unsigned int curSamFreq); #endif /* __XC__*/ @@ -76,7 +79,7 @@ void ConfigAudioPortsWrapper( buffered in port:32 p_bclk, #endif #endif - unsigned int divide, unsigned curSamFreq, unsigned int dsdMode); + in port p_mclk_in, clock clk_audio_bclk, unsigned int divide, unsigned curSamFreq); #else void ConfigAudioPortsWrapper( @@ -92,7 +95,7 @@ void ConfigAudioPortsWrapper( port p_lrclk, port p_bclk, #endif - unsigned int divide, unsigned curSamFreq, unsigned int dsdMode); + port p_mclk_in, clock clk_audio_bclk, unsigned int divide, unsigned curSamFreq); #endif /* __XC__*/ diff --git a/lib_xua/src/core/ports/audioports.xc b/lib_xua/src/core/ports/audioports.xc index 330b7f96..10fd328f 100644 --- a/lib_xua/src/core/ports/audioports.xc +++ b/lib_xua/src/core/ports/audioports.xc @@ -1,4 +1,4 @@ -// Copyright 2011-2021 XMOS LIMITED. +// Copyright 2011-2023 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #include #include @@ -6,11 +6,7 @@ #include "xua.h" #include "audioports.h" - - -extern in port p_mclk_in; extern clock clk_audio_mclk; -extern clock clk_audio_bclk; void ConfigAudioPorts( #if (I2S_CHANS_DAC != 0) || (DSD_CHANS_DAC != 0) @@ -32,16 +28,21 @@ void ConfigAudioPorts( in port p_bclk, #endif #endif -unsigned int divide, unsigned curSamFreq) + in port p_mclk_in, clock clk_audio_bclk, unsigned int divide, unsigned curSamFreq) { #if (I2S_CHANS_DAC != 0) || (I2S_CHANS_ADC != 0) #if (CODEC_MASTER == 0) +#ifdef __XS3A__ + /* Increase drive strength of clock ports to 8mA */ + asm volatile ("setc res[%0], %1" :: "r" (p_bclk), "r" (0x200006)); + asm volatile ("setc res[%0], %1" :: "r" (p_lrclk), "r" (0x200006)); +#endif + /* Note this call to stop_clock() will pause forever if the port clocking the clock-block is not low. * deliver() should return with this being the case */ stop_clock(clk_audio_bclk); - if(!isnull(p_lrclk)) { clearbuf(p_lrclk); @@ -55,44 +56,19 @@ unsigned int divide, unsigned curSamFreq) } #endif -#if (I2S_CHANS_DAC != 0) +#if (I2S_CHANS_DAC != 0)|| (DSD_CHANS_DAC != 0) for(int i = 0; i < numPortsDac; i++) { clearbuf(p_i2s_dac[i]); } #endif -#if defined(__XS2A__) || defined(__XS3A__) unsafe { /* Clock bitclock clock block from master clock pin (divided) */ configure_clock_src_divide(clk_audio_bclk, (port) p_mclk_in, (divide/2)); configure_port_clock_output(p_bclk, clk_audio_bclk); } -#else - #error XS1 no longer supported in audio core - /* For a divide of one (i.e. bitclock == master-clock) BClk is set to clock_output mode. - * In this mode it outputs an edge clock on every tick of itsassociated clock_block. - * - * For all other divides, BClk is clocked by the master clock and data - * will be output to p_bclk to generate the bit clock. - */ - if (divide == 1) /* e.g. 176.4KHz from 11.2896 */ - { - configure_port_clock_output(p_bclk, clk_audio_mclk); - - /* Generate bit clock block straight from mclk */ - configure_clock_src(clk_audio_bclk, p_mclk_in); - } - else - { - /* bit clock port from master clock clock-clock block */ - configure_out_port_no_ready(p_bclk, clk_audio_mclk, 0); - - /* Generate bit clock block from pin */ - configure_clock_src(clk_audio_bclk, p_bclk); - } -#endif if(!isnull(p_lrclk)) { @@ -100,8 +76,41 @@ unsigned int divide, unsigned curSamFreq) configure_out_port_no_ready(p_lrclk, clk_audio_bclk, 0); } -#if (I2S_CHANS_DAC != 0) - /* Clock I2S output data ports from clock block */ +#if (I2S_CHANS_ADC != 0) + /* Some adustments for timing. Sample ADC lines on negative edge and add some delay */ + if(XUA_PCM_FORMAT == XUA_PCM_FORMAT_TDM) + { + for(int i = 0; i < numPortsAdc; i++) + { + set_port_sample_delay(p_i2s_adc[i]); + set_pad_delay(p_i2s_adc[i], 4); + } + } +#endif + +#elif (CODEC_MASTER) + /* Stop bit and master clock blocks */ + stop_clock(clk_audio_bclk); + + /* Clock bclk clock-block from bclk pin */ + configure_clock_src(clk_audio_bclk, p_bclk); + + configure_in_port_no_ready(p_lrclk, clk_audio_bclk); + + /* Do some clocking shifting to get data in the valid window */ + /* E.g. Only shift when running at 88.2+ kHz TDM slave */ + int bClkDelay_fall = 0; + if(curSamFreq * I2S_CHANS_PER_FRAME * 32 >= 20000000) + { + /* 18 * 2ns = 36ns. This results in a -4ns (36 - 40) shift at 96KHz and -8ns (36 - 44) at 88.4KHz */ + bClkDelay_fall = 18; + } + + set_clock_fall_delay(clk_audio_bclk, bClkDelay_fall); +#endif + +#if (I2S_CHANS_DAC != 0) || (DSD_CHANS_DAC != 0) + /* Clock I2S/DSD output data ports from b-clock clock block */ for(int i = 0; i < numPortsDac; i++) { configure_out_port_no_ready(p_i2s_dac[i], clk_audio_bclk, 0); @@ -119,46 +128,6 @@ unsigned int divide, unsigned curSamFreq) /* Start clock blocks ticking */ start_clock(clk_audio_bclk); -#else /* CODEC_MASTER */ - - /* Stop bit and master clock blocks */ - stop_clock(clk_audio_bclk); - - /* Clock bclk clock-block from bclk pin */ - configure_clock_src(clk_audio_bclk, p_bclk); - - - /* Do some clocking shifting to get data in the valid window */ - /* E.g. Only shift when running at 88.2+ kHz TDM slave */ - int bClkDelay_fall = 0; - if(curSamFreq * I2S_CHANS_PER_FRAME * 32 >= 20000000) - { - /* 18 * 2ns = 36ns. This results in a -4ns (36 - 40) shift at 96KHz and -8ns (36 - 44) at 88.4KHz */ - bClkDelay_fall = 18; - } - - set_clock_fall_delay(clk_audio_bclk, bClkDelay_fall); - -#if (I2S_CHANS_DAC != 0) - /* Clock I2S output data ports from b-clock clock block */ - for(int i = 0; i < I2S_WIRES_DAC; i++) - { - configure_out_port_no_ready(p_i2s_dac[i], clk_audio_bclk, 0); - } -#endif - -#if (I2S_CHANS_ADC != 0) - /* Clock I2S input data ports from clock block */ - for(int i = 0; i < I2S_WIRES_ADC; i++) - { - configure_in_port_no_ready(p_i2s_adc[i], clk_audio_bclk); - } -#endif - - configure_in_port_no_ready(p_lrclk, clk_audio_bclk); - - start_clock(clk_audio_bclk); -#endif -#endif +#endif //#if (I2S_CHANS_DAC != 0) || (I2S_CHANS_ADC != 0) } diff --git a/lib_xua/src/core/support/powersave/archU_powerSaving.h b/lib_xua/src/core/support/powersave/archU_powerSaving.h deleted file mode 100644 index 51f8281d..00000000 --- a/lib_xua/src/core/support/powersave/archU_powerSaving.h +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2013-2021 XMOS LIMITED. -// This Software is subject to the terms of the XMOS Public Licence: Version 1. -#ifndef ARCH_U_POWER_SAVING_ -#define ARCH_U_POWER_SAVING_ - -/* Sets the voltage down by VOLTAGE_REDUCTION_mV (voltage is set to 10 * X + 600 mV), - * and adjusts other features to save power - */ -void archU_powerSaving(); - -#endif /*ARCH_U_POWER_SAVING_*/ diff --git a/lib_xua/src/core/support/powersave/archU_powerSaving.xc b/lib_xua/src/core/support/powersave/archU_powerSaving.xc deleted file mode 100644 index e6cd04c5..00000000 --- a/lib_xua/src/core/support/powersave/archU_powerSaving.xc +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2013-2021 XMOS LIMITED. -// This Software is subject to the terms of the XMOS Public Licence: Version 1. - -#if (XUD_SERIES_SUPPORT==1) -#include "archU_powerSaving.h" -#include -#include -#include -#include - -#if (XCC_MAJOR_VERSION >= 1300) -#include -#else -#define hwtimer_t timer -#endif - -#ifndef VOLTAGE_REDUCTION_mV -#define VOLTAGE_REDUCTION_mV 20 -#endif - -unsigned get_tile_id(tileref t); // Required for use with 12.0 tools. get_tile_id() available in xs1.h with 13.0 tools. - -#define ARCH_U_VOLTAGE_FIRST_STEP 39 // First step down from 1V -#define ARCH_U_SSWITCH_PRESCALER 8 // Sswitch down to 1/8 clk freq - -void archU_powerSaving() -{ - if (get_local_tile_id() == get_tile_id(tile[0])) - { - unsigned writeval[1]; - unsigned char writevalc[1]; - - // Reduce the VDDCORE voltage level - for (unsigned count=0; count < (VOLTAGE_REDUCTION_mV/10); count++) - { - hwtimer_t t; - int time; - - writeval[0] = (ARCH_U_VOLTAGE_FIRST_STEP - count); - write_periph_32(usb_tile, XS1_SU_PER_PWR_CHANEND_NUM, XS1_SU_PER_PWR_VOUT1_LVL_NUM, 1, writeval); - - t :> time; - time += (1 * PLATFORM_REFERENCE_MHZ); // Wait 1us per step - t when timerafter(time) :> void; - } - - // Set switch prescaler down - write_node_config_reg(tile[0], XS1_SSWITCH_CLK_DIVIDER_NUM, (ARCH_U_SSWITCH_PRESCALER - 1)); // PLL clk will be divided by value + 1 - - // Both DC-DCs in PWM mode, I/O and PLL supply on, Analogue & core on - writeval[0] = XS1_SU_PWR_VOUT1_EN_SET(0, 1); - writeval[0] = XS1_SU_PWR_VOUT2_EN_SET(writeval[0], 1); - writeval[0] = XS1_SU_PWR_VOUT5_EN_SET(writeval[0], 1); - writeval[0] = XS1_SU_PWR_VOUT6_EN_SET(writeval[0], 1); - write_periph_32(usb_tile, XS1_SU_PER_PWR_CHANEND_NUM, XS1_SU_PER_PWR_STATE_AWAKE_NUM, 1, writeval); - - // USB suspend off, voltage adjustable, sleep clock 32KHz - writeval[0] = XS1_SU_PWR_USB_PD_EN_SET(0, 1); - writeval[0] = XS1_SU_PWR_RST_VOUT_LVL_SET(writeval[0], 1); - writeval[0] = XS1_SU_PWR_LD_AWAKE_SET(writeval[0], 1); - write_periph_32(usb_tile, XS1_SU_PER_PWR_CHANEND_NUM, XS1_SU_PER_PWR_MISC_CTRL_NUM, 1, writeval); - - // Turn off on-chip silicon oscillator (20MHz or 32KHz) - writevalc[0] = XS1_SU_ON_SI_OSC_EN_SET(0, 1); - writevalc[0] = XS1_SU_ON_SI_OSC_SLOW_SET(writevalc[0], 1); - write_periph_8(usb_tile, XS1_SU_PER_OSC_CHANEND_NUM, XS1_SU_PER_OSC_ON_SI_CTRL_NUM, 1, writevalc); - } -} -#endif - diff --git a/lib_xua/src/core/support/reboot.xc b/lib_xua/src/core/support/reboot.xc index d4c33d27..64bbb4b7 100644 --- a/lib_xua/src/core/support/reboot.xc +++ b/lib_xua/src/core/support/reboot.xc @@ -1,22 +1,13 @@ -// Copyright 2011-2021 XMOS LIMITED. +// Copyright 2011-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #include #include #include #include -#define XS1_SU_PERIPH_USB_ID 0x1 - -//Normally we would enumerate the XUD_SERIES_SUPPORT possibilities using defines in -//xud.h but we have hard coded them to remove dependancy of sc_xud - -#if (XUD_SERIES_SUPPORT == 4) #include "xs2_su_registers.h" #define XS2_SU_PERIPH_USB_ID 0x1 #define PLL_MASK 0x3FFFFFFF -#else -#define PLL_MASK 0xFFFFFFFF -#endif /* Note, this function is prototyped in xs1.h only from 13 tools onwards */ unsigned get_tile_id(tileref); @@ -31,29 +22,19 @@ static void reset_tile(unsigned const tileId) read_sswitch_reg(tileId, 6, pllVal); pllVal &= PLL_MASK; write_sswitch_reg_no_ack(tileId, 6, pllVal); -} +} /* Reboots XMOS device by writing to the PLL config register * Note - resetting is per *node* not tile */ void device_reboot(void) { -#if (XUD_SERIES_SUPPORT == 1) - /* Disconnect from bus */ - unsigned data[] = {4}; - write_periph_32(usb_tile, XS1_SU_PERIPH_USB_ID, XS1_SU_PER_UIFM_FUNC_CONTROL_NUM, 1, data); - - /* Ideally we would reset SU1 here but then we loose power to the xcore and therefore the DFU flag */ - /* Disable USB and issue reset to xcore only - not analogue chip */ - write_node_config_reg(usb_tile, XS1_SU_CFG_RST_MISC_NUM,0b10); -#else - unsigned int localTileId = get_local_tile_id(); unsigned int tileId; unsigned int tileArrayLength; unsigned int localTileNum; -#if (XUD_SERIES_SUPPORT == 4) +#if defined(__XS2A__) /* Disconnect from bus */ unsigned data[] = {4}; write_periph_32(usb_tile, XS2_SU_PERIPH_USB_ID, XS1_GLX_PER_UIFM_FUNC_CONTROL_NUM, 1, data); @@ -61,12 +42,12 @@ void device_reboot(void) tileArrayLength = sizeof(tile)/sizeof(tileref); - /* Note - we could be in trouble if this doesn't return 0/1 since + /* Note - we could be in trouble if this doesn't return 0/1 since * this code doesn't properly handle any network topology other than a - * simple line + * simple line */ /* Find tile index of the local tile ID */ - for(int tileNum = 0; tileNum=0; tileNum-=2) -#else - /* Reset all tiles, starting from the remote ones */ - for(int tileNum = tileArrayLength-1; tileNum>=0; tileNum--) -#endif { /* Cannot cast tileref to unsigned! */ tileId = get_tile_id(tile[tileNum]); /* Do not reboot local tile (or tiles residing on the same node) yet */ -#if defined(__XS2A__) || defined(__XS3A__) if((localTileNum | 1) != (tileNum | 1)) - -#else - if(localTileNum != tileNum) -#endif { reset_tile(tileId); } @@ -100,7 +71,6 @@ void device_reboot(void) /* Finally reboot the node this tile resides on */ reset_tile(localTileId); -#endif while (1); } diff --git a/lib_xua/src/core/uac_hwresources.h b/lib_xua/src/core/uac_hwresources.h index 0142a2a9..c3ba8f36 100644 --- a/lib_xua/src/core/uac_hwresources.h +++ b/lib_xua/src/core/uac_hwresources.h @@ -1,49 +1,15 @@ -// Copyright 2015-2021 XMOS LIMITED. +// Copyright 2015-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #ifndef _UAC_HWRESOURCES_H_ #define _UAC_HWRESOURCES_H_ -#if XUA_USB_EN -#include "xud.h" /* XMOS USB Device Layer defines and functions */ -#endif - -#if ((XUD_SERIES_SUPPORT != XUD_U_SERIES) && (XUD_SERIES_SUPPORT != XUD_X200_SERIES)) - -/* XUD_L_SERIES and XUD_G_SERIES */ - -#if (AUDIO_IO_TILE == XUD_TILE) -/* Note: L series ref clocked clocked from USB clock when USB enabled - use another clockblock for MIDI - * if MIDI and XUD on same tile. See XUD documentation. - * - * This is a clash with S/PDIF Tx but simultaneous S/PDIF and MIDI not currently supported on single tile device - * - */ -#define CLKBLK_MIDI XS1_CLKBLK_1; -#else -#define CLKBLK_MIDI XS1_CLKBLK_REF; -#endif - -#define CLKBLK_SPDIF_TX XS1_CLKBLK_1 -#define CLKBLK_SPDIF_RX XS1_CLKBLK_1 -#define CLKBLK_MCLK XS1_CLKBLK_2 /* Note, potentially used twice */ -#define CLKBLK_ADAT_RX XS1_CLKBLK_3 -#define CLKBLK_USB_RST XS1_CLKBLK_4 /* Clock block passed into L/G series XUD */ -#define CLKBLK_FLASHLIB XS1_CLKBLK_5 /* Clock block for use by flash lib */ - -#define CLKBLK_I2S_BIT XS1_CLKBLK_3 - -#else - -/* XUD_U_SERIES, XUD_X200_SERIES */ -/* Note, U-series XUD uses clock blocks 4 and 5 - see XUD_Ports.xc */ #define CLKBLK_MIDI XS1_CLKBLK_REF; #define CLKBLK_SPDIF_TX XS1_CLKBLK_1 #define CLKBLK_SPDIF_RX XS1_CLKBLK_1 -#define CLKBLK_MCLK XS1_CLKBLK_2 /* Note, potentially used twice */ +#define CLKBLK_MCLK XS1_CLKBLK_2 #define CLKBLK_FLASHLIB XS1_CLKBLK_3 /* Clock block for use by flash lib */ -#define CLKBLK_ADAT_RX XS1_CLKBLK_REF /* Use REF for ADAT_RX on U/x200 series */ +#define CLKBLK_ADAT_RX XS1_CLKBLK_REF /* Use REF for ADAT_RX on x200/AI series */ #define CLKBLK_I2S_BIT XS1_CLKBLK_3 -#endif #endif /* _UAC_HWRESOURCES_H_ */ diff --git a/lib_xua/src/core/user/hid/user_hid.h b/lib_xua/src/core/user/hid/user_hid.h index 72d47abb..b17a4dd5 100644 --- a/lib_xua/src/core/user/hid/user_hid.h +++ b/lib_xua/src/core/user/hid/user_hid.h @@ -1,66 +1,60 @@ // Copyright 2013-2021 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. +/** + * @brief Human Interface Device (HID) API + * + * This file defines the Application Programming Interface (API) used to record HID + * events and retrieve a HID Report for sending to a host. + * The using application has the responsibility to fulfill this API. + * Document section numbers refer to the HID Device Class Definition, version 1.11. + */ + #ifndef __USER_HID_H__ #define __USER_HID_H__ -/** - * \brief HID event identifiers - * - * This enumeration defines a constant value for each HID event. - * It defines one value for each of the four GPI pins supported in the standard voice products. - * It defines a further 28 values for generic events. - */ -typedef enum hidEventId_t { - HID_EVENT_ID_GPI0 = 0, - HID_EVENT_ID_GPI1, - HID_EVENT_ID_GPI2, - HID_EVENT_ID_GPI3, - HID_EVENT_ID_EVT0, - HID_EVENT_ID_EVT1, - HID_EVENT_ID_EVT2, - HID_EVENT_ID_EVT3, - HID_EVENT_ID_EVT4, - HID_EVENT_ID_EVT5, - HID_EVENT_ID_EVT6, - HID_EVENT_ID_EVT7, - HID_EVENT_ID_EVT8, - HID_EVENT_ID_EVT9, - HID_EVENT_ID_EVT10, - HID_EVENT_ID_EVT11, - HID_EVENT_ID_EVT12, - HID_EVENT_ID_EVT13, - HID_EVENT_ID_EVT14, - HID_EVENT_ID_EVT15, - HID_EVENT_ID_EVT16, - HID_EVENT_ID_EVT17, - HID_EVENT_ID_EVT18, - HID_EVENT_ID_EVT19, - HID_EVENT_ID_EVT20, - HID_EVENT_ID_EVT21, - HID_EVENT_ID_EVT22, - HID_EVENT_ID_EVT23, - HID_EVENT_ID_EVT24, - HID_EVENT_ID_EVT25, - HID_EVENT_ID_EVT26, - HID_EVENT_ID_EVT27, - HID_EVENT_ID_INVALID = 0xffffffff, -} hidEventId_t; +#include -#define HID_DATA_BYTES 4 +/** + * \brief HID event + * + * This struct identifies: + * - the HID Report that reports an event, i.e., the ID, + * - the location within the HID Report for the event, i.e., the byte and bit, and + * - the value to report for that location (typically interpreted as a Boolean). + * It assumes only single bit flags within the HID Report. + */ +typedef struct hidEvent_t { + unsigned bit; + unsigned byte; + unsigned id; + unsigned value; +} hidEvent_t; + +#define HID_MAX_DATA_BYTES ( 4 ) +#define HID_EVENT_INVALID_ID ( 0x100 ) #if( 0 < HID_CONTROLS ) /** - * \brief Get the data for the next HID report + * \brief Get the data for the next HID Report * * \note This function returns the HID data as a list of unsigned char because the * \c XUD_SetReady_In() accepts data for transmission to the USB Host using * this type. * - * \param{out} hidData The HID data + * \param[in] id The HID Report ID (see 5.6, 6.2.2.7, 8.1 and 8.2) + * Set to zero if the application provides only one HID Report + * which does not include a Report ID + * \param[out] hidData The HID data + * If using Report IDs, this function places the Report ID in + * the first element; otherwise the first element holds the + * first byte of HID event data. + * + * \returns The length of the HID Report in the \a hidData argument + * \retval Zero means no new HID event data has been recorded for the given \a id */ -void UserHIDGetData( unsigned char hidData[ HID_DATA_BYTES ]); +size_t UserHIDGetData( const unsigned id, unsigned char hidData[ HID_MAX_DATA_BYTES ]); /** * \brief Initialize HID processing @@ -70,17 +64,18 @@ void UserHIDInit( void ); /** * \brief Record that a HID event has occurred * - * \param{in} hidEventId The identifier of an event which has occurred - * \param{in} hidEventData A list of data associated with the event - * \param{in} hidEventDataSize The length of the event data list + * \param[in] hidEvent A list of events which have occurred. + * Each element specifies a HID Report ID, a bit and byte + * within the HID Report and the value for it. + * Set the Report ID to zero if not using Report IDs + * (see 5.6, 6.2.2.7, 8.1 and 8.2). + * \param[in] hidEventCnt The length of the \a hidEvent list. * - * \note At present, this function only takes a single element of event data, i.e. - * hidEventDataSize must equal 1. - * - * \note At present, this function treats the event data as a Boolean flag. - * Zero means False; all other values mean True. + * \returns The index of the first unrecorded event in \a hidEvent + * \retval Zero indicates no events were recorded + * \retval \a hidEventCnt indicates all events were recorded */ -void UserHIDRecordEvent( const hidEventId_t hidEventId, const int * hidEventData, const unsigned hidEventDataSize ); +size_t UserHIDRecordEvent( const hidEvent_t hidEvent[], const size_t hidEventCnt ); #endif /* ( 0 < HID_CONTROLS ) */ #endif /* __USER_HID_H__ */ diff --git a/lib_xua/src/core/xuduser/xuduser.c b/lib_xua/src/core/xuduser/xuduser.c index 207906e7..c962e701 100644 --- a/lib_xua/src/core/xuduser/xuduser.c +++ b/lib_xua/src/core/xuduser/xuduser.c @@ -1,10 +1,12 @@ -// Copyright 2013-2021 XMOS LIMITED. +// Copyright 2013-2023 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. -#if XUA_USB_EN #include "xua.h" +#if XUA_USB_EN #include "hostactive.h" #include "audiostream.h" +/* Implementations over-riding empty versions in lib_xud/sec/core/XUD_User.c */ + void XUD_UserSuspend(void) __attribute__ ((weak)); void XUD_UserSuspend(void) { diff --git a/lib_xua/src/dfu/dfu.xc b/lib_xua/src/dfu/dfu.xc index 1570c7eb..849f4e28 100644 --- a/lib_xua/src/dfu/dfu.xc +++ b/lib_xua/src/dfu/dfu.xc @@ -5,19 +5,18 @@ #include #include -#ifndef NO_USB +#if XUA_USB_EN #include "xud_device.h" #include "dfu_types.h" #include "flash_interface.h" #include "dfu_interface.h" -#if (XUD_SERIES_SUPPORT==4) -/* xCORE-200 */ +#if defined(__XS2A__) /* Note range 0x7FFC8 - 0x7FFFF guarenteed to be untouched by tools */ #define FLAG_ADDRESS 0x7ffcc #else -/* Note range 0x1FFC8 - 0x1FFFF guarenteed to be untouched by tools */ -#define FLAG_ADDRESS 0x1ffcc +/* Note range 0xFFFC8 - 0xFFFFF guarenteed to be untouched by tools */ +#define FLAG_ADDRESS 0xfffcc #endif /* Store Flag to fixed address */ @@ -581,6 +580,6 @@ int DFUDeviceRequests(XUD_ep ep0_out, XUD_ep &?ep0_in, USB_SetupPacket_t &sp, ch } return returnVal; } -#endif /* NO_USB */ +#endif /* XUA_USB_EN */ #endif diff --git a/lib_xua/src/dfu/flashlib_user.c b/lib_xua/src/dfu/flashlib_user.c index 2ef7e47a..323afb86 100644 --- a/lib_xua/src/dfu/flashlib_user.c +++ b/lib_xua/src/dfu/flashlib_user.c @@ -90,9 +90,9 @@ int flash_cmd_enable_ports() #ifdef DFU_FLASH_DEVICE #ifdef QUAD_SPI_FLASH - result = fl_connectToDevice(&p_qflash, flash_devices, 1); + result = fl_connectToDevice(&p_qflash, flash_devices, sizeof(flash_devices) / sizeof(fl_QuadDeviceSpec)); #else - result = fl_connectToDevice(&p_flash, flash_devices, 1); + result = fl_connectToDevice(&p_flash, flash_devices, sizeof(flash_devices) / sizeof(fl_DeviceSpec)); #endif #else /* Use default flash list */ diff --git a/lib_xua/src/hid/hid.xc b/lib_xua/src/hid/hid.xc index f3c741a1..0f506d59 100644 --- a/lib_xua/src/hid/hid.xc +++ b/lib_xua/src/hid/hid.xc @@ -1,4 +1,4 @@ -// Copyright 2019-2021 XMOS LIMITED. +// Copyright 2019-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #include #include @@ -7,38 +7,19 @@ #include "xud.h" #include "xud_std_requests.h" #include "xua_hid.h" +#include "xua_hid_report.h" + +#define DEBUG_UNIT HID_XC +#define DEBUG_PRINT_ENABLE_HID_XC 0 +#include "debug_print.h" #if( 0 < HID_CONTROLS ) -#define MS_IN_TICKS 100000U - -static unsigned s_hidChangePending = 0U; -static unsigned s_hidCurrentPeriod = ENDPOINT_INT_INTERVAL_IN_HID * MS_IN_TICKS; -static unsigned s_hidIdleActive = 0U; -static unsigned s_hidIndefiniteDuration = 0U; -static unsigned s_hidNextReportTime = 0U; -static unsigned s_hidReportTime = 0U; - -unsafe { - volatile unsigned * unsafe s_hidChangePendingPtr = &s_hidChangePending; -} - static unsigned HidCalcNewReportTime( const unsigned currentPeriod, const unsigned reportTime, const unsigned reportToSetIdleInterval, const unsigned newPeriod ); static unsigned HidCalcReportToSetIdleInterval( const unsigned reportTime ); static unsigned HidFindSetIdleActivationPoint( const unsigned currentPeriod, const unsigned timeWithinPeriod ); static XUD_Result_t HidProcessSetIdleRequest( XUD_ep c_ep0_out, XUD_ep c_ep0_in, USB_SetupPacket_t &sp ); static unsigned HidTimeDiff( const unsigned earlierTime, const unsigned laterTime ); -void HidCalcNextReportTime( void ) -{ - s_hidNextReportTime = s_hidReportTime + s_hidCurrentPeriod; -} - -void HidCaptureReportTime( void ) -{ - timer tmr; - tmr :> s_hidReportTime; -} - XUD_Result_t HidInterfaceClassRequests( XUD_ep c_ep0_out, XUD_ep c_ep0_in, @@ -58,47 +39,28 @@ XUD_Result_t HidInterfaceClassRequests( return result; } -void HidClearChangePending( void ) +unsigned HidIsSetIdleSilenced( const unsigned id ) { - unsafe { - *s_hidChangePendingPtr = 0U; - } -} + unsigned isSilenced = hidIsIdleActive( id ); -unsigned HidIsChangePending( void ) -{ - return( s_hidChangePending != 0 ); -} + if( !isSilenced ) { + unsigned currentTime; + // Use inline assembly to access the time without creating a side-effect. + // The mapper complains if the time comes from an XC timer because this function is called in the guard of a select case. + // Appearently the use of a timer creates a side-effect that prohibits the operation of the select functionality. + asm volatile( "gettime %0" : "=r" ( currentTime )); + isSilenced = ( 0U == hidGetReportPeriod( id ) || ( timeafter( hidGetNextReportTime( id ), currentTime ))); + } -unsigned HidIsSetIdleSilenced( void ) -{ - unsigned isSilenced = s_hidIdleActive; - - if( s_hidIdleActive ) { - unsigned currentTime; - // Use inline assembly to access the time without creating a side-effect. - // The mapper complains if the time comes from an XC timer because this function is called in the guard of a select case. - // Appearently the use of a timer creates a side-effect that prohibits the operation of the select functionality. - asm volatile( "gettime %0" : "=r" ( currentTime )); - isSilenced = ( s_hidIndefiniteDuration || ( timeafter( s_hidNextReportTime, currentTime ))); - } - - return isSilenced; -} - -void HidSetChangePending( void ) -{ - unsafe { - *s_hidChangePendingPtr = 1; - } + return isSilenced; } /** * \brief Calculate the timer value for sending the next HID Report. * * With regard to Section 7.2.4 Set_Idle Request of the USB Device Class Definition for Human - * Interface Devices (HID) Version 1.11, I've interpreted 'currently executing period' and - * 'current period' to mean the previously established Set Idle duration if one has been + * Interface Devices (HID) Version 1.11, 'currently executing period' and 'current period' have + * been interpreted to mean the previously established Set Idle duration if one has been * established or the polling interval from the HID Report Descriptor if a Set Idle duration * has not been established. * @@ -112,7 +74,7 @@ void HidSetChangePending( void ) */ static unsigned HidCalcNewReportTime( const unsigned currentPeriod, const unsigned reportTime, const unsigned reportToSetIdleInterval, const unsigned newPeriod ) { - unsigned nextReportTime = 0; + unsigned nextReportTime = 0U; if( HidFindSetIdleActivationPoint( currentPeriod, reportToSetIdleInterval )) { /* Activate immediately after sending the next HID Report */ @@ -171,6 +133,29 @@ static unsigned HidFindSetIdleActivationPoint( const unsigned currentPeriod, con return result; } +/** + * \brief Configure a hid report's next report time and idle status based on a setidle request + * + * \param[in] reportId -- The report ID to modify + * \param[in] reportDuration -- The duration of the setidle request + * + */ +static void HidUpdateReportPeriod( unsigned reportId, unsigned reportDuration ) { + unsigned currentPeriod = hidGetReportPeriod( reportId ); + + hidSetIdle( reportId, ( 0U == reportDuration ) || ( ENDPOINT_INT_INTERVAL_IN_HID < reportDuration )); + + if( hidIsIdleActive( reportId )) { + unsigned reportTime = hidGetReportTime( reportId ); + unsigned reportToSetIdleInterval = HidCalcReportToSetIdleInterval( reportTime ); + unsigned nextReportTime = HidCalcNewReportTime( currentPeriod, reportTime, reportToSetIdleInterval, reportDuration * MS_IN_TICKS ); + hidSetNextReportTime( reportId, nextReportTime ); + currentPeriod = reportDuration * MS_IN_TICKS; + } + + hidSetReportPeriod( reportId, currentPeriod ); +} + /** * \brief Process a Set Idle request * @@ -208,20 +193,24 @@ static XUD_Result_t HidProcessSetIdleRequest( XUD_ep c_ep0_out, XUD_ep c_ep0_in, Any Interface value other than INTERFACE_NUMBER_HID indicates an error by the USB Host. */ - if(( 0U == reportId ) && ( INTERFACE_NUMBER_HID == interfaceNum )) { - s_hidIdleActive = (( 0U == duration ) || ( ENDPOINT_INT_INTERVAL_IN_HID < duration )); + if( INTERFACE_NUMBER_HID == interfaceNum ) { + if( hidIsReportIdValid( reportId ) ) { + HidUpdateReportPeriod( reportId, duration ); - if( s_hidIdleActive ) { - unsigned reportToSetIdleInterval = HidCalcReportToSetIdleInterval( s_hidReportTime ); - s_hidNextReportTime = HidCalcNewReportTime( s_hidCurrentPeriod, s_hidReportTime, reportToSetIdleInterval, duration * MS_IN_TICKS ); - s_hidCurrentPeriod = duration * MS_IN_TICKS; - s_hidIndefiniteDuration = ( 0U == duration ); - } else { - s_hidCurrentPeriod = ENDPOINT_INT_INTERVAL_IN_HID * MS_IN_TICKS; - s_hidIndefiniteDuration = 0U; + result = XUD_DoSetRequestStatus( c_ep0_in ); } + else if ( reportId == 0U ) { + // Wildcard request - set all report IDs to idle + unsigned startReportId = hidGetNextValidReportId(reportId); - result = XUD_DoSetRequestStatus( c_ep0_in ); + reportId = startReportId; + do { + HidUpdateReportPeriod( reportId, duration ); + reportId = hidGetNextValidReportId( reportId ); + } while( reportId != startReportId); + + result = XUD_DoSetRequestStatus( c_ep0_in ); + } } return result; diff --git a/lib_xua/src/hid/hid_report.c b/lib_xua/src/hid/hid_report.c new file mode 100644 index 00000000..204f6235 --- /dev/null +++ b/lib_xua/src/hid/hid_report.c @@ -0,0 +1,858 @@ +// Copyright 2021-2022 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#include "xua_conf_full.h" +#if( 0 < HID_CONTROLS ) + +#include +#include +#include +#include +#include + +#include "descriptor_defs.h" +#include "xua_hid_report.h" +#include "hid_report_descriptor.h" +#include "swlock.h" + + +#define HID_REPORT_ITEM_LOCATION_SIZE ( 1 ) +#define HID_REPORT_DESCRIPTOR_ITEM_COUNT ( sizeof hidReportDescriptorItems / sizeof ( USB_HID_Short_Item_t* )) +#define HID_REPORT_DESCRIPTOR_MAX_LENGTH ( HID_REPORT_DESCRIPTOR_ITEM_COUNT * \ + ( sizeof ( USB_HID_Short_Item_t ) - HID_REPORT_ITEM_LOCATION_SIZE )) + +swlock_t hidStaticVarLock = SWLOCK_INITIAL_VALUE; + +/* + * Each element in s_hidChangePending corresponds to an element in hidReports. + */ + +static unsigned s_hidChangePending[ HID_REPORT_COUNT ]; +static unsigned char s_hidReportDescriptor[ HID_REPORT_DESCRIPTOR_MAX_LENGTH ]; +static size_t s_hidReportDescriptorLength; +static unsigned s_hidReportDescriptorPrepared; + +static unsigned s_hidCurrentPeriod[ HID_REPORT_COUNT ]; +static unsigned s_hidIdleActive[ HID_REPORT_COUNT ]; +static unsigned s_hidNextReportTime[ HID_REPORT_COUNT ]; +static unsigned s_hidReportTime[ HID_REPORT_COUNT ]; + +/** + * @brief Get the bit position from the location of a report element + * + * Parameters: + * + * @param[in] location The \c location field from a \c USB_HID_Report_Element_t + * + * @return The bit position of the report element + */ +static unsigned hidGetElementBitLocation( const unsigned short location ); + +/** + * @brief Get the byte position from the location of a report element + * + * Parameters: + * + * @param[in] location The \c location field from a \c USB_HID_Report_Element_t + * + * @return The byte position of the report element within the HID report + */ +static unsigned hidGetElementByteLocation( const unsigned short location ); + +/** + * @brief Get the report identifier from the location of a report element + * + * Parameters: + * + * @param[in] location The \c location field from a \c USB_HID_Report_Element_t + * + * @return The report id of the report element within the HID report + */ +static unsigned hidGetElementReportId( const unsigned short location ); + +/** + * @brief Get the report length from the location of a report element + * + * Parameters: + * + * @param[in] location The \c location field from a \c USB_HID_Report_Element_t + * + * @return The length of the HID report + */ +static size_t hidGetElementReportLength( const unsigned short location ); + +/** + * @brief Get the number of data bytes from the header of an Item + * + * Parameters: + * + * @param[in] header The \c header field from a \c USB_HID_Short_Item + * + * @return The amount of data for the Item + */ +static unsigned hidGetItemSize( const unsigned char header ); + +/** + * @brief Get the Tag from the header of an Item + * + * Parameters: + * + * @param[in] header The \c header field from a \c USB_HID_Short_Item + * + * @return The Tag of the Item + */ +static unsigned hidGetItemTag( const unsigned char header ); + +/** + * @brief Get the Type from the header of an Item + * + * Parameters: + * + * @param[in] header The \c header field from a \c USB_HID_Short_Item + * + * @return The Type of the Item + */ +static unsigned hidGetItemType( const unsigned char header ); + +/** + * @brief Get the Usage Page number for a given byte in the HID Report + * + * Parameters: + * + * @param[in] id The HID Report ID for the Usage Page. + * A value of zero means the application does not use Report IDs. + * + * @return The USB HID Usage Page code or zero if the \a id parameter is out-of-range + */ +static unsigned hidGetUsagePage( const unsigned id ); + +/** + * @brief Translate an Item from the \c USB_HID_Short_Item format to raw bytes + * + * Parameters: + * + * @param[in] inPtr A pointer to a \c USB_HID_Short_Item + * @param[in,out] outPtrPtr A pointer to a pointer to the next available space in the raw + * byte buffer. Passed as a pointer to a pointer to allow this + * function to return the updated pointer to the raw byte buffer. + * + * @return The number of bytes placed in the raw byte buffer + */ +static size_t hidTranslateItem( const USB_HID_Short_Item_t* inPtr, unsigned char** outPtrPtr ); + +unsigned hidIsReportIdInUse ( void ) { + return !hidIsReportIdValid(0U); +} + +void hidCalcNextReportTime( const unsigned id ) +{ + swlock_acquire(&hidStaticVarLock); + for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) { + if( id == hidGetElementReportId( hidReports[ idx ]->location )) { + s_hidNextReportTime[ idx ] = s_hidReportTime[ idx ] + s_hidCurrentPeriod[ idx ]; + } + } + swlock_release(&hidStaticVarLock); +} + +void hidCaptureReportTime( const unsigned id, const unsigned time ) +{ + swlock_acquire(&hidStaticVarLock); + for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) { + if( id == hidGetElementReportId( hidReports[ idx ]->location )) { + s_hidReportTime[ idx ] = time; + } + } + swlock_release(&hidStaticVarLock); +} + +void hidClearChangePending( const unsigned id ) +{ + swlock_acquire(&hidStaticVarLock); + for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx) { + if(( id == 0U ) || ( id == hidGetElementReportId( hidReports[ idx ]->location ))) { + s_hidChangePending[ idx ] = 0U; + break; + } + } + swlock_release(&hidStaticVarLock); +} + +static unsigned hidGetElementBitLocation( const unsigned short location ) +{ + unsigned bBit = ( location & HID_REPORT_ELEMENT_LOC_BIT_MASK ) >> HID_REPORT_ELEMENT_LOC_BIT_SHIFT; + return bBit; +} + +static unsigned hidGetElementByteLocation( const unsigned short location ) +{ + unsigned bByte = ( location & HID_REPORT_ELEMENT_LOC_BYTE_MASK ) >> HID_REPORT_ELEMENT_LOC_BYTE_SHIFT; + return bByte; +} + +static unsigned hidGetElementReportId( const unsigned short location ) +{ + unsigned bReportId = ( location & HID_REPORT_ELEMENT_LOC_ID_MASK ) >> HID_REPORT_ELEMENT_LOC_ID_SHIFT; + return bReportId; +} + +static size_t hidGetElementReportLength( const unsigned short location ) +{ + size_t bReportLen = (size_t)( location & HID_REPORT_ELEMENT_LOC_LEN_MASK ) >> HID_REPORT_ELEMENT_LOC_LEN_SHIFT; + return bReportLen; +} + +static unsigned hidGetItemSize( const unsigned char header ) +{ + unsigned bSize = ( header & HID_REPORT_ITEM_HDR_SIZE_MASK ) >> HID_REPORT_ITEM_HDR_SIZE_SHIFT; + return bSize; +} + +static unsigned hidGetItemTag( const unsigned char header ) +{ + unsigned bTag = ( header & HID_REPORT_ITEM_HDR_TAG_MASK ) >> HID_REPORT_ITEM_HDR_TAG_SHIFT; + return bTag; +} + +static unsigned hidGetItemType( const unsigned char header ) +{ + unsigned bType = ( header & HID_REPORT_ITEM_HDR_TYPE_MASK ) >> HID_REPORT_ITEM_HDR_TYPE_SHIFT; + return bType; +} + +unsigned hidGetNextReportTime( const unsigned id ) { + swlock_acquire(&hidStaticVarLock); + unsigned retVal = 0U; + + for ( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) { + if( id == hidGetElementReportId( hidReports[ idx ]->location )) { + retVal = s_hidNextReportTime[ idx ]; + } + } + swlock_release(&hidStaticVarLock); + return retVal; +} + +unsigned char* hidGetReportDescriptor( void ) +{ + unsigned char* retVal = NULL; + swlock_acquire(&hidStaticVarLock); + + if( s_hidReportDescriptorPrepared ) { + retVal = s_hidReportDescriptor; + } + + swlock_release(&hidStaticVarLock); + return retVal; +} + +size_t hidGetReportDescriptorLength( void ) +{ + swlock_acquire(&hidStaticVarLock); + size_t retVal = ( s_hidReportDescriptorPrepared ) ? s_hidReportDescriptorLength : 0U; + swlock_release(&hidStaticVarLock); + return retVal; +} + +unsigned hidGetReportIdLimit ( void ) { + unsigned retVal = 0U; + + swlock_acquire(&hidStaticVarLock); + for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) { + unsigned reportId = hidGetElementReportId( hidReports[ idx ]->location ); + if( reportId >= retVal ) { + retVal = reportId + 1; + } + } + swlock_release(&hidStaticVarLock); + return retVal; +} + +unsigned hidGetNextValidReportId ( unsigned idPrev ) { + size_t retIndex = 0; + swlock_acquire(&hidStaticVarLock); + for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) { + unsigned reportId = hidGetElementReportId( hidReports[ idx ]->location ); + if( reportId == idPrev ) { + retIndex = (idx + 1) % HID_REPORT_COUNT; + break; + } + } + + unsigned retVal = hidGetElementReportId( hidReports[ retIndex ]->location ); + swlock_release(&hidStaticVarLock); + return retVal; +} + +#define HID_CONFIGURABLE_ELEMENT_COUNT ( sizeof hidConfigurableElements / sizeof ( USB_HID_Report_Element_t* )) +unsigned hidGetReportItem( + const unsigned id, + const unsigned byte, + const unsigned bit, + unsigned char* const page, + unsigned char* const header, + unsigned char data[] +) +{ + unsigned retVal = HID_STATUS_BAD_ID; + for( size_t elementIdx = 0U; elementIdx < HID_CONFIGURABLE_ELEMENT_COUNT; ++elementIdx ) { + swlock_acquire(&hidStaticVarLock); + USB_HID_Report_Element_t element = *hidConfigurableElements[ elementIdx ]; + swlock_release(&hidStaticVarLock); + + unsigned bBit = hidGetElementBitLocation( element.location ); + unsigned bByte = hidGetElementByteLocation( element.location ); + unsigned bId = hidGetElementReportId( element.location ); + + if( id == bId ) { + retVal = HID_STATUS_BAD_LOCATION; + + if(( bit == bBit ) && ( byte == bByte )) { + *page = hidGetUsagePage( id ); + *header = element.item.header; + + for( size_t dataIdx = 0U; dataIdx < HID_REPORT_ITEM_MAX_SIZE; ++data, ++dataIdx ) { + *data = element.item.data[ dataIdx ]; + } + + retVal = HID_STATUS_GOOD; + break; + } + } + } + return retVal; +} + +size_t hidGetReportLength( const unsigned id ) +{ + swlock_acquire(&hidStaticVarLock); + size_t retVal = 0U; + if( s_hidReportDescriptorPrepared ) { + for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) { + if( id == hidGetElementReportId( hidReports[ idx ]->location )) { + retVal = hidGetElementReportLength( hidReports[ idx ]->location ); + } + } + } + swlock_release(&hidStaticVarLock); + return retVal; +} + +unsigned hidGetReportPeriod( const unsigned id ) +{ + swlock_acquire(&hidStaticVarLock); + unsigned retVal = 0U; + for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx) { + if( id == hidGetElementReportId( hidReports[ idx ]->location )) { + retVal = s_hidCurrentPeriod[ idx ]; + break; + } + } + swlock_release(&hidStaticVarLock); + return retVal; +} + +unsigned hidGetReportTime( const unsigned id ) +{ + swlock_acquire(&hidStaticVarLock); + unsigned retVal = 0U; + + for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) { + if( id == hidGetElementReportId( hidReports[ idx ]->location )) { + retVal = s_hidReportTime[ idx ]; + } + } + + swlock_release(&hidStaticVarLock); + return retVal; +} + +static unsigned hidGetUsagePage( const unsigned id ) +{ + unsigned retVal = 0U; + swlock_acquire(&hidStaticVarLock); + + for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx) { + if( id == hidGetElementReportId( hidReports[ idx ]->location )) { + retVal = hidReports[ idx ]->item.data[ 0 ]; + break; + } + } + + swlock_release(&hidStaticVarLock); + return retVal; +} + +unsigned hidIsChangePending( const unsigned id ) +{ + unsigned retVal = 0U; + swlock_acquire(&hidStaticVarLock); + + for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx) { + if( id == hidGetElementReportId( hidReports[ idx ]->location )) { + retVal = ( s_hidChangePending[ idx ] != 0U ); + break; + } + } + + swlock_release(&hidStaticVarLock); + return retVal; +} + +unsigned hidIsIdleActive( const unsigned id ) +{ + unsigned retVal = 0U; + + swlock_acquire(&hidStaticVarLock); + for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx) { + if( id == hidGetElementReportId( hidReports[ idx ]->location )) { + retVal = ( s_hidIdleActive[ idx ] != 0U ); + break; + } + } + swlock_release(&hidStaticVarLock); + return retVal; +} + +unsigned hidIsReportDescriptorPrepared( void ) +{ + swlock_acquire(&hidStaticVarLock); + unsigned retVal = s_hidReportDescriptorPrepared; + swlock_release(&hidStaticVarLock); + return retVal; +} + +unsigned hidIsReportIdValid ( unsigned id ) { + size_t retVal = 0; + + swlock_acquire(&hidStaticVarLock); + for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) { + unsigned reportId = hidGetElementReportId( hidReports[ idx ]->location ); + if( reportId == id ) { + retVal = 1; + break; + } + } + + swlock_release(&hidStaticVarLock); + return retVal; +} + +void hidPrepareReportDescriptor( void ) +{ + swlock_acquire(&hidStaticVarLock); + if( !s_hidReportDescriptorPrepared ) { + s_hidReportDescriptorLength = 0U; + unsigned char* ptr = s_hidReportDescriptor; + + for( size_t idx = 0U; idx < HID_REPORT_DESCRIPTOR_ITEM_COUNT; ++idx ) { + s_hidReportDescriptorLength += hidTranslateItem( hidReportDescriptorItems[ idx ], &ptr ); + } + + s_hidReportDescriptorPrepared = 1U; + } + swlock_release(&hidStaticVarLock); +} + +void hidReportInit( void ) +{ + swlock_acquire(&hidStaticVarLock); + for( unsigned idx = 0U; idx < HID_REPORT_COUNT; ++idx ) { + s_hidCurrentPeriod[ idx ] = ENDPOINT_INT_INTERVAL_IN_HID * MS_IN_TICKS * HID_REPORT_COUNT; + } + memset( s_hidIdleActive, 0, sizeof( s_hidIdleActive ) ); + memset( s_hidChangePending, 0, sizeof( s_hidChangePending ) ); + swlock_release(&hidStaticVarLock); +} + +void hidResetReportDescriptor( void ) +{ + swlock_acquire(&hidStaticVarLock); + s_hidReportDescriptorPrepared = 0U; + swlock_release(&hidStaticVarLock); +} + +void hidSetChangePending( const unsigned id ) +{ + swlock_acquire(&hidStaticVarLock); + for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx) { + if( id == hidGetElementReportId( hidReports[ idx ]->location )) { + s_hidChangePending[ idx ] = 1U; + break; + } + } + swlock_release(&hidStaticVarLock); +} + +void hidSetIdle( const unsigned id, const unsigned state ) +{ + swlock_acquire(&hidStaticVarLock); + for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx) { + if( id == hidGetElementReportId( hidReports[ idx ]->location )) { + s_hidIdleActive[ idx ] = ( state != 0U ); + break; + } + } + swlock_release(&hidStaticVarLock); +} + +void hidSetNextReportTime( const unsigned id, const unsigned time ) +{ + swlock_acquire(&hidStaticVarLock); + for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx ) { + if( id == hidGetElementReportId( hidReports[ idx ]->location )) { + s_hidNextReportTime[ idx ] = time; + } + } + swlock_release(&hidStaticVarLock); +} + +unsigned hidSetReportItem( + const unsigned id, + const unsigned byte, + const unsigned bit, + const unsigned char page, + const unsigned char header, + const unsigned char data[] +) +{ + unsigned retVal = HID_STATUS_IN_USE; + + if( !s_hidReportDescriptorPrepared ) { + retVal = HID_STATUS_BAD_ID; + unsigned bSize = hidGetItemSize( header ); + unsigned bTag = hidGetItemTag ( header ); + unsigned bType = hidGetItemType( header ); + + if(( HID_REPORT_ITEM_MAX_SIZE < bSize ) || + ( HID_REPORT_ITEM_USAGE_TAG != bTag ) || + ( HID_REPORT_ITEM_USAGE_TYPE != bType )) { + retVal = HID_STATUS_BAD_HEADER; + } else { + for( size_t elementIdx = 0U; elementIdx < HID_CONFIGURABLE_ELEMENT_COUNT; ++elementIdx ) { + swlock_acquire(&hidStaticVarLock); + USB_HID_Report_Element_t element = *hidConfigurableElements[ elementIdx ]; + swlock_release(&hidStaticVarLock); + + unsigned bBit = hidGetElementBitLocation( element.location ); + unsigned bByte = hidGetElementByteLocation( element.location ); + unsigned bId = hidGetElementReportId( element.location ); + + if( id == bId ) { + retVal = HID_STATUS_BAD_PAGE; + unsigned pg = hidGetUsagePage( id ); + + if( page == pg ) { + retVal = HID_STATUS_BAD_LOCATION; + + if(( bit == bBit ) && ( byte == bByte )) { + element.item.header = header; + + for( size_t dataIdx = 0U; dataIdx < bSize; ++dataIdx ) { + element.item.data[ dataIdx ] = data[ dataIdx ]; + } + + for( size_t dataIdx = bSize; dataIdx < HID_REPORT_ITEM_MAX_SIZE; ++dataIdx ) { + element.item.data[ dataIdx ] = 0U; + } + + swlock_acquire(&hidStaticVarLock); + *hidConfigurableElements[ elementIdx ] = element; + swlock_release(&hidStaticVarLock); + retVal = HID_STATUS_GOOD; + break; + } + } + } + } + } + } + return retVal; +} + +void hidSetReportPeriod( const unsigned id, const unsigned period ) +{ + swlock_acquire(&hidStaticVarLock); + for( size_t idx = 0U; idx < HID_REPORT_COUNT; ++idx) { + if( id == hidGetElementReportId( hidReports[ idx ]->location )) { + s_hidCurrentPeriod[ idx ] = period; + break; + } + } + swlock_release(&hidStaticVarLock); +} + +static size_t hidTranslateItem( const USB_HID_Short_Item_t* inPtr, unsigned char** outPtrPtr ) +{ + size_t count = 0U; + *(*outPtrPtr)++ = inPtr->header; + ++count; + + unsigned dataLength = hidGetItemSize( inPtr->header ); + for( size_t idx = 0U; idx < dataLength; ++idx ) { + *(*outPtrPtr)++ = inPtr->data[ idx ]; + ++count; + } + + return count; +} + +// hid_report_descriptor.h validation functions for development purposes + +/** + * @brief Internal HID Report Descriptor validation state + */ +struct HID_validation_info { + int collectionOpenedCount; //!< Current count of open collections (to track that they are all closed) + int reportCount; //!< Current count of defined reports (to count them) + + int currentReportIdx; //!< Index of current report in hidReports array + int currentConfigurableElementIdx; //!< Index of current configurable element in hidConfigurableElements array + + unsigned char reportIds[HID_REPORT_COUNT]; // Array of report IDs (for general validation & duplication detection) + unsigned reportUsagePage[HID_REPORT_COUNT]; // Array of the usage page for each report (for general validation) + + unsigned current_bit_size; // State tracker for the current set report bit width (todo: should technically be a stack) + unsigned current_bit_count; // State tracker for the current set report count (todo: should technically be a stack) + unsigned current_bit_offset; // Current bit offset into this report (for location validation) +}; + +/** + * @brief Validation step for hidReportValidate, checking the info struct to ensure correctness of Report IDs + * + * @param info The info struct that has been built by hidReportValidate to check + * @return unsigned HID_STATUS value + */ +static unsigned hidReportValidateInfoStructReportIDs( struct HID_validation_info *info ) { + if ( info->reportCount != HID_REPORT_COUNT) { + if ( !( info->reportCount == 0 && HID_REPORT_COUNT == 1 ) ) { + // (Only if report IDs are being used) + printf("Error: The number of actual reports does not match HID_REPORT_COUNT.\n"); + return HID_STATUS_BAD_REPORT_DESCRIPTOR; + } + } + for ( size_t idx1 = 0; idx1 < HID_REPORT_COUNT; ++idx1 ) { + for ( size_t idx2 = idx1 + 1; idx2 < HID_REPORT_COUNT; ++idx2 ) { + if ( info->reportIds[idx1] == info->reportIds[idx2] ) { + printf("Error: Duplicate report ID 0x%02x.\n", info->reportIds[idx1]); + return HID_STATUS_BAD_REPORT_DESCRIPTOR; + } + } + } + for ( size_t idx = 0; idx < HID_REPORT_COUNT; ++idx ) { + if ( info->reportIds[idx] != hidGetElementReportId( hidReports[idx]->location ) ) { + printf("Error: Report ID in descriptor does not match report ID in hidReports.\n"); + return HID_STATUS_BAD_REPORT_DESCRIPTOR; + } + if ( info->reportCount && info->reportIds[idx] == 0 ) { + printf("Error: Report ID 0 is invalid.\n"); + return HID_STATUS_BAD_REPORT_DESCRIPTOR; + } + } + return HID_STATUS_GOOD; +} + +/** + * @brief Validation step for hidReportValidate, checking reports are the correct length specified in their location field + * + * @param info The info struct that has been built by hidReportValidate to check + * @return unsigned HID_STATUS value + */ +static unsigned hidReportValidateInfoStructReportLength( struct HID_validation_info *info ) { + if ( info->current_bit_offset % 8 ) { + printf("Error: HID Report not byte aligned (%d bits).\n", info->current_bit_offset); + return HID_STATUS_BAD_REPORT_DESCRIPTOR; + } + if ( ( info->current_bit_offset / 8 ) != hidGetElementReportLength( hidReports[info->currentReportIdx]->location ) ) { + printf("Error: Actual report length does not match value in location field %d != %d.\n", + ( info->current_bit_offset / 8 ), + hidGetElementReportLength( hidReports[info->currentReportIdx]->location )); + return HID_STATUS_BAD_REPORT_DESCRIPTOR; + } + return HID_STATUS_GOOD; +} + +/** + * @brief Validation step for hidReportValidate, collections are correctly opened and closed + * + * @param info The info struct that has been built by hidReportValidate to check + * @return unsigned HID_STATUS value + */ +static unsigned hidReportValidateInfoStructCollections( struct HID_validation_info *info ) { + if ( info->collectionOpenedCount ) { + printf("Error: Collections not equally opened and closed.\n"); + return HID_STATUS_BAD_REPORT_DESCRIPTOR; + } + return HID_STATUS_GOOD; +} + +/** + * @brief Validation step for hidReportValidate, High level - Checks the summarised information in the info struct by calling + * the subroutines for checking. + * + * @param info The info struct that has been built by hidReportValidate to check + * @return unsigned HID_STATUS value + */ +static unsigned hidReportValidateInfoStruct( struct HID_validation_info *info ) { + unsigned status = hidReportValidateInfoStructCollections( info ); + if( status == HID_STATUS_GOOD ) { + status = hidReportValidateInfoStructReportIDs( info ); + } + if( status == HID_STATUS_GOOD ) { + status = hidReportValidateInfoStructReportLength( info ); + } + return status; +} + +/** + * @brief Preparation step for hidReportValidate, Adds a report ID field into the information struct for validation + * + * @param info The info struct being built by hidReportValidate + * @param item The ReportId item being added + * @return unsigned HID_STATUS value + */ +static unsigned hidReportValidateAddReportId( struct HID_validation_info *info, const USB_HID_Short_Item_t *item ) { + if ( info->reportCount == 0 ) { + if ( info->current_bit_offset ) { + printf("Error: Some elements not associated with report ID.\n"); + return HID_STATUS_BAD_REPORT_DESCRIPTOR; + } + info->reportUsagePage[0] = 0; + } else { + unsigned status = hidReportValidateInfoStructReportLength( info ); + if ( status ) { + return status; + } + } + + if ( hidGetItemSize(item->header) != 1 ) { + printf("Error: ReportId field has invalid length %d (expected 1)\n", hidGetItemSize(item->header)); + return HID_STATUS_BAD_REPORT_DESCRIPTOR; + } + + info->reportIds[info->reportCount] = item->data[0]; + info->currentReportIdx = info->reportCount; + info->reportCount += 1; + info->current_bit_offset = 0; + if ( info->reportCount > HID_REPORT_COUNT ) { + printf("Error: HID_REPORT_COUNT does not match number of report IDs in descriptor.\n"); + return HID_STATUS_BAD_REPORT_DESCRIPTOR; + } + + return HID_STATUS_GOOD; +} + +/** + * @brief Preparation step for hidReportValidate, Adds a Usage Page field into the information struct for validation + * + * @param info The info struct being built by hidReportValidate + * @param item The UsagePage item being added + * @return unsigned HID_STATUS value + */ +static unsigned hidReportValidateAddUsagePageItem( struct HID_validation_info *info, const USB_HID_Short_Item_t *item ) { + + if ( info->collectionOpenedCount == 0 ) { + return HID_STATUS_GOOD; + } + if ( info->reportUsagePage[info->currentReportIdx] ) { + printf("Error: Multiple usage pages per report ID not supported by this implementation.\n"); + return HID_STATUS_BAD_REPORT_DESCRIPTOR; + } + + switch (hidGetItemSize(item->header)){ + case 1: + info->reportUsagePage[info->currentReportIdx] = item->data[0]; + break; + case 2: + info->reportUsagePage[info->currentReportIdx] = ((unsigned) item->data[1] << 8) + item->data[0]; + break; + default: + printf("Error: Invalid size for UsagePage report descriptor item.\n"); + return HID_STATUS_BAD_REPORT_DESCRIPTOR; + } + + return HID_STATUS_GOOD; +} + +/** + * @brief Preparation step for hidReportValidate, Adds a Usage field into the information struct for validation + * + * @param info The info struct being built by hidReportValidate + * @param item The Usage item being added + * @return unsigned HID_STATUS value + */ +static unsigned hidReportValidateAddUsageItem( struct HID_validation_info *info, const USB_HID_Short_Item_t *item) { + if ( ( info->currentConfigurableElementIdx < HID_CONFIGURABLE_ELEMENT_COUNT ) && + ( &(hidConfigurableElements[info->currentConfigurableElementIdx]->item) == item ) ) { + + USB_HID_Report_Element_t *element = hidConfigurableElements[info->currentConfigurableElementIdx]; + unsigned bBit = hidGetElementBitLocation( element->location ); + unsigned bByte = hidGetElementByteLocation( element->location ); + unsigned bReportId = hidGetElementReportId( element->location ); + + if ( bBit != ( info->current_bit_offset % 8 ) || bByte != ( info->current_bit_offset / 8 ) ) { + printf("Error: Locator bit/byte setting incorrect for configurable element index %d.\n", info->currentConfigurableElementIdx); + return HID_STATUS_BAD_REPORT_DESCRIPTOR; + } + if ( bReportId != info->reportIds[info->currentReportIdx] ) { + printf("Error: Locator report ID setting incorrect for configurable element index %d.\n", info->currentConfigurableElementIdx); + return HID_STATUS_BAD_REPORT_DESCRIPTOR; + } + + info->currentConfigurableElementIdx += 1; + } + return HID_STATUS_GOOD; +} + +unsigned hidReportValidate( void ) +{ + struct HID_validation_info info = {}; + unsigned status = HID_STATUS_GOOD; + + // Fill in the validation info struct by iterating through the hid report items + for ( size_t idx = 0; idx < HID_REPORT_DESCRIPTOR_ITEM_COUNT; ++idx ) { + const USB_HID_Short_Item_t *item = hidReportDescriptorItems[idx]; + unsigned bTag = hidGetItemTag ( item->header ); + unsigned bType = hidGetItemType( item->header ); + + if ( bTag == HID_REPORT_ITEM_TAG_COLLECTION && bType == HID_REPORT_ITEM_TYPE_MAIN ) { + info.collectionOpenedCount += 1; + } + else if ( bTag == HID_REPORT_ITEM_TAG_END_COLLECTION && bType == HID_REPORT_ITEM_TYPE_MAIN ) { + info.collectionOpenedCount -= 1; + if ( info.collectionOpenedCount < 0 ) { + printf("Error: Collection closed while there is no collection open.\n"); + status = HID_STATUS_BAD_REPORT_DESCRIPTOR; + } + } + else if ( bTag == HID_REPORT_ITEM_TAG_INPUT && bType == HID_REPORT_ITEM_TYPE_MAIN ) { + info.current_bit_offset += (info.current_bit_size * info.current_bit_count); + } + else if ( bTag == HID_REPORT_ITEM_TAG_REPORT_SIZE && bType == HID_REPORT_ITEM_TYPE_GLOBAL ) { + info.current_bit_size = item->data[0]; + } + else if ( bTag == HID_REPORT_ITEM_TAG_REPORT_COUNT && bType == HID_REPORT_ITEM_TYPE_GLOBAL ) { + info.current_bit_count = item->data[0]; + } + else if ( bTag == HID_REPORT_ITEM_TAG_REPORT_ID && bType == HID_REPORT_ITEM_TYPE_GLOBAL ) { + status = hidReportValidateAddReportId( &info, item ); + } + else if ( bTag == HID_REPORT_ITEM_TAG_USAGE_PAGE && bType == HID_REPORT_ITEM_TYPE_GLOBAL ) { + status = hidReportValidateAddUsagePageItem( &info, item ); + } + else if ( bTag == HID_REPORT_ITEM_TAG_USAGE && bType == HID_REPORT_ITEM_TYPE_LOCAL ) { + status = hidReportValidateAddUsageItem( &info, item ); + } + + if ( status ) { + break; + } + } + + if(status) { + return status; + } else { + return hidReportValidateInfoStruct( &info ); + } +} + +#endif // ( 0 < HID_CONTROLS ) diff --git a/lib_xua/src/hid/xua_hid.h b/lib_xua/src/hid/xua_hid.h index 1344b5a2..6730a57e 100644 --- a/lib_xua/src/hid/xua_hid.h +++ b/lib_xua/src/hid/xua_hid.h @@ -1,6 +1,12 @@ -// Copyright 2019-2021 XMOS LIMITED. +// Copyright 2019-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. +/** + * @brief Human Interface Device (HID) Class Request functions + * + * Document section numbers refer to the HID Device Class Definition, version 1.11. + */ + #ifndef __XUA_HID_H__ #define __XUA_HID_H__ @@ -9,49 +15,11 @@ #include "xud.h" #include "xud_std_requests.h" -/** - * \brief Calculate the next time to respond with a HID Report. - * - * If the USB Host has previously sent a valid HID Set_Idle request with - * a duration of zero or greater than the default reporting interval, - * the device sends HID Reports periodically or when the value of the - * payload has changed. - * - * This function calculates the time for sending the next periodic - * HID Report. - */ -void HidCalcNextReportTime( void ); - -/** - * \brief Capture the time of sending the current HID Report. - * - * If the USB Host has previously sent a valid HID Set_Idle request with - * a duration of zero or greater than the default reporting interval, - * the device sends HID Reports periodically or when the value of the - * payload has changed. - * - * This function captures the time when the HID Report was sent so that - * a subsequent call to HidCalNextReportTime() can calculate the time - * to send the next periodic HID Report. - */ -void HidCaptureReportTime( void ); - XUD_Result_t HidInterfaceClassRequests( XUD_ep c_ep0_out, XUD_ep c_ep0_in, REFERENCE_PARAM( USB_SetupPacket_t, sp )); -/** - * \brief Register that previously changed HID Report data has reported - * to the USB Host. - */ -void HidClearChangePending( void ); - -/** - * \brief Indicate if a change to the HID Report data has been received. - */ -unsigned HidIsChangePending( void ); - /** * \brief Indicate whether to send a HID Report based on elapsed time. * @@ -64,15 +32,15 @@ unsigned HidIsChangePending( void ); * whether or not the time to send the next periodic HID Report has * elapsed. * + * Parameters: + * + * @param[in] id The identifier for the HID Report (see 5.6, 6.2.2.7, 8.1 and 8.2) + * A value of zero means the application does not use Report IDs. + * * \return A Boolean value indicating whether or not to send the HID Report. * \retval 1 -- Do not send the HID Report * \retval 0 -- Send the HID Report */ -unsigned HidIsSetIdleSilenced( void ); - -/** - * \brief Register that a change to the HID Report data has been received. - */ -void HidSetChangePending( void ); +unsigned HidIsSetIdleSilenced( const unsigned id ); #endif // __XUA_HID_H__ diff --git a/lib_xua/src/hid/xua_hid_descriptor.h b/lib_xua/src/hid/xua_hid_descriptor.h index 4fed07a2..4dd44007 100644 --- a/lib_xua/src/hid/xua_hid_descriptor.h +++ b/lib_xua/src/hid/xua_hid_descriptor.h @@ -11,6 +11,8 @@ #ifndef _HID_DESCRIPTOR_ #define _HID_DESCRIPTOR_ +#define HID_DESCRIPTOR_LENGTH_FIELD_OFFSET ( 7 ) + /* USB HID Descriptor (section 6.2.1) */ typedef struct { diff --git a/lib_xua/src/hid/xua_hid_descriptor_contents.h b/lib_xua/src/hid/xua_hid_descriptor_contents.h index d91e7b15..766c323e 100644 --- a/lib_xua/src/hid/xua_hid_descriptor_contents.h +++ b/lib_xua/src/hid/xua_hid_descriptor_contents.h @@ -12,15 +12,15 @@ #include "xua_hid_descriptor.h" -#define HID_DESCRIPTOR_LENGTH_0 0x09 /* Size of descriptor in Bytes */ -#define HID_DESCRIPTOR_TYPE_0 0x21 /* HID 0x21 */ -#define HID_BCD_VERSION_LO 0x10 /* HID class specification release */ -#define HID_BCD_VERSION_HI 0x01 -#define HID_COUNTRY_CODE 0x00 /* Country code of localized hardware */ -#define HID_NUM_DESCRIPTORS 0x01 /* Number of class descriptors */ -#define HID_DESCRIPTOR_TYPE_1 0x22 /* Type of 1st class descriptor, Report 0x22 */ -#define HID_DESCRIPTOR_LENGTH_1_LO sizeof(hidReportDescriptor) & 0xff -#define HID_DESCRIPTOR_LENGTH_1_HI sizeof(hidReportDescriptor) >> 8 +#define HID_DESCRIPTOR_LENGTH_0 ( 0x09 ) /* Size of descriptor in Bytes */ +#define HID_DESCRIPTOR_TYPE_0 ( 0x21 ) /* HID 0x21 */ +#define HID_BCD_VERSION_LO ( 0x10 ) /* HID class specification release */ +#define HID_BCD_VERSION_HI ( 0x01 ) +#define HID_COUNTRY_CODE ( 0x00 ) /* Country code of localized hardware */ +#define HID_NUM_DESCRIPTORS ( 0x01 ) /* Number of class descriptors */ +#define HID_DESCRIPTOR_TYPE_1 ( 0x22 ) /* Type of 1st class descriptor, Report 0x22 */ +#define HID_DESCRIPTOR_LENGTH_1_LO ( 0x00 ) /* Length of 1st class descriptor, set to zero */ +#define HID_DESCRIPTOR_LENGTH_1_HI ( 0x00 ) /* since only pre-processor directives allowed here */ #endif // _HID_DESCRIPTOR_CONTENTS_ diff --git a/lib_xua/src/hid/xua_hid_endpoint_descriptor_contents.h b/lib_xua/src/hid/xua_hid_endpoint_descriptor_contents.h index bba85c72..d6bcc6b3 100644 --- a/lib_xua/src/hid/xua_hid_endpoint_descriptor_contents.h +++ b/lib_xua/src/hid/xua_hid_endpoint_descriptor_contents.h @@ -12,11 +12,11 @@ #include "descriptor_defs.h" -#define HID_ENDPOINT_DESCRIPTOR_LENGTH 0x07 /* Size of descriptor in Bytes */ -#define HID_ENDPOINT_DESCRIPTOR_TYPE 0x05 /* Endpoint 0x05 */ -#define HID_ENDPOINT_ATTRIBUTES 0x03 /* Interrupt */ -#define HID_ENDPOINT_DESCRIPTOR_PACKET_SIZE_LO 0x40 -#define HID_ENDPOINT_DESCRIPTOR_PACKET_SIZE_HI 0x00 +#define HID_ENDPOINT_DESCRIPTOR_LENGTH ( 0x07 ) /* Size of descriptor in Bytes */ +#define HID_ENDPOINT_DESCRIPTOR_TYPE ( 0x05 ) /* Endpoint 0x05 */ +#define HID_ENDPOINT_ATTRIBUTES ( 0x03 ) /* Interrupt */ +#define HID_ENDPOINT_DESCRIPTOR_PACKET_SIZE_LO ( 0x40 ) +#define HID_ENDPOINT_DESCRIPTOR_PACKET_SIZE_HI ( 0x00 ) #endif // _HID_ENDPOINT_DESCRIPTOR_CONTENTS_ diff --git a/lib_xua/src/hid/xua_hid_interface_descriptor_contents.h b/lib_xua/src/hid/xua_hid_interface_descriptor_contents.h index 20df1815..83d69fbd 100644 --- a/lib_xua/src/hid/xua_hid_interface_descriptor_contents.h +++ b/lib_xua/src/hid/xua_hid_interface_descriptor_contents.h @@ -12,14 +12,14 @@ #include "descriptor_defs.h" -#define HID_INTERFACE_DESCRIPTOR_LENGTH 0x09 /* Size of descriptor in Bytes */ -#define HID_INTERFACE_DESCRIPTOR_TYPE 0x04 /* Interface 0x04 */ -#define HID_INTERFACE_ALTERNATE_SETTING 0x00 /* Value used alternate interfaces using SetInterface Request */ -#define HID_INTERFACE_NUMBER_OF_ENDPOINTS 0x01 /* Number of endpoitns for this interface (excluding 0) */ -#define HID_INTERFACE_CLASS 0x03 -#define HID_INTERFACE_SUBCLASS 0x00 /* No boot device */ -#define HID_INTERFACE_PROTOCOL 0x00 -#define HID_INTERFACE_STRING_DESCRIPTOR_INDEX 0x00 +#define HID_INTERFACE_DESCRIPTOR_LENGTH ( 0x09 ) /* Size of descriptor in Bytes */ +#define HID_INTERFACE_DESCRIPTOR_TYPE ( 0x04 ) /* Interface 0x04 */ +#define HID_INTERFACE_ALTERNATE_SETTING ( 0x00 ) /* Value used alternate interfaces using SetInterface Request */ +#define HID_INTERFACE_NUMBER_OF_ENDPOINTS ( 0x01 ) /* Number of endpoitns for this interface (excluding 0) */ +#define HID_INTERFACE_CLASS ( 0x03 ) +#define HID_INTERFACE_SUBCLASS ( 0x00 ) /* No boot device */ +#define HID_INTERFACE_PROTOCOL ( 0x00 ) +#define HID_INTERFACE_STRING_DESCRIPTOR_INDEX ( 0x00 ) #endif // _HID_INTERFACE_DESCRIPTOR_CONTENTS_ diff --git a/lib_xua/src/hid/xua_hid_report.h b/lib_xua/src/hid/xua_hid_report.h new file mode 100644 index 00000000..6ab87cd8 --- /dev/null +++ b/lib_xua/src/hid/xua_hid_report.h @@ -0,0 +1,551 @@ +// Copyright 2021-2022 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +/** + * @brief Human Interface Device (HID) Report descriptor + * + * This file defines the structure of the HID Report descriptor and declares + * functions for manipulating it. + * Because the Report descriptor defines the length of the HID Report, this file + * declares a function for obtaining the Report length as well. + * The using application has the responsibility to define the report descriptor + * structure and default contents in their hid_report_descriptor.h file. + * Document section numbers refer to the HID Device Class Definition, version 1.11. + */ + +#ifndef _XUA_HID_REPORT_ +#define _XUA_HID_REPORT_ + +#include + +#include "xua_hid_report_descriptor_constants.h" + +#define HID_REPORT_ITEM_HDR_SIZE_MASK ( 0x03 ) +#define HID_REPORT_ITEM_HDR_SIZE_SHIFT ( 0U ) + +#define HID_REPORT_ITEM_HDR_TAG_MASK ( 0xF0 ) +#define HID_REPORT_ITEM_HDR_TAG_SHIFT ( 4U ) + +#define HID_REPORT_ITEM_HDR_TYPE_MASK ( 0x0C ) +#define HID_REPORT_ITEM_HDR_TYPE_SHIFT ( 2U ) + +#define HID_REPORT_ELEMENT_LOC_BIT_MASK ( 0x0070 ) +#define HID_REPORT_ELEMENT_LOC_BIT_SHIFT ( 4U ) + +#define HID_REPORT_ELEMENT_LOC_BYTE_MASK ( 0x000F ) +#define HID_REPORT_ELEMENT_LOC_BYTE_SHIFT ( 0U ) + +#define HID_REPORT_ELEMENT_LOC_ID_MASK ( 0xF000 ) +#define HID_REPORT_ELEMENT_LOC_ID_SHIFT ( 12U ) + +#define HID_REPORT_ELEMENT_LOC_LEN_MASK ( 0x0F00 ) +#define HID_REPORT_ELEMENT_LOC_LEN_SHIFT ( 8U ) + +#define HID_REPORT_ITEM_MAX_SIZE ( 2U ) + +#define HID_REPORT_ITEM_USAGE_TAG ( 0U ) +#define HID_REPORT_ITEM_USAGE_TYPE ( 2U ) + +/** + * @brief Helper macro to configure the location field of USB_HID_Report_Element_t. + * + * @param id The report ID that this element is within. + * @param len (only relevant for the usage_page elements in hidReports) The length + * of the report under this report ID. + * @param byte The byte location of this element in the report. + * @param bit The bit location (within the byte) of this element in the report. + */ +#define HID_REPORT_SET_LOC(id, len, byte, bit) (\ + (( id << HID_REPORT_ELEMENT_LOC_ID_SHIFT ) & HID_REPORT_ELEMENT_LOC_ID_MASK ) | \ + (( len << HID_REPORT_ELEMENT_LOC_LEN_SHIFT ) & HID_REPORT_ELEMENT_LOC_LEN_MASK ) | \ + (( byte << HID_REPORT_ELEMENT_LOC_BYTE_SHIFT ) & HID_REPORT_ELEMENT_LOC_BYTE_MASK ) | \ + (( bit << HID_REPORT_ELEMENT_LOC_BIT_SHIFT ) & HID_REPORT_ELEMENT_LOC_BIT_MASK )) + +/** + * @brief Helper macro to configure the header field of USB_HID_Short_Item_t + * + * @param size The size of the report descriptor item (valid values: 0, 1, 2) + * @param type The type of the report descriptor item + * @param tag The tag + */ +#define HID_REPORT_SET_HEADER(size, type, tag) (\ + (( size << HID_REPORT_ITEM_HDR_SIZE_SHIFT) & HID_REPORT_ITEM_HDR_SIZE_MASK ) |\ + (( type << HID_REPORT_ITEM_HDR_TYPE_SHIFT) & HID_REPORT_ITEM_HDR_TYPE_MASK ) |\ + (( tag << HID_REPORT_ITEM_HDR_TAG_SHIFT ) & HID_REPORT_ITEM_HDR_TAG_MASK ) ) + +#define HID_STATUS_GOOD ( 0U ) +#define HID_STATUS_BAD_HEADER ( 1U ) +#define HID_STATUS_BAD_ID ( 2U ) +#define HID_STATUS_BAD_LOCATION ( 3U ) +#define HID_STATUS_BAD_PAGE ( 4U ) +#define HID_STATUS_IN_USE ( 5U ) +#define HID_STATUS_BAD_REPORT_DESCRIPTOR ( 6U ) + +#define MS_IN_TICKS 100000U + +/** + * @brief USB HID Report Descriptor Short Item + * + * @note + * To reduce memory use, this type does not support Short Items with 4 data bytes. + * See section 6.2.2.2 + * + * Elements: + * + * header - the item prefix containing the size, type and tag fields (see 6.2.2.2) + * Format (bit range): bSize (0:1), bType (2:3), bTag (4:7) + * data - a two byte array for holding the item's data + * The bSize field indicates which data bytes are in use + * id - a non-standard extension identifying the HID Report ID associated with + * the item (see 5.6, 6.2.2.7, 8.1 and 8.2) + * location - a non-standard extension locating the item within the HID Report + * Format (bit range): iByte (0:3), iBit (4:6), Reserved (7) + */ +typedef struct +{ + unsigned char header; + unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ]; +} USB_HID_Short_Item_t; + +typedef struct +{ + USB_HID_Short_Item_t item; + unsigned short location; +} USB_HID_Report_Element_t; + +/** + * \brief Calculate the next time to respond with a HID Report. + * + * If the USB Host has previously sent a valid HID Set_Idle request with + * a duration of zero or greater than the default reporting interval, + * the device sends HID Reports periodically or when the value of the + * payload has changed. + * + * This function calculates the time for sending the next periodic + * HID Report. + * + * Parameters: + * + * @param[in] id The identifier for the HID Report (see 5.6, 6.2.2.7, 8.1 and 8.2) + * A value of zero means the application does not use Report IDs. + */ +void hidCalcNextReportTime( const unsigned id ); + +/** + * \brief Capture the time of sending the current HID Report. + * + * If the USB Host has previously sent a valid HID Set_Idle request with + * a duration of zero or greater than the default reporting interval, + * the device sends HID Reports periodically or when the value of the + * payload has changed. + * + * This function captures the time when the HID Report was sent so that + * a subsequent call to HidCalNextReportTime() can calculate the time + * to send the next periodic HID Report. + * + * Parameters: + * + * @param[in] id The identifier for the HID Report (see 5.6, 6.2.2.7, 8.1 and 8.2) + * A value of zero means the application does not use Report IDs. + * + * @param[in] time The time when the HID Report for the given \a id was sent. + */ +void hidCaptureReportTime( const unsigned id, const unsigned time ); + +/** + * \brief Register that a previously changed HID Report data has been sent + * to the USB Host. + * + * HID processing maintains a list of HID Reports with changed data not yet + * reported to the USB Host. + * + * Applications that have only one HID Report may or may not use a Report ID. + * Applications that have more than one HID Report must use Report IDs. + * + * For applications that do not use Report IDs, the list contains one element. + * That element tracks whether or not an unreported change has occurred in the + * HID data. + * For applications that use Report IDs, the list contains one element per + * Report ID. + * Each element tracks unreported changes for the corresponding Report ID. + * + * Calling this function for a given Report ID indicates that the changed + * HID data has been reported to the USB Host. + * + * \warning This function will fail silently if given an id that is not + * either the value zero (in the case that Report IDs are not in use), + * or a Report ID that is in use. + * + * \param[in] id A HID Report ID. + * Use zero if the application does not use Report IDs. + */ +void hidClearChangePending( const unsigned id ); + +/** + * @brief Get the next valid report ID - iterator style. + * + * This function will loop around and start returning the first report ID again once it has + * returned all valid report IDs. + * + * @param idPrev The previous returned id, or 0 if this is the first call + * @return unsigned The next valid report ID. + */ +unsigned hidGetNextValidReportId ( unsigned idPrev ); + +/** + * @brief Get the HID Report descriptor + * + * This function returns a pointer to the USB HID Report descriptor. + * It returns NULL if the Report descriptor has not been prepared, + * i.e., no one has called \c hidPrepareReportDescriptor(). + * + * @note An XC-callable version of this function has not been provided. + * XC requires explicit declaration of the kind of pointer returned, + * hence an XC implementation of the function. + * + * @return A pointer to a list of unsigned char containing the Report descriptor + */ +#if !defined(__XC__) +unsigned char* hidGetReportDescriptor( void ); +#endif + +/** + * @brief Get the length of the HID Report descriptor + * + * This function returns the length of the USB HID Report descriptor. + * It returns zero if the Report descriptor has not been prepared, + * i.e., no one has called \c hidPrepareReportDescriptor(). + * + * @return The length of the Report descriptor in bytes + */ +size_t hidGetReportDescriptorLength( void ); + +/** + * \brief Get the upper limit of HID Report identifiers + * + * This function returns the upper limit of the HID Report identifiers. + * The upper limit has a value one greater than the maximum HID Report identifier. + * In the case that HID Report identifiers are not in use, this function returns the value 1. + * + * \returns The upper limit of HID Report identifiers + */ +unsigned hidGetReportIdLimit ( void ); + +/** + * @brief Get a HID Report descriptor item + * + * Parameters: + * + * @param[in] id The identifier for the HID Report (see 5.6, 6.2.2.7, 8.1 and 8.2). + * A value of zero means the application does not use Report IDs. + * @param[in] byte The byte position of the control within the HID Report + * @param[in] bit The bit position of the control within the \a byte + * @param[out] page The USB HID Usage Page code for the Item (see 5.5) + * @param[out] header The LSB of the Item containing the bSize, bType and bTag fields (see 6.2.2.2) + * @param[out] data A two element array containing data bytes for the Item + * + * @return A status value + * @retval \c HID_STATUS_GOOD Item successfully returned + * @retval \c HID_STATUS_BAD_ID The \a id argument specifies a non-existant HID Report + * @retval \c HID_STATUS_BAD_LOCATION The \a bit or \a byte arguments specify a location outside + * of the HID Report + */ +#if defined(__XC__) +unsigned hidGetReportItem( + const unsigned id, + const unsigned byte, + const unsigned bit, + unsigned char* unsafe const page, + unsigned char* unsafe const header, + unsigned char* unsafe const data); +#else +unsigned hidGetReportItem( + const unsigned id, + const unsigned byte, + const unsigned bit, + unsigned char* const page, + unsigned char* const header, + unsigned char data[]); +#endif + +/** + * @brief Get the time to send the next HID Report for the given \a id + * + * Parameters: + * + * @param[in] id The identifier for the HID Report (see 5.6, 6.2.2.7, 8.1 and 8.2) + * A value of zero means the application does not use Report IDs. + * + * @returns The time at which to send the next HID Report for the given \a id + */ +unsigned hidGetNextReportTime( const unsigned id ); + +/** + * @brief Get the length of the HID Report + * + * This function returns the length of the USB HID Report. + * It returns zero if the Report descriptor has not been prepared, + * i.e., no one has called \c hidPrepareReportDescriptor(), + * or if the \a id argument specifies a non-existent HID Report + * + * Parameters: + * + * @param[in] id The identifier for the HID Report (see 5.6, 6.2.2.7, 8.1 and 8.2) + * A value of zero means the application does not use Report IDs. + * + * @return The length of the Report in bytes + */ +size_t hidGetReportLength( const unsigned id ); + +/** + * @brief Get the HID Report period for the given \a id + * + * Parameters: + * + * @param[in] id The identifier for the HID Report (see 5.6, 6.2.2.7, 8.1 and 8.2) + * A value of zero means the application does not use Report IDs. + * + * @returns The period for the given HID Report \a id in units of ms. + * The value zero means the period is indefinite. + */ +unsigned hidGetReportPeriod( const unsigned id ); + +/** + * @brief Get the HID Report time for the given \a id + * + * Parameters: + * + * @param[in] id The identifier for the HID Report (see 5.6, 6.2.2.7, 8.1 and 8.2) + * A value of zero means the application does not use Report IDs. + * + * @returns The time of the last call to \c hidCaptureReportTime() + */ +unsigned hidGetReportTime( const unsigned id ); + +/** + * \brief Indicate if a change to the HID Report data has been received. + * + * HID processing maintains a list of HID Reports with changed data not yet + * reported to the USB Host. + * + * Applications that have only one HID Report may or may not use a Report ID. + * Applications that have more than one HID Report must use Report IDs. + * + * For applications that do not use Report IDs, the list contains one element. + * That element tracks whether or not an unreported change has occurred in the + * HID data. + * For applications that use Report IDs, the list contains one element per + * Report ID. + * Each element tracks unreported changes for the corresponding Report ID. + * + * Calling this function with a given Report ID returns an indication of + * whether unreported HID data exists for that Report ID. + * + * \warning This function will return zero if given an id that is not + * either the value zero (in the case that Report IDs are not in use), + * or a Report ID that is in use. + * + * \param[in] id A HID Report ID. + * Use zero if the application does not use Report IDs. + * + * \returns A Boolean indicating whether the given \a id has a changed + * HID Report not yet sent to the USB Host. + * \retval True The given \a id has changed HID Report data. + * \retval False The given \a id does not have changed HID Report data. + */ +unsigned hidIsChangePending( const unsigned id ); + +/** + * @brief Indicate if the HID report for the given \a id is idle + * + * Parameters: + * + * @param[in] id The identifier for the HID Report (see 5.6, 6.2.2.7, 8.1 and 8.2) + * + * \returns A Boolean indicating whether the HID Report for the given \a id is idle. + * \retval True The HID Report is idle. + * \retval False The HID Report is not idle. + */ +unsigned hidIsIdleActive( const unsigned id ); + +/** + * @brief Indicate if the HID Report descriptor has been prepared + * + * \returns A Boolean indicating whether the HID Report descriptor has been prepared. + * \retval True The HID Report descriptor has been prepared. + * \retval False The HID Report descriptor has not been prepared. + */ + unsigned hidIsReportDescriptorPrepared( void ); + +/** + * @brief Does the application use Report IDs? + * + * If the application is not using Report IDs, then the id value that is passed around + * everywhere can just be zero. Otherwise zero is an invalid ID. + * + * @return Boolean + * @retval 1 Report IDs are in use + * @retval 0 Report IDs are not in use + */ +unsigned hidIsReportIdInUse ( void ); + +/** + * @brief Is the provided report ID valid for passing to other functions. + * + * e.g If Report IDs are not in use, then only 0 will return true. + * e.g If Report IDs are in use, then 0 will return false and the report IDs that + * are in use will return true when passed to this function. + * + * @param id The ID to check + * @return boolean + * @retval 0 The report ID is not valid, other functions may fail silently + * @retval 1 The report ID is valid and can be used as the argument to other functions + */ +unsigned hidIsReportIdValid ( unsigned id ); + +/** + * @brief Prepare the USB HID Report descriptor + * + * After preparation, \c hidGetReportDescriptor() returns a list suitable for transmission over USB. + * Call this function after altering one or more Report Items using \c hidSetReportItem(). + */ +void hidPrepareReportDescriptor( void ); + +/** + * @brief Initialise the USB HID Report functionality + * + * Call this function before using any other functions in this API. + */ +void hidReportInit( void ); + +/** + * @brief Reset the USB HID Report descriptor + * + * After reset, \c hidGetReportDescriptor() returns NULL until a subsequent call to + * \c hidPrepareReportDescriptor() occurs. + * Call this function before altering one or more Report Items using \c hidSetReportItem(). + */ +void hidResetReportDescriptor( void ); + +/** + * \brief Register that a change to the HID Report data has been received. + * + * HID processing maintains a list of HID Reports with changed data not yet + * reported to the USB Host. + * + * Applications that have only one HID Report may or may not use a Report ID. + * Applications that have more than one HID Report must use Report IDs. + * + * For applications that do not use Report IDs, the list contains one element. + * That element tracks whether or not an unreported change has occurred in the + * HID data. + * For applications that use Report IDs, the list contains one element per + * Report ID. + * Each element tracks unreported changes for the corresponding Report ID. + * + * Calling this function with a given Report ID indicates that the HID data + * for that Report ID has changed and has not yet been reported to the USB + * Host. + * + * \warning This function will fail silently if given an id that is not + * either the value zero (in the case that Report IDs are not in use), + * or a Report ID that is in use. + * + * \param[in] id A HID Report ID. + * Use zero if the application does not use Report IDs. + */ +void hidSetChangePending( const unsigned id ); + +/** + * @brief Set the HID Report Idle state for the given \a id + * + * Parameters: + * + * @param[in] id The identifier for the HID Report (see 5.6, 6.2.2.7, 8.1 and 8.2) + * A value of zero means the application does not use Report IDs. + * + * @param[in] state A Boolean indicating the Idle state + * If true, the HID Report for the given \a id is Idle, otherwise it + * is not Idle. + */ +void hidSetIdle( const unsigned id, const unsigned state ); + +/** + * @brief Set the time to send the HID Report for the given \a id + * + * Parameters: + * + * @param[in] id The identifier for the HID Report (see 5.6, 6.2.2.7, 8.1 and 8.2) + * A value of zero means the application does not use Report IDs. + * + * @param[in] time The time to send the HID Report for the given \a id. + */ +void hidSetNextReportTime( const unsigned id, const unsigned time ); + +/** + * @brief Modify a HID Report descriptor item + * + * @warning This function does not check that the length of the \a data array matches the value of + * the bSize field in the \a header. For safe operation use a \a data array of at least + * \c HID_REPORT_ITEM_MAX_SIZE bytes in length. + * + * Parameters: + * + * @param[in] id The identifier for the HID Report (see 5.6, 6.2.2.7, 8.1 and 8.2) + * A value of zero means the application does not use Report IDs. + * @param[in] byte The byte position of the control within the HID Report + * @param[in] bit The bit position of the control within the \a byte + * @param[in] page The USB HID Usage Page code for the Item (see 5.5) + * @param[in] header The LSB of the Item containing the bSize, bType and bTag fields (see 6.2.2.2) + * @param[in] data An array containing data bytes or NULL for an Item with no data + * + * @return A status value + * @retval \c HID_STATUS_GOOD Item successfully updated + * @retval \c HID_STATUS_BAD_HEADER The Item header specified a data size greater than 2 or + * a Tag or Type inconsistent with a Usage Item + * @retval \c HID_STATUS_BAD_ID The \a id argument specifies a non-existent HID Report + * @retval \c HID_STATUS_BAD_LOCATION The \a bit or \a byte arguments specify a location outside + * of the HID Report + * @retval \c HID_STATUS_BAD_PAGE The \a byte argument specifies a location for controls from + * a Usage Page other than the one given by the \a page parameter + * @retval \c HID_STATUS_IN_USE The Report descriptor is in use + */ +unsigned hidSetReportItem( + const unsigned id, + const unsigned byte, + const unsigned bit, + const unsigned char page, + const unsigned char header, + const unsigned char data[]); + +/** + * @brief Set the HID Report period for the given \a id + * + * Parameters: + * + * @param[in] id The identifier for the HID Report (see 5.6, 6.2.2.7, 8.1 and 8.2) + * A value of zero means the application does not use Report IDs. + * + * @param[in] period The period for sending the HID Report in units of ms. + * This period must be a multiple of 4 ms. + * Use zero to indicate an indefinite period. + */ +void hidSetReportPeriod( const unsigned id, const unsigned period ); + +/** + * @brief Development function: Validate the contents of hid_report_descriptor.h for common errors, printing + * error messages if any issues were found. + * + * This function is intended for use when developing the contents of hid_report_descriptor.h, which is static, + * so shouldn't be required for use in a production application. + * + * @return Validation result + * @retval HID_STATUS_GOOD The validation found no issues with the data structures defined + * in hid_report_descriptor.h + * @retval HID_STATUS_BAD_REPORT_DESCRIPTOR The validation encountered an issue with the data structures + * defined in hid_report_descriptor.h . More information is + * provided in the printed messages. + */ +unsigned hidReportValidate( void ); + +#endif // _XUA_HID_REPORT_ diff --git a/lib_xua/src/hid/xua_hid_report_descriptor_constants.h b/lib_xua/src/hid/xua_hid_report_descriptor_constants.h new file mode 100644 index 00000000..c48b6f15 --- /dev/null +++ b/lib_xua/src/hid/xua_hid_report_descriptor_constants.h @@ -0,0 +1,66 @@ +// Copyright 2021-2022 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +/** + * @brief Human Interface Device (HID) Report descriptor constants + * + * This file defines a collection of constants from the USB HID documents. + * This includes constants from: + * - Device Class Definition for Human Interface Devices, version 1.11 + * - HID Usage Tables for Universal Serial Bus, version 1.22 + * + * This file is incomplete, but can be expanded with new constants as necessary. + */ + +#ifndef _XUA_HID_REPORT_DESCRIPTOR_CONSTANTS_ +#define _XUA_HID_REPORT_DESCRIPTOR_CONSTANTS_ + + +// Constants from the USB Device Class Definition for HID for type +#define HID_REPORT_ITEM_TYPE_MAIN ( 0x00 ) +#define HID_REPORT_ITEM_TYPE_GLOBAL ( 0x01 ) +#define HID_REPORT_ITEM_TYPE_LOCAL ( 0x02 ) +#define HID_REPORT_ITEM_TYPE_RESERVED ( 0x03 ) + +// Constants from the USB Device Class Definition for HID for tag +// Main items +#define HID_REPORT_ITEM_TAG_INPUT ( 0x08 ) +#define HID_REPORT_ITEM_TAG_OUTPUT ( 0x09 ) +#define HID_REPORT_ITEM_TAG_FEATURE ( 0x0B ) +#define HID_REPORT_ITEM_TAG_COLLECTION ( 0x0A ) +#define HID_REPORT_ITEM_TAG_END_COLLECTION ( 0x0C ) + +// Global items +#define HID_REPORT_ITEM_TAG_USAGE_PAGE ( 0x00 ) +#define HID_REPORT_ITEM_TAG_LOGICAL_MINIMUM ( 0x01 ) +#define HID_REPORT_ITEM_TAG_LOGICAL_MAXIMUM ( 0x02 ) +#define HID_REPORT_ITEM_TAG_PHYSICAL_MINIMUM ( 0x03 ) +#define HID_REPORT_ITEM_TAG_PHYSICAL_MAXIMUM ( 0x04 ) +#define HID_REPORT_ITEM_TAG_UNIT_EXPONENT ( 0x05 ) +#define HID_REPORT_ITEM_TAG_UNIT ( 0x06 ) +#define HID_REPORT_ITEM_TAG_REPORT_SIZE ( 0x07 ) +#define HID_REPORT_ITEM_TAG_REPORT_ID ( 0x08 ) +#define HID_REPORT_ITEM_TAG_REPORT_COUNT ( 0x09 ) +#define HID_REPORT_ITEM_TAG_PUSH ( 0x0A ) +#define HID_REPORT_ITEM_TAG_POP ( 0x0B ) + +// Local items +#define HID_REPORT_ITEM_TAG_USAGE ( 0x00 ) +#define HID_REPORT_ITEM_TAG_USAGE_MINIMUM ( 0x01 ) +#define HID_REPORT_ITEM_TAG_USAGE_MAXIMUM ( 0x02 ) +#define HID_REPORT_ITEM_TAG_DESIGNATOR_INDEX ( 0x03 ) +#define HID_REPORT_ITEM_TAG_DESIGNATOR_MINIMUM ( 0x04 ) +#define HID_REPORT_ITEM_TAG_DESIGNATOR_MAXIMUM ( 0x05 ) +#define HID_REPORT_ITEM_TAG_STRING_INDEX ( 0x07 ) +#define HID_REPORT_ITEM_TAG_STRING_MINIMUM ( 0x08 ) +#define HID_REPORT_ITEM_TAG_STRING_MAXIMUM ( 0x09 ) +#define HID_REPORT_ITEM_TAG_DELIMITER ( 0x0A ) + +// Constants from HID Usage Tables +// Usage page IDs (incomplete) +#define USB_HID_USAGE_PAGE_ID_GENERIC_DESKTOP ( 0x01 ) +#define USB_HID_USAGE_PAGE_ID_KEYBOARD ( 0x07 ) +#define USB_HID_USAGE_PAGE_ID_TELEPHONY_DEVICE ( 0x0B ) +#define USB_HID_USAGE_PAGE_ID_CONSUMER ( 0x0C ) + +#endif // _XUA_HID_REPORT_DESCRIPTOR_CONSTANTS_ diff --git a/lib_xua/src/midi/usb_midi.xc b/lib_xua/src/midi/usb_midi.xc index ecb5059d..a7a97cac 100644 --- a/lib_xua/src/midi/usb_midi.xc +++ b/lib_xua/src/midi/usb_midi.xc @@ -1,10 +1,10 @@ -// Copyright 2011-2021 XMOS LIMITED. +// Copyright 2011-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #include #include #include #include -#include "usb_midi.h" +#include "xua_midi.h" #include "midiinparse.h" #include "midioutparse.h" #include "queue.h" @@ -24,12 +24,8 @@ static unsigned makeSymbol(unsigned data) #define RATE 31250 -#ifndef MIDI_SHIFT_TX -#define MIDI_SHIFT_TX 0 -#endif - -static unsigned bit_time = XS1_TIMER_MHZ * 1000000 / (unsigned) RATE; -static unsigned bit_time_2 = (XS1_TIMER_MHZ * 1000000 / (unsigned) RATE) / 2; +static const unsigned bit_time = XS1_TIMER_MHZ * 1000000 / (unsigned) RATE; +static const unsigned bit_time_2 = (XS1_TIMER_MHZ * 1000000 / (unsigned) RATE) / 2; // For debugging int mr_count = 0; // MIDI received (from HOST) @@ -58,16 +54,18 @@ timer iAPTimer; void usb_midi( #if (MIDI_RX_PORT_WIDTH == 4) -buffered in port:4 ?p_midi_in, + buffered in port:4 ?p_midi_in, #else -buffered in port:1 ?p_midi_in, + buffered in port:1 ?p_midi_in, #endif port ?p_midi_out, - clock ?clk_midi, - chanend ?c_midi, - unsigned cable_number, - chanend ?c_iap, chanend ?c_i2c, // iOS stuff - port ?p_scl, port ?p_sda + clock ?clk_midi, + chanend ?c_midi, + unsigned cable_number +#ifdef IAP + , chanend ?c_iap, chanend ?c_i2c, + port ?p_scl, port ?p_sda +#endif ) { unsigned symbol = 0x0; // Symbol in progress of being sent out @@ -241,7 +239,6 @@ buffered in port:1 ?p_midi_in, } p_midi_out <: (1< txT; txT += bit_time; txPT += bit_time; @@ -253,7 +250,6 @@ buffered in port:1 ?p_midi_in, txT += bit_time; // Should this be after the output otherwise be double the length of the high before the start bit txPT += bit_time; p_midi_out @ txPT <: ((symbol & 1)<>= 1; if (symbol == 0) { @@ -274,10 +270,8 @@ buffered in port:1 ?p_midi_in, if (is_ack) { // have we got more data to send - //printstr("ack\n"); if (!queue_is_empty(midi_to_host_fifo)) { - //printstr("uart->decouple\n"); outuint(c_midi, queue_pop_word(midi_to_host_fifo, midi_to_host_fifo_arr)); th_count++; } diff --git a/lib_xua/src/midi/usb_midi_support.xc b/lib_xua/src/midi/usb_midi_support.xc index 1ee5d080..b44c2b32 100644 --- a/lib_xua/src/midi/usb_midi_support.xc +++ b/lib_xua/src/midi/usb_midi_support.xc @@ -1,5 +1,5 @@ -// Copyright 2011-2021 XMOS LIMITED. +// Copyright 2011-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #include #define __MIDI_IMPL -#include "usb_midi.h" +#include "xua_midi.h" diff --git a/python/setup.py b/python/setup.py index 98d3d6a5..d3a7c32c 100644 --- a/python/setup.py +++ b/python/setup.py @@ -14,6 +14,8 @@ setuptools.setup( packages=setuptools.find_packages(), install_requires=[ 'flake8~=3.8', + 'pytest~=6.0', + 'pytest-xdist~=1.34', ], dependency_links=[ ], diff --git a/requirements.txt b/requirements.txt index 99e7dd7b..b7e17c79 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,6 +18,8 @@ # same modules should appear in the setup.py list as given below. flake8==3.8.3 +pytest==6.0.0 +pytest-xdist==1.34.0 # Development dependencies # @@ -32,3 +34,4 @@ flake8==3.8.3 # of its own setup.py file, then this list must include an entry for that # setup.py file, e.g., '-e .' or '-e ./python' (without the quotes). -e ./python +-e ./../test_support diff --git a/tests/.gitignore b/tests/.gitignore index f0bcfb69..3bbde6ff 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1 +1,2 @@ +*/runners/* test_results.csv diff --git a/tests/app_test_i2s_loopback/xk_216_mc/cs4384.h b/tests/app_test_i2s_loopback/xk_216_mc/cs4384.h deleted file mode 100644 index 6de83e81..00000000 --- a/tests/app_test_i2s_loopback/xk_216_mc/cs4384.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2017-2021 XMOS LIMITED. -// This Software is subject to the terms of the XMOS Public Licence: Version 1. -#ifndef CS4384_H_ -#define CS4384_H_ - -//Address on I2C bus -#define CS4384_I2C_ADDR (0x18) - -//Register Addresess -#define CS4384_CHIP_REV 0x01 -#define CS4384_MODE_CTRL 0x02 -#define CS4384_PCM_CTRL 0x03 -#define CS4384_DSD_CTRL 0x04 -#define CS4384_FLT_CTRL 0x05 -#define CS4384_INV_CTRL 0x06 -#define CS4384_GRP_CTRL 0x07 -#define CS4384_RMP_MUTE 0x08 -#define CS4384_MUTE_CTRL 0x09 -#define CS4384_MIX_PR1 0x0a -#define CS4384_VOL_A1 0x0b -#define CS4384_VOL_B1 0x0c -#define CS4384_MIX_PR2 0x0d -#define CS4384_VOL_A2 0x0e -#define CS4384_VOL_B2 0x0f -#define CS4384_MIX_PR3 0x10 -#define CS4384_VOL_A3 0x11 -#define CS4384_VOL_B3 0x12 -#define CS4384_MIX_PR4 0x13 -#define CS4384_VOL_A4 0x14 -#define CS4384_VOL_B4 0x15 -#define CS4384_CM_MODE 0x16 - -#endif /* CS4384_H_ */ diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..3117626c --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,41 @@ +# Copyright 2022-2023 XMOS LIMITED. +# This Software is subject to the terms of the XMOS Public Licence: Version 1. +import pytest +import time + + +@pytest.fixture() +def test_file(request): + return str(request.node.fspath) + + +@pytest.fixture(scope="session") # Use same seed for whole run +def test_seed(request): + + seed = str(int(time.time())) + # We dont need the following since pytest will print the values of our fixtures on a failure + # capmanager = request.config.pluginmanager.getplugin("capturemanager") + # with capmanager.global_and_fixture_disabled(): + # print("Using seed: "+ seed) + return seed + + +def pytest_addoption(parser): + parser.addoption( + "--enabletracing", + action="store_true", + default=False, + help="Run tests with instruction tracing", + ) + + parser.addoption( + "--enablevcdtracing", + action="store_true", + default=False, + help="Run tests with vcd tracing", + ) + + +@pytest.fixture +def options(request): + yield request.config.option diff --git a/tests/runtests.py b/tests/runtests.py deleted file mode 100755 index 0fbc7f5d..00000000 --- a/tests/runtests.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python2.7 -# Copyright 2018-2021 XMOS LIMITED. -# This Software is subject to the terms of the XMOS Public Licence: Version 1. -import xmostest -import os.path - -if __name__ == "__main__": - - - xmostest.init() - - xmostest.register_group("lib_xua", - "i2s_loopback_sim_tests", - "I2S loopback simulator tests", - - """ -Tests are performed by running the audiohub code connected to a -loopback plugin -""") - - xmostest.runtests() - - xmostest.finish() diff --git a/tests/test_i2s_loopback.py b/tests/test_i2s_loopback.py index 3e65e2f6..c824a355 100644 --- a/tests/test_i2s_loopback.py +++ b/tests/test_i2s_loopback.py @@ -1,51 +1,89 @@ -#!/usr/bin/env python -# Copyright 2018-2021 XMOS LIMITED. +# Copyright 2018-2022 XMOS LIMITED. # This Software is subject to the terms of the XMOS Public Licence: Version 1. -import xmostest +import pytest +import Pyxsim +from Pyxsim import testers +import os +import sys -def runtest_one_config(env, format, i2s_role, num_chans_in, num_chans_out, sample_rate): - testlevel = 'smoke' - resources = xmostest.request_resource('xsim') - binary = 'app_test_i2s_loopback/bin/{env}_{format}_{i2s_role}_{num_chans_in}in_{num_chans_out}out_{sample_rate}/app_test_i2s_loopback_{env}_{format}_{i2s_role}_{num_chans_in}in_{num_chans_out}out_{sample_rate}.xe'.format(env=env, format=format, i2s_role=i2s_role, num_chans_in=num_chans_in, num_chans_out=num_chans_out, sample_rate=sample_rate) - tester = xmostest.ComparisonTester(open('pass.expect'), - 'lib_xua', - 'i2s_loopback_sim_tests', - 'i2s_loopback', - {'env':env, - 'format':format, - 'i2s_role':i2s_role, - 'num_chans_in':num_chans_in, - 'num_chans_out':num_chans_out, - 'sample_rate':sample_rate}) - tester.set_min_testlevel(testlevel) - loopback_args= '-port tile[0] XS1_PORT_1M 1 0 -port tile[0] XS1_PORT_1I 1 0 ' + \ - '-port tile[0] XS1_PORT_1N 1 0 -port tile[0] XS1_PORT_1J 1 0 ' + \ - '-port tile[0] XS1_PORT_1O 1 0 -port tile[0] XS1_PORT_1K 1 0 ' + \ - '-port tile[0] XS1_PORT_1P 1 0 -port tile[0] XS1_PORT_1L 1 0 ' + \ - '-port tile[0] XS1_PORT_1A 1 0 -port tile[0] XS1_PORT_1F 1 0 ' - if i2s_role == 'slave': - loopback_args += '-port tile[0] XS1_PORT_1B 1 0 -port tile[0] XS1_PORT_1H 1 0 ' #bclk - loopback_args += '-port tile[0] XS1_PORT_1C 1 0 -port tile[0] XS1_PORT_1G 1 0 ' #lrclk +@pytest.fixture() +def test_file(request): + return str(request.node.fspath) - max_cycles = 1500000 #enough to reach the 10 skip + 100 test in sim at 48kHz - xmostest.run_on_simulator(resources['xsim'], binary, tester=tester, simargs=['--max-cycles', str(max_cycles), '--plugin', 'LoopbackPort.dll', loopback_args]) -def runtest(): - runtest_one_config('simulation', 'i2s', 'master', 2, 2, '48khz') - runtest_one_config('simulation', 'i2s', 'slave', 2, 2, '48khz') +def do_test( + pcm_format, i2s_role, channel_count, sample_rate, test_file, options, capfd +): - runtest_one_config('simulation', 'i2s', 'master', 2, 2, '192khz') - runtest_one_config('simulation', 'i2s', 'slave', 2, 2, '192khz') + build_options = [] + output = [] + testname, _ = os.path.splitext(os.path.basename(test_file)) - runtest_one_config('simulation', 'i2s', 'master', 8, 8, '48khz') - runtest_one_config('simulation', 'i2s', 'slave', 8, 8, '48khz') + desc = f"simulation_{pcm_format}_{i2s_role}_{channel_count}in_{channel_count}out_{sample_rate}" + binary = f"{testname}/bin/{desc}/{testname}_{desc}.xe" - runtest_one_config('simulation', 'i2s', 'master', 8, 8, '192khz') - runtest_one_config('simulation', 'i2s', 'slave', 8, 8, '192khz') + tester = testers.ComparisonTester(open("pass.expect")) - runtest_one_config('simulation', 'tdm', 'master', 8, 8, '48khz') - runtest_one_config('simulation', 'tdm', 'slave', 8, 8, '48khz') + loopback_args = ( + "-port tile[0] XS1_PORT_1M 1 0 -port tile[0] XS1_PORT_1I 1 0 " + + "-port tile[0] XS1_PORT_1N 1 0 -port tile[0] XS1_PORT_1J 1 0 " + + "-port tile[0] XS1_PORT_1O 1 0 -port tile[0] XS1_PORT_1K 1 0 " + + "-port tile[0] XS1_PORT_1P 1 0 -port tile[0] XS1_PORT_1L 1 0 " + + "-port tile[0] XS1_PORT_1A 1 0 -port tile[0] XS1_PORT_1F 1 0 " + ) + if i2s_role == "slave": + loopback_args += ( + "-port tile[0] XS1_PORT_1B 1 0 -port tile[0] XS1_PORT_1H 1 0 " # bclk + ) + loopback_args += ( + "-port tile[0] XS1_PORT_1C 1 0 -port tile[0] XS1_PORT_1G 1 0 " # lrclk + ) - runtest_one_config('simulation', 'tdm', 'master', 16, 16, '48khz') - runtest_one_config('simulation', 'tdm', 'slave', 16, 16, '48khz') + max_cycles = 1500000 # enough to reach the 10 skip + 100 test in sim at 48kHz + + simargs = [ + "--max-cycles", + str(max_cycles), + "--plugin", + "LoopbackPort.dll", + loopback_args, + ] + + result = Pyxsim.run_on_simulator( + binary, + tester=tester, + simargs=simargs, + capfd=capfd, + instTracing=options.enabletracing, + vcdTracing=options.enablevcdtracing, + ) + + return result + + +@pytest.mark.parametrize("i2s_role", ["master", "slave"]) +@pytest.mark.parametrize("pcm_format", ["i2s", "tdm"]) +@pytest.mark.parametrize("channel_count", [2, 8, 16]) +@pytest.mark.parametrize("sample_rate", ["48khz", "96khz", "192khz"]) +def test_i2s_loopback( + i2s_role, pcm_format, channel_count, sample_rate, test_file, options, capfd +): + + if pcm_format == "i2s" and channel_count == 16: + pytest.skip("Invalid parameter combination") + + if pcm_format == "i2s" and sample_rate not in ["48khz", "192khz"]: + pytest.skip("Invalid parameter combination") + + if pcm_format == "tdm" and channel_count == 2: + pytest.skip("Invalid parameter combination") + + if pcm_format == "tdm" and sample_rate == "192khz": + pytest.skip("Invalid parameter combination") + + result = do_test( + pcm_format, i2s_role, channel_count, sample_rate, test_file, options, capfd + ) + + assert result diff --git a/tests/app_test_i2s_loopback/Makefile b/tests/test_i2s_loopback/Makefile similarity index 53% rename from tests/app_test_i2s_loopback/Makefile rename to tests/test_i2s_loopback/Makefile index 4f54fc80..32615bb1 100644 --- a/tests/app_test_i2s_loopback/Makefile +++ b/tests/test_i2s_loopback/Makefile @@ -1,76 +1,99 @@ -# Copyright (c) 2016, XMOS Ltd, All rights reserved TARGET = xk-audio-216-mc.xn -USED_MODULES = lib_xua \ - module_i2c_shared module_i2c_single_port lib_logging +USED_MODULES = lib_xua lib_i2c lib_logging -BUILD_FLAGS = -O0 -g -lflash -DXUD_SERIES_SUPPORT=4 -fxscope -save-temps -march=xs2a -DUSB_TILE=tile[1] +BUILD_FLAGS = -O0 -g -lflash -DXUD_CORE_CLOCK=600 -fxscope -save-temps -march=xs2a -DUSB_TILE=tile[1] BUILD_FLAGS_i2s_master_2in_2out_48khz = $(BUILD_FLAGS) \ - -D ADAT_RX=0 -D ADAT_TX=0 -D SPDIF_RX=0 -D SPDIF_TX=0 -D MIDI=0 \ + -D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \ -D NUM_USB_CHAN_IN=2 -D NUM_USB_CHAN_OUT=2 -DI2S_CHANS_ADC=2 -DI2S_CHANS_DAC=2 \ -D DEFAULT_FREQ=48000 BUILD_FLAGS_i2s_slave_2in_2out_48khz = $(BUILD_FLAGS) \ - -D ADAT_RX=0 -D ADAT_TX=0 -D SPDIF_RX=0 -D SPDIF_TX=0 -D MIDI=0 \ + -D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \ -D NUM_USB_CHAN_IN=2 -D NUM_USB_CHAN_OUT=2 -DI2S_CHANS_ADC=2 -DI2S_CHANS_DAC=2 \ -D DEFAULT_FREQ=48000 -DCODEC_MASTER=1 BUILD_FLAGS_i2s_master_2in_2out_192khz = $(BUILD_FLAGS) \ - -D ADAT_RX=0 -D ADAT_TX=0 -D SPDIF_RX=0 -D SPDIF_TX=0 -D MIDI=0 \ + -D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \ -D NUM_USB_CHAN_IN=2 -D NUM_USB_CHAN_OUT=2 -D I2S_CHANS_ADC=2 -D I2S_CHANS_DAC=2 \ -D DEFAULT_FREQ=192000 BUILD_FLAGS_i2s_slave_2in_2out_192khz = $(BUILD_FLAGS) \ - -D ADAT_RX=0 -D ADAT_TX=0 -D SPDIF_RX=0 -D SPDIF_TX=0 -D MIDI=0 \ + -D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \ -D NUM_USB_CHAN_IN=2 -D NUM_USB_CHAN_OUT=2 -DI2S_CHANS_ADC=2 -DI2S_CHANS_DAC=2 \ -D DEFAULT_FREQ=192000 -DCODEC_MASTER=1 BUILD_FLAGS_i2s_master_8in_8out_48khz = $(BUILD_FLAGS) \ - -D ADAT_RX=0 -D ADAT_TX=0 -D SPDIF_RX=0 -D SPDIF_TX=0 -D MIDI=0 \ + -D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \ -D NUM_USB_CHAN_IN=8 -D NUM_USB_CHAN_OUT=8 -D I2S_CHANS_ADC=8 -D I2S_CHANS_DAC=8 \ -D DEFAULT_FREQ=48000 BUILD_FLAGS_i2s_slave_8in_8out_48khz = $(BUILD_FLAGS) \ - -D ADAT_RX=0 -D ADAT_TX=0 -D SPDIF_RX=0 -D SPDIF_TX=0 -D MIDI=0 \ + -D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \ -D NUM_USB_CHAN_IN=8 -D NUM_USB_CHAN_OUT=8 -D I2S_CHANS_ADC=8 -D I2S_CHANS_DAC=8 \ -D DEFAULT_FREQ=48000 -DCODEC_MASTER=1 BUILD_FLAGS_i2s_master_8in_8out_192khz = $(BUILD_FLAGS) \ - -D ADAT_RX=0 -D ADAT_TX=0 -D SPDIF_RX=0 -D SPDIF_TX=0 -D MIDI=0 \ + -D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \ -D NUM_USB_CHAN_IN=8 -D NUM_USB_CHAN_OUT=8 -D I2S_CHANS_ADC=8 -D I2S_CHANS_DAC=8 \ -D DEFAULT_FREQ=192000 \ -O2 # optimisations to meet timing BUILD_FLAGS_i2s_slave_8in_8out_192khz = $(BUILD_FLAGS) \ - -D ADAT_RX=0 -D ADAT_TX=0 -D SPDIF_RX=0 -D SPDIF_TX=0 -D MIDI=0 \ + -D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \ -D NUM_USB_CHAN_IN=8 -D NUM_USB_CHAN_OUT=8 -D I2S_CHANS_ADC=8 -D I2S_CHANS_DAC=8 \ -D DEFAULT_FREQ=192000 -DCODEC_MASTER=1 \ -O2 # optimisations to meet timing -BUILD_FLAGS_tdm_master_8in_8out_48khz = $(BUILD_FLAGS) -D I2S_MODE_TDM=1 \ - -D ADAT_RX=0 -D ADAT_TX=0 -D SPDIF_RX=0 -D SPDIF_TX=0 -D MIDI=0 \ +BUILD_FLAGS_tdm_master_8in_8out_48khz = $(BUILD_FLAGS) -D XUA_PCM_FORMAT=XUA_PCM_FORMAT_TDM \ + -D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \ -D NUM_USB_CHAN_IN=8 -D NUM_USB_CHAN_OUT=8 -D I2S_CHANS_ADC=8 -D I2S_CHANS_DAC=8 \ -D DEFAULT_FREQ=48000 \ -O2 # optimisations to meet timing -BUILD_FLAGS_tdm_slave_8in_8out_48khz = $(BUILD_FLAGS) -D I2S_MODE_TDM=1 \ - -D ADAT_RX=0 -D ADAT_TX=0 -D SPDIF_RX=0 -D SPDIF_TX=0 -D MIDI=0 \ +BUILD_FLAGS_tdm_master_8in_8out_96khz = $(BUILD_FLAGS) -D XUA_PCM_FORMAT=XUA_PCM_FORMAT_TDM \ + -D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \ + -D NUM_USB_CHAN_IN=8 -D NUM_USB_CHAN_OUT=8 -D I2S_CHANS_ADC=8 -D I2S_CHANS_DAC=8 \ + -D DEFAULT_FREQ=96000 \ + -O3 # optimisations to meet timing + +BUILD_FLAGS_tdm_slave_8in_8out_48khz = $(BUILD_FLAGS) -D XUA_PCM_FORMAT=XUA_PCM_FORMAT_TDM \ + -D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \ -D NUM_USB_CHAN_IN=8 -D NUM_USB_CHAN_OUT=8 -D I2S_CHANS_ADC=8 -D I2S_CHANS_DAC=8 \ -D DEFAULT_FREQ=48000 -DCODEC_MASTER=1 \ -O2 # optimisations to meet timing -BUILD_FLAGS_tdm_master_16in_16out_48khz = $(BUILD_FLAGS) -D I2S_MODE_TDM=1 \ - -D ADAT_RX=0 -D ADAT_TX=0 -D SPDIF_RX=0 -D SPDIF_TX=0 -D MIDI=0 \ +BUILD_FLAGS_tdm_slave_8in_8out_96khz = $(BUILD_FLAGS) -D XUA_PCM_FORMAT=XUA_PCM_FORMAT_TDM \ + -D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \ + -D NUM_USB_CHAN_IN=8 -D NUM_USB_CHAN_OUT=8 -D I2S_CHANS_ADC=8 -D I2S_CHANS_DAC=8 \ + -D DEFAULT_FREQ=96000 -DCODEC_MASTER=1 \ + -O2 # optimisations to meet timing + +BUILD_FLAGS_tdm_master_16in_16out_48khz = $(BUILD_FLAGS) -D XUA_PCM_FORMAT=XUA_PCM_FORMAT_TDM \ + -D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \ -D NUM_USB_CHAN_IN=16 -D NUM_USB_CHAN_OUT=16 -D I2S_CHANS_ADC=16 -D I2S_CHANS_DAC=16 \ -D DEFAULT_FREQ=48000 \ -O2 # optimisations to meet timing -BUILD_FLAGS_tdm_slave_16in_16out_48khz = $(BUILD_FLAGS) -D I2S_MODE_TDM=1 \ - -D ADAT_RX=0 -D ADAT_TX=0 -D SPDIF_RX=0 -D SPDIF_TX=0 -D MIDI=0 \ +BUILD_FLAGS_tdm_master_16in_16out_96khz = $(BUILD_FLAGS) -D XUA_PCM_FORMAT=XUA_PCM_FORMAT_TDM \ + -D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \ + -D NUM_USB_CHAN_IN=16 -D NUM_USB_CHAN_OUT=16 -D I2S_CHANS_ADC=16 -D I2S_CHANS_DAC=16 \ + -D DEFAULT_FREQ=96000 \ + -O2 # optimisations to meet timing + +BUILD_FLAGS_tdm_slave_16in_16out_48khz = $(BUILD_FLAGS) -D XUA_PCM_FORMAT=XUA_PCM_FORMAT_TDM \ + -D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \ -D NUM_USB_CHAN_IN=16 -D NUM_USB_CHAN_OUT=16 -D I2S_CHANS_ADC=16 -D I2S_CHANS_DAC=16 \ -D DEFAULT_FREQ=48000 -DCODEC_MASTER=1 \ -O2 # optimisations to meet timing +BUILD_FLAGS_tdm_slave_16in_16out_96khz = $(BUILD_FLAGS) -D XUA_PCM_FORMAT=XUA_PCM_FORMAT_TDM \ + -D XUA_ADAT_RX_EN=0 -D XUA_ADAT_TX_EN=0 -D XUA_SPDIF_RX_EN=0 -D XUA_SPDIF_TX_EN=0 -D MIDI=0 \ + -D NUM_USB_CHAN_IN=16 -D NUM_USB_CHAN_OUT=16 -D I2S_CHANS_ADC=16 -D I2S_CHANS_DAC=16 \ + -D DEFAULT_FREQ=96000 -DCODEC_MASTER=1 \ + -O2 # optimisations to meet timing + + #XCC_FLAGS_hardware_i2s_master_2in_2out_48khz = -D HARDWARE $(BUILD_FLAGS_i2s_master_2in_2out_48khz) #XCC_FLAGS_hardware_i2s_master_2in_2out_192khz = -D HARDWARE $(BUILD_FLAGS_i2s_master_2in_2out_192khz) #XCC_FLAGS_hardware_i2s_master_8in_8out_48khz = -D HARDWARE $(BUILD_FLAGS_i2s_master_8in_8out_48khz) @@ -90,10 +113,14 @@ XCC_FLAGS_simulation_i2s_master_8in_8out_192khz = -D SIMULATION $(BUILD_FLAGS_i2 XCC_FLAGS_simulation_i2s_slave_8in_8out_192khz = -D SIMULATION $(BUILD_FLAGS_i2s_slave_8in_8out_192khz) XCC_FLAGS_simulation_tdm_master_8in_8out_48khz = -D SIMULATION $(BUILD_FLAGS_tdm_master_8in_8out_48khz) +XCC_FLAGS_simulation_tdm_master_8in_8out_96khz = -D SIMULATION $(BUILD_FLAGS_tdm_master_8in_8out_96khz) XCC_FLAGS_simulation_tdm_slave_8in_8out_48khz = -D SIMULATION $(BUILD_FLAGS_tdm_slave_8in_8out_48khz) +XCC_FLAGS_simulation_tdm_slave_8in_8out_96khz = -D SIMULATION $(BUILD_FLAGS_tdm_slave_8in_8out_96khz) XCC_FLAGS_simulation_tdm_master_16in_16out_48khz = -D SIMULATION $(BUILD_FLAGS_tdm_master_16in_16out_48khz) +XCC_FLAGS_simulation_tdm_master_16in_16out_96khz = -D SIMULATION $(BUILD_FLAGS_tdm_master_16in_16out_96khz) XCC_FLAGS_simulation_tdm_slave_16in_16out_48khz = -D SIMULATION $(BUILD_FLAGS_tdm_slave_16in_16out_48khz) +XCC_FLAGS_simulation_tdm_slave_16in_16out_96khz = -D SIMULATION $(BUILD_FLAGS_tdm_slave_16in_16out_96khz) XMOS_MAKE_PATH ?= ../.. -include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common diff --git a/tests/app_test_i2s_loopback/debug_conf.h b/tests/test_i2s_loopback/debug_conf.h similarity index 86% rename from tests/app_test_i2s_loopback/debug_conf.h rename to tests/test_i2s_loopback/debug_conf.h index aadb0014..11cedc16 100644 --- a/tests/app_test_i2s_loopback/debug_conf.h +++ b/tests/test_i2s_loopback/debug_conf.h @@ -1,4 +1,4 @@ -// Copyright 2016-2021 XMOS LIMITED. +// Copyright 2016-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #ifndef __debug_conf_h__ #define __debug_conf_h__ diff --git a/tests/test_i2s_loopback/hid_report_descriptor.h b/tests/test_i2s_loopback/hid_report_descriptor.h new file mode 100644 index 00000000..354e199d --- /dev/null +++ b/tests/test_i2s_loopback/hid_report_descriptor.h @@ -0,0 +1,167 @@ +// Copyright 2021-2022 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#ifndef __hid_report_descriptor_h__ +#define __hid_report_descriptor_h__ + +#include "xua_hid_report.h" + +#if 0 +/* Existing static report descriptor kept for reference */ +unsigned char hidReportDescriptor[] = +{ + 0x05, 0x0c, /* Usage Page (Consumer Device) */ + 0x09, 0x01, /* Usage (Consumer Control) */ + 0xa1, 0x01, /* Collection (Application) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x25, 0x01, /* Logical Maximum (1) */ + 0x09, 0xb0, /* Usage (Play) */ + 0x09, 0xb5, /* Usage (Scan Next Track) */ + 0x09, 0xb6, /* Usage (Scan Previous Track) */ + 0x09, 0xe9, /* Usage (Volume Up) */ + 0x09, 0xea, /* Usage (Volume Down) */ + 0x09, 0xe2, /* Usage (Mute) */ + 0x75, 0x01, /* Report Size (1) */ + 0x95, 0x06, /* Report Count (6) */ + 0x81, 0x02, /* Input (Data, Var, Abs) */ + 0x95, 0x02, /* Report Count (2) */ + 0x81, 0x01, /* Input (Cnst, Ary, Abs) */ + 0xc0 /* End collection */ +}; +#endif + +/* + * Define non-configurable items in the HID Report descriptor. + */ +static const USB_HID_Short_Item_t hidCollectionApplication = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_MAIN, HID_REPORT_ITEM_TAG_COLLECTION), + .data = { 0x01, 0x00 } }; +static const USB_HID_Short_Item_t hidCollectionEnd = { + .header = HID_REPORT_SET_HEADER(0, HID_REPORT_ITEM_TYPE_MAIN, HID_REPORT_ITEM_TAG_END_COLLECTION), + .data = { 0x00, 0x00 } }; + +static const USB_HID_Short_Item_t hidInputConstArray = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_MAIN, HID_REPORT_ITEM_TAG_INPUT), + .data = { 0x01, 0x00 } }; +static const USB_HID_Short_Item_t hidInputDataVar = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_MAIN, HID_REPORT_ITEM_TAG_INPUT), + .data = { 0x02, 0x00 } }; + +static const USB_HID_Short_Item_t hidLogicalMaximum0 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_LOGICAL_MAXIMUM), + .data = { 0x00, 0x00 } }; +static const USB_HID_Short_Item_t hidLogicalMaximum1 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_LOGICAL_MAXIMUM), + .data = { 0x01, 0x00 } }; +static const USB_HID_Short_Item_t hidLogicalMinimum0 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_LOGICAL_MINIMUM), + .data = { 0x00, 0x00 } }; + +static const USB_HID_Short_Item_t hidReportCount2 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_COUNT), + .data = { 0x02, 0x00 } }; +static const USB_HID_Short_Item_t hidReportCount6 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_COUNT), + .data = { 0x06, 0x00 } }; +static const USB_HID_Short_Item_t hidReportSize1 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_SIZE), + .data = { 0x01, 0x00 } }; + +static const USB_HID_Short_Item_t hidUsageConsumerControl = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .data = { 0x01, 0x00 } }; + +/* + * Define the HID Report Descriptor Item, Usage Page, Report ID and length for each HID Report + * For internal purposes, a report element with ID of 0 must be included if report IDs are not being used. + */ +static const USB_HID_Report_Element_t hidReportPageConsumer = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_USAGE_PAGE), + .item.data = { USB_HID_USAGE_PAGE_ID_CONSUMER, 0x00 }, + .location = HID_REPORT_SET_LOC( 0, 1, 0, 0 ) }; + +/* + * Define configurable items in the HID Report descriptor. + */ +static USB_HID_Report_Element_t hidUsageByte0Bit5 = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .item.data = { 0xE2, 0x00 }, + .location = HID_REPORT_SET_LOC( 0, 0, 0, 5 ) +}; // Mute +static USB_HID_Report_Element_t hidUsageByte0Bit4 = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .item.data = { 0xEA, 0x00 }, + .location = HID_REPORT_SET_LOC( 0, 0, 0, 4 ) +}; // Vol- +static USB_HID_Report_Element_t hidUsageByte0Bit3 = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .item.data = { 0xE9, 0x00 }, + .location = HID_REPORT_SET_LOC( 0, 0, 0, 3 ) +}; // Vol+ +static USB_HID_Report_Element_t hidUsageByte0Bit2 = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .item.data = { 0xB6, 0x00 }, + .location = HID_REPORT_SET_LOC( 0, 0, 0, 2 ) +}; // Scan Prev +static USB_HID_Report_Element_t hidUsageByte0Bit1 = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .item.data = { 0xB5, 0x00 }, + .location = HID_REPORT_SET_LOC( 0, 0, 0, 1 ) +}; // Scan Next +static USB_HID_Report_Element_t hidUsageByte0Bit0 = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .item.data = { 0xB0, 0x00 }, + .location = HID_REPORT_SET_LOC( 0, 0, 0, 0 ) +}; // Play + +/* + * List the configurable items in the HID Report descriptor. + */ +static USB_HID_Report_Element_t* const hidConfigurableElements[] = { + &hidUsageByte0Bit0, + &hidUsageByte0Bit1, + &hidUsageByte0Bit2, + &hidUsageByte0Bit3, + &hidUsageByte0Bit4, + &hidUsageByte0Bit5 +}; + +/* + * List HID Reports, one per Report ID. This should be a usage page item with the relevant + * If not using report IDs - still have one with report ID 0 + */ +static const USB_HID_Report_Element_t* const hidReports[] = { + &hidReportPageConsumer +}; + +/* + * List all items in the HID Report descriptor. + */ +static const USB_HID_Short_Item_t * const hidReportDescriptorItems[] = { + &(hidReportPageConsumer.item), + &hidUsageConsumerControl, + &hidCollectionApplication, + &hidLogicalMinimum0, + &hidLogicalMaximum1, + &(hidUsageByte0Bit0.item), + &(hidUsageByte0Bit1.item), + &(hidUsageByte0Bit2.item), + &(hidUsageByte0Bit3.item), + &(hidUsageByte0Bit4.item), + &(hidUsageByte0Bit5.item), + &hidReportSize1, + &hidReportCount6, + &hidInputDataVar, + &hidLogicalMaximum0, + &hidReportCount2, + &hidInputConstArray, + &hidCollectionEnd +}; + +/* + * Define the number of HID Reports + * Due to XC not supporting designated initializers, this constant has a hard-coded value. + * It must equal ( sizeof hidReports / sizeof ( USB_HID_Report_Element_t* )) + */ +#define HID_REPORT_COUNT ( 1 ) + +#endif // __hid_report_descriptor_h__ diff --git a/tests/app_test_i2s_loopback/main.xc b/tests/test_i2s_loopback/main.xc similarity index 96% rename from tests/app_test_i2s_loopback/main.xc rename to tests/test_i2s_loopback/main.xc index 9d012a49..a98bc9ec 100644 --- a/tests/app_test_i2s_loopback/main.xc +++ b/tests/test_i2s_loopback/main.xc @@ -1,4 +1,4 @@ -// Copyright 2016-2021 XMOS LIMITED. +// Copyright 2016-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #include #include @@ -81,7 +81,7 @@ buffered out port:32 p_bclk = PORT_I2S_BCLK; /* I2S L/R-clock */ in port p_mclk_in = PORT_MCLK_IN; /* Clock-block declarations */ -clock clk_audio_bclk = on tile[AUDIO_IO_TILE]: XS1_CLKBLK_1; /* Bit clock */ +clock clk_audio_bclk = on tile[AUDIO_IO_TILE]: XS1_CLKBLK_1; /* Bit clock */ clock clk_audio_mclk = on tile[AUDIO_IO_TILE]: XS1_CLKBLK_2; /* Master clock */ #ifdef SIMULATION @@ -189,21 +189,21 @@ void checker(chanend c_checker, int disable) #ifdef SIMULATION -out port p_mclk_gen = on tile[AUDIO_IO_TILE] : XS1_PORT_1A; +out port p_mclk_gen = on tile[AUDIO_IO_TILE] : XS1_PORT_1A; clock clk_audio_mclk_gen = on tile[AUDIO_IO_TILE] : XS1_CLKBLK_3; void master_mode_clk_setup(void); #if CODEC_MASTER -out port p_bclk_gen = on tile[AUDIO_IO_TILE] : XS1_PORT_1B; +out port p_bclk_gen = on tile[AUDIO_IO_TILE] : XS1_PORT_1B; clock clk_audio_bclk_gen = on tile[AUDIO_IO_TILE] : XS1_CLKBLK_4; -out port p_lrclk_gen = on tile[AUDIO_IO_TILE] : XS1_PORT_1C; +out port p_lrclk_gen = on tile[AUDIO_IO_TILE] : XS1_PORT_1C; clock clk_audio_lrclk_gen = on tile[AUDIO_IO_TILE] : XS1_CLKBLK_5; void slave_mode_clk_setup(const unsigned samFreq, const unsigned chans_per_frame); #endif #endif -#if I2S_MODE_TDM +#if (XUA_PCM_FORMAT == XUA_PCM_FORMAT_TDM) const int i2s_tdm_mode = 8; #else const int i2s_tdm_mode = 2; @@ -214,11 +214,11 @@ int main(void) chan c_checker; chan c_out; - par + par { - on tile[AUDIO_IO_TILE]: - { - par + on tile[AUDIO_IO_TILE]: + { + par { XUA_AudioHub(c_out, clk_audio_mclk, clk_audio_bclk, p_mclk_in, p_lrclk, p_bclk, p_i2s_dac, p_i2s_adc); generator(c_checker, c_out); diff --git a/tests/app_test_i2s_loopback/simulation.xc b/tests/test_i2s_loopback/simulation.xc similarity index 89% rename from tests/app_test_i2s_loopback/simulation.xc rename to tests/test_i2s_loopback/simulation.xc index 68541ca1..44513a04 100644 --- a/tests/app_test_i2s_loopback/simulation.xc +++ b/tests/test_i2s_loopback/simulation.xc @@ -1,4 +1,4 @@ -// Copyright 2016-2021 XMOS LIMITED. +// Copyright 2016-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #ifdef SIMULATION @@ -22,7 +22,7 @@ void AudioHwInit() extern clock clk_audio_mclk_gen; -extern out port p_mclk_gen; +extern out port p_mclk_gen; void master_mode_clk_setup(void) { configure_clock_rate(clk_audio_mclk_gen, 25, 1); // Slighly faster than typical MCLK of 24.576MHz @@ -35,17 +35,17 @@ void master_mode_clk_setup(void) #if CODEC_MASTER -extern out port p_bclk_gen; +extern out port p_bclk_gen; extern clock clk_audio_bclk_gen; -extern out port p_lrclk_gen; +extern out port p_lrclk_gen; extern clock clk_audio_lrclk_gen; void slave_mode_clk_setup(const unsigned samFreq, const unsigned chans_per_frame){ const unsigned data_bits = 32; const unsigned mclk_freq = 24576000; - const unsigned mclk_bclk_ratio = mclk_freq / (chans_per_frame * samFreq * data_bits); - const unsigned bclk_lrclk_ratio = (chans_per_frame * data_bits); // 48.828Hz LRCLK + const unsigned mclk_bclk_ratio = mclk_freq / (chans_per_frame * samFreq * data_bits); + const unsigned bclk_lrclk_ratio = (chans_per_frame * data_bits); // 48.828Hz LRCLK //bclk configure_clock_src_divide(clk_audio_bclk_gen, p_mclk_gen, mclk_bclk_ratio/2); diff --git a/tests/app_test_i2s_loopback/usb_device.h b/tests/test_i2s_loopback/usb_device.h similarity index 95% rename from tests/app_test_i2s_loopback/usb_device.h rename to tests/test_i2s_loopback/usb_device.h index 1f5aaa89..9d21df47 100644 --- a/tests/app_test_i2s_loopback/usb_device.h +++ b/tests/test_i2s_loopback/usb_device.h @@ -1,4 +1,4 @@ -// Copyright 2016-2021 XMOS LIMITED. +// Copyright 2016-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #ifndef __usb_device_h__ #define __usb_device_h__ diff --git a/tests/app_test_i2s_loopback/usb_device.xc b/tests/test_i2s_loopback/usb_device.xc similarity index 96% rename from tests/app_test_i2s_loopback/usb_device.xc rename to tests/test_i2s_loopback/usb_device.xc index 7a205735..4f755d1c 100644 --- a/tests/app_test_i2s_loopback/usb_device.xc +++ b/tests/test_i2s_loopback/usb_device.xc @@ -1,4 +1,4 @@ -// Copyright 2018-2021 XMOS LIMITED. +// Copyright 2018-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #if 0 #include diff --git a/tests/app_test_i2s_loopback/xk_216_mc/audiohw.xc b/tests/test_i2s_loopback/xk_216_mc/audiohw.xc similarity index 89% rename from tests/app_test_i2s_loopback/xk_216_mc/audiohw.xc rename to tests/test_i2s_loopback/xk_216_mc/audiohw.xc index 3a8cde53..ed1d5f7e 100644 --- a/tests/app_test_i2s_loopback/xk_216_mc/audiohw.xc +++ b/tests/test_i2s_loopback/xk_216_mc/audiohw.xc @@ -1,4 +1,4 @@ -// Copyright 2016-2021 XMOS LIMITED. +// Copyright 2016-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #ifdef HARDWARE @@ -8,7 +8,7 @@ #include "devicedefines.h" #include #include "gpio_access.h" -#include "i2c_shared.h" +#include "i2c.h" #include "cs4384.h" #include "cs5368.h" #include "cs2100.h" @@ -25,16 +25,10 @@ on tile[0] : out port p_gpio = XS1_PORT_8C; -#ifndef IAP -/* If IAP not enabled, i2c ports not declared - still needs for DAC config */ -on tile [0] : struct r_i2c r_i2c = {XS1_PORT_4A}; -#else -extern struct r_i2c r_i2c; -#endif +port p_i2c = on tile[0]:PORT_I2C; -#define DAC_REGWRITE(reg, val) {data[0] = val; i2c_shared_master_write_reg(r_i2c, CS4384_I2C_ADDR, reg, data, 1);} -#define DAC_REGREAD(reg, val) {i2c_shared_master_read_reg(r_i2c, CS4384_I2C_ADDR, reg, val, 1);} -#define ADC_REGWRITE(reg, val) {data[0] = val; i2c_shared_master_write_reg(r_i2c, CS5368_I2C_ADDR, reg, data, 1);} +#define DAC_REGWRITE(reg, val) {result = i2c.write_reg(CS4384_I2C_ADDR, reg, val);} +#define ADC_REGWRITE(reg, val) {result = i2c.write_reg(CS5368_I2C_ADDR, reg, val);} #ifdef USE_FRACTIONAL_N @@ -45,14 +39,15 @@ extern struct r_i2c r_i2c; #define PLL_SYNC_FREQ 300 #endif -#define CS2100_REGREAD(reg, data) {data[0] = 0xAA; i2c_master_read_reg(CS2100_I2C_DEVICE_ADDR, reg, data, 1, r_i2c);} -#define CS2100_REGREAD_ASSERT(reg, data, expected) {data[0] = 0xAA; i2c_master_read_reg(CS2100_I2C_DEVICE_ADDR, reg, data, 1, r_i2c); assert(data[0] == expected);} -#define CS2100_REGWRITE(reg, val) {data[0] = val; i2c_master_write_reg(CS2100_I2C_DEVICE_ADDR, reg, data, 1, r_i2c);} +#define CS2100_REGREAD(reg, data) {data[0] = i2c.read_reg(CS2100_I2C_DEVICE_ADDR, reg, result);} +#define CS2100_REGREAD_ASSERT(reg, data, expected) {data[0] = i2c.read_reg(CS2100_I2C_DEVICE_ADDR, reg, result); assert(data[0] == expected);} +#define CS2100_REGWRITE(reg, val) {result = i2c.write_reg(CS2100_I2C_DEVICE_ADDR, reg, val);} /* Init of CS2100 */ -void PllInit(void) +void PllInit(client interface i2c_master_if i2c) { unsigned char data[1] = {0}; + i2c_regop_res_t result; #if XCORE_200_MC_AUDIO_HW_VERSION < 2 /* Enable init */ @@ -73,12 +68,15 @@ void PllInit(void) CS2100_REGREAD_ASSERT(CS2100_GLOBAL_CONFIG, data, 0x01); CS2100_REGREAD_ASSERT(CS2100_FUNC_CONFIG_1, data, 0x08); CS2100_REGREAD_ASSERT(CS2100_FUNC_CONFIG_2, data, 0x00); + + i2c.shutdown(); } /* Setup PLL multiplier */ -void PllMult(unsigned output, unsigned ref) +void PllMult(unsigned output, unsigned ref, client interface i2c_master_if i2c) { unsigned char data[1] = {0}; + i2c_regop_res_t result; /* PLL expects 12:20 format, convert output and ref to 12:20 */ /* Shift up the dividend by 12 to retain format... */ @@ -120,9 +118,6 @@ void AudioHwInit(chanend ?c_codec) start_clock(clk_pll_sync); #endif - /* Init the i2c module */ - i2c_shared_master_init(r_i2c); - /* Assert reset to ADC and DAC */ set_gpio(P_GPIO_DAC_RST_N, 0); set_gpio(P_GPIO_ADC_RST_N, 0); @@ -143,7 +138,12 @@ void AudioHwInit(chanend ?c_codec) set_gpio(P_GPIO_PLL_SEL, 1); /* Initialise external PLL */ - PllInit(); + i2c_master_if i2c[1]; + par + { + i2c_master_single_port(i2c, 1, p_i2c, 10, 0, 1, 0); + PllInit(i2c[0]); + } #endif #ifdef IAP @@ -155,10 +155,11 @@ void AudioHwInit(chanend ?c_codec) /* Configures the external audio hardware for the required sample frequency. * See gpio.h for I2C helper functions and gpio access */ -void AudioHwConfig(unsigned samFreq, unsigned mClk, chanend ?c_codec, unsigned dsdMode, - unsigned sampRes_DAC, unsigned sampRes_ADC) +void AudioHwConfig2(unsigned samFreq, unsigned mClk, chanend ?c_codec, unsigned dsdMode, + unsigned sampRes_DAC, unsigned sampRes_ADC, client interface i2c_master_if i2c) { - unsigned char data[1] = {0}; + unsigned char data[1] = {0}; + i2c_regop_res_t result; /* Put ADC and DAC into reset */ set_gpio(P_GPIO_ADC_RST_N, 0); @@ -167,7 +168,7 @@ void AudioHwConfig(unsigned samFreq, unsigned mClk, chanend ?c_codec, unsigned d /* Set master clock select appropriately */ #if defined(USE_FRACTIONAL_N) /* Configure external fractional-n clock multiplier for 300Hz -> mClkFreq */ - PllMult(mClk, PLL_SYNC_FREQ); + PllMult(mClk, PLL_SYNC_FREQ, i2c); #endif /* Allow some time for mclk to lock and MCLK to stabilise - this is important to avoid glitches at start of stream */ { @@ -365,8 +366,20 @@ void AudioHwConfig(unsigned samFreq, unsigned mClk, chanend ?c_codec, unsigned d DAC_REGWRITE(CS4384_MODE_CTRL, 0b10000000); } #endif + + i2c.shutdown(); return; } -//: + +void AudioHwConfig(unsigned samFreq, unsigned mClk, chanend ?c_codec, unsigned dsdMode, + unsigned sampRes_DAC, unsigned sampRes_ADC) +{ + i2c_master_if i2c[1]; + par + { + i2c_master_single_port(i2c, 1, p_i2c, 10, 0, 1, 0); + AudioHwConfig2(samFreq, mClk, c_codec, dsdMode, sampRes_DAC, sampRes_ADC, i2c[0]); + } +} #endif diff --git a/tests/app_test_i2s_loopback/xk_216_mc/cs2100.h b/tests/test_i2s_loopback/xk_216_mc/cs2100.h similarity index 93% rename from tests/app_test_i2s_loopback/xk_216_mc/cs2100.h rename to tests/test_i2s_loopback/xk_216_mc/cs2100.h index bc5a0cdd..9e2938a7 100644 --- a/tests/app_test_i2s_loopback/xk_216_mc/cs2100.h +++ b/tests/test_i2s_loopback/xk_216_mc/cs2100.h @@ -1,4 +1,4 @@ -// Copyright 2018-2021 XMOS LIMITED. +// Copyright 2018-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #define CS2100_I2C_DEVICE_ADDR (0x9c>>1) diff --git a/examples/AN00246_xua_example/src/cs4384.h b/tests/test_i2s_loopback/xk_216_mc/cs4384.h similarity index 96% rename from examples/AN00246_xua_example/src/cs4384.h rename to tests/test_i2s_loopback/xk_216_mc/cs4384.h index 6de83e81..34ccbc6c 100644 --- a/examples/AN00246_xua_example/src/cs4384.h +++ b/tests/test_i2s_loopback/xk_216_mc/cs4384.h @@ -1,4 +1,4 @@ -// Copyright 2017-2021 XMOS LIMITED. +// Copyright 2017-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #ifndef CS4384_H_ #define CS4384_H_ diff --git a/tests/app_test_i2s_loopback/xk_216_mc/cs5368.h b/tests/test_i2s_loopback/xk_216_mc/cs5368.h similarity index 93% rename from tests/app_test_i2s_loopback/xk_216_mc/cs5368.h rename to tests/test_i2s_loopback/xk_216_mc/cs5368.h index d2de278d..ce7ed892 100644 --- a/tests/app_test_i2s_loopback/xk_216_mc/cs5368.h +++ b/tests/test_i2s_loopback/xk_216_mc/cs5368.h @@ -1,4 +1,4 @@ -// Copyright 2017-2021 XMOS LIMITED. +// Copyright 2017-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #ifndef _CS5368_H_ #define _CS5368_H_ diff --git a/tests/app_test_i2s_loopback/xk_216_mc/gpio_access.c b/tests/test_i2s_loopback/xk_216_mc/gpio_access.c similarity index 96% rename from tests/app_test_i2s_loopback/xk_216_mc/gpio_access.c rename to tests/test_i2s_loopback/xk_216_mc/gpio_access.c index 6d302c55..64f31c44 100644 --- a/tests/app_test_i2s_loopback/xk_216_mc/gpio_access.c +++ b/tests/test_i2s_loopback/xk_216_mc/gpio_access.c @@ -1,4 +1,4 @@ -// Copyright 2016-2021 XMOS LIMITED. +// Copyright 2016-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #ifdef HARDWARE #include diff --git a/tests/app_test_i2s_loopback/xk_216_mc/gpio_access.h b/tests/test_i2s_loopback/xk_216_mc/gpio_access.h similarity index 98% rename from tests/app_test_i2s_loopback/xk_216_mc/gpio_access.h rename to tests/test_i2s_loopback/xk_216_mc/gpio_access.h index 93f66fee..3856619b 100644 --- a/tests/app_test_i2s_loopback/xk_216_mc/gpio_access.h +++ b/tests/test_i2s_loopback/xk_216_mc/gpio_access.h @@ -1,4 +1,4 @@ -// Copyright 2016-2021 XMOS LIMITED. +// Copyright 2016-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. #ifndef __gpio_access_h__ #define __gpio_access_h__ diff --git a/tests/app_test_i2s_loopback/xk_216_mc/xk-audio-216-mc.xn b/tests/test_i2s_loopback/xk_216_mc/xk-audio-216-mc.xn similarity index 93% rename from tests/app_test_i2s_loopback/xk_216_mc/xk-audio-216-mc.xn rename to tests/test_i2s_loopback/xk_216_mc/xk-audio-216-mc.xn index a9f765b0..cffd732f 100644 --- a/tests/app_test_i2s_loopback/xk_216_mc/xk-audio-216-mc.xn +++ b/tests/test_i2s_loopback/xk_216_mc/xk-audio-216-mc.xn @@ -83,10 +83,12 @@ - + + + diff --git a/tests/app_test_i2s_loopback/xua_conf.h b/tests/test_i2s_loopback/xua_conf.h similarity index 74% rename from tests/app_test_i2s_loopback/xua_conf.h rename to tests/test_i2s_loopback/xua_conf.h index bec236b1..dc381a65 100644 --- a/tests/app_test_i2s_loopback/xua_conf.h +++ b/tests/test_i2s_loopback/xua_conf.h @@ -1,17 +1,24 @@ -// Copyright 2016-2021 XMOS LIMITED. +// Copyright 2016-2022 XMOS LIMITED. // This Software is subject to the terms of the XMOS Public Licence: Version 1. -#ifndef __custom_defines_h__ -#define __custom_defines_h__ +#ifndef _XUA_CONF_H_ +#define _XUA_CONF_H_ #define EXCLUDE_USB_AUDIO_MAIN -#define NUM_PDM_MICS 0 +#define XUA_NUM_PDM_MICS 0 #define XUD_TILE 1 #define AUDIO_IO_TILE 0 #define MIXER 0 + +#ifndef MCLK_441 #define MCLK_441 (512 * 44100) +#endif + +#ifndef MCLK_48 #define MCLK_48 (512 * 48000) -#define MIN_FREQ 44100 -#define MAX_FREQ 192000 +#endif + +#define MIN_FREQ (44100) +#define MAX_FREQ (192000) #define SPDIF_TX_INDEX 0 #define VENDOR_STR "XMOS" #define VENDOR_ID 0x20B1 @@ -26,4 +33,4 @@ #define MIC_DUAL_ENABLED 1 //Use single thread, dual PDM mic #define XUA_MIC_FRAME_SIZE 240 -#endif // __custom_defines_h__ +#endif diff --git a/tests/test_mixer_routing_input.py b/tests/test_mixer_routing_input.py new file mode 100644 index 00000000..7550c78b --- /dev/null +++ b/tests/test_mixer_routing_input.py @@ -0,0 +1,45 @@ +# Copyright 2023 XMOS LIMITED. +# This Software is subject to the terms of the XMOS Public Licence: Version 1. +import pytest +import Pyxsim +from Pyxsim import testers +import os +import sys + + +def do_test(options, capfd, test_file, test_seed): + + testname, _ = os.path.splitext(os.path.basename(test_file)) + + binary = f"{testname}/bin/{testname}.xe" + + tester = testers.ComparisonTester(open("pass.expect")) + + max_cycles = 15000000 + + simargs = [ + "--max-cycles", + str(max_cycles), + ] + + build_options = [] + build_options += ["TEST_SEED=" + str(test_seed)] + + result = Pyxsim.run_on_simulator( + binary, + tester=tester, + build_options=build_options, + simargs=simargs, + capfd=capfd, + instTracing=options.enabletracing, + vcdTracing=options.enablevcdtracing, + ) + + return result + + +def test_mixer_routing_input(options, capfd, test_file, test_seed): + + result = do_test(options, capfd, test_file, test_seed) + + assert result diff --git a/tests/test_mixer_routing_input/Makefile b/tests/test_mixer_routing_input/Makefile new file mode 100644 index 00000000..f8e3a49d --- /dev/null +++ b/tests/test_mixer_routing_input/Makefile @@ -0,0 +1,19 @@ + +DEBUG ?= 0 + +ifeq ($(DEBUG),1) +TEST_DEBUG_FLAGS = -g -DDEBUG_PRINT_ENABLE=1 +else +TEST_DEBUG_FLAGS = +endif + +TEST_FLAGS = -DTEST_SEED=$(TEST_SEED) $(TEST_DEBUG_FLAGS) + +XCC_FLAGS = -O3 $(TEST_FLAGS) + +TARGET = test_xs3_600.xn + +USED_MODULES = lib_xua lib_logging lib_random + +XMOS_MAKE_PATH ?= ../.. +-include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common diff --git a/tests/test_mixer_routing_input/src/main.xc b/tests/test_mixer_routing_input/src/main.xc new file mode 100644 index 00000000..90bc7216 --- /dev/null +++ b/tests/test_mixer_routing_input/src/main.xc @@ -0,0 +1,243 @@ +// Copyright 2022-2023 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +/* Tests that routing of mixer inputs behaves as expected + * + * The device supports MAX_MIX_COUNT mixers each with MIX_INPUTS inputs. + * + * This test also assumes/checks that the default routing into each of the MIX_INPUTS inputs into + * each of the M mixer units is as follows: + * + * MIXER[0]: + * USB_FROM_HOST[0] -> MIXER[0].INPUT[0] + * USB_FROM_HOST[1] -> MIXER[0].INPUT[1] + * ... + USB_TO_HOST[0] -> MIXER[0].INPUT[NUM_USB_CHAN_OUT] + USB_TO_HOST[1] -> MIXER[0].INPUT[NUM_USB_CHAN_OUT+1] + ... + + * MIXER[MAX_MIX_COUNT-1]: + * USB_FROM_HOST[0] -> MIXER[MAX_MIX_COUNT-1].INPUT[0] + * USB_FROM_HOST[1] -> MIXER[MAX_MIX_COUNT-1].INPUT[1] + * ... + * +*/ +#include +#include +#include "platform.h" +#include "xua.h" +#include "debug_print.h" +#include "assert.h" +#include "random.h" + +#ifndef TEST_ITERATIONS +#define TEST_ITERATIONS (300) +#endif + +#include "./../test_mixer_routing_output/src/mixer_test_shared.h" + +struct ModelMixer +{ + uint32_t deviceMap[NUM_USB_CHAN_OUT]; + uint32_t hostMap[NUM_USB_CHAN_IN]; + uint32_t mixMap_input[MAX_MIX_COUNT]; + uint32_t mixMap_src[MAX_MIX_COUNT]; + uint32_t mixOutput[MAX_MIX_COUNT]; +}; + +void InitModel(struct ModelMixer &modelMixer) +{ + for(size_t i = 0; i < NUM_USB_CHAN_OUT; i++) + { + modelMixer.deviceMap[i] = i; + } + + for(size_t i = 0; i < NUM_USB_CHAN_IN; i++) + { + modelMixer.hostMap[i] = NUM_USB_CHAN_OUT+i; + } + + for(size_t i = 0; i < MAX_MIX_COUNT; i++) + { + // This test only allows for one "active" input to each mixer + modelMixer.mixMap_src[i] = i; + modelMixer.mixMap_input[i] = i; + + uint32_t sample = i; + SET_SOURCE(sample, SRC_HOST); + SET_CHANNEL(sample, i); + modelMixer.mixOutput[i] = sample; + } +} + +void GenExpectedSamples(struct ModelMixer &modelMixer, + uint32_t modelOut[NUM_USB_CHAN_OUT], + uint32_t modelIn[NUM_USB_CHAN_IN]) +{ + /* First generate model mix outputs - run MIX tiles to allow mix inputs derived from mix outputs to propagate */ + for(int j = 0; j < MAX_MIX_COUNT; j++) + { + for(size_t i = 0; i < MAX_MIX_COUNT; i++) + { + int src = modelMixer.mixMap_src[i]; + modelMixer.mixOutput[i] = CreateSample(modelMixer.mixOutput, src); + } + } + + for(size_t i = 0; i + + + tileref tile[2] + + + + + + + + + + + + + + + + + + diff --git a/tests/test_mixer_routing_input/src/xua_conf.h b/tests/test_mixer_routing_input/src/xua_conf.h new file mode 100644 index 00000000..c27f54a6 --- /dev/null +++ b/tests/test_mixer_routing_input/src/xua_conf.h @@ -0,0 +1,45 @@ +// Copyright 2016-2023 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#ifndef _XUA_CONF_H_ +#define _XUA_CONF_H_ + +#define NUM_USB_CHAN_OUT (10) +#define NUM_USB_CHAN_IN (10) +#define I2S_CHANS_DAC (10) +#define I2S_CHANS_ADC (10) + +#define EXCLUDE_USB_AUDIO_MAIN + +#define MIXER (1) +#define MAX_MIX_COUNT (8) + +#define UAC_FORCE_FEEDBACK_EP (0) +#define XUA_NUM_PDM_MICS 0 +#define XUD_TILE 1 +#define AUDIO_IO_TILE 0 + +#ifndef MCLK_441 +#define MCLK_441 (512 * 44100) +#endif + +#ifndef MCLK_48 +#define MCLK_48 (512 * 48000) +#endif + +#define MIN_FREQ (44100) +#define MAX_FREQ (192000) +#define SPDIF_TX_INDEX 0 +#define VENDOR_STR "XMOS" +#define VENDOR_ID 0x20B1 +#define PRODUCT_STR_A2 "Test device" +#define PRODUCT_STR_A1 "Test device" +#define PID_AUDIO_1 1 +#define PID_AUDIO_2 2 +#define AUDIO_CLASS 2 +#define AUDIO_CLASS_FALLBACK 0 +#define BCD_DEVICE 0x1234 +#define XUA_DFU_EN 0 +#define MIC_DUAL_ENABLED 1 //Use single thread, dual PDM mic +#define XUA_MIC_FRAME_SIZE 240 + +#endif diff --git a/tests/test_mixer_routing_input_ctrl.py b/tests/test_mixer_routing_input_ctrl.py new file mode 100644 index 00000000..d867c42e --- /dev/null +++ b/tests/test_mixer_routing_input_ctrl.py @@ -0,0 +1,45 @@ +# Copyright 2023 XMOS LIMITED. +# This Software is subject to the terms of the XMOS Public Licence: Version 1. +import pytest +import Pyxsim +from Pyxsim import testers +import os +import sys + + +def do_test(options, capfd, test_file, test_seed): + + testname, _ = os.path.splitext(os.path.basename(test_file)) + + binary = f"{testname}/bin/{testname}.xe" + + tester = testers.ComparisonTester(open("pass.expect")) + + max_cycles = 15000000 + + simargs = [ + "--max-cycles", + str(max_cycles), + ] + + build_options = [] + build_options += ["TEST_SEED=" + str(test_seed)] + + result = Pyxsim.run_on_simulator( + binary, + tester=tester, + build_options=build_options, + simargs=simargs, + capfd=capfd, + instTracing=options.enabletracing, + vcdTracing=options.enablevcdtracing, + ) + + return result + + +def test_mixer_routing_output(options, capfd, test_file, test_seed): + + result = do_test(options, capfd, test_file, test_seed) + + assert result diff --git a/tests/test_mixer_routing_input_ctrl/Makefile b/tests/test_mixer_routing_input_ctrl/Makefile new file mode 100644 index 00000000..a66c3717 --- /dev/null +++ b/tests/test_mixer_routing_input_ctrl/Makefile @@ -0,0 +1,19 @@ + +DEBUG ?= 0 + +ifeq ($(DEBUG),1) +TEST_DEBUG_FLAGS = -g -DDEBUG_PRINT_ENABLE=1 +else +TEST_DEBUG_FLAGS = +endif + +TEST_FLAGS = -DTEST_SEED=$(TEST_SEED) $(TEST_DEBUG_FLAGS) -DXUD_WEAK_API=1 + +XCC_FLAGS = -O3 $(TEST_FLAGS) + +TARGET = test_xs3_600.xn + +USED_MODULES = lib_xua lib_logging lib_random + +XMOS_MAKE_PATH ?= ../.. +-include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common diff --git a/tests/test_mixer_routing_input_ctrl/src/main.xc b/tests/test_mixer_routing_input_ctrl/src/main.xc new file mode 100644 index 00000000..19df35e9 --- /dev/null +++ b/tests/test_mixer_routing_input_ctrl/src/main.xc @@ -0,0 +1,200 @@ +// Copyright 2022-2023 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +/* This tests checks the parsing of control requests to endpoint 0 cause the correct changes in mixer output routing */ + +#include +#include +#include "platform.h" +#include "xua.h" +#include "debug_print.h" +#include "assert.h" +#include "xud.h" +#include "usbaudio20.h" +#include "random.h" + +#ifndef TEST_ITERATIONS +#define TEST_ITERATIONS (100) +#endif + +#include "./../test_mixer_routing_output/src/mixer_test_shared.h" + +/* Mixer input mapping - from xua_endpoint0.c */ +extern unsigned char mixSel[MAX_MIX_COUNT][MIX_INPUTS]; + +/* From xua_ep0_uacreqs.xc */ +int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, chanend ?c_audioControl, chanend ?c_mix_ctl, chanend ?c_clk_ctl); + +/* From xua_endpoint0.c */ +void InitLocalMixerState(); + +int g_src = 0; + +/* Override func in lib_xud/src/user/client/XUD_EpFunctions.c for testing purposes */ +XUD_Result_t XUD_GetBuffer(XUD_ep ep_out, unsigned char buffer[], REFERENCE_PARAM(unsigned, length)) +{ + buffer[0] = g_src; + return XUD_RES_OKAY; +} + +XUD_Result_t XUD_DoSetRequestStatus(XUD_ep ep_in) +{ + return XUD_RES_OKAY; +} + +XUD_Result_t XUD_SetBuffer_EpMax(XUD_ep ep_in, unsigned char buffer[], unsigned datalength, unsigned epMax) +{ + assert(g_src == buffer[0]); + assert(datalength == 1); + return XUD_RES_OKAY; +} + +unsafe +{ + extern int volatile * const unsafe samples_to_device_map; + extern int volatile * const unsafe samples_to_host_map; +} + +void Fake_Endpoint0(chanend c_mix_ctl) +{ + XUD_ep ep0_out; /* Never initialised but not used */ + XUD_ep ep0_in; /* Never initialised but not used */ + USB_SetupPacket_t sp; + + random_generator_t rg = random_create_generator_from_seed(TEST_SEED); + + InitLocalMixerState(); + + sp.bmRequestType.Type = USB_BM_REQTYPE_TYPE_CLASS; // Note, parsing of this won't be tested since we call AudioClassRequests directly + sp.bmRequestType.Recipient = USB_BM_REQTYPE_RECIP_INTER; // Note, parsing of this won't be tested since we call AudioClassRequests directly + + for(int testIter = 0; testIter < TEST_ITERATIONS; testIter++) + { + int unitId = ID_XU_MIXSEL; + unsigned mix = (random_get_random_number(rg) % (MAX_MIX_COUNT + 1)); // Mixs indexed from 1 + unsigned input = random_get_random_number(rg) % MIX_INPUTS; + + /* Note, we don't currently support a mix input dervived from another mix + * This is not trivial to test since the current mixer implementation only allows for one + * config update per "trigger" + */ + unsigned src = random_get_random_number(rg) % (NUM_USB_CHAN_IN + NUM_USB_CHAN_OUT); + + debug_printf("Mapping mix %d input %d", mix, input); + debug_printf(" from %d", src); + PrintSourceString(src); + debug_printf("\n"); + + /* Create Control request data for routing change */ + int cs = mix; + int cn = input; + sp.bmRequestType.Direction = USB_BM_REQTYPE_DIRECTION_H2D; + sp.bRequest = CUR; + sp.wValue = cn | (cs << 8); + sp.wIndex = (unitId << 8); + sp.wLength = 1; + + g_src = src; /* This will get picked up by out implementation of XUD_GetBuffer */ + + /* Call the function used by Endpoint0 to parse the control data and update the mixer output routing */ + AudioClassRequests_2(ep0_out, ep0_in, sp, null, c_mix_ctl, null); + + /* Note, there is a race risk here. This could be resolved by adding a handshake to UpdateMixerOutputRouting() etc */ + + /* Now check the mixer setting have been modified as expected. To do this we inspect "internal" + * mixer and endpoint 0 state. + * + * Going forward we might wish to enhance the mixer API such that it can be tested as a black box. + * This would require the addition of "GET" API over it's ctrl channel + */ + + sp.bmRequestType.Direction = USB_BM_REQTYPE_DIRECTION_D2H; + + if(mix == 0) + { + /* If mix is 0 then we need to check that all mixers have been updated */ + for(int i = 0; i < MAX_MIX_COUNT; i++) + { + assert(g_src == mixSel[i][cn]); + + /* Need to read back from each mixer individually */ + sp.wValue = cn | ((i + 1)<< 8); + AudioClassRequests_2(ep0_out, ep0_in, sp, null, c_mix_ctl, null); + } + } + else + { + assert(g_src == mixSel[cs-1][cn]); + + /* Test read back. Note, the checking is in our overridden implementation of XUD_SetBuffer_EpMax*/ + AudioClassRequests_2(ep0_out, ep0_in, sp, null, c_mix_ctl, null); + } + + } + + printstrln("PASS"); + exit(0); +} + +void Fake_XUA_AudioHub_CtrlTest(chanend c_mix_aud) +{ + int readBuffNo = 0; + unsigned underflowWord = 0; + + /* Continually send/receive samples to/from mixer, no checking of samples since this is purely a control test */ + while(1) + { + unsigned command = DoSampleTransfer(c_mix_aud, readBuffNo, underflowWord); + } +} + +void Fake_XUA_Buffer_Decouple_CtrlTest(chanend c_dec_mix) +{ + unsigned samplesIn[NUM_USB_CHAN_IN]; + unsigned underflowSample; + + /* Continually send/receive samples to/from mixer, no checking of samples since this is purely a control test */ + while(1) + { + select + { + case inuint_byref(c_dec_mix, underflowSample): + + for(int i = 0; i < NUM_USB_CHAN_OUT; i++) + { + outuint(c_dec_mix, 0); + } + + for(int i = 0; i < NUM_USB_CHAN_IN; i++) + { + samplesIn[i] = inuint(c_dec_mix); + } + + break; + } + } +} + +int main() +{ + chan c_dec_mix; + chan c_mix_aud; + chan c_mix_ctl; + + par + { + /* We need "fake" versions of the AudioHub and Decouple to keep the mixer running and taking updates via + * it's control channel */ + Fake_XUA_Buffer_Decouple_CtrlTest(c_dec_mix); + Fake_XUA_AudioHub_CtrlTest(c_mix_aud); + + /* Mixer from lib_xua */ + mixer(c_dec_mix, c_mix_aud, c_mix_ctl); + + Fake_Endpoint0(c_mix_ctl); + } + + /* TODO to hit this we need to fully close down i.e. kill mixer */ + return 0; +} + diff --git a/tests/test_mixer_routing_input_ctrl/src/test_xs3_600.xn b/tests/test_mixer_routing_input_ctrl/src/test_xs3_600.xn new file mode 100644 index 00000000..20e8eeb6 --- /dev/null +++ b/tests/test_mixer_routing_input_ctrl/src/test_xs3_600.xn @@ -0,0 +1,24 @@ + + + + tileref tile[2] + + + + + + + + + + + + + + + + + + diff --git a/tests/test_mixer_routing_input_ctrl/src/xua_conf.h b/tests/test_mixer_routing_input_ctrl/src/xua_conf.h new file mode 100644 index 00000000..c27f54a6 --- /dev/null +++ b/tests/test_mixer_routing_input_ctrl/src/xua_conf.h @@ -0,0 +1,45 @@ +// Copyright 2016-2023 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#ifndef _XUA_CONF_H_ +#define _XUA_CONF_H_ + +#define NUM_USB_CHAN_OUT (10) +#define NUM_USB_CHAN_IN (10) +#define I2S_CHANS_DAC (10) +#define I2S_CHANS_ADC (10) + +#define EXCLUDE_USB_AUDIO_MAIN + +#define MIXER (1) +#define MAX_MIX_COUNT (8) + +#define UAC_FORCE_FEEDBACK_EP (0) +#define XUA_NUM_PDM_MICS 0 +#define XUD_TILE 1 +#define AUDIO_IO_TILE 0 + +#ifndef MCLK_441 +#define MCLK_441 (512 * 44100) +#endif + +#ifndef MCLK_48 +#define MCLK_48 (512 * 48000) +#endif + +#define MIN_FREQ (44100) +#define MAX_FREQ (192000) +#define SPDIF_TX_INDEX 0 +#define VENDOR_STR "XMOS" +#define VENDOR_ID 0x20B1 +#define PRODUCT_STR_A2 "Test device" +#define PRODUCT_STR_A1 "Test device" +#define PID_AUDIO_1 1 +#define PID_AUDIO_2 2 +#define AUDIO_CLASS 2 +#define AUDIO_CLASS_FALLBACK 0 +#define BCD_DEVICE 0x1234 +#define XUA_DFU_EN 0 +#define MIC_DUAL_ENABLED 1 //Use single thread, dual PDM mic +#define XUA_MIC_FRAME_SIZE 240 + +#endif diff --git a/tests/test_mixer_routing_output.py b/tests/test_mixer_routing_output.py new file mode 100644 index 00000000..d867c42e --- /dev/null +++ b/tests/test_mixer_routing_output.py @@ -0,0 +1,45 @@ +# Copyright 2023 XMOS LIMITED. +# This Software is subject to the terms of the XMOS Public Licence: Version 1. +import pytest +import Pyxsim +from Pyxsim import testers +import os +import sys + + +def do_test(options, capfd, test_file, test_seed): + + testname, _ = os.path.splitext(os.path.basename(test_file)) + + binary = f"{testname}/bin/{testname}.xe" + + tester = testers.ComparisonTester(open("pass.expect")) + + max_cycles = 15000000 + + simargs = [ + "--max-cycles", + str(max_cycles), + ] + + build_options = [] + build_options += ["TEST_SEED=" + str(test_seed)] + + result = Pyxsim.run_on_simulator( + binary, + tester=tester, + build_options=build_options, + simargs=simargs, + capfd=capfd, + instTracing=options.enabletracing, + vcdTracing=options.enablevcdtracing, + ) + + return result + + +def test_mixer_routing_output(options, capfd, test_file, test_seed): + + result = do_test(options, capfd, test_file, test_seed) + + assert result diff --git a/tests/test_mixer_routing_output/Makefile b/tests/test_mixer_routing_output/Makefile new file mode 100644 index 00000000..f8e3a49d --- /dev/null +++ b/tests/test_mixer_routing_output/Makefile @@ -0,0 +1,19 @@ + +DEBUG ?= 0 + +ifeq ($(DEBUG),1) +TEST_DEBUG_FLAGS = -g -DDEBUG_PRINT_ENABLE=1 +else +TEST_DEBUG_FLAGS = +endif + +TEST_FLAGS = -DTEST_SEED=$(TEST_SEED) $(TEST_DEBUG_FLAGS) + +XCC_FLAGS = -O3 $(TEST_FLAGS) + +TARGET = test_xs3_600.xn + +USED_MODULES = lib_xua lib_logging lib_random + +XMOS_MAKE_PATH ?= ../.. +-include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common diff --git a/tests/test_mixer_routing_output/src/main.xc b/tests/test_mixer_routing_output/src/main.xc new file mode 100644 index 00000000..b204bf8d --- /dev/null +++ b/tests/test_mixer_routing_output/src/main.xc @@ -0,0 +1,224 @@ +// Copyright 2022-2023 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +/* Tests that routing of mixer outputs behaves as expected + * + * "Outputs" from the device are to the USB host of one of the various audio interaces supported. + + * This test assumes/checks the default routing for the USB host & audio interfaces is as follows: + * + * USB_FROM_HOST[0] -> AUD_INTERFACE_OUTPUT[0] + * USB_FROM_HOST[1] -> AUD_INTERFACE_OUTPUT[1] + * ... + * USB_TO_HOST[0] <- AUD_INTERFACE_INPUT[0] + * USB_TO_HOST[1] <- AUD_INTERFACE_INPUT[1] + * ... + * + * This test also assumes/checks that the default routing into each of the MIX_INPUTS inputs into + * each of the M mixer units is as follows: + * + * MIXER[0]: + * USB_FROM_HOST[0] -> MIXER[0].INPUT[0] + * USB_FROM_HOST[1] -> MIXER[0].INPUT[1] + * ... + USB_TO_HOST[0] -> MIXER[0].INPUT[NUM_USB_CHAN_OUT] + USB_TO_HOST[1] -> MIXER[0].INPUT[NUM_USB_CHAN_OUT+1] + ... + + * MIXER[MAX_MIX_COUNT-1]: + * USB_FROM_HOST[0] -> MIXER[MAX_MIX_COUNT-1].INPUT[0] + * USB_FROM_HOST[1] -> MIXER[MAX_MIX_COUNT-1].INPUT[1] + * ... + * + * (If the number of mixer inputs > NUM_USB_CHAN_OUT then see ordering in comment regarding + * SOURCE_COUNT below) + * + * By default none of the MAX_MIX_COUNT output from the mixers are routed anywwhere, but this test ensures + * that they can be. + * + * This test assumes that none of the mixer weights are changed. + * This test does not test changing the inputs to the mixer. +*/ +#include +#include +#include "platform.h" +#include "xua.h" +#include "debug_print.h" +#include "assert.h" +#include "random.h" + +#ifndef TEST_ITERATIONS +#define TEST_ITERATIONS (100) +#endif + +#include "./mixer_test_shared.h" + +void UpdateModel(uint32_t modelOut[CHANNEL_MAP_AUD_SIZE], uint32_t modelMixerOut[MAX_MIX_COUNT], uint32_t modelIn[NUM_USB_CHAN_IN], + int map, int dst, int src) +{ + unsigned sample = 0; + if(src == (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT)) + { + SET_SOURCE(sample, SRC_OFF); + } + else if(src >= (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN)) + { + src -= (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN); + sample = modelMixerOut[src]; + } + else if (src >= NUM_USB_CHAN_IN) + { + SET_SOURCE(sample, SRC_AUDIF); + src -= NUM_USB_CHAN_OUT; + SET_CHANNEL(sample, src); + } + else + { + SET_SOURCE(sample, SRC_HOST); + SET_CHANNEL(sample, src); + } + + switch(map) + { + case SET_SAMPLES_TO_DEVICE_MAP: + modelOut[dst] = sample; + break; + + case SET_SAMPLES_TO_HOST_MAP: + modelIn[dst] = sample; + break; + + default: + assert(0); + break; + } +} + +/* This task configures the routing and maintains a model of the expected routing output + * it provides this to the Fake AudioHub and Fake Decouple tasks such that they can self check + */ +void stim(chanend c_stim_ah, chanend c_stim_de, chanend c_mix_ctl) +{ + uint32_t modelOut[CHANNEL_MAP_AUD_SIZE]; + uint32_t modelIn[CHANNEL_MAP_USB_SIZE]; + uint32_t modelMixerOut[MAX_MIX_COUNT]; + uint32_t testCmd[] = {SET_SAMPLES_TO_HOST_MAP, SET_SAMPLES_TO_DEVICE_MAP}; + + random_generator_t rg = random_create_generator_from_seed(TEST_SEED); + + /* By default the mixer should output samples from USB host unmodified + * See mixer.xc L780 + */ + for(size_t i = 0; i < MAX_MIX_COUNT; i++) + { + uint32_t sample = 0; + SET_SOURCE(sample, SRC_HOST); + SET_CHANNEL(sample, i); + modelMixerOut[i] = sample; + } + + /* Init modelOut for default routing */ + /* Default routing is USB[0] -> AUD_IF[0] etc */ + for(size_t i = 0; i < CHANNEL_MAP_AUD_SIZE; i++) + { + uint32_t sample = 0; + SET_SOURCE(sample, SRC_HOST); + SET_CHANNEL(sample, i); + modelOut[i] = sample; + } + + /* Init modelIn for default routing */ + /* Default routing is AUD_IF[0] -> USB[0] etc */ + for(size_t i = 0; i < NUM_USB_CHAN_IN; i++) + { + uint32_t sample = 0; + SET_SOURCE(sample, SRC_AUDIF); + SET_CHANNEL(sample, i); + modelIn[i] = sample; + } + + /* Check default routing */ + /* Send expected to AudioHub */ + SendTrigger(c_stim_ah, 2); + SendExpected(c_stim_ah, c_stim_de, modelOut, modelIn); + + for(int testIter = 0; testIter < TEST_ITERATIONS; testIter++) + { + /* Make a random update to the routing - route a random source to a random destination */ + unsigned map = testCmd[random_get_random_number(rg) % (sizeof(testCmd)/sizeof(testCmd[0]))]; + unsigned dst = random_get_random_number(rg) % CHANNEL_MAP_AUD_SIZE; // TODO this should be CHANNEL_MAP_USB_SIZE for SET_SAMPLES_TO_HOST_MAP + unsigned src = random_get_random_number(rg) % (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT); + + switch(map) + { + case SET_SAMPLES_TO_DEVICE_MAP: + debug_printf("Mapping output to AudioIF: %d", dst); + PrintDestString(map, dst); + debug_printf(" from %d", src); + PrintSourceString(src); + debug_printf("\n"); + + /* Update the mixer */ + SendTrigger(c_stim_ah, 1); + UpdateMixerOutputRouting(c_mix_ctl, map, dst, src); + break; + + case SET_SAMPLES_TO_HOST_MAP: + debug_printf("Mapping output to Host : %d", dst); + PrintDestString(map, dst); + debug_printf(" from %d", src); + PrintSourceString(src); + debug_printf("\n"); + + /* Update the mixer */ + SendTrigger(c_stim_ah, 1); + UpdateMixerOutputRouting(c_mix_ctl, map, dst, src); + break; + + default: + printstr("ERROR: Bad cmd in stim(): "); + printintln(map); + break; + } + + /* Update the model */ + UpdateModel(modelOut, modelMixerOut, modelIn, map, dst, src); + + SendExpected(c_stim_ah, c_stim_de, modelOut, modelIn); + + } + + /* Send kill messages to Fake AudioHub & Fake Decouple */ + outct(c_stim_ah, XS1_CT_END); + inct(c_stim_ah); + + outct(c_stim_de, XS1_CT_END); + inct(c_stim_de); + + printstrln("PASS"); + exit(0); +} + +int main() +{ + chan c_dec_mix; + chan c_mix_aud; + chan c_mix_ctl; + chan c_stim_ah; + chan c_stim_de; + + par + { + Fake_XUA_Buffer_Decouple(c_dec_mix, c_stim_de); + Fake_XUA_AudioHub(c_mix_aud, c_stim_ah); + + /* Mixer from lib_xua */ + mixer(c_dec_mix, c_mix_aud, c_mix_ctl); + + stim(c_stim_ah, c_stim_de, c_mix_ctl); + } + + /* TODO to hit this we need to fully close down i.e. kill mixer */ + return 0; +} + diff --git a/tests/test_mixer_routing_output/src/mixer_test_shared.h b/tests/test_mixer_routing_output/src/mixer_test_shared.h new file mode 100644 index 00000000..02953af3 --- /dev/null +++ b/tests/test_mixer_routing_output/src/mixer_test_shared.h @@ -0,0 +1,325 @@ +// Copyright 2023 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#ifndef TEST_SEED +#error TEST_SEED must be defined! +#endif + +/* A limitation of the design is that the number of routable output destinations cannot be larger than NUM_USB_CHAN_OUT. + * This is due to the transfer samples from Mixer to AudioHub tasks being in blocks of NUM_USB_CHAN_OUT. + * This is not normally an issue - since every physical output interface channel on the device is normally derived from a + * USB channel from the host, but it certainly is a restriction. + */ +#define CHANNEL_MAP_AUD_SIZE NUM_USB_CHAN_OUT + +#define CHANNEL_MAP_USB_SIZE NUM_USB_CHAN_IN + +/* Number of channel sources, the channel ordering is as follows + * i.e. + * [0:NUM_USB_CHAN_OUT-1] : Channels from USB Host + * [NUM_USB_CHAN_OUT:NUM_USB_CHAN_IN-1] : Channels from Audio Interfaces + * [NUM_USB_CHAN_N:MAX_MIX_COUNT-1] : Channels from Mixers + * [MAX_MIX_COUNT]: "Off" (Essentially samples always 0) + */ +/* Note, One larger for an "off" channel for mixer sources" */ +#define SOURCE_COUNT (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT + 1) + +#define SET_EXPECTED (9) +#define TRIGGER (7) + +// Test sample format: +// byte[0]: Sample counter +// byte[1]: Channel +// byte[3]: Source (HOST:1/AUD IF:0) +#define SRC_HOST (2) +#define SRC_AUDIF (1) +#define SRC_OFF (0) // Important that this is 0 since mixer will generate 0 samples for 'off' + +#define GET_COUNT(x) ((x>>8) & 0xff) +#define GET_CHANNEL(x) ((x >> 16) & 0xff) +#define GET_SOURCE(x) ((x >> 24) & 0xff) + +#define SET_COUNT(x, y) y = y & 0xff; x = x | (y<<8); +#define SET_CHANNEL(x, y) y = y & 0xff; x = x | (y<<16); +#define SET_SOURCE(x, y) x = x | (y<<24); + +void exit(int); + +#pragma select handler +static inline void testct_byref(chanend c, unsigned &isCt) +{ + isCt = testct(c); +} + +void SendTrigger(chanend c_stim_ah, int count) +{ + for(int i = 0; i < count; i++) + outuint(c_stim_ah, TRIGGER); +} + +uint32_t CreateSample(uint32_t modelMixerOutput[], int src) +{ + uint32_t sample = 0; + + if(src == (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT)) + { + SET_SOURCE(sample, SRC_OFF); + } + else if(src >= (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN)) + { + src -= (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN); + sample = modelMixerOutput[src]; + } + else if (src >= NUM_USB_CHAN_IN) + { + SET_SOURCE(sample, SRC_AUDIF); + src -= NUM_USB_CHAN_OUT; + SET_CHANNEL(sample, src); + } + else + { + SET_SOURCE(sample, SRC_HOST); + SET_CHANNEL(sample, src); + } + + return sample; +} + +void PrintSourceString(unsigned source) +{ + debug_printf(" "); + if(source < NUM_USB_CHAN_OUT) + { + debug_printf("(DEVICE IN - HOST%d)", source); + } + else if(source < (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN)) + { + debug_printf("(DEVICE IN - AudioIF %d)", source - NUM_USB_CHAN_OUT); + } + else if(source < (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT)) + { + debug_printf("(MIX %d)", source - NUM_USB_CHAN_OUT - NUM_USB_CHAN_IN); + } + else + debug_printf("(off)"); + debug_printf(" "); +} + +void PrintDestString(unsigned map, unsigned dest) +{ + switch(map) + { + case SET_SAMPLES_TO_DEVICE_MAP: + debug_printf("(DEVICE OUT - AudioIF)"); + break; + case SET_SAMPLES_TO_HOST_MAP: + debug_printf("(DEVICE OUT - HOST)"); + break; + } +} + +void PrintSample(unsigned sample) +{ + debug_printf("SOURCE: "); + if(GET_SOURCE(sample) == SRC_HOST) + debug_printf("HOST "); + else if(GET_SOURCE(sample) == SRC_AUDIF) + debug_printf("AUDIF "); + else if(GET_SOURCE(sample) == SRC_OFF) + debug_printf("OFF "); + else + debug_printf("UNKNOWN "); + + debug_printf("CHANNEL: %d", GET_CHANNEL(sample)); +} + +/* Required by lib_xua */ +void AudioHwInit() +{ + return; +} + +/* Required by lib_xua */ +void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC) +{ + return; +} + +/* From xua_ep0_uacreqs.xc */ +void UpdateMixerOutputRouting(chanend c_mix_ctl, unsigned map, unsigned dst, unsigned src); +void UpdateMixMap(chanend c_mix_ctl, int mix, int input, int src); +void UpdateMixerWeight(chanend c_mix_ctl, int mix, int index, unsigned val); + +void CheckBlock(unsigned samplesOut[], uint32_t expectedOut[], size_t len) +{ + int fail = 0;; + for(int j = 0; j < len; j++) + { + debug_printf("%d: Expected: ", j); + PrintSample(expectedOut[j]); + debug_printf("\n"); + if(expectedOut[j] != samplesOut[j]) + { + printstr("ERROR: Actual: "); + PrintSample(samplesOut[j]); + debug_printf(" (%x)", samplesOut[j]); + printstr("\n"); + fail = 1; + } + //assert(expectedOut[j] == samplesOut[j]); + } + assert(!fail); +} + +/* Sending expected also causes fake_audiohub and fake_decouple to run sample checks */ +void SendExpected(chanend c_stim_ah, chanend c_stim_de, uint32_t modelOut[], uint32_t modelIn[]) +{ + /* Send expected to AudioHub */ + outuint(c_stim_ah, SET_EXPECTED); + + for(int i = 0; i < CHANNEL_MAP_AUD_SIZE; i++) + { + outuint(c_stim_ah, modelOut[i]); + } + + /* Wait for handshake back and move on to next test */ + inuint(c_stim_ah); + + /* Send expected to Decouple */ + outuint(c_stim_de, SET_EXPECTED); + for(int i = 0; i < NUM_USB_CHAN_IN; i++) + { + outuint(c_stim_de, modelIn[i]); + } + + /* Wait for handshake back and move on to next test */ + inuint(c_stim_de); +} + + + + +/* From xua_audiohub.xc */ +extern unsigned samplesOut[NUM_USB_CHAN_OUT]; +extern unsigned samplesIn[2][NUM_USB_CHAN_IN]; +#include "xua_audiohub_st.h" + +int Fake_XUA_AudioHub(chanend c_mix_aud, chanend c_stim) +{ + int readBuffNo = 0; + unsigned underflowWord = 0; + uint32_t expectedOut[NUM_USB_CHAN_OUT]; + unsigned ct = 0; + unsigned cmd = 0; + + for(size_t i = 0; i < NUM_USB_CHAN_IN; i++) + { + /* Note, we only used readBufNo = 0 */ + unsigned sample = 0; + SET_SOURCE(sample, SRC_AUDIF); + SET_CHANNEL(sample, i); + samplesIn[0][i] = sample; + } + + while(!ct) + { + + select + { + case testct_byref(c_stim, ct): + + if(!ct) + { + cmd = inuint(c_stim); + + switch(cmd) + { + case SET_EXPECTED: + + for(int j = 0; j < NUM_USB_CHAN_OUT; j++) + { + expectedOut[j] = inuint(c_stim); + } + + debug_printf("AudioHub:\n"); + CheckBlock(samplesOut, expectedOut, NUM_USB_CHAN_OUT); + /* Handshake back */ + outuint(c_stim, 0); + break; + + case TRIGGER: + /* This will populate samplesOut and send out samplesIn[readBuffNo] */ + unsigned command = DoSampleTransfer(c_mix_aud, readBuffNo, underflowWord); + break; + + default: + printstr("ERROR: bad cmd in Fake_XUA_AudioHub: "); + printintln(cmd); + assert(0); + break; + } + } + break; + } + } + + outct(c_stim, XS1_CT_END); + inct(c_stim); + return 0; +} + +int Fake_XUA_Buffer_Decouple(chanend c_dec_mix, chanend c_stim) +{ + uint32_t expectedSamplesIn[NUM_USB_CHAN_IN]; + unsigned samplesIn[NUM_USB_CHAN_IN]; + unsigned ct; + unsigned underflowSample; + + while(!ct) + { + select + { + case inuint_byref(c_dec_mix, underflowSample): + + for(int i = 0; i < NUM_USB_CHAN_OUT; i++) + { + unsigned sample = 0; + SET_SOURCE(sample, SRC_HOST); + SET_CHANNEL(sample, i); + outuint(c_dec_mix, sample); + } + + for(int i = 0; i < NUM_USB_CHAN_IN; i++) + { + samplesIn[i] = inuint(c_dec_mix); + } + + break; + + case testct_byref(c_stim, ct): + + if(!ct) + { + inuint(c_stim); // TODO don't really need this + + /* Get expected */ + for(int j = 0; j < NUM_USB_CHAN_IN; j++) + { + expectedSamplesIn[j] = inuint(c_stim); + } + + debug_printf("Decouple:\n"); + CheckBlock(samplesIn, expectedSamplesIn, NUM_USB_CHAN_IN); + + /* Handshake back */ + outuint(c_stim, 0); + } + break; + } + } + + outct(c_stim, XS1_CT_END); + inct(c_stim); + return 0; +} + + diff --git a/tests/test_mixer_routing_output/src/test_xs3_600.xn b/tests/test_mixer_routing_output/src/test_xs3_600.xn new file mode 100644 index 00000000..20e8eeb6 --- /dev/null +++ b/tests/test_mixer_routing_output/src/test_xs3_600.xn @@ -0,0 +1,24 @@ + + + + tileref tile[2] + + + + + + + + + + + + + + + + + + diff --git a/tests/test_mixer_routing_output/src/xua_conf.h b/tests/test_mixer_routing_output/src/xua_conf.h new file mode 100644 index 00000000..c27f54a6 --- /dev/null +++ b/tests/test_mixer_routing_output/src/xua_conf.h @@ -0,0 +1,45 @@ +// Copyright 2016-2023 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#ifndef _XUA_CONF_H_ +#define _XUA_CONF_H_ + +#define NUM_USB_CHAN_OUT (10) +#define NUM_USB_CHAN_IN (10) +#define I2S_CHANS_DAC (10) +#define I2S_CHANS_ADC (10) + +#define EXCLUDE_USB_AUDIO_MAIN + +#define MIXER (1) +#define MAX_MIX_COUNT (8) + +#define UAC_FORCE_FEEDBACK_EP (0) +#define XUA_NUM_PDM_MICS 0 +#define XUD_TILE 1 +#define AUDIO_IO_TILE 0 + +#ifndef MCLK_441 +#define MCLK_441 (512 * 44100) +#endif + +#ifndef MCLK_48 +#define MCLK_48 (512 * 48000) +#endif + +#define MIN_FREQ (44100) +#define MAX_FREQ (192000) +#define SPDIF_TX_INDEX 0 +#define VENDOR_STR "XMOS" +#define VENDOR_ID 0x20B1 +#define PRODUCT_STR_A2 "Test device" +#define PRODUCT_STR_A1 "Test device" +#define PID_AUDIO_1 1 +#define PID_AUDIO_2 2 +#define AUDIO_CLASS 2 +#define AUDIO_CLASS_FALLBACK 0 +#define BCD_DEVICE 0x1234 +#define XUA_DFU_EN 0 +#define MIC_DUAL_ENABLED 1 //Use single thread, dual PDM mic +#define XUA_MIC_FRAME_SIZE 240 + +#endif diff --git a/tests/test_mixer_routing_output_ctrl.py b/tests/test_mixer_routing_output_ctrl.py new file mode 100644 index 00000000..d867c42e --- /dev/null +++ b/tests/test_mixer_routing_output_ctrl.py @@ -0,0 +1,45 @@ +# Copyright 2023 XMOS LIMITED. +# This Software is subject to the terms of the XMOS Public Licence: Version 1. +import pytest +import Pyxsim +from Pyxsim import testers +import os +import sys + + +def do_test(options, capfd, test_file, test_seed): + + testname, _ = os.path.splitext(os.path.basename(test_file)) + + binary = f"{testname}/bin/{testname}.xe" + + tester = testers.ComparisonTester(open("pass.expect")) + + max_cycles = 15000000 + + simargs = [ + "--max-cycles", + str(max_cycles), + ] + + build_options = [] + build_options += ["TEST_SEED=" + str(test_seed)] + + result = Pyxsim.run_on_simulator( + binary, + tester=tester, + build_options=build_options, + simargs=simargs, + capfd=capfd, + instTracing=options.enabletracing, + vcdTracing=options.enablevcdtracing, + ) + + return result + + +def test_mixer_routing_output(options, capfd, test_file, test_seed): + + result = do_test(options, capfd, test_file, test_seed) + + assert result diff --git a/tests/test_mixer_routing_output_ctrl/Makefile b/tests/test_mixer_routing_output_ctrl/Makefile new file mode 100644 index 00000000..a66c3717 --- /dev/null +++ b/tests/test_mixer_routing_output_ctrl/Makefile @@ -0,0 +1,19 @@ + +DEBUG ?= 0 + +ifeq ($(DEBUG),1) +TEST_DEBUG_FLAGS = -g -DDEBUG_PRINT_ENABLE=1 +else +TEST_DEBUG_FLAGS = +endif + +TEST_FLAGS = -DTEST_SEED=$(TEST_SEED) $(TEST_DEBUG_FLAGS) -DXUD_WEAK_API=1 + +XCC_FLAGS = -O3 $(TEST_FLAGS) + +TARGET = test_xs3_600.xn + +USED_MODULES = lib_xua lib_logging lib_random + +XMOS_MAKE_PATH ?= ../.. +-include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common diff --git a/tests/test_mixer_routing_output_ctrl/src/main.xc b/tests/test_mixer_routing_output_ctrl/src/main.xc new file mode 100644 index 00000000..5e393088 --- /dev/null +++ b/tests/test_mixer_routing_output_ctrl/src/main.xc @@ -0,0 +1,221 @@ +// Copyright 2022-2023 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +/* This tests checks the parsing of control requests to endpoint 0 cause the correct changes in mixer output routing */ + +#include +#include +#include "platform.h" +#include "xua.h" +#include "debug_print.h" +#include "assert.h" +#include "xud.h" +#include "usbaudio20.h" +#include "random.h" + +#ifndef TEST_ITERATIONS +#define TEST_ITERATIONS (100) +#endif + +#include "./../test_mixer_routing_output/src/mixer_test_shared.h" + +/* Device channel mapping */ +extern unsigned char channelMapAud[NUM_USB_CHAN_OUT]; +extern unsigned char channelMapUsb[NUM_USB_CHAN_IN]; + +/* From xua_ep0_uacreqs.xc */ +int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, USB_SetupPacket_t &sp, chanend ?c_audioControl, chanend ?c_mix_ctl, chanend ?c_clk_ctl); + +/* From xua_endpoint0.c */ +void InitLocalMixerState(); + +int g_src = 0; + +/* Override func in lib_xud/src/user/client/XUD_EpFunctions.c for testing purposes */ +XUD_Result_t XUD_GetBuffer(XUD_ep ep_out, unsigned char buffer[], REFERENCE_PARAM(unsigned, length)) +{ + buffer[0] = g_src; + return XUD_RES_OKAY; +} + +XUD_Result_t XUD_DoSetRequestStatus(XUD_ep ep_in) +{ + return XUD_RES_OKAY; +} + +XUD_Result_t XUD_SetBuffer_EpMax(XUD_ep ep_in, unsigned char buffer[], unsigned datalength, unsigned epMax) +{ + assert(g_src == buffer[0]); + assert(datalength == 1); + return XUD_RES_OKAY; +} + +unsafe +{ + extern int volatile * const unsafe samples_to_device_map; + extern int volatile * const unsafe samples_to_host_map; +} + +void Fake_Endpoint0(chanend c_mix_ctl) +{ + XUD_ep ep0_out; /* Never initialised but not used */ + XUD_ep ep0_in; /* Never initialised but not used */ + unsigned unitIds[] = {ID_XU_OUT, ID_XU_IN}; + USB_SetupPacket_t sp; + + random_generator_t rg = random_create_generator_from_seed(TEST_SEED); + + InitLocalMixerState(); + + sp.bmRequestType.Type = USB_BM_REQTYPE_TYPE_CLASS; // Note, parsing of this won't be tested since we call AudioClassRequests directly + sp.bmRequestType.Recipient = USB_BM_REQTYPE_RECIP_INTER; // Note, parsing of this won't be tested since we call AudioClassRequests directly + + for(int testIter = 0; testIter < TEST_ITERATIONS; testIter++) + { + int unitId = unitIds[random_get_random_number(rg) % (sizeof(unitIds)/sizeof(unitIds[0]))]; + unsigned dst = random_get_random_number(rg); + + /* Note, we don't currently support a mix input derived from another mix + * This is not trivial to test since the current mixer implementation only allows for one + * config update per "trigger" + */ + int src = random_get_random_number(rg) % NUM_USB_CHAN_IN + NUM_USB_CHAN_OUT; + + switch(unitId) + { + case ID_XU_OUT: + dst %= CHANNEL_MAP_AUD_SIZE; + debug_printf("Mapping output to AudioIF: %d", dst); + debug_printf(" from %d", src); + PrintSourceString(src); + debug_printf("\n"); + break; + + case ID_XU_IN: + dst %= CHANNEL_MAP_USB_SIZE; + debug_printf("Mapping output to Host : %d", dst); + debug_printf(" from %d", src); + PrintSourceString(src); + debug_printf("\n"); + break; + + default: + printstr("ERROR: Bad cmd in stim(): "); + printintln(unitId); + break; + } + + /* Create Control request data for routing change */ + sp.bmRequestType.Direction = USB_BM_REQTYPE_DIRECTION_H2D; + sp.bRequest = CUR; + sp.wValue = dst & 0xff; + sp.wIndex = (unitId << 8); + sp.wLength = 1; + + g_src = src; /* This will get picked up by out implementation of XUD_GetBuffer */ + + /* Call the function used by Endpoint0() to parse the control data and update the mixer output routing */ + AudioClassRequests_2(ep0_out, ep0_in, sp, null, c_mix_ctl, null); + + /* Note, there is a race risk here. This could be resolved by adding a handshake to UpdateMixerOutputRouting() etc */ + + /* Now check the mixer setting have been modified as expected. To do this we inspect "internal" + * mixer and endpoint 0 state. + * + * Going forward we might wish to enhance the mixer API such that it can be tested as a black box. + * This would require the addition of "GET" API over it's ctrl channel + */ + switch(unitId) + { + case ID_XU_OUT: + assert(g_src == channelMapAud[dst]); + unsafe + { + assert(g_src == samples_to_device_map[dst]); + } + break; + + case ID_XU_IN: + assert(g_src == channelMapUsb[dst]); + unsafe + { + assert(g_src == samples_to_host_map[dst]); + } + break; + + default: + assert(0); + break; + } + + /* Test read back. Note, the checking is our overridden implementation of XUD_SetBuffer_EpMax*/ + sp.bmRequestType.Direction = USB_BM_REQTYPE_DIRECTION_D2H; + AudioClassRequests_2(ep0_out, ep0_in, sp, null, c_mix_ctl, null); + } + + printstrln("PASS"); + exit(0); +} + +void Fake_XUA_AudioHub_CtrlTest(chanend c_mix_aud) +{ + int readBuffNo = 0; + unsigned underflowWord = 0; + + /* Continually send/receive samples to/from mixer, no checking of samples since this is purely a control test */ + while(1) + { + unsigned command = DoSampleTransfer(c_mix_aud, readBuffNo, underflowWord); + } +} + +void Fake_XUA_Buffer_Decouple_CtrlTest(chanend c_dec_mix) +{ + unsigned samplesIn[NUM_USB_CHAN_IN]; + unsigned underflowSample; + + /* Continually send/receive samples to/from mixer, no checking of samples since this is purely a control test */ + while(1) + { + select + { + case inuint_byref(c_dec_mix, underflowSample): + + for(int i = 0; i < NUM_USB_CHAN_OUT; i++) + { + outuint(c_dec_mix, 0); + } + + for(int i = 0; i < NUM_USB_CHAN_IN; i++) + { + samplesIn[i] = inuint(c_dec_mix); + } + + break; + } + } +} + +int main() +{ + chan c_dec_mix; + chan c_mix_aud; + chan c_mix_ctl; + + par + { + /* We need "fake" versions of the AudioHub and Decouple to keep the mixer running and taking updates via + * it's control channel */ + Fake_XUA_Buffer_Decouple_CtrlTest(c_dec_mix); + Fake_XUA_AudioHub_CtrlTest(c_mix_aud); + + /* Mixer from lib_xua */ + mixer(c_dec_mix, c_mix_aud, c_mix_ctl); + + Fake_Endpoint0(c_mix_ctl); + } + + /* TODO to hit this we need to fully close down i.e. kill mixer */ + return 0; +} + diff --git a/tests/test_mixer_routing_output_ctrl/src/test_xs3_600.xn b/tests/test_mixer_routing_output_ctrl/src/test_xs3_600.xn new file mode 100644 index 00000000..20e8eeb6 --- /dev/null +++ b/tests/test_mixer_routing_output_ctrl/src/test_xs3_600.xn @@ -0,0 +1,24 @@ + + + + tileref tile[2] + + + + + + + + + + + + + + + + + + diff --git a/tests/test_mixer_routing_output_ctrl/src/xua_conf.h b/tests/test_mixer_routing_output_ctrl/src/xua_conf.h new file mode 100644 index 00000000..c27f54a6 --- /dev/null +++ b/tests/test_mixer_routing_output_ctrl/src/xua_conf.h @@ -0,0 +1,45 @@ +// Copyright 2016-2023 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#ifndef _XUA_CONF_H_ +#define _XUA_CONF_H_ + +#define NUM_USB_CHAN_OUT (10) +#define NUM_USB_CHAN_IN (10) +#define I2S_CHANS_DAC (10) +#define I2S_CHANS_ADC (10) + +#define EXCLUDE_USB_AUDIO_MAIN + +#define MIXER (1) +#define MAX_MIX_COUNT (8) + +#define UAC_FORCE_FEEDBACK_EP (0) +#define XUA_NUM_PDM_MICS 0 +#define XUD_TILE 1 +#define AUDIO_IO_TILE 0 + +#ifndef MCLK_441 +#define MCLK_441 (512 * 44100) +#endif + +#ifndef MCLK_48 +#define MCLK_48 (512 * 48000) +#endif + +#define MIN_FREQ (44100) +#define MAX_FREQ (192000) +#define SPDIF_TX_INDEX 0 +#define VENDOR_STR "XMOS" +#define VENDOR_ID 0x20B1 +#define PRODUCT_STR_A2 "Test device" +#define PRODUCT_STR_A1 "Test device" +#define PID_AUDIO_1 1 +#define PID_AUDIO_2 2 +#define AUDIO_CLASS 2 +#define AUDIO_CLASS_FALLBACK 0 +#define BCD_DEVICE 0x1234 +#define XUA_DFU_EN 0 +#define MIC_DUAL_ENABLED 1 //Use single thread, dual PDM mic +#define XUA_MIC_FRAME_SIZE 240 + +#endif diff --git a/tests/test_sync_clk_basic.py b/tests/test_sync_clk_basic.py new file mode 100644 index 00000000..cd2c99c4 --- /dev/null +++ b/tests/test_sync_clk_basic.py @@ -0,0 +1,58 @@ +# Copyright 2022 XMOS LIMITED. +# This Software is subject to the terms of the XMOS Public Licence: Version 1. +import pytest +import Pyxsim +from Pyxsim import testers +import os +import sys + + +@pytest.fixture() +def test_file(request): + return str(request.node.fspath) + + +def do_test(bus_speed, test_file, options, capfd): + + testname, _ = os.path.splitext(os.path.basename(test_file)) + + binary = f"{testname}/bin/{bus_speed}/{testname}_{bus_speed}.xe" + + tester = testers.ComparisonTester(open("pass.expect")) + + loopback_args = ( + "-port tile[0] XS1_PORT_1M 1 0 -port tile[0] XS1_PORT_1I 1 0 " + + "-port tile[0] XS1_PORT_1N 1 0 -port tile[0] XS1_PORT_1J 1 0 " + + "-port tile[0] XS1_PORT_1O 1 0 -port tile[0] XS1_PORT_1K 1 0 " + + "-port tile[0] XS1_PORT_1P 1 0 -port tile[0] XS1_PORT_1L 1 0 " + + "-port tile[0] XS1_PORT_1A 1 0 -port tile[0] XS1_PORT_1B 1 0 " + ) + + max_cycles = 15000000 # enough to reach the 10 skip + 100 test in sim at 48kHz + + simargs = [ + "--max-cycles", + str(max_cycles), + "--plugin", + "LoopbackPort.dll", + loopback_args, + ] + + result = Pyxsim.run_on_simulator( + binary, + tester=tester, + simargs=simargs, + capfd=capfd, + instTracing=options.enabletracing, + vcdTracing=options.enablevcdtracing, + ) + + return result + + +@pytest.mark.parametrize("bus_speed", ["FS", "HS"]) +def test_sync_clk_basic(bus_speed, test_file, options, capfd): + + result = do_test(bus_speed, test_file, options, capfd) + + assert result diff --git a/tests/test_sync_clk_basic/Makefile b/tests/test_sync_clk_basic/Makefile new file mode 100644 index 00000000..513f2614 --- /dev/null +++ b/tests/test_sync_clk_basic/Makefile @@ -0,0 +1,17 @@ + +TEST_FLAGS ?= + +XCC_FLAGS_HS = -O3 -g -DXUD_CORE_CLOCK=600 -save-temps -DUSB_TILE=tile[0] -DLOCAL_CLOCK_INCREMENT=10000 -DLOCAL_CLOCK_MARGIN=100 \ + -DBUS_SPEED=2 \ + $(TEST_FLAGS) + +XCC_FLAGS_FS = -O3 -g -DXUD_CORE_CLOCK=600 -save-temps -DUSB_TILE=tile[0] -DLOCAL_CLOCK_INCREMENT=10000 -DLOCAL_CLOCK_MARGIN=100 \ + -DBUS_SPEED=1 \ + $(TEST_FLAGS) + +TARGET = test_xs3_600.xn + +USED_MODULES = lib_xua + +XMOS_MAKE_PATH ?= ../.. +-include $(XMOS_MAKE_PATH)/xcommon/module_xcommon/build/Makefile.common diff --git a/tests/test_sync_clk_basic/src/main.xc b/tests/test_sync_clk_basic/src/main.xc new file mode 100644 index 00000000..d6c5b519 --- /dev/null +++ b/tests/test_sync_clk_basic/src/main.xc @@ -0,0 +1,196 @@ +// Copyright 2022 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +/* Simple test to ensure reference clock to CS2100 device continues when SOF clock not available + * Note, this test uses "nice" numbers (i.e. MISSIG_SOFS %8 == 0) and therefore doesn't check + * for a graceful change over from internal to SOF clock + */ + +#include "platform.h" +#include "xua.h" +#include "print.h" +#include "xud.h" + +#define EP_COUNT_IN (3) +#define EP_COUNT_OUT (3) + +out port p_pll_ref = XS1_PORT_1A; +in port p_off_mclk = XS1_PORT_1M; +in port p_pll_loop = XS1_PORT_1B; /* Note, this is externally looped back using the loopback plugin */ + +/* Purely for debug/viewing on VCD */ +out port p_test0 = XS1_PORT_1C; +out port p_test1 = XS1_PORT_1D; + +#ifndef BUS_SPEED +#error BUS_SPEED should be defined +#endif + +/* To speed this test up we divide all delays by 10. This is also the case for the delays in the clock generation code */ +#if(BUS_SPEED == 2) // XUD_SPEED_HS + #define SOF_PERIOD_TICKS (12500/10) + #define SOF_DIVIDE (1) +#else + #define SOF_PERIOD_TICKS ((12500*8)/10) + #define SOF_DIVIDE (8) +#endif + +#ifndef MISSING_SOF_PERIOD +/* By default skip a whole number of SOF periods (easy case) + * Note, app_test_sync_plugin/Makefiles sets this to something more nasty */ +#define MISSING_SOF_PERIOD (8 * SOF_PERIOD_TICKS) +#endif + +void exit(int); + +void delay(unsigned d) +{ + timer t; + unsigned time; + t :> time; + t when timerafter(time + d) :> int x; +} + +/* From lib_xud */ +void SetupEndpoints(chanend c_ep_out[], int noEpOut, chanend c_ep_in[], int noEpIn, XUD_EpType epTypeTableOut[], XUD_EpType epTypeTableIn[]); + +void AudioHwInit() +{ + return; +} + +void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC) +{ + return; +} + +void driveSofs(chanend c_sof, int count) +{ + static int frame = 0; + + /* For HS frame should only increment every 8 SOFs, but this is a "dont care" */ + for(int i = 0; i < count; i++) + { + outuint(c_sof, frame++); + delay(SOF_PERIOD_TICKS); + } +} + +void fake_xud(chanend c_out[], chanend c_in[], chanend c_sof) +{ + timer t; + unsigned time; + + + /* Makes traces a bit nicer to look at */ + t :> time; + t when timerafter(SOF_PERIOD_TICKS * 2) :> int x; + + p_test0 <: 1; + + /* Endpoint type tables */ + XUD_EpType epTypeTableOut[EP_COUNT_OUT] = {XUD_EPTYPE_CTL, XUD_EPTYPE_ISO, XUD_EPTYPE_DIS}; + XUD_EpType epTypeTableIn[EP_COUNT_IN] = {XUD_EPTYPE_CTL, XUD_EPTYPE_ISO, XUD_EPTYPE_ISO}; + + SetupEndpoints(c_out, EP_COUNT_OUT, c_in, EP_COUNT_IN, epTypeTableOut, epTypeTableIn); + + driveSofs(c_sof, 32/SOF_DIVIDE); + + p_test0 <: 0; + + /* Sim missing SOFs */ + delay(MISSING_SOF_PERIOD); + + p_test0 <: 1; + + driveSofs(c_sof, 16/SOF_DIVIDE); + + p_test0 <: 0; + + delay(MISSING_SOF_PERIOD); + + p_test0 <: 1; + + driveSofs(c_sof, 16/SOF_DIVIDE); + + p_test0 <: 0; + +} + +extern XUD_BusSpeed_t g_curUsbSpeed; + +#define MARGIN (1500/10) +#define EXPECTED_PERIOD (100000/10) + +void checker() +{ + timer t; + unsigned t0, t1; + unsigned x = 0; + int fail = 0; + + p_test1 <: 1; + + p_pll_loop when pinseq(1) :> x; + p_pll_loop when pinseq(0) :> x; + p_pll_loop when pinseq(1) :> x; + + for(int i = 0; i < 12; i++) + { + p_pll_loop when pinsneq(x) :> x; + t :> t0; + p_pll_loop when pinsneq(x) :> x; + t :> t1; + + int period = t1-t0; + + /* Check the period of the reference clock we are generating */ + if(period > (EXPECTED_PERIOD + MARGIN)) + { + printstr("Period too long: "); + printintln(period); + fail = 1; + } + else if(period < (EXPECTED_PERIOD - MARGIN)) + { + printstr("Period too short: "); + printintln(period); + fail = 1; + } + } + if(!fail) + printstrln("PASS"); + + p_test1 <: 0; + + exit(0); +} + + +int main() +{ + chan c_out[EP_COUNT_OUT]; + chan c_in[EP_COUNT_IN]; + chan c_sof; + chan c_aud_ctl; + + interface pll_ref_if i_pll_ref; + + par + { + PllRefPinTask(i_pll_ref, p_pll_ref); + + { + g_curUsbSpeed = BUS_SPEED; + + XUA_Buffer_Ep(c_out[1], /* USB Audio Out*/ + c_in[1], /* USB Audio In */ + c_sof, c_aud_ctl, p_off_mclk, i_pll_ref + ); + } + + fake_xud(c_out, c_in, c_sof); + + checker(); + } +} diff --git a/tests/test_sync_clk_basic/src/test_xs3_600.xn b/tests/test_sync_clk_basic/src/test_xs3_600.xn new file mode 100644 index 00000000..20e8eeb6 --- /dev/null +++ b/tests/test_sync_clk_basic/src/test_xs3_600.xn @@ -0,0 +1,24 @@ + + + + tileref tile[2] + + + + + + + + + + + + + + + + + + diff --git a/tests/test_sync_clk_basic/src/xua_conf.h b/tests/test_sync_clk_basic/src/xua_conf.h new file mode 100644 index 00000000..b5bc7ef7 --- /dev/null +++ b/tests/test_sync_clk_basic/src/xua_conf.h @@ -0,0 +1,44 @@ +// Copyright 2016-2022 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#ifndef _XUA_CONF_H_ +#define _XUA_CONF_H_ + +#define NUM_USB_CHAN_OUT (2) +#define NUM_USB_CHAN_IN (2) +#define I2S_CHANS_DAC (2) +#define I2S_CHANS_ADC (2) + +#define XUA_SYNCMODE XUA_SYNCMODE_SYNC +#define UAC_FORCE_FEEDBACK_EP (0) + +#define EXCLUDE_USB_AUDIO_MAIN +#define XUA_NUM_PDM_MICS 0 +#define XUD_TILE 1 +#define AUDIO_IO_TILE 0 +#define MIXER 0 + +#ifndef MCLK_441 +#define MCLK_441 (512 * 44100) +#endif + +#ifndef MCLK_48 +#define MCLK_48 (512 * 48000) +#endif + +#define MIN_FREQ (44100) +#define MAX_FREQ (192000) +#define SPDIF_TX_INDEX 0 +#define VENDOR_STR "XMOS" +#define VENDOR_ID 0x20B1 +#define PRODUCT_STR_A2 "Test device" +#define PRODUCT_STR_A1 "Test device" +#define PID_AUDIO_1 1 +#define PID_AUDIO_2 2 +#define AUDIO_CLASS 2 +#define AUDIO_CLASS_FALLBACK 0 +#define BCD_DEVICE 0x1234 +#define XUA_DFU_EN 0 +#define MIC_DUAL_ENABLED 1 //Use single thread, dual PDM mic +#define XUA_MIC_FRAME_SIZE 240 + +#endif diff --git a/tests/test_sync_clk_plugin.py b/tests/test_sync_clk_plugin.py new file mode 100644 index 00000000..b580ece9 --- /dev/null +++ b/tests/test_sync_clk_plugin.py @@ -0,0 +1,12 @@ +# Copyright 2022 XMOS LIMITED. +# This Software is subject to the terms of the XMOS Public Licence: Version 1. +import pytest +from test_sync_clk_basic import do_test +from test_sync_clk_basic import test_file + + +@pytest.mark.parametrize("bus_speed", ["FS", "HS"]) +def test_sync_clk_plugin(bus_speed, test_file, options, capfd): + pytest.xfail("This is a known failure due to issue #275") + result = do_test(bus_speed, test_file, options, capfd) + assert result diff --git a/tests/test_sync_clk_plugin/Makefile b/tests/test_sync_clk_plugin/Makefile new file mode 100644 index 00000000..08eec76a --- /dev/null +++ b/tests/test_sync_clk_plugin/Makefile @@ -0,0 +1,4 @@ + +TEST_FLAGS = -DMISSING_SOF_PERIOD=7500 + +include ../test_sync_clk_basic/Makefile diff --git a/tests/test_sync_clk_plugin/src/main.xc b/tests/test_sync_clk_plugin/src/main.xc new file mode 100644 index 00000000..e66994ea --- /dev/null +++ b/tests/test_sync_clk_plugin/src/main.xc @@ -0,0 +1,8 @@ +// Copyright 2022 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +/* This tests for a smooth transition from internal to SOF clock. + * Unlike test_sync_clk_basic it does not use a "nice" value for MISSING_SOFS + */ + +#include "../test_sync_clk_basic/src/main.xc" diff --git a/tests/test_sync_clk_plugin/src/test_xs3_600.xn b/tests/test_sync_clk_plugin/src/test_xs3_600.xn new file mode 100644 index 00000000..20e8eeb6 --- /dev/null +++ b/tests/test_sync_clk_plugin/src/test_xs3_600.xn @@ -0,0 +1,24 @@ + + + + tileref tile[2] + + + + + + + + + + + + + + + + + + diff --git a/tests/test_sync_clk_plugin/src/xua_conf.h b/tests/test_sync_clk_plugin/src/xua_conf.h new file mode 100644 index 00000000..b5bc7ef7 --- /dev/null +++ b/tests/test_sync_clk_plugin/src/xua_conf.h @@ -0,0 +1,44 @@ +// Copyright 2016-2022 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#ifndef _XUA_CONF_H_ +#define _XUA_CONF_H_ + +#define NUM_USB_CHAN_OUT (2) +#define NUM_USB_CHAN_IN (2) +#define I2S_CHANS_DAC (2) +#define I2S_CHANS_ADC (2) + +#define XUA_SYNCMODE XUA_SYNCMODE_SYNC +#define UAC_FORCE_FEEDBACK_EP (0) + +#define EXCLUDE_USB_AUDIO_MAIN +#define XUA_NUM_PDM_MICS 0 +#define XUD_TILE 1 +#define AUDIO_IO_TILE 0 +#define MIXER 0 + +#ifndef MCLK_441 +#define MCLK_441 (512 * 44100) +#endif + +#ifndef MCLK_48 +#define MCLK_48 (512 * 48000) +#endif + +#define MIN_FREQ (44100) +#define MAX_FREQ (192000) +#define SPDIF_TX_INDEX 0 +#define VENDOR_STR "XMOS" +#define VENDOR_ID 0x20B1 +#define PRODUCT_STR_A2 "Test device" +#define PRODUCT_STR_A1 "Test device" +#define PID_AUDIO_1 1 +#define PID_AUDIO_2 2 +#define AUDIO_CLASS 2 +#define AUDIO_CLASS_FALLBACK 0 +#define BCD_DEVICE 0x1234 +#define XUA_DFU_EN 0 +#define MIC_DUAL_ENABLED 1 //Use single thread, dual PDM mic +#define XUA_MIC_FRAME_SIZE 240 + +#endif diff --git a/tests/xua_unit_tests/CMakeLists.txt b/tests/xua_unit_tests/CMakeLists.txt new file mode 100644 index 00000000..6bccf6d6 --- /dev/null +++ b/tests/xua_unit_tests/CMakeLists.txt @@ -0,0 +1,103 @@ +cmake_minimum_required(VERSION 3.13) + +set(XMOS_TOOLS_PATH $ENV{XMOS_TOOL_PATH}/bin) + +#********************** +# Setup XMOS toolchain +#********************** +if(NOT DEFINED ENV{XUA_PATH}) + message(FATAL_ERROR "XUA_PATH environment variable not defined") + # some more commands +endif() +include("$ENV{XUA_PATH}/cmake_utils/xmos_toolchain.cmake") + +#********************** +# Project +#********************** +# Disable in-source build. +#if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") +# message(FATAL_ERROR "In-source build is not allowed! Please specify a build folder.\n\tex:cmake -B build") +#endif() + + +## Define project +project(xua_unit_tests VERSION 0.1.0) + +## Enable languages for project +enable_language(CXX XC C ASM) + +message(STATUS "CAME HERE") +add_custom_target("runners" ALL) +add_custom_command( + TARGET runners + COMMAND python generate_unity_runners.py + COMMENT "generate unity runners" +) + +message(STATUS "CAME HERE 1") +file( GLOB APP_SOURCES src/test*.xc ) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin) +foreach( testsourcefile ${APP_SOURCES} ) + get_filename_component(ITEM_NAME ${testsourcefile} NAME_WE) + message(STATUS "item_name " ${ITEM_NAME}) + add_executable(${ITEM_NAME}) + set(APP_COMPILER_FLAGS + "-O2" + "-g" + "-Wall" + "-report" + "-fxscope" + "-target=XCORE-AI-EXPLORER" + "${CMAKE_CURRENT_SOURCE_DIR}/config.xscope" + "-DHID_CONTROLS=1" + "-DUNITY_SUPPORT_64" + "-DUNITY_INCLUDE_DOUBLE" + ) + set_source_files_properties( + "runners/${ITEM_NAME}/${ITEM_NAME}_Runner.c" + PROPERTIES GENERATED TRUE + ) + + set(APP_SRCS + ${testsourcefile} + "runners/${ITEM_NAME}/${ITEM_NAME}_Runner.c" + "${CMAKE_CURRENT_SOURCE_DIR}/../../../Unity/src/unity.c" + ) + set(APP_INCLUDES + "src" + "${CMAKE_CURRENT_SOURCE_DIR}/../../../Unity/src" + ) + set(APP_DEPENDENT_MODULES + "lib_xua(>=2.0.0)" + "lib_logging(>=3.0.0)" + "lib_xassert(>=4.0.0)" + "lib_xud(>=2.0.0)" + "lib_spdif(>=4.0.0)" + "lib_mic_array(>=4.0.0)" + ) + + include("$ENV{XUA_PATH}/cmake_utils/xua.cmake") + set_target_properties(${ITEM_NAME} PROPERTIES OUTPUT_NAME ${ITEM_NAME}.xe) + target_compile_options(${ITEM_NAME} PRIVATE ${APP_COMPILER_FLAGS}) + + target_include_directories(${ITEM_NAME} + PRIVATE ${APP_INCLUDES} + PRIVATE ${XUA_INCLUDES_ALL} + ) + + target_sources(${ITEM_NAME} + PRIVATE ${APP_SRCS} + PRIVATE ${XUA_SRCS_ALL} + ) + add_dependencies(${ITEM_NAME} runners) + target_link_options(${ITEM_NAME} PRIVATE ${APP_COMPILER_FLAGS}) + ## Set any additional flags only for C++ + set(CMAKE_CXX_FLAGS "-std=c++11") + +endforeach( testsourcefile ${APP_SOURCES} ) + +message(STATUS ${APP_SOURCES}) + +message(STATUS "CAME HERE 2") +## Register the application +#XMOS_REGISTER_APP() diff --git a/tests/xua_unit_tests/README.md b/tests/xua_unit_tests/README.md new file mode 100644 index 00000000..d09cf8cc --- /dev/null +++ b/tests/xua_unit_tests/README.md @@ -0,0 +1,32 @@ +# xua_unit_tests test application + +This example builds the xua_unit_tests application for XCORE AI + +## Prerequisites for building + +[XMOS Toolchain 15.0.3](https://www.xmos.com/software/tools/) or newer. + +Install [CMake](https://cmake.org/download/) version 3.13 or newer. + +## Building for xCORE + +Set environment variable for lib_xua path: + + > export XUA_PATH= + +cd to lib_xua/tests/xua_unit_tests + +Run cmake and build + + > cmake . + > make + +## Run on hardware + +Ensure your XCORE AI EXPLORER board is powered up and connected to the XTAG debugger. +Make sure the input.wav file is copied into the build directory + + > pytest -n 1 + + +You should see the tests collected by pytest pass diff --git a/tests/xua_unit_tests/config.xscope b/tests/xua_unit_tests/config.xscope new file mode 100644 index 00000000..bfdf1f86 --- /dev/null +++ b/tests/xua_unit_tests/config.xscope @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/xua_unit_tests/conftest.py b/tests/xua_unit_tests/conftest.py new file mode 100644 index 00000000..2128a9b7 --- /dev/null +++ b/tests/xua_unit_tests/conftest.py @@ -0,0 +1,129 @@ +# Copyright 2021-2022 XMOS LIMITED. +# This Software is subject to the terms of the XMOS Public Licence: Version 1. +from __future__ import print_function +from builtins import str +import os.path +import pytest +import subprocess + +target = os.environ.get("TARGET", "all_possible") +print("target = ", target) + + +def pytest_collect_file(parent, path): + if path.ext == ".xe": + if target == "all_possible": + return UnityTestSource.from_parent(parent, fspath=path) + if target == "XCOREAI" and ("xcoreai" in path.basename): + return UnityTestSource.from_parent(parent, fspath=path) + if target == "XCORE200" and ("xcore200" in path.basename): + return UnityTestSource.from_parent(parent, fspath=path) + + +class UnityTestSource(pytest.File): + def collect(self): + # Find the binary built from the runner for this test file + # + # Assume the following directory layout: + # unit_tests/ <- Test root directory + # |-- bin/ <- Compiled binaries of the test runners + # |-- conftest.py <- This file + # |-- runners/ <- Auto-generated buildable source of test binaries + # |-- src/ <- Unity test functions + # `-- wscript <- Build system file used to generate/build runners + xe_name = ((os.path.basename(self.name)).split("."))[0] + ".xe" + test_bin_path = os.path.join("bin", xe_name) + + yield UnityTestExecutable.from_parent(self, name=self.name) + + +class UnityTestExecutable(pytest.Item): + def __init__(self, name, parent): + super(UnityTestExecutable, self).__init__(name, parent) + self._nodeid = self.name # Override the naming to suit C better + + def runtest(self): + # Run the binary in the simulator + simulator_fail = False + test_output = None + try: + if "xcore200" in self.name: + print("run axe for executable ", self.name) + test_output = subprocess.check_output(["axe", self.name], text=True) + else: + print("run xrun for executable ", self.name) + test_output = subprocess.check_output( + ["xrun", "--io", "--id", "0", self.name], + text=True, + stderr=subprocess.STDOUT, + ) + except subprocess.CalledProcessError as e: + # Unity exits non-zero if an assertion fails + simulator_fail = True + test_output = e.output + + # Parse the Unity output + unity_pass = False + test_output = test_output.split("\n") + for line in test_output: + if "test" in line: + test_report = line.split(":") + # Unity output is as follows: + # :::PASS + # :::FAIL: + test_source = test_report[0] + line_number = test_report[1] + test_case = test_report[2] + result = test_report[3] + failure_reason = None + print(("\n {}()".format(test_case)), end=" ") + if result == "PASS": + unity_pass = True + continue + if result == "FAIL": + failure_reason = test_report[4] + print("") # Insert line break after test_case print + raise UnityTestException( + self, + { + "test_source": test_source, + "line_number": line_number, + "test_case": test_case, + "failure_reason": failure_reason, + }, + ) + + if simulator_fail: + raise Exception(self, "Simulation failed.") + if not unity_pass: + raise Exception(self, "Unity test output not found.") + print("") # Insert line break after final test_case which passed + + def repr_failure(self, excinfo): + if isinstance(excinfo.value, UnityTestException): + return "\n".join( + [ + str(self.parent).strip("<>"), + "{}:{}:{}()".format( + excinfo.value[1]["test_source"], + excinfo.value[1]["line_number"], + excinfo.value[1]["test_case"], + ), + "Failure reason:", + excinfo.value[1]["failure_reason"], + ] + ) + else: + return str(excinfo.value) + + def reportinfo(self): + # It's not possible to give sensible line number info for an executable + # so we return it as 0. + # + # The source line number will instead be recovered from the Unity print + # statements. + return self.fspath, 0, self.name + + +class UnityTestException(Exception): + pass diff --git a/tests/xua_unit_tests/generate_unity_runners.py b/tests/xua_unit_tests/generate_unity_runners.py new file mode 100644 index 00000000..7276d969 --- /dev/null +++ b/tests/xua_unit_tests/generate_unity_runners.py @@ -0,0 +1,134 @@ +# Copyright 2021-2022 XMOS LIMITED. +# This Software is subject to the terms of the XMOS Public Licence: Version 1. +import glob +import os.path +import subprocess +import sys + +UNITY_TEST_DIR = "src" +UNITY_TEST_PREFIX = "test_" +UNITY_RUNNER_DIR = "runners" +UNITY_RUNNER_SUFFIX = "_Runner" +project_root = os.path.join("..", "..", "..") + + +def get_ruby(): + """ + Check ruby is avaliable and return the command to invoke it. + """ + interpreter_name = "ruby" + try: + dev_null = open(os.devnull, "w") + # Call the version command to check the interpreter can be run + subprocess.check_call( + [interpreter_name, "--version"], stdout=dev_null, close_fds=True + ) + except OSError as e: + print("Failed to run Ruby interpreter: {}".format(e), file=sys.stderr) + exit(1) # TODO: Check this is the correct way to kill xwaf on error + + return interpreter_name + + +def get_unity_runner_generator(project_root_path): + """ + Check the Unity generate_test_runner script is avaliable, and return the + path to it. + """ + unity_runner_generator = os.path.join( + project_root_path, "Unity", "auto", "generate_test_runner.rb" + ) + if not os.path.exists(unity_runner_generator): + print("Unity repo not found in workspace", file=sys.stderr) + exit(1) # TODO: Check this is the correct way to kill xwaf on error + return unity_runner_generator + + +def get_test_name(test_path): + """ + Return the test name by removing the extension from the filename. + """ + return os.path.splitext(os.path.basename(test_path))[0] + + +def get_file_type(filename): + """ + Return the extension from the filename. + """ + return filename.rsplit(".")[-1:][0] + + +def generate_unity_runner( + project_root_path, unity_test_path, unity_runner_dir, unity_runner_suffix +): + """ + Invoke the Unity runner generation script for the given test file, and + return the path to the generated file. The output directory will be created + if it does not already exist. + """ + runner_path = os.path.join( + os.path.join(unity_runner_dir, get_test_name(unity_test_path)) + ) + if not os.path.exists(runner_path): + os.makedirs(runner_path) + + unity_runner_path = os.path.join( + runner_path, get_test_name(unity_test_path) + unity_runner_suffix + "." + "c" + ) + + try: + subprocess.check_call( + [ + get_ruby(), + get_unity_runner_generator(project_root_path), + unity_test_path, + unity_runner_path, + ] + ) + except OSError as e: + print( + "Ruby generator failed for {}\n\t{}".format(unity_test_path, e), + file=sys.stderr, + ) + exit(1) # TODO: Check this is the correct way to kill xwaf on error + + +def find_unity_test_paths(unity_test_dir, unity_test_prefix): + """ + Return a list of all file paths with the unity_test_prefix found in the + unity_test_dir. + """ + return glob.glob(os.path.join(unity_test_dir, unity_test_prefix + "*")) + + +def find_unity_tests(unity_test_dir, unity_test_prefix): + """ + Return a dictionary of all {test names, test language} pairs with the + unity_test_prefix found in the unity_test_dir. + """ + unity_test_paths = find_unity_test_paths(unity_test_dir, unity_test_prefix) + print("unity_test_paths = ", unity_test_paths) + return {get_test_name(path): get_file_type(path) for path in unity_test_paths} + + +def find_unity_test_paths(unity_test_dir, unity_test_prefix): + """ + Return a list of all file paths with the unity_test_prefix found in the + unity_test_dir. + """ + return glob.glob(os.path.join(unity_test_dir, unity_test_prefix + "*")) + + +def generate_runners(): + UNITY_TESTS = find_unity_tests(UNITY_TEST_DIR, UNITY_TEST_PREFIX) + print("UNITY_TESTS = ", UNITY_TESTS) + unity_test_paths = find_unity_test_paths(UNITY_TEST_DIR, UNITY_TEST_PREFIX) + print("unity_test_paths = ", unity_test_paths) + for unity_test_path in unity_test_paths: + generate_unity_runner( + project_root, unity_test_path, UNITY_RUNNER_DIR, UNITY_RUNNER_SUFFIX + ) + + +if __name__ == "__main__": + generate_runners() diff --git a/tests/xua_unit_tests/src/test_multi_report/hid_report_descriptor.h b/tests/xua_unit_tests/src/test_multi_report/hid_report_descriptor.h new file mode 100644 index 00000000..e01361aa --- /dev/null +++ b/tests/xua_unit_tests/src/test_multi_report/hid_report_descriptor.h @@ -0,0 +1,296 @@ +// Copyright 2021 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +#ifndef __hid_report_descriptor_h__ +#define __hid_report_descriptor_h__ + +#include "xua_hid_report.h" + +#define USB_HID_REPORT_ID_KEYBOARD ( 0x01 ) +#define USB_HID_REPORT_ID_CONSUMER ( 0x02 ) +#define USB_HID_REPORT_ID_TELEPHONY ( 0x03 ) + +/* + * Define non-configurable items in the HID Report descriptor. + * (These are short items as the location field isn't relevant for them) + */ +static const USB_HID_Short_Item_t hidCollectionApplication = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_MAIN, HID_REPORT_ITEM_TAG_COLLECTION), + .data = { 0x01, 0x00 } }; +static const USB_HID_Short_Item_t hidCollectionEnd = { + .header = HID_REPORT_SET_HEADER(0, HID_REPORT_ITEM_TYPE_MAIN, HID_REPORT_ITEM_TAG_END_COLLECTION), + .data = { 0x00, 0x00 } }; + + +static const USB_HID_Short_Item_t hidInputConstArray = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_MAIN, HID_REPORT_ITEM_TAG_INPUT), + .data = { 0x01, 0x00 } }; +static const USB_HID_Short_Item_t hidInputDataVar = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_MAIN, HID_REPORT_ITEM_TAG_INPUT), + .data = { 0x02, 0x00 } }; + +static const USB_HID_Short_Item_t hidLogicalMaximum0 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_LOGICAL_MAXIMUM), + .data = { 0x00, 0x00 } }; +static const USB_HID_Short_Item_t hidLogicalMaximum1 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_LOGICAL_MAXIMUM), + .data = { 0x01, 0x00 } }; +static const USB_HID_Short_Item_t hidLogicalMinimum0 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_LOGICAL_MINIMUM), + .data = { 0x00, 0x00 } }; + +static const USB_HID_Short_Item_t hidReportCount1 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_COUNT), + .data = { 0x01, 0x00 } }; +static const USB_HID_Short_Item_t hidReportCount4 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_COUNT), + .data = { 0x04, 0x00 } }; +static const USB_HID_Short_Item_t hidReportCount6 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_COUNT), + .data = { 0x06, 0x00 } }; +static const USB_HID_Short_Item_t hidReportCount7 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_COUNT), + .data = { 0x07, 0x00 } }; +static const USB_HID_Short_Item_t hidReportSize1 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_SIZE), + .data = { 0x01, 0x00 } }; + +static const USB_HID_Short_Item_t hidUsagePageGenericDesktop = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_USAGE_PAGE), + .data = { USB_HID_USAGE_PAGE_ID_GENERIC_DESKTOP, 0x00 }}; +static const USB_HID_Short_Item_t hidUsagePageConsumerControl = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_USAGE_PAGE), + .data = { USB_HID_USAGE_PAGE_ID_CONSUMER, 0x00 }}; +static const USB_HID_Short_Item_t hidUsagePageTelephony = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_USAGE_PAGE), + .data = { USB_HID_USAGE_PAGE_ID_TELEPHONY_DEVICE, 0x00 }}; + +static const USB_HID_Short_Item_t hidUsageKeyboard = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .data = { 0x06, 0x00 }}; +static const USB_HID_Short_Item_t hidUsageConsumerControl = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .data = { 0x01, 0x00 }}; +static const USB_HID_Short_Item_t hidUsageTelephonyHeadset = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .data = { 0x05, 0x00 }}; + +/* + * Define the HID Report Descriptor Item, Usage Page, Report ID and length for each HID Report + * For internal purposes, a report element with ID of 0 must be included if report IDs are not being used. + */ +static const USB_HID_Short_Item_t hidReportId1 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_ID), + .data = { USB_HID_REPORT_ID_KEYBOARD, 0x00 } }; +static const USB_HID_Short_Item_t hidReportId2 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_ID), + .data = { USB_HID_REPORT_ID_CONSUMER, 0x00 } }; +static const USB_HID_Short_Item_t hidReportId3 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_ID), + .data = { USB_HID_REPORT_ID_TELEPHONY, 0x00 } }; + +static const USB_HID_Report_Element_t hidReportKeyboard = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_USAGE_PAGE), + .item.data = { USB_HID_USAGE_PAGE_ID_KEYBOARD, 0x00 }, + .location = HID_REPORT_SET_LOC( USB_HID_REPORT_ID_KEYBOARD, 1, 0, 0 ) +}; + +static const USB_HID_Report_Element_t hidReportConsumer = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_USAGE_PAGE), + .item.data = { USB_HID_USAGE_PAGE_ID_CONSUMER, 0x00 }, + .location = HID_REPORT_SET_LOC( USB_HID_REPORT_ID_CONSUMER, 2, 0, 0 ) +}; + +static const USB_HID_Report_Element_t hidReportTelephony = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_USAGE_PAGE), + .item.data = { USB_HID_USAGE_PAGE_ID_TELEPHONY_DEVICE, 0x00 }, + .location = HID_REPORT_SET_LOC( USB_HID_REPORT_ID_TELEPHONY, 1, 0, 0 ) +}; + +/* + * Define configurable elements in the HID Report descriptor. + */ +static USB_HID_Report_Element_t hidUsageReport1Byte0Bit0 = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .item.data = { 0x17, 0x00 }, + .location = HID_REPORT_SET_LOC( USB_HID_REPORT_ID_KEYBOARD, 0, 0, 0 ) +}; // 't' + +static USB_HID_Report_Element_t hidUsageReport1Byte0Bit2 = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .item.data = { 0x72, 0x00 }, + .location = HID_REPORT_SET_LOC( USB_HID_REPORT_ID_KEYBOARD, 0, 0, 2 ) +}; // F23 + +static USB_HID_Report_Element_t hidUsageReport1Byte0Bit3 = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .item.data = { 0x73, 0x00 }, + .location = HID_REPORT_SET_LOC( USB_HID_REPORT_ID_KEYBOARD, 0, 0, 3 ) +}; // F24 + +static USB_HID_Report_Element_t hidUsageReport2Byte0Bit0 = { + .item.header = HID_REPORT_SET_HEADER(2, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .item.data = { 0x26, 0x02 }, + .location = HID_REPORT_SET_LOC( USB_HID_REPORT_ID_CONSUMER, 0, 0, 0 ) +}; // AC Stop + +static USB_HID_Report_Element_t hidUsageReport2Byte0Bit1 = { + .item.header = HID_REPORT_SET_HEADER(2, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .item.data = { 0x21, 0x02 }, + .location = HID_REPORT_SET_LOC( USB_HID_REPORT_ID_CONSUMER, 0, 0, 1 ) +}; // AC Search + +static USB_HID_Report_Element_t hidUsageReport2Byte0Bit2 = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .item.data = { 0xE2, 0x00 }, + .location = HID_REPORT_SET_LOC( USB_HID_REPORT_ID_CONSUMER, 0, 0, 2 ) +}; // Mute + +static USB_HID_Report_Element_t hidUsageReport2Byte0Bit4 = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .item.data = { 0xCF, 0x00 }, + .location = HID_REPORT_SET_LOC( USB_HID_REPORT_ID_CONSUMER, 0, 0, 4 ) +}; // Voice Command + +static USB_HID_Report_Element_t hidUsageReport2Byte0Bit6 = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .item.data = { 0xE9, 0x00 }, + .location = HID_REPORT_SET_LOC( USB_HID_REPORT_ID_CONSUMER, 0, 0, 6 ) +}; // Vol+ + +static USB_HID_Report_Element_t hidUsageReport2Byte0Bit7 = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .item.data = { 0xEA, 0x00 }, + .location = HID_REPORT_SET_LOC( USB_HID_REPORT_ID_CONSUMER, 0, 0, 7 ) +}; // Vol- + +static USB_HID_Report_Element_t hidUsageReport2Byte1Bit7 = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .item.data = { 0xE5, 0x00 }, + .location = HID_REPORT_SET_LOC( USB_HID_REPORT_ID_CONSUMER, 0, 1, 7 ) +}; // Bass boost + +static USB_HID_Report_Element_t hidUsageReport3Byte0Bit0 = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .item.data = { 0x20, 0x00 }, + .location = HID_REPORT_SET_LOC( USB_HID_REPORT_ID_TELEPHONY, 0, 0, 0 ) +}; // Hook Switch + +static USB_HID_Report_Element_t hidUsageReport3Byte0Bit1 = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .item.data = { 0x2F, 0x00 }, + .location = HID_REPORT_SET_LOC( USB_HID_REPORT_ID_TELEPHONY, 0, 0, 1 ) +}; // Phone Mute + +/* + * List the configurable elements in the HID Report. + */ +static USB_HID_Report_Element_t* const hidConfigurableElements[] = { + &hidUsageReport1Byte0Bit0, + &hidUsageReport1Byte0Bit2, + &hidUsageReport1Byte0Bit3, + + &hidUsageReport2Byte0Bit0, + &hidUsageReport2Byte0Bit1, + &hidUsageReport2Byte0Bit2, + &hidUsageReport2Byte0Bit4, + &hidUsageReport2Byte0Bit6, + &hidUsageReport2Byte0Bit7, + &hidUsageReport2Byte1Bit7, + + &hidUsageReport3Byte0Bit0, + &hidUsageReport3Byte0Bit1 +}; + +/* + * List HID Reports, one per Report ID. This should be a usage page item with the locator filled out with ID and size + * If not using report IDs - still have one with report ID 0 + */ +static const USB_HID_Report_Element_t* const hidReports[] = { + &hidReportKeyboard, + &hidReportConsumer, + &hidReportTelephony +}; + +/* + * List all items in the HID Report descriptor. + */ +static const USB_HID_Short_Item_t* const hidReportDescriptorItems[] = { + &hidUsagePageGenericDesktop, + &hidUsageKeyboard, + &hidReportSize1, + &hidLogicalMinimum0, + &hidCollectionApplication, // Report 1 + &hidReportId1, + &(hidReportKeyboard.item), + &hidLogicalMaximum1, + &hidReportCount1, + &(hidUsageReport1Byte0Bit0.item), + &hidInputDataVar, + &hidLogicalMaximum0, + &hidInputConstArray, + &hidLogicalMaximum1, + &(hidUsageReport1Byte0Bit2.item), + &hidInputDataVar, + &(hidUsageReport1Byte0Bit3.item), + &hidInputDataVar, + &hidLogicalMaximum0, + &hidReportCount4, + &hidInputConstArray, + &hidCollectionEnd, + &hidUsagePageConsumerControl, + &hidUsageConsumerControl, + &hidCollectionApplication, // Report 2 + &hidReportId2, + &(hidReportConsumer.item), + &hidLogicalMaximum1, + &hidReportCount1, + &(hidUsageReport2Byte0Bit0.item), + &hidInputDataVar, + &(hidUsageReport2Byte0Bit1.item), + &hidInputDataVar, + &(hidUsageReport2Byte0Bit2.item), + &hidInputDataVar, + &hidLogicalMaximum0, + &hidInputConstArray, + &hidLogicalMaximum1, + &(hidUsageReport2Byte0Bit4.item), + &hidInputDataVar, + &hidLogicalMaximum0, + &hidInputConstArray, + &hidLogicalMaximum1, + &(hidUsageReport2Byte0Bit6.item), + &hidInputDataVar, + &(hidUsageReport2Byte0Bit7.item), + &hidInputDataVar, + &hidLogicalMaximum0, + &hidReportCount7, + &hidInputConstArray, + &hidLogicalMaximum1, + &hidReportCount1, + &(hidUsageReport2Byte1Bit7.item), + &hidInputDataVar, + &hidCollectionEnd, + &hidUsagePageTelephony, + &hidUsageTelephonyHeadset, + &hidCollectionApplication, // Report 3 + &hidReportId3, + &(hidReportTelephony.item), + &(hidUsageReport3Byte0Bit0.item), + &hidInputDataVar, + &(hidUsageReport3Byte0Bit1.item), + &hidInputDataVar, + &hidLogicalMaximum0, + &hidReportCount6, + &hidInputConstArray, + &hidCollectionEnd, +}; + +/* + * Define the number of HID Reports + * Due to XC not supporting designated initializers, this constant has a hard-coded value. + * It must equal ( sizeof hidReports / sizeof ( USB_HID_Report_Element_t* )) + */ +#define HID_REPORT_COUNT ( 3 ) + +#endif // __hid_report_descriptor_h__ diff --git a/tests/xua_unit_tests/src/test_multi_report/test_hid_multi_report.c b/tests/xua_unit_tests/src/test_multi_report/test_hid_multi_report.c new file mode 100644 index 00000000..d9fbac7c --- /dev/null +++ b/tests/xua_unit_tests/src/test_multi_report/test_hid_multi_report.c @@ -0,0 +1,887 @@ +// Copyright 2021-2022 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#include +#include + +#include "xua_unit_tests.h" +#include "xua_hid_report.h" + +// Test constants related to the report descriptor defined in hid_report_descriptor.h +#define REPORT1_MAX_VALID_BIT ( 3 ) +#define REPORT1_MAX_VALID_BYTE ( 0 ) +#define REPORT1_MIN_VALID_BIT ( 0 ) +#define REPORT1_MIN_VALID_BYTE ( 0 ) + +#define REPORT2_MAX_VALID_BIT ( 7 ) +#define REPORT2_MAX_VALID_BYTE ( 1 ) +#define REPORT2_MIN_VALID_BIT ( 0 ) +#define REPORT2_MIN_VALID_BYTE ( 0 ) + +#define REPORT3_MAX_VALID_BIT ( 1 ) +#define REPORT3_MAX_VALID_BYTE ( 0 ) +#define REPORT3_MIN_VALID_BIT ( 0 ) +#define REPORT3_MIN_VALID_BYTE ( 0 ) + +#define HID_REPORT_LENGTH ( 3 ) +#define HID_REPORT_COUNT ( 3 ) +#define HID_REPORTID_LIMIT ( 4 ) + +// Constants from USB HID Usage Tables +#define KEYBOARD_PAGE ( 0x07 ) +#define CONSUMER_PAGE ( 0x0C ) +#define TELEPHONY_DEVICE_PAGE ( 0x0B ) +#define LOUDNESS_CONTROL ( 0xE7 ) +#define PHONE_KEY_9 ( 0xB9 ) +#define KEYBOARD_X ( 0x1B ) +#define PHONE_HOST_HOLD ( 0x010A ) + +static unsigned construct_usage_header( unsigned size ) +{ + unsigned header = 0x00; + + header |= ( HID_REPORT_ITEM_USAGE_TAG << HID_REPORT_ITEM_HDR_TAG_SHIFT ) & HID_REPORT_ITEM_HDR_TAG_MASK; + header |= ( HID_REPORT_ITEM_USAGE_TYPE << HID_REPORT_ITEM_HDR_TYPE_SHIFT ) & HID_REPORT_ITEM_HDR_TYPE_MASK; + + header |= ( size << HID_REPORT_ITEM_HDR_SIZE_SHIFT ) & HID_REPORT_ITEM_HDR_SIZE_MASK; + + return header; +} + +void setUp( void ) +{ + hidReportInit(); + hidResetReportDescriptor(); +} + +void test_validate_report( void ) { + unsigned retVal = hidReportValidate(); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); +} + +void test_reportid_in_use( void ) { + unsigned reportIdInUse = hidIsReportIdInUse(); + TEST_ASSERT_EQUAL_UINT( 1, reportIdInUse ); +} + +void test_get_next_valid_report_id( void ) { + unsigned reportId = 0U; + + reportId = hidGetNextValidReportId(reportId); + TEST_ASSERT_EQUAL_UINT( 1, reportId ); + + reportId = hidGetNextValidReportId(reportId); + TEST_ASSERT_EQUAL_UINT( 2, reportId ); + + reportId = hidGetNextValidReportId(reportId); + TEST_ASSERT_EQUAL_UINT( 3, reportId ); + + reportId = hidGetNextValidReportId(reportId); + TEST_ASSERT_EQUAL_UINT( 1, reportId ); +} + +void test_is_report_id_valid( void ) { + unsigned isValid = 0; + + unsigned reportId = 0; + isValid = hidIsReportIdValid( reportId ); + TEST_ASSERT_EQUAL_UINT( 0, isValid ); + + reportId = 1; + isValid = hidIsReportIdValid( reportId ); + TEST_ASSERT_EQUAL_UINT( 1, isValid ); + + reportId = 2; + isValid = hidIsReportIdValid( reportId ); + TEST_ASSERT_EQUAL_UINT( 1, isValid ); + + reportId = 3; + isValid = hidIsReportIdValid( reportId ); + TEST_ASSERT_EQUAL_UINT( 1, isValid ); + + reportId = 4; + isValid = hidIsReportIdValid( reportId ); + TEST_ASSERT_EQUAL_UINT( 0, isValid ); +} + +// Basic report descriptor tests +void test_unprepared_hidGetReportDescriptor( void ) +{ + unsigned char* reportDescPtr = hidGetReportDescriptor(); + TEST_ASSERT_NULL( reportDescPtr ); + + for (unsigned reportId = 1; reportId <= HID_REPORT_COUNT; reportId++) + { + unsigned reportLength = hidGetReportLength( reportId ); + TEST_ASSERT_EQUAL_UINT( 0, reportLength ); + } +} + +void test_prepared_hidGetReportDescriptor( void ) +{ + hidPrepareReportDescriptor(); + unsigned char* reportDescPtr = hidGetReportDescriptor(); + TEST_ASSERT_NOT_NULL( reportDescPtr ); + + unsigned reportId = 1; + unsigned reportLength = hidGetReportLength( reportId ); + TEST_ASSERT_EQUAL_UINT( 1, reportLength ); + + reportId = 2; + reportLength = hidGetReportLength( reportId ); + TEST_ASSERT_EQUAL_UINT( 2, reportLength ); + + reportId = 3; + reportLength = hidGetReportLength( reportId ); + TEST_ASSERT_EQUAL_UINT( 1, reportLength ); +} + +void test_reset_unprepared_hidGetReportDescriptor( void ) +{ + hidPrepareReportDescriptor(); + hidResetReportDescriptor(); + unsigned char* reportDescPtr = hidGetReportDescriptor(); + TEST_ASSERT_NULL( reportDescPtr ); +} + +void test_reset_prepared_hidGetReportDescriptor( void ) +{ + hidPrepareReportDescriptor(); + hidResetReportDescriptor(); + hidPrepareReportDescriptor(); + unsigned char* reportDescPtr = hidGetReportDescriptor(); + TEST_ASSERT_NOT_NULL( reportDescPtr ); +} + +void test_report_id_limit( void ) +{ + unsigned reportIdLimit = hidGetReportIdLimit(); + TEST_ASSERT_EQUAL_UINT( HID_REPORTID_LIMIT, reportIdLimit ); +} + +// Basic item tests +void test_max_loc_hidGetReportItem( void ) +{ + unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ]; + unsigned char header; + unsigned char page; + + unsigned reportId = 1; + unsigned bit = REPORT1_MAX_VALID_BIT; + unsigned byte = REPORT1_MAX_VALID_BYTE; + unsigned retVal = hidGetReportItem( reportId, byte, bit, &page, &header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); + TEST_ASSERT_EQUAL_UINT( KEYBOARD_PAGE, page ); + TEST_ASSERT_EQUAL_UINT( 0x09, header ); + TEST_ASSERT_EQUAL_UINT( 0x73, data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0x00, data[ 1 ]); + + reportId = 2; + bit = REPORT2_MAX_VALID_BIT; + byte = REPORT2_MAX_VALID_BYTE; + retVal = hidGetReportItem( reportId, byte, bit, &page, &header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); + TEST_ASSERT_EQUAL_UINT( CONSUMER_PAGE, page ); + TEST_ASSERT_EQUAL_UINT( 0x09, header ); + TEST_ASSERT_EQUAL_UINT( 0xE5, data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0x00, data[ 1 ]); + + reportId = 3; + bit = REPORT3_MAX_VALID_BIT; + byte = REPORT3_MAX_VALID_BYTE; + retVal = hidGetReportItem( reportId, byte, bit, &page, &header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); + TEST_ASSERT_EQUAL_UINT( TELEPHONY_DEVICE_PAGE, page ); + TEST_ASSERT_EQUAL_UINT( 0x09, header ); + TEST_ASSERT_EQUAL_UINT( 0x2F, data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0x00, data[ 1 ]); +} + + +void test_min_loc_hidGetReportItem( void ) +{ + unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ]; + unsigned char header; + unsigned char page; + + unsigned reportId = 1; + unsigned bit = REPORT1_MIN_VALID_BIT; + unsigned byte = REPORT1_MIN_VALID_BYTE; + unsigned retVal = hidGetReportItem( reportId, byte, bit, &page, &header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); + TEST_ASSERT_EQUAL_UINT( KEYBOARD_PAGE, page ); + TEST_ASSERT_EQUAL_UINT( 0x09, header ); + TEST_ASSERT_EQUAL_UINT( 0x17, data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0x00, data[ 1 ]); + + reportId = 2; + bit = REPORT2_MIN_VALID_BIT; + byte = REPORT2_MIN_VALID_BYTE; + retVal = hidGetReportItem( reportId, byte, bit, &page, &header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); + TEST_ASSERT_EQUAL_UINT( CONSUMER_PAGE, page ); + TEST_ASSERT_EQUAL_UINT( 0x0A, header ); + TEST_ASSERT_EQUAL_UINT( 0x26, data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0x02, data[ 1 ]); + + reportId = 3; + bit = REPORT3_MIN_VALID_BIT; + byte = REPORT3_MIN_VALID_BYTE; + retVal = hidGetReportItem( reportId, byte, bit, &page, &header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); + TEST_ASSERT_EQUAL_UINT( TELEPHONY_DEVICE_PAGE, page ); + TEST_ASSERT_EQUAL_UINT( 0x09, header ); + TEST_ASSERT_EQUAL_UINT( 0x20, data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0x00, data[ 1 ]); +} + +void test_invalid_report_id( void ) +{ + unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xBA, 0xD2 }; + unsigned char header = 0x33; + unsigned char page = 0x44; + + unsigned reportId = 0; + unsigned bit = REPORT1_MIN_VALID_BIT; + unsigned byte = REPORT1_MIN_VALID_BYTE; + unsigned retVal = hidGetReportItem( reportId, byte, bit, &page, &header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_ID, retVal ); + TEST_ASSERT_EQUAL_UINT( 0x44, page ); + TEST_ASSERT_EQUAL_UINT( 0x33, header ); + TEST_ASSERT_EQUAL_UINT( 0xBA, data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0xD2, data[ 1 ]); +} + +void test_unused_report_id( void ) +{ + unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xBA, 0xD2 }; + unsigned char header = 0x33; + unsigned char page = 0x44; + + unsigned reportId = 8; + unsigned bit = REPORT1_MIN_VALID_BIT; + unsigned byte = REPORT1_MIN_VALID_BYTE; + unsigned retVal = hidGetReportItem( reportId, byte, bit, &page, &header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_ID, retVal ); + TEST_ASSERT_EQUAL_UINT( 0x44, page ); + TEST_ASSERT_EQUAL_UINT( 0x33, header ); + TEST_ASSERT_EQUAL_UINT( 0xBA, data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0xD2, data[ 1 ]); +} + +void test_overflow_bit_hidGetReportItem( void ) +{ + unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xBA, 0xD1 }; + unsigned char header = 0xAA; + unsigned char page = 0x44; + + unsigned reportId = 1; + unsigned bit = REPORT1_MAX_VALID_BIT + 1; + unsigned byte = REPORT1_MAX_VALID_BYTE; + unsigned retVal = hidGetReportItem( reportId, byte, bit, &page, &header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); + TEST_ASSERT_EQUAL_UINT( 0x44, page ); + TEST_ASSERT_EQUAL_UINT( 0xAA, header ); + TEST_ASSERT_EQUAL_UINT( 0xBA, data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0xD1, data[ 1 ]); + + reportId = 2; + bit = REPORT2_MAX_VALID_BIT + 1; + byte = REPORT2_MAX_VALID_BYTE; + retVal = hidGetReportItem( reportId, byte, bit, &page, &header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); + TEST_ASSERT_EQUAL_UINT( 0x44, page ); + TEST_ASSERT_EQUAL_UINT( 0xAA, header ); + TEST_ASSERT_EQUAL_UINT( 0xBA, data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0xD1, data[ 1 ]); + + reportId = 3; + bit = REPORT3_MAX_VALID_BIT + 1; + byte = REPORT3_MAX_VALID_BYTE; + retVal = hidGetReportItem( reportId, byte, bit, &page, &header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); + TEST_ASSERT_EQUAL_UINT( 0x44, page ); + TEST_ASSERT_EQUAL_UINT( 0xAA, header ); + TEST_ASSERT_EQUAL_UINT( 0xBA, data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0xD1, data[ 1 ]); +} + +void test_overflow_byte_hidGetReportItem( void ) +{ + unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xBA, 0xD1 }; + unsigned char header = 0xAA; + unsigned char page = 0x44; + + unsigned reportId = 1; + unsigned bit = REPORT1_MAX_VALID_BIT; + unsigned byte = REPORT1_MAX_VALID_BYTE + 1; + unsigned retVal = hidGetReportItem( reportId, byte, bit, &page, &header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); + TEST_ASSERT_EQUAL_UINT( 0x44, page ); + TEST_ASSERT_EQUAL_UINT( 0xAA, header ); + TEST_ASSERT_EQUAL_UINT( 0xBA, data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0xD1, data[ 1 ]); + + reportId = 2; + bit = REPORT2_MAX_VALID_BIT; + byte = REPORT2_MAX_VALID_BYTE + 1; + retVal = hidGetReportItem( reportId, byte, bit, &page, &header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); + TEST_ASSERT_EQUAL_UINT( 0x44, page ); + TEST_ASSERT_EQUAL_UINT( 0xAA, header ); + TEST_ASSERT_EQUAL_UINT( 0xBA, data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0xD1, data[ 1 ]); + + reportId = 3; + bit = REPORT3_MAX_VALID_BIT; + byte = REPORT3_MAX_VALID_BYTE + 1; + retVal = hidGetReportItem( reportId, byte, bit, &page, &header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); + TEST_ASSERT_EQUAL_UINT( 0x44, page ); + TEST_ASSERT_EQUAL_UINT( 0xAA, header ); + TEST_ASSERT_EQUAL_UINT( 0xBA, data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0xD1, data[ 1 ]); +} + +void test_underflow_bit_hidGetReportItem( void ) +{ + unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xBA, 0xD1 }; + unsigned char header = 0xAA; + unsigned char page = 0x44; + + unsigned reportId = 1; + unsigned bit = REPORT1_MIN_VALID_BIT - 1; + unsigned byte = REPORT1_MIN_VALID_BYTE; + unsigned retVal = hidGetReportItem( reportId, byte, bit, &page, &header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); + TEST_ASSERT_EQUAL_UINT( 0x44, page ); + TEST_ASSERT_EQUAL_UINT( 0xAA, header ); + TEST_ASSERT_EQUAL_UINT( 0xBA, data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0xD1, data[ 1 ]); + + reportId = 2; + bit = REPORT2_MIN_VALID_BIT - 1; + byte = REPORT2_MIN_VALID_BYTE; + retVal = hidGetReportItem( reportId, byte, bit, &page, &header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); + TEST_ASSERT_EQUAL_UINT( 0x44, page ); + TEST_ASSERT_EQUAL_UINT( 0xAA, header ); + TEST_ASSERT_EQUAL_UINT( 0xBA, data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0xD1, data[ 1 ]); + + reportId = 3; + bit = REPORT3_MIN_VALID_BIT - 1; + byte = REPORT3_MIN_VALID_BYTE; + retVal = hidGetReportItem( reportId, byte, bit, &page, &header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); + TEST_ASSERT_EQUAL_UINT( 0x44, page ); + TEST_ASSERT_EQUAL_UINT( 0xAA, header ); + TEST_ASSERT_EQUAL_UINT( 0xBA, data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0xD1, data[ 1 ]); +} + +void test_underflow_byte_hidGetReportItem( void ) +{ + unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xBA, 0xD1 }; + unsigned char header = 0xAA; + unsigned char page = 0x44; + + unsigned reportId = 1; + unsigned bit = REPORT1_MIN_VALID_BIT; + unsigned byte = REPORT1_MIN_VALID_BYTE - 1; + unsigned retVal = hidGetReportItem( reportId, byte, bit, &page, &header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); + TEST_ASSERT_EQUAL_UINT( 0x44, page ); + TEST_ASSERT_EQUAL_UINT( 0xAA, header ); + TEST_ASSERT_EQUAL_UINT( 0xBA, data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0xD1, data[ 1 ]); + + reportId = 2; + bit = REPORT2_MIN_VALID_BIT; + byte = REPORT2_MIN_VALID_BYTE - 1; + retVal = hidGetReportItem( reportId, byte, bit, &page, &header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); + TEST_ASSERT_EQUAL_UINT( 0x44, page ); + TEST_ASSERT_EQUAL_UINT( 0xAA, header ); + TEST_ASSERT_EQUAL_UINT( 0xBA, data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0xD1, data[ 1 ]); + + reportId = 3; + bit = REPORT3_MIN_VALID_BIT; + byte = REPORT3_MIN_VALID_BYTE - 1; + retVal = hidGetReportItem( reportId, byte, bit, &page, &header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); + TEST_ASSERT_EQUAL_UINT( 0x44, page ); + TEST_ASSERT_EQUAL_UINT( 0xAA, header ); + TEST_ASSERT_EQUAL_UINT( 0xBA, data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0xD1, data[ 1 ]); +} + +// Configurable and non-configurable item tests +void test_configurable_item_hidSetReportItem( void ) +{ + const unsigned reportId = 1; + const unsigned bit = REPORT1_MIN_VALID_BIT; + const unsigned byte = REPORT1_MIN_VALID_BYTE; + unsigned char data[ 1 ] = { KEYBOARD_X }; + unsigned char header = construct_usage_header( sizeof( data ) / sizeof( unsigned char )); + unsigned char page = KEYBOARD_PAGE; + + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); + + retVal = hidGetReportItem( reportId, byte, bit, &page, &header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); + TEST_ASSERT_EQUAL_UINT( KEYBOARD_PAGE, page ); + TEST_ASSERT_EQUAL_UINT( 0x09, header ); + TEST_ASSERT_EQUAL_UINT( KEYBOARD_X, data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0x00, data[ 1 ]); +} + +// Testing that the high byte of the report gets correctly cleared +void test_configurable_item_hidSetReportItem_multibyte_orig( void ) +{ + const unsigned reportId = 2; + const unsigned bit = 1; // This byte&bit combo is originally set be 2 bytes long in the header + const unsigned byte = 0; + unsigned char data[ 1 ] = { LOUDNESS_CONTROL }; + unsigned char header = construct_usage_header( sizeof( data ) / sizeof( unsigned char )); + unsigned char page = CONSUMER_PAGE; + + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); + + retVal = hidGetReportItem( reportId, byte, bit, &page, &header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); + TEST_ASSERT_EQUAL_UINT( CONSUMER_PAGE, page ); + TEST_ASSERT_EQUAL_UINT( 0x09, header ); + TEST_ASSERT_EQUAL_UINT( LOUDNESS_CONTROL, data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0x00, data[ 1 ]); +} + +void test_nonconfigurable_item_hidSetReportItem( void ) +{ + const unsigned reportId = 1; + const unsigned bit = 1; // This bit and byte combination should not appear in the + const unsigned byte = 0; // hidConfigurableElements list in hid_report_descriptors.c. + const unsigned char data[ 1 ] = { KEYBOARD_X }; + const unsigned char header = construct_usage_header( sizeof data / sizeof( unsigned char )); + const unsigned char page = KEYBOARD_PAGE; + + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); +} + +// Bit range tests +void test_max_bit_hidSetReportItem( void ) +{ + const unsigned char header = construct_usage_header( 0 ); + + unsigned reportId = 1; + unsigned bit = REPORT1_MAX_VALID_BIT; // See the hidConfigurableElements list in hid_report_descriptors.c. + unsigned byte = REPORT1_MAX_VALID_BYTE; + unsigned char page = KEYBOARD_PAGE; + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); + + reportId = 2; + bit = REPORT2_MAX_VALID_BIT; + byte = REPORT2_MAX_VALID_BYTE; + page = CONSUMER_PAGE; + retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); + + reportId = 3; + bit = REPORT3_MAX_VALID_BIT; + byte = REPORT3_MAX_VALID_BYTE; + page = TELEPHONY_DEVICE_PAGE; + retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); +} + +void test_min_bit_hidSetReportItem( void ) +{ + const unsigned char header = construct_usage_header( 0 ); + + unsigned reportId = 1; + unsigned bit = REPORT1_MIN_VALID_BIT; // See the hidConfigurableElements list in hid_report_descriptors.c. + unsigned byte = REPORT1_MIN_VALID_BYTE; + unsigned char page = KEYBOARD_PAGE; + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); + + reportId = 2; + bit = REPORT2_MIN_VALID_BIT; + byte = REPORT2_MIN_VALID_BYTE; + page = CONSUMER_PAGE; + retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); + + reportId = 3; + bit = REPORT3_MIN_VALID_BIT; + byte = REPORT3_MIN_VALID_BYTE; + page = TELEPHONY_DEVICE_PAGE; + retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); +} + +void test_overflow_bit_hidSetReportItem( void ) +{ + const unsigned char header = construct_usage_header( 0 ); + + unsigned reportId = 1; + unsigned bit = REPORT1_MAX_VALID_BIT + 1; // See the hidConfigurableElements list in hid_report_descriptors.c. + unsigned byte = REPORT1_MAX_VALID_BYTE; + unsigned char page = KEYBOARD_PAGE; + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); + + reportId = 2; + bit = REPORT2_MAX_VALID_BIT + 1; + byte = REPORT2_MAX_VALID_BYTE; + page = CONSUMER_PAGE; + retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); + + reportId = 3; + bit = REPORT3_MAX_VALID_BIT + 1; + byte = REPORT3_MAX_VALID_BYTE; + page = TELEPHONY_DEVICE_PAGE; + retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); +} + +void test_underflow_bit_hidSetReportItem( void ) +{ + const unsigned char header = construct_usage_header( 0 ); + + unsigned reportId = 1; + unsigned bit = REPORT1_MIN_VALID_BIT - 1; // See the hidConfigurableElements list in hid_report_descriptors.c. + unsigned byte = REPORT1_MIN_VALID_BYTE; + unsigned char page = KEYBOARD_PAGE; + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); + + reportId = 2; + bit = REPORT2_MIN_VALID_BIT - 1; + byte = REPORT2_MIN_VALID_BYTE; + page = CONSUMER_PAGE; + retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); + + reportId = 3; + bit = REPORT3_MIN_VALID_BIT - 1; + byte = REPORT3_MIN_VALID_BYTE; + page = TELEPHONY_DEVICE_PAGE; + retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); +} + +void test_overflow_byte_hidSetReportItem( void ) +{ + const unsigned char header = construct_usage_header( 0 ); + + unsigned reportId = 1; + unsigned bit = REPORT1_MAX_VALID_BIT; // See the hidConfigurableElements list in hid_report_descriptors.c. + unsigned byte = REPORT1_MAX_VALID_BYTE + 1; + unsigned char page = KEYBOARD_PAGE; + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); + + reportId = 2; + bit = REPORT2_MAX_VALID_BIT; + byte = REPORT2_MAX_VALID_BYTE + 1; + page = CONSUMER_PAGE; + retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); + + reportId = 3; + bit = REPORT3_MAX_VALID_BIT; + byte = REPORT3_MAX_VALID_BYTE + 1; + page = TELEPHONY_DEVICE_PAGE; + retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); +} + +void test_underflow_byte_hidSetReportItem( void ) +{ + const unsigned char header = construct_usage_header( 0 ); + + unsigned reportId = 1; + unsigned bit = REPORT1_MIN_VALID_BIT; // See the hidConfigurableElements list in hid_report_descriptors.c. + unsigned byte = REPORT1_MIN_VALID_BYTE - 1; + unsigned char page = KEYBOARD_PAGE; + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); + + reportId = 2; + bit = REPORT2_MIN_VALID_BIT; + byte = REPORT2_MIN_VALID_BYTE - 1; + page = CONSUMER_PAGE; + retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); + + reportId = 3; + bit = REPORT3_MIN_VALID_BIT; + byte = REPORT3_MIN_VALID_BYTE - 1; + page = TELEPHONY_DEVICE_PAGE; + retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); +} + +// Size range tests +void test_max_size_hidSetReportItem( void ) +{ + const unsigned reportId = 1; + const unsigned bit = REPORT1_MIN_VALID_BIT; + const unsigned byte = REPORT1_MIN_VALID_BYTE; + const unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0x00 }; + const unsigned char header = construct_usage_header( HID_REPORT_ITEM_MAX_SIZE ); + const unsigned char page = KEYBOARD_PAGE; + + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); +} + +void test_min_size_hidSetReportItem( void ) +{ + const unsigned reportId = 1; + const unsigned bit = REPORT1_MIN_VALID_BIT; + const unsigned byte = REPORT1_MIN_VALID_BYTE; + const unsigned char header = construct_usage_header( 0x00 ); + const unsigned char page = KEYBOARD_PAGE; + + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); +} + +void test_unsupported_size_hidSetReportItem( void ) +{ + const unsigned reportId = 0; + const unsigned bit = REPORT1_MIN_VALID_BIT; + const unsigned byte = REPORT1_MIN_VALID_BYTE; + const unsigned char header = construct_usage_header( 0x03 ); + const unsigned char page = KEYBOARD_PAGE; + + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_HEADER, retVal ); +} + +// Combined function tests +void test_initial_modification_without_subsequent_preparation( void ) +{ + const unsigned reportId = 2; + const unsigned bit = REPORT2_MIN_VALID_BIT; + const unsigned byte = REPORT2_MIN_VALID_BYTE; + const unsigned char data[ 1 ] = { LOUDNESS_CONTROL }; + const unsigned char header = construct_usage_header( sizeof data / sizeof( unsigned char )); + const unsigned char page = CONSUMER_PAGE; + + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); + + unsigned char* reportDescPtr = hidGetReportDescriptor(); + TEST_ASSERT_NULL( reportDescPtr ); +} + +void test_initial_modification_with_subsequent_preparation( void ) +{ + const unsigned reportId = 2; + const unsigned bit = REPORT2_MIN_VALID_BIT; + const unsigned byte = REPORT2_MIN_VALID_BYTE; + const unsigned char data[ 1 ] = { LOUDNESS_CONTROL }; + const unsigned char header = construct_usage_header( sizeof data / sizeof( unsigned char )); + const unsigned char page = CONSUMER_PAGE; + + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); + + hidPrepareReportDescriptor(); + unsigned char* reportDescPtr = hidGetReportDescriptor(); + TEST_ASSERT_NOT_NULL( reportDescPtr ); +} + +void test_initial_modification_with_subsequent_verification_1( void ) +{ + const unsigned reportId = 2; + const unsigned bit = REPORT2_MIN_VALID_BIT; + const unsigned byte = REPORT2_MIN_VALID_BYTE; + + unsigned char get_data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xFF, 0xFF }; + unsigned char get_header = 0xFF; + unsigned char get_page = 0xFF; + + const unsigned char set_data[ 1 ] = { LOUDNESS_CONTROL }; + const unsigned char set_header = construct_usage_header( sizeof set_data / sizeof( unsigned char )); + const unsigned char set_page = CONSUMER_PAGE; + + unsigned setRetVal = hidSetReportItem( reportId, byte, bit, set_page, set_header, set_data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, setRetVal ); + + unsigned getRetVal = hidGetReportItem( reportId, byte, bit, &get_page, &get_header, get_data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, getRetVal ); + TEST_ASSERT_EQUAL_UINT( set_page, get_page ); + TEST_ASSERT_EQUAL_UINT( set_header, get_header ); + TEST_ASSERT_EQUAL_UINT( set_data[ 0 ], get_data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0, get_data[ 1 ]); // Should be MSB of data from hidUsageByte0Bit0 in hid_report_descriptor.h +} + +void test_initial_modification_with_subsequent_verification_2( void ) +{ + const unsigned reportId = 3; + const unsigned bit = REPORT3_MIN_VALID_BIT; + const unsigned byte = REPORT3_MIN_VALID_BYTE; + + { + unsigned char get_data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xFF, 0xFF }; + unsigned char get_header = 0xFF; + unsigned char get_page = 0xFF; + + const unsigned char set_data[ 2 ] = {( PHONE_HOST_HOLD & 0x00FF ), (( PHONE_HOST_HOLD & 0xFF00 ) >> 8 )}; + const unsigned char set_header = construct_usage_header( sizeof set_data / sizeof( unsigned char )); + const unsigned char set_page = TELEPHONY_DEVICE_PAGE; + + unsigned setRetVal = hidSetReportItem( reportId, byte, bit, set_page, set_header, set_data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, setRetVal ); + + unsigned getRetVal = hidGetReportItem( reportId, byte, bit, &get_page, &get_header, get_data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, getRetVal ); + TEST_ASSERT_EQUAL_UINT( set_page, get_page ); + TEST_ASSERT_EQUAL_UINT( set_header, get_header ); + TEST_ASSERT_EQUAL_UINT( set_data[ 0 ], get_data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( set_data[ 1 ], get_data[ 1 ]); + } + + { + unsigned char get_data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xFF, 0xFF }; + unsigned char get_header = 0xFF; + unsigned char get_page = 0xFF; + + const unsigned char set_data[ 1 ] = { PHONE_KEY_9 }; + const unsigned char set_header = construct_usage_header( sizeof set_data / sizeof( unsigned char )); + const unsigned char set_page = TELEPHONY_DEVICE_PAGE; + + unsigned setRetVal = hidSetReportItem( reportId, byte, bit, set_page, set_header, set_data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, setRetVal ); + + unsigned getRetVal = hidGetReportItem( reportId, byte, bit, &get_page, &get_header, get_data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, getRetVal ); + TEST_ASSERT_EQUAL_UINT( set_page, get_page ); + TEST_ASSERT_EQUAL_UINT( set_header, get_header ); + TEST_ASSERT_EQUAL_UINT( set_data[ 0 ], get_data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0, get_data[ 1 ]); // The call to hidSetReportItem with size 1 in the header should return the MSB to zero + } +} + +//setIdle and associated timing functionality tests +void test_set_idle( void ) +{ + unsigned reportId = 1; + unsigned reportId2 = 2; + + unsigned setIdle = hidIsIdleActive( reportId ); + TEST_ASSERT_EQUAL_UINT( 0, setIdle ); + + setIdle = hidIsIdleActive( reportId2 ); + TEST_ASSERT_EQUAL_UINT( 0, setIdle ); + + hidSetIdle( reportId, 1 ); + setIdle = hidIsIdleActive( reportId ); + TEST_ASSERT_EQUAL_UINT( 1, setIdle ); + + setIdle = hidIsIdleActive( reportId2 ); + TEST_ASSERT_EQUAL_UINT( 0, setIdle ); +} + +void test_set_all_idle( void ) +{ + unsigned reportId = 1; + unsigned reportId2 = 2; + + unsigned setIdle = hidIsIdleActive( reportId ); + TEST_ASSERT_EQUAL_UINT( 0, setIdle ); + + setIdle = hidIsIdleActive( reportId2 ); + TEST_ASSERT_EQUAL_UINT( 0, setIdle ); + + for ( reportId = 1; reportId <= HID_REPORT_COUNT; ++reportId ) { + hidSetIdle( reportId, 1 ); + setIdle = hidIsIdleActive( reportId ); + TEST_ASSERT_EQUAL_UINT( 1, setIdle ); + } +} + +void test_change_pending( void ) +{ + unsigned reportId = 1; + unsigned reportId2 = 2; + + unsigned changePending = hidIsChangePending( reportId ); + TEST_ASSERT_EQUAL_UINT( 0, changePending ); + + changePending = hidIsChangePending( reportId2 ); + TEST_ASSERT_EQUAL_UINT( 0, changePending ); + + hidSetChangePending( reportId ); + changePending = hidIsChangePending( reportId ); + TEST_ASSERT_EQUAL_UINT( 1, changePending ); + + changePending = hidIsChangePending( reportId2 ); + TEST_ASSERT_EQUAL_UINT( 0, changePending ); +} + +void test_change_pending_all( void ) +{ + unsigned reportId = 1; + + unsigned changePending = hidIsChangePending( reportId ); + TEST_ASSERT_EQUAL_UINT( 0, changePending ); + + for ( reportId = 1; reportId <= HID_REPORT_COUNT; ++reportId ) { + hidSetChangePending( reportId ); + changePending = hidIsChangePending( reportId ); + TEST_ASSERT_EQUAL_UINT( 1, changePending ); + } +} + +void test_report_time( void ) +{ + unsigned reportTime1 = 123; + unsigned reportTime2 = 456; + + hidCaptureReportTime(1, reportTime1); + hidCaptureReportTime(2, reportTime2); + reportTime1 = hidGetReportTime(1); + reportTime2 = hidGetReportTime(2); + + TEST_ASSERT_EQUAL_UINT(123, reportTime1); + TEST_ASSERT_EQUAL_UINT(456, reportTime2); +} + +void test_report_time_calc( void ) +{ + unsigned reportTime1 = 123; + unsigned reportTime2 = 456; + unsigned reportPeriod1 = 10; + unsigned reportPeriod2 = 5; + + hidCaptureReportTime(1, reportTime1); + hidCaptureReportTime(2, reportTime2); + hidSetReportPeriod(1, reportPeriod1); + hidSetReportPeriod(2, reportPeriod2); + reportTime1 = hidGetReportTime(1); + reportTime2 = hidGetReportTime(2); + reportPeriod1 = hidGetReportPeriod(1); + reportPeriod2 = hidGetReportPeriod(2); + + TEST_ASSERT_EQUAL_UINT(123, reportTime1); + TEST_ASSERT_EQUAL_UINT(456, reportTime2); + TEST_ASSERT_EQUAL_UINT(10, reportPeriod1); + TEST_ASSERT_EQUAL_UINT(5, reportPeriod2); + + hidCalcNextReportTime(1); + hidCalcNextReportTime(2); + unsigned nextReportTime1 = hidGetNextReportTime(1); + unsigned nextReportTime2 = hidGetNextReportTime(2); + TEST_ASSERT_EQUAL_UINT( reportTime1 + reportPeriod1, nextReportTime1 ); + TEST_ASSERT_EQUAL_UINT( reportTime2 + reportPeriod2, nextReportTime2 ); +} diff --git a/tests/xua_unit_tests/src/test_simple/hid_report_descriptor.h b/tests/xua_unit_tests/src/test_simple/hid_report_descriptor.h new file mode 100644 index 00000000..ced88b83 --- /dev/null +++ b/tests/xua_unit_tests/src/test_simple/hid_report_descriptor.h @@ -0,0 +1,145 @@ +// Copyright 2021-2022 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +#ifndef __hid_report_descriptor_h__ +#define __hid_report_descriptor_h__ + +#include "xua_hid_report.h" + +/* + * Define non-configurable items in the HID Report descriptor. + * (These are short items as the location field isn't relevant for them) + */ +static const USB_HID_Short_Item_t hidCollectionApplication = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_MAIN, HID_REPORT_ITEM_TAG_COLLECTION), + .data = { 0x01, 0x00 } }; +static const USB_HID_Short_Item_t hidCollectionEnd = { + .header = HID_REPORT_SET_HEADER(0, HID_REPORT_ITEM_TYPE_MAIN, HID_REPORT_ITEM_TAG_END_COLLECTION), + .data = { 0x00, 0x00 } }; +static const USB_HID_Short_Item_t hidCollectionLogical = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_MAIN, HID_REPORT_ITEM_TAG_COLLECTION), + .data = { 0x02, 0x00 } }; + +static const USB_HID_Short_Item_t hidInputConstArray = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_MAIN, HID_REPORT_ITEM_TAG_INPUT), + .data = { 0x01, 0x00 } }; +static const USB_HID_Short_Item_t hidInputDataVar = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_MAIN, HID_REPORT_ITEM_TAG_INPUT), + .data = { 0x02, 0x00 } }; + +static const USB_HID_Short_Item_t hidLogicalMaximum0 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_LOGICAL_MAXIMUM), + .data = { 0x00, 0x00 } }; +static const USB_HID_Short_Item_t hidLogicalMaximum1 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_LOGICAL_MAXIMUM), + .data = { 0x01, 0x00 } }; +static const USB_HID_Short_Item_t hidLogicalMinimum0 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_LOGICAL_MINIMUM), + .data = { 0x00, 0x00 } }; + +static const USB_HID_Short_Item_t hidReportCount1 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_COUNT), + .data = { 0x01, 0x00 } }; +static const USB_HID_Short_Item_t hidReportCount6 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_COUNT), + .data = { 0x06, 0x00 } }; +static const USB_HID_Short_Item_t hidReportCount7 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_COUNT), + .data = { 0x07, 0x00 } }; +static const USB_HID_Short_Item_t hidReportSize1 = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_REPORT_SIZE), + .data = { 0x01, 0x00 } }; + +static const USB_HID_Short_Item_t hidUsageConsumerControl = { + .header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .data = { 0x01, 0x00 } }; + +/* + * Define the HID Report Descriptor Item, Usage Page, Report ID and length for each HID Report + * For internal purposes, a report element with ID of 0 must be included if report IDs are not being used. + */ +static const USB_HID_Report_Element_t hidReportPageConsumer = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_GLOBAL, HID_REPORT_ITEM_TAG_USAGE_PAGE), + .item.data = { USB_HID_USAGE_PAGE_ID_CONSUMER, 0x00 }, + .location = HID_REPORT_SET_LOC( 0, 2, 0, 0 ) +}; + +/* + * Define configurable items in the HID Report descriptor. + */ +static USB_HID_Report_Element_t hidUsageByte0Bit0 = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .item.data = { 0xE2, 0x00 }, + .location = HID_REPORT_SET_LOC(0, 0, 0, 0) +}; // Mute + +static USB_HID_Report_Element_t hidUsageByte1Bit7 = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .item.data = { 0xEA, 0x00 }, + .location = HID_REPORT_SET_LOC(0, 0, 1, 7) +}; // Vol- +static USB_HID_Report_Element_t hidUsageByte1Bit0 = { + .item.header = HID_REPORT_SET_HEADER(1, HID_REPORT_ITEM_TYPE_LOCAL, HID_REPORT_ITEM_TAG_USAGE), + .item.data = { 0xE9, 0x00 }, + .location = HID_REPORT_SET_LOC(0, 0, 1, 0) +}; // Vol+ + +/* + * List the configurable elements in the HID Report descriptor. + */ +static USB_HID_Report_Element_t* const hidConfigurableElements[] = { + &hidUsageByte0Bit0, + &hidUsageByte1Bit0, + &hidUsageByte1Bit7 +}; + +/* + * List HID Reports, one per Report ID. This should be a usage page item with the relevant + * If not using report IDs - still have one with report ID 0 + */ +static const USB_HID_Report_Element_t* const hidReports[] = { + &hidReportPageConsumer +}; + +/* + * List all items in the HID Report descriptor. + */ +static const USB_HID_Short_Item_t* const hidReportDescriptorItems[] = { + &(hidReportPageConsumer.item), + &hidUsageConsumerControl, + &hidCollectionApplication, + &hidReportSize1, + &hidLogicalMinimum0, + &hidCollectionLogical, // Byte 0 + &hidLogicalMaximum1, + &hidReportCount1, + &(hidUsageByte0Bit0.item), + &hidInputDataVar, + &hidLogicalMaximum0, + &hidReportCount7, + &hidInputConstArray, + &hidCollectionEnd, + &hidCollectionLogical, // Byte 1 + &hidLogicalMaximum1, + &hidReportCount1, + &(hidUsageByte1Bit0.item), + &hidInputDataVar, + &hidLogicalMaximum0, + &hidReportCount6, + &hidInputConstArray, + &hidReportCount1, + &hidLogicalMaximum1, + &(hidUsageByte1Bit7.item), + &hidInputDataVar, + &hidCollectionEnd, + &hidCollectionEnd +}; + +/* + * Define the number of HID Reports + * Due to XC not supporting designated initializers, this constant has a hard-coded value. + * It must equal ( sizeof hidReports / sizeof ( USB_HID_Report_Element_t* )) + */ +#define HID_REPORT_COUNT ( 1 ) + +#endif // __hid_report_descriptor_h__ diff --git a/tests/xua_unit_tests/src/test_simple/test_hid.c b/tests/xua_unit_tests/src/test_simple/test_hid.c new file mode 100644 index 00000000..3bd930bc --- /dev/null +++ b/tests/xua_unit_tests/src/test_simple/test_hid.c @@ -0,0 +1,650 @@ +// Copyright 2021-2022 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#include +#include + +#include "xua_unit_tests.h" +#include "xua_hid_report.h" + +// Test constants related to the report descriptor defined in hid_report_descriptor.h +#define MAX_VALID_BIT ( 7 ) +#define MAX_VALID_BYTE ( 1 ) + +#define MIN_VALID_BIT ( 0 ) +#define MIN_VALID_BYTE ( 0 ) + +#define HID_REPORT_LENGTH ( 2 ) +#define HID_REPORT_COUNT ( 1 ) +#define HID_REPORTID_LIMIT ( 1 ) + +// Constants from the USB HID Usage Tables +#define CONSUMER_CONTROL_PAGE ( 0x0C ) +#define LOUDNESS_CONTROL ( 0xE7 ) +#define AL_CONTROL_PANEL ( 0x019F ) + +static unsigned construct_usage_header( unsigned size ) +{ + unsigned header = 0x00; + + header |= ( HID_REPORT_ITEM_USAGE_TAG << HID_REPORT_ITEM_HDR_TAG_SHIFT ) & HID_REPORT_ITEM_HDR_TAG_MASK; + header |= ( HID_REPORT_ITEM_USAGE_TYPE << HID_REPORT_ITEM_HDR_TYPE_SHIFT ) & HID_REPORT_ITEM_HDR_TYPE_MASK; + + header |= ( size << HID_REPORT_ITEM_HDR_SIZE_SHIFT ) & HID_REPORT_ITEM_HDR_SIZE_MASK; + + return header; +} + +void setUp( void ) +{ + hidReportInit(); + hidResetReportDescriptor(); +} + +void test_validate_report( void ) { + unsigned retVal = hidReportValidate(); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); +} + +void test_reportid_in_use( void ) { + unsigned reportIdInUse = hidIsReportIdInUse(); + TEST_ASSERT_EQUAL_UINT( 0, reportIdInUse ); +} + +void test_get_next_valid_report_id( void ) { + unsigned reportId = 0U; + + reportId = hidGetNextValidReportId(reportId); + TEST_ASSERT_EQUAL_UINT( 0, reportId ); + + reportId = hidGetNextValidReportId(reportId); + TEST_ASSERT_EQUAL_UINT( 0, reportId ); +} + +void test_is_report_id_valid( void ) { + unsigned isValid = 0; + + unsigned reportId = 0; + isValid = hidIsReportIdValid( reportId ); + TEST_ASSERT_EQUAL_UINT( 1, isValid ); + + reportId = 1; + isValid = hidIsReportIdValid( reportId ); + TEST_ASSERT_EQUAL_UINT( 0, isValid ); +} + +// Basic report descriptor tests +void test_unprepared_hidGetReportDescriptor( void ) +{ + const unsigned reportId = 0; + unsigned char* reportDescPtr = hidGetReportDescriptor(); + TEST_ASSERT_NULL( reportDescPtr ); + + unsigned reportLength = hidGetReportLength( reportId ); + TEST_ASSERT_EQUAL_UINT( 0, reportLength ); +} + +void test_prepared_hidGetReportDescriptor( void ) +{ + const unsigned reportId = 0; + + hidPrepareReportDescriptor(); + unsigned char* reportDescPtr = hidGetReportDescriptor(); + TEST_ASSERT_NOT_NULL( reportDescPtr ); + + unsigned reportLength = hidGetReportLength( reportId ); + TEST_ASSERT_EQUAL_UINT( HID_REPORT_LENGTH, reportLength ); +} + +void test_reset_unprepared_hidGetReportDescriptor( void ) +{ + hidPrepareReportDescriptor(); + hidResetReportDescriptor(); + unsigned char* reportDescPtr = hidGetReportDescriptor(); + TEST_ASSERT_NULL( reportDescPtr ); +} + +void test_reset_prepared_hidGetReportDescriptor( void ) +{ + hidPrepareReportDescriptor(); + hidResetReportDescriptor(); + hidPrepareReportDescriptor(); + unsigned char* reportDescPtr = hidGetReportDescriptor(); + TEST_ASSERT_NOT_NULL( reportDescPtr ); +} + +void test_report_id_limit( void ) +{ + unsigned reportIdLimit = hidGetReportIdLimit(); + TEST_ASSERT_EQUAL_UINT( HID_REPORTID_LIMIT, reportIdLimit ); +} + +// Basic item tests +void test_max_loc_hidGetReportItem( void ) +{ + const unsigned reportId = 0; + const unsigned bit = MAX_VALID_BIT; + const unsigned byte = MAX_VALID_BYTE; + unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ]; + unsigned char header; + unsigned char page; + + unsigned retVal = hidGetReportItem( reportId, byte, bit, &page, &header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); + TEST_ASSERT_EQUAL_UINT( CONSUMER_CONTROL_PAGE, page ); + TEST_ASSERT_EQUAL_UINT( 0x09, header ); + TEST_ASSERT_EQUAL_UINT( 0xEA, data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0x00, data[ 1 ]); +} + +void test_min_loc_hidGetReportItem( void ) +{ + const unsigned reportId = 0; + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ]; + unsigned char header; + unsigned char page; + + unsigned retVal = hidGetReportItem( reportId, byte, bit, &page, &header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); + TEST_ASSERT_EQUAL_UINT( CONSUMER_CONTROL_PAGE, page ); + TEST_ASSERT_EQUAL_UINT( 0x09, header ); + TEST_ASSERT_EQUAL_UINT( 0xE2, data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0x00, data[ 1 ]); +} + +void test_overflow_bit_hidGetReportItem( void ) +{ + const unsigned reportId = 0; + const unsigned bit = MAX_VALID_BIT + 1; + const unsigned byte = MAX_VALID_BYTE; + unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xBA, 0xD1 }; + unsigned char header = 0xAA; + unsigned char page = 0x44; + + unsigned retVal = hidGetReportItem( reportId, byte, bit, &page, &header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); + TEST_ASSERT_EQUAL_UINT( 0x44, page ); + TEST_ASSERT_EQUAL_UINT( 0xAA, header ); + TEST_ASSERT_EQUAL_UINT( 0xBA, data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0xD1, data[ 1 ]); +} + +void test_overflow_byte_hidGetReportItem( void ) +{ + const unsigned reportId = 0; + const unsigned bit = MAX_VALID_BIT; + const unsigned byte = MAX_VALID_BYTE + 1; + unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xBA, 0xD1 }; + unsigned char header = 0xAA; + unsigned char page = 0x44; + + unsigned retVal = hidGetReportItem( reportId, byte, bit, &page, &header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); + TEST_ASSERT_EQUAL_UINT( 0x44, page ); + TEST_ASSERT_EQUAL_UINT( 0xAA, header ); + TEST_ASSERT_EQUAL_UINT( 0xBA, data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0xD1, data[ 1 ]); +} + +void test_underflow_bit_hidGetReportItem( void ) +{ + const unsigned reportId = 0; + const int bit = MIN_VALID_BIT - 1; + const unsigned byte = MIN_VALID_BYTE; + unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xBA, 0xD1 }; + unsigned char header = 0xAA; + unsigned char page = 0x44; + + unsigned retVal = hidGetReportItem( reportId, byte, ( unsigned ) bit, &page, &header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); + TEST_ASSERT_EQUAL_UINT( 0x44, page ); + TEST_ASSERT_EQUAL_UINT( 0xAA, header ); + TEST_ASSERT_EQUAL_UINT( 0xBA, data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0xD1, data[ 1 ]); +} + +void test_underflow_byte_hidGetReportItem( void ) +{ + const unsigned reportId = 0; + const unsigned bit = MIN_VALID_BIT; + const int byte = MIN_VALID_BYTE - 1; + unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xBA, 0xD1 }; + unsigned char header = 0xAA; + unsigned char page = 0x44; + + unsigned retVal = hidGetReportItem( reportId, ( unsigned ) byte, bit, &page, &header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); + TEST_ASSERT_EQUAL_UINT( 0x44, page ); + TEST_ASSERT_EQUAL_UINT( 0xAA, header ); + TEST_ASSERT_EQUAL_UINT( 0xBA, data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0xD1, data[ 1 ]); +} + +// Configurable and non-configurable item tests +void test_configurable_item_hidSetReportItem( void ) +{ + const unsigned reportId = 0; + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char data[ 1 ] = { LOUDNESS_CONTROL }; + const unsigned char header = construct_usage_header( sizeof data / sizeof( unsigned char )); + const unsigned char page = CONSUMER_CONTROL_PAGE; + + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); +} + +void test_nonconfigurable_item_hidSetReportItem( void ) +{ + const unsigned reportId = 0; + const unsigned bit = MAX_VALID_BIT; // This bit and byte combination should not appear in the + const unsigned byte = MIN_VALID_BYTE; // hidConfigurableElements list in hid_report_descriptors.c. + const unsigned char data[ 1 ] = { LOUDNESS_CONTROL }; + const unsigned char header = construct_usage_header( sizeof data / sizeof( unsigned char )); + const unsigned char page = CONSUMER_CONTROL_PAGE; + + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); +} + +// Bit range tests +void test_max_bit_hidSetReportItem( void ) +{ + const unsigned reportId = 0; + const unsigned bit = MAX_VALID_BIT; // Only byte 1 has bit 7 not reserved, See the + const unsigned byte = MAX_VALID_BYTE; // hidConfigurableElements list in hid_report_descriptors.c. + const unsigned char header = construct_usage_header( 0 ); + const unsigned char page = CONSUMER_CONTROL_PAGE; + + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); +} + +void test_min_bit_hidSetReportItem( void ) +{ + const unsigned reportId = 0; + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char header = construct_usage_header( 0 ); + const unsigned char page = CONSUMER_CONTROL_PAGE; + + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); +} + +void test_overflow_bit_hidSetReportItem( void ) +{ + const unsigned reportId = 0; + const unsigned bit = MAX_VALID_BIT + 1; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char header = construct_usage_header( 0 ); + const unsigned char page = CONSUMER_CONTROL_PAGE; + + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); +} + +void test_underflow_bit_hidSetReportItem( void ) +{ + const unsigned reportId = 0; + const int bit = MIN_VALID_BIT - 1; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char header = construct_usage_header( 0 ); + const unsigned char page = CONSUMER_CONTROL_PAGE; + + unsigned retVal = hidSetReportItem( reportId, byte, ( unsigned ) bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); +} + +// Byte range tests +void test_max_byte_hidSetReportItem( void ) +{ + const unsigned reportId = 0; + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MAX_VALID_BYTE; + const unsigned char header = construct_usage_header( 0 ); + const unsigned char page = CONSUMER_CONTROL_PAGE; + + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); +} + +void test_min_byte_hidSetReportItem( void ) +{ + const unsigned reportId = 0; + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char header = construct_usage_header( 0 ); + const unsigned char page = CONSUMER_CONTROL_PAGE; + + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); +} + +void test_overflow_byte_hidSetReportItem( void ) +{ + const unsigned reportId = 0; + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MAX_VALID_BYTE + 1; + const unsigned char header = construct_usage_header( 0 ); + const unsigned char page = CONSUMER_CONTROL_PAGE; + + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); +} + +void test_underflow_byte_hidSetReportItem( void ) +{ + const unsigned reportId = 0; + const unsigned bit = MIN_VALID_BIT; + const int byte = MIN_VALID_BYTE - 1; + const unsigned char header = construct_usage_header( 0 ); + const unsigned char page = CONSUMER_CONTROL_PAGE; + + unsigned retVal = hidSetReportItem( reportId, ( unsigned ) byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_LOCATION, retVal ); +} + +// Size range tests +void test_max_size_hidSetReportItem( void ) +{ + const unsigned reportId = 0; + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0x00 }; + const unsigned char header = construct_usage_header( HID_REPORT_ITEM_MAX_SIZE ); + const unsigned char page = CONSUMER_CONTROL_PAGE; + + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); +} + +void test_min_size_hidSetReportItem( void ) +{ + const unsigned reportId = 0; + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char header = construct_usage_header( 0x00 ); + const unsigned char page = CONSUMER_CONTROL_PAGE; + + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); +} + +void test_unsupported_size_hidSetReportItem( void ) +{ + const unsigned reportId = 0; + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char header = construct_usage_header( 0x03 ); + const unsigned char page = CONSUMER_CONTROL_PAGE; + + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_HEADER, retVal ); +} + +// Header tag and type tests +void test_bad_tag_hidSetReportItem( void ) +{ + const unsigned reportId = 0; + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char good_header = construct_usage_header( 0x00 ); + const unsigned char page = CONSUMER_CONTROL_PAGE; + + for( unsigned tag = 0x01; tag <= 0x0F; ++tag ) { + unsigned char bad_header = good_header | (( tag << HID_REPORT_ITEM_HDR_TAG_SHIFT ) & HID_REPORT_ITEM_HDR_TAG_MASK ); + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, bad_header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_HEADER, retVal ); + } +} + +void test_global_type_hidSetReportItem( void ) +{ + const unsigned reportId = 0; + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char header = ( construct_usage_header( 0x00 ) & ~HID_REPORT_ITEM_HDR_TYPE_MASK ) | + (( HID_REPORT_ITEM_TYPE_GLOBAL << HID_REPORT_ITEM_HDR_TYPE_SHIFT ) & HID_REPORT_ITEM_HDR_TYPE_MASK ); + const unsigned char page = CONSUMER_CONTROL_PAGE; + + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_HEADER, retVal ); +} + +void test_local_type_hidSetReportItem( void ) +{ + const unsigned reportId = 0; + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char header = ( construct_usage_header( 0x00 ) & ~HID_REPORT_ITEM_HDR_TYPE_MASK ) | + (( HID_REPORT_ITEM_TYPE_LOCAL << HID_REPORT_ITEM_HDR_TYPE_SHIFT ) & HID_REPORT_ITEM_HDR_TYPE_MASK ); + const unsigned char page = CONSUMER_CONTROL_PAGE; + + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); +} + +void test_main_type_hidSetReportItem( void ) +{ + const unsigned reportId = 0; + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char header = ( construct_usage_header( 0x00 ) & ~HID_REPORT_ITEM_HDR_TYPE_MASK ) | + (( HID_REPORT_ITEM_TYPE_MAIN << HID_REPORT_ITEM_HDR_TYPE_SHIFT ) & HID_REPORT_ITEM_HDR_TYPE_MASK ); + const unsigned char page = CONSUMER_CONTROL_PAGE; + + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_HEADER, retVal ); +} + +void test_reserved_type_hidSetReportItem( void ) +{ + const unsigned reportId = 0; + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char header = ( construct_usage_header( 0x00 ) & ~HID_REPORT_ITEM_HDR_TYPE_MASK ) | + (( HID_REPORT_ITEM_TYPE_RESERVED << HID_REPORT_ITEM_HDR_TYPE_SHIFT ) & HID_REPORT_ITEM_HDR_TYPE_MASK ); + const unsigned char page = CONSUMER_CONTROL_PAGE; + + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, NULL ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_BAD_HEADER, retVal ); +} + +// Combined function tests +void test_initial_modification_without_subsequent_preparation( void ) +{ + const unsigned reportId = 0; + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char data[ 1 ] = { LOUDNESS_CONTROL }; + const unsigned char header = construct_usage_header( sizeof data / sizeof( unsigned char )); + const unsigned char page = CONSUMER_CONTROL_PAGE; + + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); + + unsigned char* reportDescPtr = hidGetReportDescriptor(); + TEST_ASSERT_NULL( reportDescPtr ); +} + +void test_initial_modification_with_subsequent_preparation( void ) +{ + const unsigned reportId = 0; + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char data[ 1 ] = { LOUDNESS_CONTROL }; + const unsigned char header = construct_usage_header( sizeof data / sizeof( unsigned char )); + const unsigned char page = CONSUMER_CONTROL_PAGE; + + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); + + hidPrepareReportDescriptor(); + unsigned char* reportDescPtr = hidGetReportDescriptor(); + TEST_ASSERT_NOT_NULL( reportDescPtr ); +} + +void test_initial_modification_with_subsequent_verification_1( void ) +{ + const unsigned reportId = 0; + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + + unsigned char get_data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xFF, 0xFF }; + unsigned char get_header = 0xFF; + unsigned char get_page = 0xFF; + + const unsigned char set_data[ 1 ] = { LOUDNESS_CONTROL }; + const unsigned char set_header = construct_usage_header( sizeof set_data / sizeof( unsigned char )); + const unsigned char set_page = CONSUMER_CONTROL_PAGE; + + unsigned setRetVal = hidSetReportItem( reportId, byte, bit, set_page, set_header, set_data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, setRetVal ); + + unsigned getRetVal = hidGetReportItem( reportId, byte, bit, &get_page, &get_header, get_data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, getRetVal ); + TEST_ASSERT_EQUAL_UINT( set_page, get_page ); + TEST_ASSERT_EQUAL_UINT( set_header, get_header ); + TEST_ASSERT_EQUAL_UINT( set_data[ 0 ], get_data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0, get_data[ 1 ]); // Should be MSB of data from hidUsageByte0Bit0 in hid_report_descriptor.h +} + +void test_initial_modification_with_subsequent_verification_2( void ) +{ + const unsigned reportId = 0; + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + + { + unsigned char get_data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xFF, 0xFF }; + unsigned char get_header = 0xFF; + unsigned char get_page = 0xFF; + + const unsigned char set_data[ 2 ] = {( AL_CONTROL_PANEL & 0x00FF ), (( AL_CONTROL_PANEL & 0xFF00 ) >> 8 )}; + const unsigned char set_header = construct_usage_header( sizeof set_data / sizeof( unsigned char )); + const unsigned char set_page = CONSUMER_CONTROL_PAGE; + + unsigned setRetVal = hidSetReportItem( reportId, byte, bit, set_page, set_header, set_data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, setRetVal ); + + unsigned getRetVal = hidGetReportItem( reportId, byte, bit, &get_page, &get_header, get_data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, getRetVal ); + TEST_ASSERT_EQUAL_UINT( set_page, get_page ); + TEST_ASSERT_EQUAL_UINT( set_header, get_header ); + TEST_ASSERT_EQUAL_UINT( set_data[ 0 ], get_data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( set_data[ 1 ], get_data[ 1 ]); + } + + { + unsigned char get_data[ HID_REPORT_ITEM_MAX_SIZE ] = { 0xFF, 0xFF }; + unsigned char get_header = 0xFF; + unsigned char get_page = 0xFF; + + const unsigned char set_data[ 1 ] = { LOUDNESS_CONTROL }; + const unsigned char set_header = construct_usage_header( sizeof set_data / sizeof( unsigned char )); + const unsigned char set_page = CONSUMER_CONTROL_PAGE; + + unsigned setRetVal = hidSetReportItem( reportId, byte, bit, set_page, set_header, set_data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, setRetVal ); + + unsigned getRetVal = hidGetReportItem( reportId, byte, bit, &get_page, &get_header, get_data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, getRetVal ); + TEST_ASSERT_EQUAL_UINT( set_page, get_page ); + TEST_ASSERT_EQUAL_UINT( set_header, get_header ); + TEST_ASSERT_EQUAL_UINT( set_data[ 0 ], get_data[ 0 ]); + TEST_ASSERT_EQUAL_UINT( 0, get_data[ 1 ]); // The call to hidSetReportItem with size 1 in the header should return the MSB to zero + } +} + +void test_modification_without_subsequent_preparation( void ) +{ + hidPrepareReportDescriptor(); + unsigned char* reportDescPtr = hidGetReportDescriptor(); + TEST_ASSERT_NOT_NULL( reportDescPtr ); + + const unsigned reportId = 0; + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char data[ 1 ] = { LOUDNESS_CONTROL }; + const unsigned char header = construct_usage_header( sizeof data / sizeof( unsigned char )); + const unsigned char page = CONSUMER_CONTROL_PAGE; + + hidResetReportDescriptor(); + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); + + reportDescPtr = hidGetReportDescriptor(); + TEST_ASSERT_NULL( reportDescPtr ); +} + +void test_modification_with_subsequent_preparation( void ) +{ + hidPrepareReportDescriptor(); + unsigned char* reportDescPtr = hidGetReportDescriptor(); + TEST_ASSERT_NOT_NULL( reportDescPtr ); + + const unsigned reportId = 0; + const unsigned bit = MIN_VALID_BIT; + const unsigned byte = MIN_VALID_BYTE; + const unsigned char data[ 1 ] = { LOUDNESS_CONTROL }; + const unsigned char header = construct_usage_header( sizeof data / sizeof( unsigned char )); + const unsigned char page = CONSUMER_CONTROL_PAGE; + + hidResetReportDescriptor(); + unsigned retVal = hidSetReportItem( reportId, byte, bit, page, header, data ); + TEST_ASSERT_EQUAL_UINT( HID_STATUS_GOOD, retVal ); + + hidPrepareReportDescriptor(); + reportDescPtr = hidGetReportDescriptor(); + TEST_ASSERT_NOT_NULL( reportDescPtr ); +} + +//setIdle functionality tests +void test_set_idle( void ) +{ + unsigned reportId = 0; + + unsigned setIdle = hidIsIdleActive( reportId ); + TEST_ASSERT_EQUAL_UINT( 0, setIdle ); + + hidSetIdle( reportId, 1 ); + setIdle = hidIsIdleActive( reportId ); + TEST_ASSERT_EQUAL_UINT( 1, setIdle ); +} + +void test_change_pending( void ) +{ + unsigned reportId = 0; + + unsigned changePending = hidIsChangePending( reportId ); + TEST_ASSERT_EQUAL_UINT( 0, changePending ); + + hidSetChangePending( reportId ); + changePending = hidIsChangePending( reportId ); + TEST_ASSERT_EQUAL_UINT( 1, changePending ); + + hidClearChangePending( reportId ); + changePending = hidIsChangePending( reportId ); + TEST_ASSERT_EQUAL_UINT( 0, changePending ); +} + +void test_report_time( void ) +{ + unsigned reportTime = 123; + unsigned reportPeriod = 10; + + hidSetReportPeriod(0, reportPeriod); + hidCaptureReportTime(0, reportTime); + reportTime = hidGetReportTime(0); + reportPeriod = hidGetReportPeriod(0); + + TEST_ASSERT_EQUAL_UINT(123, reportTime); + TEST_ASSERT_EQUAL_UINT(10, reportPeriod); + + hidCalcNextReportTime(0); + + unsigned nextReportTime = hidGetNextReportTime(0); + TEST_ASSERT_EQUAL_UINT(133, nextReportTime); +} diff --git a/tests/xua_unit_tests/src/xua_conf.h b/tests/xua_unit_tests/src/xua_conf.h new file mode 100644 index 00000000..798c5e43 --- /dev/null +++ b/tests/xua_unit_tests/src/xua_conf.h @@ -0,0 +1,37 @@ +// Copyright 2017-2021 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +#define NUM_USB_CHAN_OUT 2 +#define NUM_USB_CHAN_IN 2 +#define I2S_CHANS_DAC 2 +#define I2S_CHANS_ADC 2 +#define MCLK_441 (512 * 44100) +#define MCLK_48 (512 * 48000) +#define MIN_FREQ 48000 +#define MAX_FREQ 48000 + +#define EXCLUDE_USB_AUDIO_MAIN +#define XUA_NUM_PDM_MICS 0 + +#define PDM_TILE 2 +#define XUD_TILE 1 +#define AUDIO_IO_TILE 1 + +#define MIXER 0 + +#define SPDIF_TX_INDEX 0 +#define VENDOR_STR "XMOS" +#define VENDOR_ID 0x20B1 +#define PRODUCT_STR_A2 "XMOS USB Audio Class" +#define PRODUCT_STR_A1 "XMOS USB Audio Class" +#define PID_AUDIO_1 1 +#define PID_AUDIO_2 2 +#define AUDIO_CLASS 2 +#define AUDIO_CLASS_FALLBACK 0 +#define BCD_DEVICE 0x1234 +#define XUA_DFU_EN 0 + +/* TODO */ +#define XUA_DFU XUA_DFU_EN + +#define FB_USE_REF_CLOCK 1 diff --git a/tests/xua_unit_tests/src/xua_unit_test_helper.xc b/tests/xua_unit_tests/src/xua_unit_test_helper.xc new file mode 100644 index 00000000..a2df0db9 --- /dev/null +++ b/tests/xua_unit_tests/src/xua_unit_test_helper.xc @@ -0,0 +1,28 @@ +// Copyright 2021 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#ifdef __XC__ + +#include +#include +#include + +#endif // __XC__ + + +in port p_mclk_in = XS1_PORT_1D; + +/* Clock-block declarations */ +clock clk_audio_bclk = on tile[0]: XS1_CLKBLK_1; /* Bit clock */ +clock clk_audio_mclk = on tile[0]: XS1_CLKBLK_2; /* Master clock */ + +// Supply missing but unused function +void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC) +{ + ; // nothing +} + +// Supply missing but unused function +void AudioHwInit() +{ + ; // nothing +} diff --git a/tests/xua_unit_tests/src/xua_unit_tests.h b/tests/xua_unit_tests/src/xua_unit_tests.h new file mode 100644 index 00000000..0b1d82d9 --- /dev/null +++ b/tests/xua_unit_tests/src/xua_unit_tests.h @@ -0,0 +1,9 @@ +// Copyright 2021 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#ifndef XUA_UNIT_TESTS_H_ +#define XUA_UNIT_TESTS_H_ + +#include "unity.h" +#include "xua_conf.h" + +#endif /* XUA_UNIT_TESTS_H_ */ diff --git a/tests/xua_unit_tests/wscript b/tests/xua_unit_tests/wscript new file mode 100644 index 00000000..a31dbfaf --- /dev/null +++ b/tests/xua_unit_tests/wscript @@ -0,0 +1,260 @@ +from __future__ import print_function +import glob +import os.path +import subprocess +import sys +from waflib import Options +from waflib.Build import BuildContext, CleanContext + +TARGETS = ['xcore200', 'xcoreai'] + +def get_ruby(): + """ + Check ruby is avaliable and return the command to invoke it. + """ + interpreter_name = 'ruby' + try: + dev_null = open(os.devnull, 'w') + # Call the version command to check the interpreter can be run + subprocess.check_call([interpreter_name, '--version'], + stdout=dev_null, + close_fds=True) + except OSError as e: + print("Failed to run Ruby interpreter: {}".format(e), file=sys.stderr) + exit(1) # TODO: Check this is the correct way to kill xwaf on error + + return interpreter_name + + +def get_unity_runner_generator(project_root_path): + """ + Check the Unity generate_test_runner script is avaliable, and return the + path to it. + """ + unity_runner_generator = os.path.join( + project_root_path, 'Unity', 'auto', 'generate_test_runner.rb') + if not os.path.exists(unity_runner_generator): + print("Unity repo not found in workspace", file=sys.stderr) + exit(1) # TODO: Check this is the correct way to kill xwaf on error + return unity_runner_generator + + +def get_test_name(test_path): + """ + Return the test name by removing the extension from the filename. + """ + return os.path.splitext(os.path.basename(test_path))[0] + + +def get_file_type(filename): + """ + Return the extension from the filename. + """ + return filename.rsplit('.')[-1:][0] + + +def generate_unity_runner(project_root_path, unity_test_path, unity_runner_dir, + unity_runner_suffix): + """ + Invoke the Unity runner generation script for the given test file, and + return the path to the generated file. The output directory will be created + if it does not already exist. + """ + runner_path = os.path.join(os.path.join(unity_runner_dir, get_test_name(unity_test_path))) + if not os.path.exists(runner_path): + os.makedirs(runner_path) + + unity_runner_path = os.path.join( + runner_path, get_test_name(unity_test_path) + unity_runner_suffix + + '.' + 'c') + + try: + subprocess.check_call([get_ruby(), + get_unity_runner_generator(project_root_path), + unity_test_path, + unity_runner_path]) + except OSError as e: + print("Ruby generator failed for {}\n\t{}".format(unity_test_path, e), + file=sys.stderr) + exit(1) # TODO: Check this is the correct way to kill xwaf on error + + +def add_unity_runner_build_config(waf_conf, project_root_path, unity_test_path, + unity_runner_build_flags, target): + """ + Add a config to xwaf to build each Unity test runner into an xCORE + executable. + """ + print(f"get_test_name(unity_test_path) = {get_test_name(unity_test_path)}. target = {target}") + waf_conf.setenv(get_test_name(unity_test_path) + '_' + target) + waf_conf.load('xwaf.compiler_xcc') + waf_conf.env.XCC_FLAGS = unity_runner_build_flags + waf_conf.env.PROJECT_ROOT = project_root_path + # TODO: can the xwaf boilerplate help here? + + +def prepare_unity_test_for_build(waf_conf, project_root_path, unity_test_path, + unity_runner_dir, unity_runner_suffix, target): + print("unity_test_path: " + str(unity_test_path)) + generate_unity_runner(project_root_path, unity_test_path, + unity_runner_dir, unity_runner_suffix) + runner_build_flags = '' # Could extract flags from the test name + add_unity_runner_build_config(waf_conf, project_root_path, unity_test_path, + runner_build_flags, target) + + +def find_unity_test_paths(unity_test_dir, unity_test_prefix): + """ + Return a list of all file paths with the unity_test_prefix found in the + unity_test_dir. + """ + file_list = [] + for root, dirs, files in os.walk(unity_test_dir): + for f in files: + if f.startswith(unity_test_prefix): + file_list.append(os.path.join(root,f)) + return file_list + + +def find_unity_tests(unity_test_dir, unity_test_prefix): + """ + Return a dictionary of all {test names, test language} pairs with the + unity_test_prefix found in the unity_test_dir. + """ + unity_test_paths = find_unity_test_paths(unity_test_dir, unity_test_prefix) + return {get_test_name(path): {"language": get_file_type(path), "dir": os.path.dirname(path)} + for path in unity_test_paths} + + +def generate_all_unity_runners(waf_conf, project_root_path, + unity_test_dir, unity_test_prefix, + unity_runner_dir, unity_runner_suffix): + """ + Generate a runner and a build config for each test file in the + unity_test_dir. + """ + # FIXME: pass unity_tests in? + unity_test_paths = find_unity_test_paths(unity_test_dir, unity_test_prefix) + for trgt in TARGETS: + for unity_test_path in unity_test_paths: + prepare_unity_test_for_build(waf_conf, project_root_path, + unity_test_path, + unity_runner_dir, unity_runner_suffix, trgt) + + +# TODO: can the xwaf boilerplate help here? +def create_waf_contexts(configs): + for trgt in TARGETS: + for test_name, params in configs.items(): + print(f"test_name {test_name}, test_language {params['language']}") + for ctx in (BuildContext, CleanContext): + raw_context = ctx.__name__.replace('Context', '').lower() + + class tmp(ctx): + cmd = raw_context + '_' + test_name + '_' + trgt + variant = test_name + '_' + trgt + #cmd = raw_context + '_' + test_name + #variant = test_name + language = params["language"] + target = trgt + runner = test_name + directory = params["dir"] + print(f"cmd {cmd}, variant {variant}, language {language}") + + +UNITY_TEST_DIR = 'src' +UNITY_TEST_PREFIX = 'test_' +UNITY_RUNNER_DIR = 'runners' +UNITY_RUNNER_SUFFIX = '_Runner' +UNITY_TESTS = find_unity_tests(UNITY_TEST_DIR, UNITY_TEST_PREFIX) + +print("UNITY_TESTS: " + str(UNITY_TESTS)) + +create_waf_contexts(UNITY_TESTS) + +def options(opt): + opt.add_option('--target', action='store', default='xcore200') + opt.load('xwaf.xcommon') + +def configure(conf): + # TODO: move the call to generate_all_unity_runners() to build() + project_root = os.path.join('..', '..', '..') + generate_all_unity_runners(conf, project_root, + UNITY_TEST_DIR, UNITY_TEST_PREFIX, + UNITY_RUNNER_DIR, UNITY_RUNNER_SUFFIX) + conf.load('xwaf.xcommon') + +def build(bld): + if not bld.variant: + print('Adding test runners to build queue') + trgt = [ + c for c in TARGETS if c == bld.options.target + ] + + if len(trgt) == 0: + bld.fatal('specify a target with --target.\nAvailable targets: {}'.format(', '.join(TARGETS))) + return + + for name in UNITY_TESTS: + Options.commands.insert(0, 'build_' + name + '_' + trgt[0]) + #Options.commands.insert(0, 'build_' + name) + print('Build queue {}'.format(Options.commands)) + else: + print('Building runner {}'.format(bld.runner)) + bld.env.XSCOPE = bld.path.find_resource('config.xscope') + + depends_on = ['lib_xua', + 'lib_xud', + 'lib_spdif', + 'lib_mic_array', + 'lib_logging', + 'lib_xassert', + 'Unity'] + + makefile_opts = {} + makefile_opts['SOURCE_DIRS'] = ['src', bld.directory, os.path.join('runners',bld.runner)] + if(bld.target == 'xcoreai'): + print('TARGET XCOREAI') + makefile_opts['TARGET'] = ['XCORE-AI-EXPLORER'] + else: + print('TARGET XCORE200') + makefile_opts['TARGET'] = ['XCORE-200-EXPLORER'] + + makefile_opts['INCLUDE_DIRS'] = ['src', + bld.directory, + '../../lib_xua/api', + '../../lib_xua/src/core/pdm_mics', + '../../lib_xua/src/hid', + '../../../lib_xud/lib_xud/src/user/class'] + + makefile_opts['XCC_FLAGS'] = ['-O2', + '-g', + '-Wall', + '-DHID_CONTROLS=1', + '-DUNITY_SUPPORT_64', + '-DUNITY_INCLUDE_DOUBLE', + '-DXUD_CORE_CLOCK=600', + '-DXUD_SERIES_SUPPORT=4'] + + makefile_opts['APP_NAME'] = [bld.variant] + makefile_opts['USED_MODULES'] = depends_on + makefile_opts['XCOMMON_MAKEFILE'] = ['Makefile.common'] + bld.do_xcommon(makefile_opts) + + +def test(bld): + # Call pytest to run Unity tests inside axe or xsim + try: + test_output = subprocess.check_output(['pytest']) + except subprocess.CalledProcessError as e: + # pytest exits non-zero if an assertion fails + test_output = e.output + print(test_output) + + +# TODO: ensure clean deletes the runners dir/ +def dist(ctx): + ctx.load('xwaf.xcommon') + +def distcheck(ctx): + ctx.load('xwaf.xcommon') diff --git a/xpd.xml b/xpd.xml deleted file mode 100644 index 65d892c5..00000000 --- a/xpd.xml +++ /dev/null @@ -1,120 +0,0 @@ - - - - - XMOS USB Audio Reference Designes - DFU - - - - UAC2 - - - XR-USB-AUDIO-20-MC - USB Audio UAC2 - - - - module_queue - MIDI - - - USB Audio Shared Components. For use in the XMOS USB Audio Refererence Designs. - module_dfu/doc - False - git://git/apps/sc_usb_audio - USB Audio Shared Components - xross - XM-004719-DH - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - XM-004720-SM - XMOS - - - - - - 1.0 - \ No newline at end of file