Initial move to lib structure

This commit is contained in:
xross
2017-10-10 09:48:54 +01:00
parent 7776f74cba
commit 4d4765dc77
132 changed files with 44 additions and 2800 deletions

34
lib_xua/module_build_info Normal file
View File

@@ -0,0 +1,34 @@
# You can set flags specifically for your module by using the MODULE_XCC_FLAGS
# variable. So the following
#
# MODULE_XCC_FLAGS = $(XCC_FLAGS) -O3
#
# specifies that everything in the modules should have the application
# build flags with -O3 appended (so the files will build at
# optimization level -O3).
#
# You can also set MODULE_XCC_C_FLAGS, MODULE_XCC_XC_FLAGS etc..
MODULE_XCC_FLAGS = $(XCC_FLAGS) -O3 -DREF_CLK_FREQ=100 -fasm-linenum -fcomment-asm -fsubword-select -DXUD_FULL_PIDTABLE=1
OPTIONAL_HEADERS += xua_conf.h
VERSION = 1.0.0
DEPENDENT_MODULES = lib_logging(>=2.0.0) lib_xassert(>=2.0.0) lib_xud(>=1.0.0)
#usb_audio
EXCLUDE_FILES += descriptors_2.rst
XCC_FLAGS_endpoint0.c = -Os -mno-dual-issue $(XCC_FLAGS)
XCC_FLAGS_dbcalc.xc = -Os -mno-dual-issue $(XCC_FLAGS)
XCC_FLAGS_audiorequests.xc = -Os -mno-dual-issue $(XCC_FLAGS)
XCC_FLAGS_flashlib_user.c = -Os -mno-dual-issue $(XCC_FLAGS)
XCC_FLAGS_audioports.c = -Os -mno-dual-issue $(XCC_FLAGS)
XCC_FLAGS_audioports.xc = -Os -mno-dual-issue $(XCC_FLAGS)
#dfu
XCC_FLAGS_dfu.xc = -Os -mno-dual-issue $(XCC_FLAGS)
XCC_FLAGS_flash_interface.c = -Os -mno-dual-issue $(XCC_FLAGS)

View File

@@ -0,0 +1,11 @@
module_dfu
==========
:scope: General Use
:description: module_dfu
:keywords: DFU
:boards: XMOS USB Audio Reference Designes
Code providing firmware upgrade over USB.
./host provides an example of a host application for DFU. This is written and tested for OSX. LibUSB is used to aid cross-platform porting.

View File

@@ -0,0 +1 @@
SOURCE_INCLUDE_DIRS=../host/xmosdfu

View File

@@ -0,0 +1,18 @@
.. _usb_audiosec_building_xmos_dfu:
Building the XMOS DFU loader - macOS
====================================
The XMOS DFU loader is provided as source as part of the USB Audio
framework, located in sc_usb_audio/module_dfu/host/xmos_dfu_osx.
The loader is compiled using libusb, the code for the loader is contained in the
file ``xmosdfu.cpp``
To build the loader a Makefile is provided, which can be run as follows:
``make -f Makefile.OSX all``
This Makefile contains the following:
.. literalinclude:: Makefile.OSX

View File

@@ -0,0 +1,91 @@
Creating factory and upgrade images with XFLASH
===============================================
Installing the factory image to the device
------------------------------------------
The DFU device interface is enabled by default in the XMOS USB Audio framework
(see ``devicedefines.h``), and explicitly enabled in each reference design
by the following line included in the ``customdefines.h`` file of each
application::
#define DFU (1)
Use the XMOS Development Tools to run the command:
``xflash --boot-partition-size 0x30000 --factory usb_audio.xe``
Where the size passed using the ``--boot-partition-size n`` argument specifies
in bytes the minimum size required to store the boot loader, factory image and
any upgrade images.
The following can be used as a guide to help calculate the required boot
partition size for a design:
- boot partition size = loader size + maximum size of factory image +
maximum size of upgrade images + padding to sector boundaries
Where:
- loader size = 0x1000 bytes, or 0x4000 bytes if secure boot functionality is
used (for tools version 13.1.0 and older)
- maximum size of factory image = number of xCORE tiles *
xCORE SRAM size
- maximum size of upgrade images = (number of xCORE tiles *
xCORE SRAM size) * number of images to be held in flash concurrently
The above example sets the boot partition to a size appropriate for designs
based on a single xCORE tile, where a single upgrade image is required
in flash at any one time.
This programs the factory default firmware image into the flash device.
The device will now support the DFU mechanism, and can use it to safely receive
firmware updates, as well as revert to the factory firmware image when required,
such as in the event of a failed upgrade attempt.
Creating the upgrade image
--------------------------
To use the firmware upgrade mechanism you need to build a firmware upgrade
image:
#. Edit the ``customdefines.h`` file of the application for the hardware in use,
and change the Device Version Number by defining ``BCD_DEVICE``.
#. Rebuild the application.
To generate the firmware upgrade image run the following command:
``xflash --factory-version 13 --upgrade 1 usb_audio.xe -o new_firmware.bin``
Where the tools version passed using the ``--factory-version version``
argument specifies the version of the tools used to create the factory image.
This should be passed as ``12`` for images created using tools versions 10, 11
and 12.
The ``--upgrade id xe-file [size]`` argument specifies xe-file as an upgrade
image with version ``id``. Each version number must be a unique number greater
than 0.
You should now have the file ``new_firmware.bin`` which contains the
firmware with the newly specified Device Version Number.
Related documents
-----------------
.. only :: latex
For further details of the DFU features included in the XMOS USB Audio
framework and their configuration please see the "Device Firmware Upgrade
(DFU)" (:ref:`usb_audio_sec_dfu`) and the "Configuration Defines"
(:ref:`sec_custom_defines_api`) sections of the USB Audio Design Guide.
.. only :: html
For further details of the DFU features included in the XMOS USB Audio
framework and their configuration please see the :ref:`usb_audio_sec_dfu`
and the :ref:`sec_custom_defines_api` sections of the USB Audio Design
Guide.
For further details on the use of XFLASH to create factory and upgrade firmware
images please see the XFLASH Command-Line Manual section of the
`xTIMEcomposer User Guide <https://www.xmos.com/published/xtimecomposer-user-guide>`_.

View File

@@ -0,0 +1,10 @@
DFU loader for XMOS USB AUDIO devices
=====================================
.. toctree::
Overview <overview>
Creating factory and upgrade images with XFLASH <factory_image>
Using the DFU loader - Windows (via the Thesycon driver) <thesycon_dfu>
Using the DFU loader - macOS (via the XMOS DFU loader) <xmos_dfu>
Building the XMOS DFU loader - macOS <building_xmos_dfu>

View File

@@ -0,0 +1,21 @@
Overview
========
The DFU loader is a flash device firmware upgrade mechanism. To work correctly
your development board must contain the latest DFU enabled firmware.
The firmware upgrade for XMOS USB devices implementation uses the USB standard
DFU device class mechanism and is based on the following specification:
http://www.usb.org/developers/devclass_docs/DFU_1.1.pdf
Supported functionality:
- Download of new firmware to device
- Upload of existing firmware from device
- Revert device back to factory image
- Automatic reboot of device on firmware upgrade
You must use XMOS Development Tools version 10.4.1 (or later).
The DFU device on Windows requires the Theyscon USB Audio 2.0 Windows driver
version 1.13.3 or later.

View File

@@ -0,0 +1,70 @@
Using the DFU loader - Windows (via the Thesycon driver)
========================================================
Thesycon provide both GUI and CLI DFU tools, TUSBAudioDfu.exe and dfucons.exe
respectively. The use of the GUI tool is not covered by this document.
The correct installation of the Thesycon driver and DFU tools exceeds
the scope of this document.
Set up the image loader
-----------------------
Run the DFU console tool (``dfucons.exe``) from the Thesycon install folder,
in a Command Prompt by navigating to:
``C:\Program Files\Thesycon\TUSBAudio_Driver\``
To check the device has been detected, run the following command in the DFU
console:
``dfucons info``
The console shows the DFU devices that have been detected.
Download new firmware
---------------------
To program the new firmware run the command:
``dfucons download new_firmware.bin``
Note that once this is done the device restarts. The original factory default
application is still present but the device is now running the upgraded
application firmware.
You can check the device has been updated by running the command:
``dfucons info``
This will display the device revision.
Uploading existing firmware from the device
-------------------------------------------
You can retrieve a firmware image from the device, providing an upgrade image is
present.
Run the command:
``dfucons upload currentfirmware.bin``
The file ``currentfirmware.bin`` contains the latest upgrade image. This file is
an exact copy of the data from the flash and can be downloaded to the device
again to test.
Reverting firmware to factory image
-----------------------------------
To revert the device back to its factory (i.e XFLASH) installed state from the
new firmware, run the command:
``dfucons revertfactory``
The device will now be running, and only contain the factory firmware, which can
be seen by checking the device version once more.
Related documents
-----------------
For further details on the use of the Thesycon DFU tools please see
`Thesycon USB Audio 2.0 Driver for Windows User Manual <https://www.xmos.com/published/usb-audio-class-20-evaluation-driver-windows>`_.

View File

@@ -0,0 +1,49 @@
Using the DFU loader - macOS (via the XMOS DFU loader)
======================================================
The XMOS DFU loader is provided as source as part of the XMOS USB Audio software
framework, see :ref:`usb_audiosec_building_xmos_dfu`.
Set up the image loader
-----------------------
#. Open a terminal
#. Change directory to where the loader has been built
#. Run the command:
``source setup.sh``
Download new firmware
---------------------
To program the new firmware run the command:
``./xmosdfu --download new_firmware.bin``
Note that once this is done the device restarts. The original factory default
application is still present but the device is now running the upgraded
application firmware.
Uploading existing firmware from the device
-------------------------------------------
You can retrieve a firmware image from the device, providing an upgrade image is
present.
Run the command:
``./xmosdfu --upload currentfirmware.bin``
The file ``currentfirmware.bin`` contains the latest upgrade image. This file is
an exact copy of the data from the flash and can be downloaded to the device
again to test.
Reverting firmware to factory image
-----------------------------------
To revert the device back to its factory (i.e XFLASH) installed state from the
new firmware, run the command:
``./xmosdfu --revertfactory``
The device will now be running, and only contain the factory firmware.

View File

@@ -0,0 +1,22 @@
# This file describes how this module will affect the application
# using it. The file is included in the Makefile of the main application.
#
# The module contributes to the build of the application by extending
# the following variables:
#
# SOURCE_DIRS - directories with source files to be included in the build
# INCLUDE_DIRS - directories to be added to the include path during the build
# LIB_DIRS - directories containing libraries to be linked into the build
#
# Note that all the source files in each directory in SOURCE_DIRS
# will be compiled (you do not need to name the files individually).
#
MODULE_DIRS = src
SOURCE_DIRS += $(MODULE_DIRS)
INCLUDE_DIRS += $(MODULE_DIRS)
MODULE_XCC_FLAGS = -Os -mno-dual-issue $(XCC_FLAGS)
MODULE_C_FLAGS = -Os -mno-dual-issue $(XCC_FLAGS)

View File

@@ -0,0 +1 @@
Code providing firmware upgrade over USB.

97
lib_xua/src/dfu/src/dfu.h Normal file
View File

@@ -0,0 +1,97 @@
#ifndef _DFU_H_
#define _DFU_H_ 1
#include <xccompat.h>
#include "xud_device.h"
#ifndef DFU_VENDOR_ID
#error DFU_VENDOR_ID not defined!
#endif
#ifndef DFU_PID
#error DFU_PID not defined!
#endif
#ifndef DFU_BCD_DEVICE
#error DFU_BCD_DEVICE not defined!
#endif
#ifndef DFU_SERIAL_STR_INDEX
/* By default no serial string */
#define DFU_SERIAL_STR_INDEX (0x00)
#endif
#ifndef DFU_PRODUCT_STR_INDEX
#error DFU_PROFUCT_INDEX not defined!!
#endif
#ifndef DFU_MANUFACTURER_STR_INDEX
#error DFU_MANUFACTURE_STR_INDEX not defined!!
#endif
unsigned char DFUdevDesc[] = {
18, /* 0 bLength : Size of descriptor in Bytes (18 Bytes) */
1, /* 1 bdescriptorType */
0, /* 2 bcdUSB */
2, /* 3 bcdUSB */
0x00, /* 4 bDeviceClass: See interface */
0x00, /* 5 bDeviceSubClass: See interface */
0, /* 6 bDeviceProtocol: See interface */
64, /* 7 bMaxPacketSize */
(DFU_VENDOR_ID & 0xFF), /* 8 idVendor */
(DFU_VENDOR_ID >> 8), /* 9 idVendor */
(DFU_PID & 0xFF), /* 10 idProduct */
(DFU_PID >> 8), /* 11 idProduct */
(DFU_BCD_DEVICE & 0xFF), /* 12 bcdDevice : Device release number */
(DFU_BCD_DEVICE >> 8), /* 13 bcdDevice : Device release number */
DFU_MANUFACTURER_STR_INDEX, /* 14 iManufacturer : Index of manufacturer string */
DFU_PRODUCT_STR_INDEX, /* 15 iProduct : Index of product string descriptor */
DFU_SERIAL_STR_INDEX, /* 16 iSerialNumber : Index of serial number decriptor */
0x01 /* 17 bNumConfigurations : Number of possible configs */
};
unsigned char DFUcfgDesc[] = {
/* Standard USB device descriptor */
0x09, /* 0 bLength */
USB_DESCTYPE_CONFIGURATION, /* 1 bDescriptorType */
0x1b, /* 2 wTotalLength */
0x00, /* 3 wTotalLength */
1, /* 4 bNumInterface: Number of interfaces*/
0x01, /* 5 bConfigurationValue */
0x00, /* 6 iConfiguration */
0xC0, /* 7 bmAttributes */
0x32, /* 8 bMaxPower */
/* Standard DFU class interface descriptor */
0x09, /* 0 bLength : Size of this descriptor, in bytes. (field size 1 bytes) */
0x04, /* 1 bDescriptorType : INTERFACE descriptor. (field size 1 bytes) */
0x00, /* 2 bInterfaceNumber : Index of this interface. (field size 1 bytes) */
0x00, /* 3 bAlternateSetting : Index of this setting. (field size 1 bytes) */
0x00, /* 4 bNumEndpoints : 0 endpoints. (field size 1 bytes) */
0xFE, /* 5 bInterfaceClass : AUDIO. (field size 1 bytes) */
0x01, /* 6 bInterfaceSubclass : AUDIO_CONTROL. (field size 1 bytes) */
0x02, /* 7 bInterfaceProtocol : Unused. (field size 1 bytes) */
0x00, /* 8 iInterface : Unused. (field size 1 bytes) */
/* DFU 1.1 Run-Time DFU Functional Descriptor */
0x09, /* 0 Size */
0x21, /* 1 bDescriptorType : DFU FUNCTIONAL */
0x07, /* 2 bmAttributes */
0xFA, /* 3 wDetachTimeOut */
0x00, /* 4 wDetachTimeOut */
0x40, /* 5 wTransferSize */
0x00, /* 6 wTransferSize */
0x10, /* 7 bcdDFUVersion */
0x01, /* 8 bcdDFUVersion */
};
int DFUReportResetState(NULLABLE_RESOURCE(chanend , c_user_cmd));
int DFUDeviceRequests(XUD_ep c_ep0_out, NULLABLE_REFERENCE_PARAM(XUD_ep, ep0_in), REFERENCE_PARAM(USB_SetupPacket_t, sp),
NULLABLE_RESOURCE(chanend, c_user_cmd), unsigned int altInterface, CLIENT_INTERFACE(i_dfu, dfuInterface), REFERENCE_PARAM(int, reset));
/* Helper function for C */
void DFUDelay(unsigned d);
#endif /* _DFU_H_ */

581
lib_xua/src/dfu/src/dfu.xc Normal file
View File

@@ -0,0 +1,581 @@
#include <xs1.h>
#include <platform.h>
#include "devicedefines.h"
#ifndef NO_USB
#include "xud_device.h"
#include "dfu_types.h"
#include "flash_interface.h"
#include "dfu_interface.h"
#if (XUD_SERIES_SUPPORT==4)
/* xCORE-200 */
/* 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
#endif
/* Store Flag to fixed address */
static void SetDFUFlag(unsigned x)
{
asm volatile("stw %0, %1[0]" :: "r"(x), "r"(FLAG_ADDRESS));
}
/* Load flag from fixed address */
static unsigned GetDFUFlag()
{
unsigned x;
asm volatile("ldw %0, %1[0]" : "=r"(x) : "r"(FLAG_ADDRESS));
return x;
}
static int g_DFU_state = STATE_APP_IDLE;
static int DFU_status = DFU_OK;
static timer DFUTimer;
static unsigned int DFUTimerStart = 0;
static unsigned int DFUResetTimeout = 100000000; // 1 second default
static int DFU_flash_connected = 0;
static unsigned int subPagesLeft = 0;
extern void DFUCustomFlashEnable();
extern void DFUCustomFlashDisable();
void DFUDelay(unsigned d)
{
timer tmr;
unsigned s;
tmr :> s;
tmr when timerafter(s + d) :> void;
}
/* Return non-zero on error */
static int DFU_OpenFlash()
{
if (!DFU_flash_connected)
{
unsigned int cmd_data[16];
DFUCustomFlashEnable();
int error = flash_cmd_init();
if(error)
{
return error;
}
DFU_flash_connected = 1;
}
return 0;
}
static int DFU_CloseFlash(chanend ?c_user_cmd)
{
if (DFU_flash_connected)
{
unsigned int cmd_data[16];
DFUCustomFlashDisable();
flash_cmd_deinit();
DFU_flash_connected = 0;
}
return 0;
}
static int DFU_Detach(unsigned int timeout, chanend ?c_user_cmd, unsigned &DFU_state)
{
if (DFU_state == STATE_APP_IDLE)
{
DFU_state = STATE_APP_DETACH;
DFU_OpenFlash();
// Setup DFU timeout value
DFUResetTimeout = timeout * 100000;
// Start DFU reset timer
DFUTimer :> DFUTimerStart;
}
else
{
DFU_state = STATE_DFU_ERROR;
}
return 0;
}
static int DFU_Dnload(unsigned int request_len, unsigned int block_num, const unsigned request_data[16], chanend ?c_user_cmd, int &return_data_len, unsigned &DFU_state)
{
unsigned int fromDfuIdle = 0;
return_data_len = 0;
int error;
// Get DFU packets here, sequence is
// DFU_DOWNLOAD -> DFU_DOWNLOAD_SYNC
// GET_STATUS -> DFU_DOWNLOAD_SYNC (flash busy) || DFU_DOWNLOAD_IDLE
// REPEAT UNTIL DFU_DOWNLOAD with 0 length -> DFU_MANIFEST_SYNC
if((error = DFU_OpenFlash()))
{
return error;
}
switch (DFU_state)
{
case STATE_DFU_IDLE:
case STATE_DFU_DOWNLOAD_IDLE:
break;
default:
DFU_state = STATE_DFU_ERROR;
return 1;
}
if ((DFU_state == STATE_DFU_IDLE) && (request_len == 0))
{
DFU_state = STATE_DFU_ERROR;
return 1;
}
else if (DFU_state == STATE_DFU_IDLE)
{
fromDfuIdle = 1;
}
else
{
fromDfuIdle = 0;
}
if (request_len == 0)
{
// Host signalling complete download
int i = 0;
unsigned int cmd_data[16];
if (subPagesLeft)
{
unsigned int subPagePad[16] = {0};
for (i = 0; i < subPagesLeft; i++)
{
flash_cmd_write_page_data((subPagePad, unsigned char[64]));
}
}
cmd_data[0] = 2; // Terminate write
flash_cmd_write_page((cmd_data, unsigned char[]));
DFU_state = STATE_DFU_MANIFEST_SYNC;
}
else
{
unsigned int i = 0;
unsigned int flash_cmd = 0;
unsigned int flash_page_index = 0;
unsigned int cmd_data[16];
if (fromDfuIdle)
{
unsigned s = 0;
// Erase flash on first block
flash_cmd_erase_all();
}
// Program firmware, STATE_DFU_DOWNLOAD_BUSY not currently used
if (!(block_num % 4))
{
cmd_data[0] = !fromDfuIdle; // 0 for first page, 1 for other pages.
flash_cmd_write_page((cmd_data, unsigned char[64]));
subPagesLeft = 4;
}
for (i = 0; i < 16; i++)
{
cmd_data[i] = request_data[i];
}
flash_cmd_write_page_data((cmd_data, unsigned char[64]));
subPagesLeft--;
DFU_state = STATE_DFU_DOWNLOAD_SYNC;
}
return 0;
}
static int DFU_Upload(unsigned int request_len, unsigned int block_num, unsigned data_out[16], unsigned &DFU_state)
{
unsigned int cmd_data[1];
unsigned int firstRead = 0;
// Start at flash address 0
// Keep reading flash pages until read_page returns 1 (address out of range)
// Return terminating upload packet at this point
DFU_OpenFlash();
switch (DFU_state)
{
case STATE_DFU_IDLE:
case STATE_DFU_UPLOAD_IDLE:
break;
default:
DFU_state = STATE_DFU_ERROR;
return 0;
}
if ((DFU_state == STATE_DFU_IDLE) && (request_len == 0))
{
DFU_state = STATE_DFU_ERROR;
return 0;
}
else if (DFU_state == STATE_DFU_IDLE)
{
firstRead = 1;
subPagesLeft = 0;
}
if (!subPagesLeft)
{
cmd_data[0] = !firstRead;
// Read whole (256bytes) page from the image on the flash into a memory buffer
flash_cmd_read_page((cmd_data, unsigned char[1]));
subPagesLeft = 4;
// If address out of range, terminate!
if (cmd_data[0] == 1)
{
subPagesLeft = 0;
// Back to idle state, upload complete
DFU_state = STATE_DFU_IDLE;
return 0;
}
}
// Get 64 bytes of page data from memory
flash_cmd_read_page_data((data_out, unsigned char[64]));
subPagesLeft--;
DFU_state = STATE_DFU_UPLOAD_IDLE;
return 64;
}
static int DFU_GetStatus(unsigned int request_len, unsigned data_buffer[16], chanend ?c_user_cmd, unsigned &DFU_state)
{
unsigned int timeout = 0;
data_buffer[0] = timeout << 8 | (unsigned char)DFU_status;
switch (DFU_state)
{
case STATE_DFU_MANIFEST:
case STATE_DFU_MANIFEST_WAIT_RESET:
DFU_state = STATE_DFU_ERROR;
break;
case STATE_DFU_DOWNLOAD_BUSY:
// If download completes -> DFU_DOWNLOAD_SYNC
// Currently all transactions are synchronous so no busy state
break;
case STATE_DFU_DOWNLOAD_SYNC:
DFU_state = STATE_DFU_DOWNLOAD_IDLE;
break;
case STATE_DFU_MANIFEST_SYNC:
// Check if complete here
DFU_state = STATE_DFU_IDLE;
break;
default:
break;
}
data_buffer[1] = DFU_state;
return 6;
}
static int DFU_ClrStatus(unsigned &DFU_state)
{
if (DFU_state == STATE_DFU_ERROR)
{
DFU_state = STATE_DFU_IDLE;
}
else
{
DFU_state = STATE_DFU_ERROR;
}
return 0;
}
static int DFU_GetState(unsigned int request_len, unsigned int request_data[16], chanend ?c_user_cmd, unsigned &DFU_state)
{
request_data[0] = DFU_state;
switch (DFU_state)
{
case STATE_DFU_DOWNLOAD_BUSY:
case STATE_DFU_MANIFEST:
case STATE_DFU_MANIFEST_WAIT_RESET:
DFU_state = STATE_DFU_ERROR;
break;
default:
break;
}
return 1;
}
static int DFU_Abort(unsigned &DFU_state)
{
DFU_state = STATE_DFU_IDLE;
return 0;
}
// Tell the DFU state machine that a USB reset has occured
int DFUReportResetState(chanend ?c_user_cmd)
{
unsigned int inDFU = 0;
unsigned int currentTime = 0;
unsigned flag;
flag = GetDFUFlag();
//#define START_IN_DFU 1
#ifdef START_IN_DFU
flag = 0x11042011;
#endif
if (flag == 0x11042011)
{
unsigned int cmd_data[16];
inDFU = 1;
g_DFU_state = STATE_DFU_IDLE;
return inDFU;
}
switch(g_DFU_state)
{
case STATE_APP_DETACH:
case STATE_DFU_IDLE:
g_DFU_state = STATE_DFU_IDLE;
DFUTimer :> currentTime;
if (currentTime - DFUTimerStart > DFUResetTimeout)
{
g_DFU_state = STATE_APP_IDLE;
inDFU = 0;
}
else
{
inDFU = 1;
}
break;
case STATE_APP_IDLE:
case STATE_DFU_DOWNLOAD_SYNC:
case STATE_DFU_DOWNLOAD_BUSY:
case STATE_DFU_DOWNLOAD_IDLE:
case STATE_DFU_MANIFEST_SYNC:
case STATE_DFU_MANIFEST:
case STATE_DFU_MANIFEST_WAIT_RESET:
case STATE_DFU_UPLOAD_IDLE:
case STATE_DFU_ERROR:
inDFU = 0;
g_DFU_state = STATE_APP_IDLE;
break;
default:
g_DFU_state = STATE_DFU_ERROR;
inDFU = 1;
break;
}
if (!inDFU)
{
DFU_CloseFlash(c_user_cmd);
}
return inDFU;
}
static int XMOS_DFU_RevertFactory(chanend ?c_user_cmd)
{
unsigned s = 0;
DFU_OpenFlash();
flash_cmd_erase_all();
DFUTimer :> s;
DFUTimer when timerafter(s + 25000000) :> s; // Wait for flash erase
return 0;
}
static int XMOS_DFU_SelectImage(unsigned int index, chanend ?c_user_cmd)
{
// Select the image index for firmware update
// Currently not used or implemented
return 0;
}
static int XMOS_DFU_SaveState()
{
return 0;
}
static int XMOS_DFU_LoadState()
{
return 0;
}
[[distributable]]
void DFUHandler(server interface i_dfu i, chanend ?c_user_cmd)
{
while(1)
{
select
{
case i.HandleDfuRequest(USB_SetupPacket_t &sp, unsigned data_buffer[], unsigned data_buffer_length, unsigned dfuState)
-> {unsigned reset_device_after_ack, int return_data_len, int dfu_reset_override, int returnVal, unsigned newDfuState}:
reset_device_after_ack = 0;
return_data_len = 0;
dfu_reset_override = 0;
unsigned tmpDfuState = dfuState;
returnVal = 0;
// Map Standard DFU commands onto device level firmware upgrade mechanism
switch (sp.bRequest)
{
case DFU_DETACH:
tmpDfuState = dfuState;
return_data_len = DFU_Detach(sp.wValue, c_user_cmd, tmpDfuState);
newDfuState = tmpDfuState;;
break;
case DFU_DNLOAD:
unsigned data[16];
for(int i = 0; i < 16; i++)
data[i] = data_buffer[i];
returnVal = DFU_Dnload(sp.wLength, sp.wValue, data, c_user_cmd, return_data_len, tmpDfuState);
break;
case DFU_UPLOAD:
unsigned data_out[16];
return_data_len = DFU_Upload(sp.wLength, sp.wValue, data_out, tmpDfuState);
for(int i = 0; i < 16; i++)
data_buffer[i] = data_out[i];
break;
case DFU_GETSTATUS:
unsigned data_out[16];
return_data_len = DFU_GetStatus(sp.wLength, data_out, c_user_cmd, tmpDfuState);
for(int i = 0; i < 16; i++)
data_buffer[i] = data_out[i];
break;
case DFU_CLRSTATUS:
return_data_len = DFU_ClrStatus(tmpDfuState);
break;
case DFU_GETSTATE:
unsigned data_out[16];
return_data_len = DFU_GetState(sp.wLength, data_out, c_user_cmd, tmpDfuState);
for(int i = 0; i < 16; i++)
data_buffer[i] = data_out[i];
break;
case DFU_ABORT:
return_data_len = DFU_Abort(tmpDfuState);
break;
/* XMOS Custom DFU requests */
case XMOS_DFU_RESETDEVICE:
reset_device_after_ack = 1;
return_data_len = 0;
break;
case XMOS_DFU_REVERTFACTORY:
return_data_len = XMOS_DFU_RevertFactory(c_user_cmd);
break;
case XMOS_DFU_RESETINTODFU:
reset_device_after_ack = 1;
dfu_reset_override = 0x11042011;
return_data_len = 0;
break;
case XMOS_DFU_RESETFROMDFU:
reset_device_after_ack = 1;
dfu_reset_override = 0;
return_data_len = 0;
break;
case XMOS_DFU_SELECTIMAGE:
return_data_len = XMOS_DFU_SelectImage(sp.wValue, c_user_cmd);
break;
case XMOS_DFU_SAVESTATE:
/* Save passed state to flash */
return_data_len = XMOS_DFU_SaveState();
break;
case XMOS_DFU_RESTORESTATE:
/* Restore saved state from flash */
return_data_len = XMOS_DFU_LoadState();
break;
default:
break;
}
newDfuState = tmpDfuState;
break;
case i.finish():
return;
}
}
}
int DFUDeviceRequests(XUD_ep ep0_out, XUD_ep &?ep0_in, USB_SetupPacket_t &sp, chanend ?c_user_cmd, unsigned int altInterface, client interface i_dfu i,int &reset)
{
unsigned int return_data_len = 0;
unsigned int data_buffer_len = 0;
unsigned int data_buffer[17];
unsigned int reset_device_after_ack = 0;
int returnVal = 0;
unsigned int dfuState = g_DFU_state;
int dfuResetOverride;
if(sp.bmRequestType.Direction == USB_BM_REQTYPE_DIRECTION_H2D)
{
// Host to device
if (sp.wLength)
XUD_GetBuffer(ep0_out, (data_buffer, unsigned char[]), data_buffer_len);
}
/* Interface used here such that the handler can be on another tile */
{reset_device_after_ack, return_data_len, dfuResetOverride, returnVal, dfuState} = i.HandleDfuRequest(sp, data_buffer, data_buffer_len, g_DFU_state);
SetDFUFlag(dfuResetOverride);
/* Update our version of dfuState */
g_DFU_state = dfuState;
/* Check if the request was handled */
if(returnVal == 0)
{
if (sp.bmRequestType.Direction == USB_BM_REQTYPE_DIRECTION_D2H && sp.wLength != 0)
{
returnVal = XUD_DoGetRequest(ep0_out, ep0_in, (data_buffer, unsigned char[]), return_data_len, return_data_len);
}
else
{
returnVal = XUD_DoSetRequestStatus(ep0_in);
}
// If device reset requested, handle after command acknowledgement
if (reset_device_after_ack)
{
reset = 1;
}
}
return returnVal;
}
#endif /* NO_USB */

View File

@@ -0,0 +1,14 @@
#ifndef __DFU_INTERFACE_H__
#define __DFU_INTERFACE_H__
#include "xud_device.h"
interface i_dfu
{
{unsigned, int, int, int, unsigned} HandleDfuRequest(USB_SetupPacket_t &sp, unsigned data_buffer[], unsigned data_buffer_length, unsigned dfuState);
void finish();
};
#endif

View File

@@ -0,0 +1,48 @@
// Default Command requests (from Spec)
#define DFU_DETACH 0
#define DFU_DNLOAD 1
#define DFU_UPLOAD 2
#define DFU_GETSTATUS 3
#define DFU_CLRSTATUS 4
#define DFU_GETSTATE 5
#define DFU_ABORT 6
// XMOS Alternate Setting Command Requests
#define XMOS_DFU_RESETDEVICE 0xf0
#define XMOS_DFU_REVERTFACTORY 0xf1
#define XMOS_DFU_RESETINTODFU 0xf2
#define XMOS_DFU_RESETFROMDFU 0xf3
#define XMOS_DFU_SELECTIMAGE 0xf4
#define XMOS_DFU_SAVESTATE 0xf5
#define XMOS_DFU_RESTORESTATE 0xf6
// DFU States
#define STATE_APP_IDLE 0x00
#define STATE_APP_DETACH 0x01
#define STATE_DFU_IDLE 0x02
#define STATE_DFU_DOWNLOAD_SYNC 0x03
#define STATE_DFU_DOWNLOAD_BUSY 0x04
#define STATE_DFU_DOWNLOAD_IDLE 0x05
#define STATE_DFU_MANIFEST_SYNC 0x06
#define STATE_DFU_MANIFEST 0x07
#define STATE_DFU_MANIFEST_WAIT_RESET 0x08
#define STATE_DFU_UPLOAD_IDLE 0x09
#define STATE_DFU_ERROR 0x0a
// DFU error conditions
#define DFU_OK 0x00 // No error condition is present.
#define DFU_errTARGET 0x01 // File is not targeted for use by this device.
#define DFU_errFILE 0x02 // File is for this device but fails some vendor-specific verification test.
#define DFU_errWRITE 0x03 // Device is unable to write memory.
#define DFU_errERASE 0x04 // Memory erase function failed.
#define DFU_errCHECK_ERASED 0x05 // Memory erase check failed.
#define DFU_errPROG 0x06 // Program memory function failed.
#define DFU_errVERIFY 0x07 // Programmed memory failed verification.
#define DFU_errADDRESS 0x08 // Cannot program memory due to received address that is out of range.
#define DFU_errNOTDONE 0x09 // Received DFU_DNLOAD with wLength = 0, but device does not think it has all of the data yet.
#define DFU_errFIRMWARE 0x0A // Devices firmware is corrupt. It cannot return to run-time (non-DFU) operations
#define DFU_errVENDOR 0x0B // iString indicates a vendor-specific error.
#define DFU_errUSBR 0x0C // Device detected unexpected USB reset signaling.
#define DFU_errPOR 0x0D // Device detected unexpected power on reset.
#define DFU_errUNKNOWN 0x0E // Something went wrong, but the device does not know what it was
#define DFU_errSTALLEDPKT 0x0F // Device stalled an unexpected request.

View File

@@ -0,0 +1,248 @@
#include <xs1.h>
#include <flash.h>
#include <flashlib.h>
#include <string.h>
#include <xclib.h>
/* Defines flash area to erase on first DFU download request received
*
* Flash library will round it up to the nearest sector, e.g. 4KB
*
* XS2 internal flash IS25LQ016B takes 70ms to erase one sector
* 128KB will take over 2 seconds, for instance
*
* Your host software might implement a 5sec timeout as per USB spec 9.2.6.1,
* and 5 seconds is just over 300KB
*/
#ifndef FLASH_MAX_UPGRADE_SIZE
#define FLASH_MAX_UPGRADE_SIZE (128 * 1024)
#endif
#define FLASH_ERROR() do {} while(0)
static int flash_device_open = 0;
static fl_BootImageInfo factory_image;
static fl_BootImageInfo upgrade_image;
static int upgrade_image_valid = 0;
static int current_flash_subpage_index = 0;
static unsigned char current_flash_page_data[256];
int flash_cmd_enable_ports() __attribute__ ((weak));
int flash_cmd_enable_ports() {
return 0;
}
int flash_cmd_disable_ports() __attribute__ ((weak));
int flash_cmd_disable_ports() {
return 0;
}
void DFUCustomFlashEnable() __attribute__ ((weak));
void DFUCustomFlashEnable()
{
return;
}
void DFUCustomFlashDisable() __attribute__ ((weak));
void DFUCustomFlashDisable()
{
return;
}
/* Returns non-zero for error */
int flash_cmd_init(void)
{
fl_BootImageInfo image;
if (!flash_device_open)
{
if (flash_cmd_enable_ports())
flash_device_open = 1;
}
if (!flash_device_open)
{
return 1;
}
#ifndef QUAD_SPI_FLASH
// Disable flash protection
fl_setProtection(0);
#endif
if (fl_getFactoryImage(&image) != 0)
{
return 1;
}
factory_image = image;
if (fl_getNextBootImage(&image) == 0)
{
upgrade_image_valid = 1;
upgrade_image = image;
}
return 0;
}
int flash_cmd_deinit(void)
{
if (!flash_device_open)
return 0;
flash_cmd_disable_ports();
flash_device_open = 0;
return 0;
}
int flash_cmd_read_page(unsigned char *data)
{
if (!upgrade_image_valid)
{
*(unsigned int *)data = 1;
return 4;
}
if (*(unsigned int *)data == 0)
{
fl_startImageRead(&upgrade_image);
}
current_flash_subpage_index = 0;
if (fl_readImagePage(current_flash_page_data) == 0)
{
*(unsigned int *)data = 0;
}
else
{
*(unsigned int *)data = 1;
}
return 4;
}
int flash_cmd_read_page_data(unsigned char *data)
{
unsigned char *page_data_ptr = &current_flash_page_data[current_flash_subpage_index * 64];
memcpy(data, page_data_ptr, 64);
current_flash_subpage_index++;
return 64;
}
static void begin_write()
{
int result;
// TODO this will take a long time. To minimise the amount of time spent
// paused on this operation it would be preferable to move to this to a
// seperate command, e.g. start_write.
do
{
result = fl_startImageAdd(&factory_image, FLASH_MAX_UPGRADE_SIZE, 0);
} while (result > 0);
if (result < 0)
FLASH_ERROR();
}
static int pages_written = 0;
int flash_cmd_write_page(unsigned char *data)
{
unsigned int flag = *(unsigned int *)data;
if (upgrade_image_valid)
{
return 0;
}
switch (flag)
{
case 0:
// First page.
begin_write();
pages_written = 0;
// fallthrough
case 1:
// Do nothing.
break;
case 2:
// Termination.
if (fl_endWriteImage() != 0)
FLASH_ERROR();
// Sanity check
fl_BootImageInfo image = factory_image;
if (fl_getNextBootImage(&image) != 0)
FLASH_ERROR();
break;
}
current_flash_subpage_index = 0;
return 0;
}
int flash_cmd_write_page_data(unsigned char *data)
{
unsigned char *page_data_ptr = &current_flash_page_data[current_flash_subpage_index * 64];
if (upgrade_image_valid)
{
return 0;
}
if (current_flash_subpage_index >= 4)
{
return 0;
}
memcpy(page_data_ptr, data, 64);
current_flash_subpage_index++;
if (current_flash_subpage_index == 4)
{
if (fl_writeImagePage(current_flash_page_data) != 0)
FLASH_ERROR();
pages_written++;
}
return 0;
}
int flash_cmd_erase_all(void)
{
fl_BootImageInfo tmp_image = upgrade_image;
if (upgrade_image_valid)
{
if (fl_deleteImage(&upgrade_image) != 0)
{
FLASH_ERROR();
}
// Keep deleting all upgrade images
// TODO Perhaps using replace would be nicer...
while(1)
{
if (fl_getNextBootImage(&tmp_image) == 0)
{
if (fl_deleteImage(&tmp_image) != 0)
{
FLASH_ERROR();
}
}
else
{
break;
}
}
upgrade_image_valid = 0;
}
return 0;
}

View File

@@ -0,0 +1,33 @@
#ifndef _flash_interface_h_
#define _flash_interface_h_
int flash_cmd_init(void);
/**
* Prepare to write a page of a new upgrade image.
* The first word of data should be set to 0 if it is the first page,
* 1 for all other pages and 2 to terminate the write (no further data is sent).
*/
int flash_cmd_write_page(unsigned char []);
/**
* Provide upgrade image data. flash_cmd_write_page() must be called previously.
* Once a page of data has been provided it is written to the device.
*/
int flash_cmd_write_page_data(unsigned char []);
/**
* Read a page of data from the upgrade image.
* If the first word of data is 0 the page is read from the start of the
* upgrade image, otherwise the next page in the image will be read.
* On return the first word of data is written with 1 if there is nothing to
* read and 0 otherwise.
*/
int flash_cmd_read_page(unsigned char []);
/**
* Get data previously read by flash_cmd_read_page().
*/
int flash_cmd_read_page_data(unsigned char []);
int flash_cmd_erase_all(void);
int flash_cmd_reboot(void);
int flash_cmd_init(void);
int flash_cmd_deinit(void);
#endif /*_flash_interface_h_*/

4
lib_xua/src/dfu/wscript Normal file
View File

@@ -0,0 +1,4 @@
def use_module(bld):
sources = bld.path.ant_glob(['src/*.xc', 'src/*.c'])
bld.env.XCC_FLAGS = ['-Os', '-mno-dual-issue'] + bld.env.XCC_FLAGS
bld.module(source=sources, includes=['src'])

View File

@@ -0,0 +1,9 @@
Queuing Module For USB Audio Framework
======================================
:scope: General Use
:description: queue
:keywords: UAC2
:boards:

View File

@@ -0,0 +1,14 @@
# You can set flags specifically for your module by using the MODULE_XCC_FLAGS
# variable. So the following
#
# MODULE_XCC_FLAGS = $(XCC_FLAGS) -O3
#
# specifies that everything in the modules should have the application
# build flags with -O3 appended (so the files will build at
# optimization level -O3).
#
# You can also set MODULE_XCC_C_FLAGS, MODULE_XCC_XC_FLAGS etc..
MODULE_XCC_XC_FLAGS = $(XCC_XC_FLAGS)
DEPENDENT_MODULES =

View File

@@ -0,0 +1 @@
One line module description.

View File

@@ -0,0 +1,65 @@
#ifndef QUEUE_H_
#define QUEUE_H_
#define assert(x) asm("ecallf %0"::"r"(x));
typedef struct queue_t {
/// Read index.
unsigned rdptr;
/// Write index.
unsigned wrptr;
unsigned size;
unsigned mask;
} queue_t;
inline int is_power_of_2(unsigned x) {
return x != 0 && (x & (x - 1)) == 0;
}
inline void queue_init(queue_t &q, unsigned size) {
assert(is_power_of_2(size));
q.rdptr = 0;
q.wrptr = 0;
q.size = size;
q.mask = size - 1; // Assumes power of two.
}
inline int queue_is_empty(const queue_t &q) {
return q.wrptr == q.rdptr;
}
inline int queue_is_full(const queue_t &q) {
return q.wrptr - q.rdptr == q.size;
}
inline void queue_push_word(queue_t &q, unsigned array[], unsigned data)
{
assert(!queue_is_full(q));
array[q.wrptr++ & q.mask] = data;
}
inline unsigned queue_pop_word(queue_t &q, unsigned array[]) {
assert(!queue_is_empty(q));
return array[q.rdptr++ & q.mask];
}
inline void queue_push_byte(queue_t &q, unsigned char array[], unsigned data)
{
assert(!queue_is_full(q));
array[q.wrptr++ & q.mask] = data;
}
inline unsigned queue_pop_byte(queue_t &q, unsigned char array[]) {
assert(!queue_is_empty(q));
return array[q.rdptr++ & q.mask];
}
inline unsigned queue_items(const queue_t &q) {
return q.wrptr - q.rdptr;
}
inline unsigned queue_space(const queue_t &q) {
return q.size - queue_items(q);
}
#endif /* QUEUE_H_ */

View File

@@ -0,0 +1,13 @@
#include "queue.h"
// Force external definitions of inline functions.
extern inline int is_power_of_2(unsigned x);
extern inline void queue_init(queue_t &q, unsigned size);
extern inline int queue_is_empty(const queue_t &q);
extern inline int queue_is_full(const queue_t &q);
extern inline void queue_push_word(queue_t &q, unsigned array[], unsigned data);
extern inline unsigned queue_pop_word(queue_t &q, unsigned array[]);
extern inline void queue_push_byte(queue_t &q, unsigned char array[], unsigned data);
extern inline unsigned queue_pop_byte(queue_t &q, unsigned char array[]);
extern inline unsigned queue_space(const queue_t &q);
extern inline unsigned queue_items(const queue_t &q);

View File

@@ -0,0 +1,2 @@
def use_module(bld):
bld.module(source='src/queue.xc')

View File

@@ -0,0 +1,9 @@
USB AUDIO MODULE
================
:scope: General Use
:description: Core USB Audio Module
:keywords: USB Audio UAC2
:boards: XR-USB-AUDIO-20-MC

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,17 @@
#include "userbuffermanagement.h"
#include "xccompat.h"
#include "devicedefines.h"
/* Default implementation for UserBufferManagementInit() */
void __attribute__ ((weak)) UserBufferManagementInit(CLIENT_INTERFACE(audManage_if, i_audMan))
{
/* Do nothing */
}
/* Default implementation for UserBufferManagement() */
void __attribute__ ((weak)) UserBufferManagement(unsigned sampsFromUsbToAudio[],
unsigned sampsFromAudioToUsb[],
CLIENT_INTERFACE(audManage_if, i_audMan))
{
/* Do nothing */
}

View File

@@ -0,0 +1,13 @@
#ifndef _USERBUFFERMANAGEMENT_H_
#define _USERBUFFERMANAGEMENT_H_
#include "xua_audio.h"
#include <xccompat.h>
void UserBufferManagementInit(CLIENT_INTERFACE(audManage_if, i_audMan));
void UserBufferManagement(unsigned sampsFromUsbToAudio[],
unsigned sampsFromAudioToUsb[],
CLIENT_INTERFACE(audManage_if, i_audMan));
#endif // _USERBUFFERMANAGEMENT_H_

View File

@@ -0,0 +1,55 @@
#ifndef __audio_h__
#define __audio_h__
#if __XC__
#ifndef NO_USB
#include "dfu_interface.h"
#endif
typedef interface audManage_if
{
[[guarded]]
void transfer_buffers(int * unsafe in_aud_buf, int * unsafe in_usb_buf,
int * unsafe out_usb_buf, int * unsafe out_aud_buf);
[[guarded]]
void transfer_samples(int in_mic_buf[], int in_spk_buf[], int out_mic_buf[], int out_spk_buf[]);
} audManage_if;
/** The audio driver thread.
*
* This function drives I2S ports and handles samples to/from other digital
* I/O threads.
*
* \param c_in Audio sample channel connected to the mixer() thread or the
* decouple() thread
* \param c_dig channel connected to the clockGen() thread for
* receiving/transmitting samples
* \param c_config An optional channel that will be passed on to the
* CODEC configuration functions.
*/
void audio(chanend ?c_in,
#if defined(SPDIF_TX) && (SPDIF_TX_TILE != AUDIO_IO_TILE)
chanend c_spdif_tx,
#endif
#if(defined(SPDIF_RX) || defined(ADAT_RX))
chanend c_dig,
#endif
chanend ?c_config, chanend ?c_adc
#if (XUD_TILE != 0) && (AUDIO_IO_TILE == 0)
, server interface i_dfu ?dfuInterface
#endif
#if (NUM_PDM_MICS > 0)
, chanend c_pdm_in
#endif
, client audManage_if i_audMan
);
void SpdifTxWrapper(chanend c_spdif_tx);
#endif // __XC__
#endif // __audio_h__

View File

@@ -0,0 +1,13 @@
#ifndef _CODEC_H_
#define _CODEC_H_
/* These functions must be implemented for the CODEC/ADC/DAC arrangement of a specific design */
/* Any required clocking and CODEC initialisation - run once at start up */
void AudioHwInit(chanend ?c_codec);
/* Configure audio hardware (clocking, CODECs etc) for a specific mClk/Sample frquency - run on every sample frequency change */
void AudioHwConfig(unsigned samFreq, unsigned mClk, chanend ?c_codec, unsigned dsdMode,
unsigned sampRes_DAC, unsigned sampRes_ADC);
#endif

View File

@@ -0,0 +1,13 @@
/* Deafult implementations of AudioStreamStop() and AudioStreamStart(). Both can be over-ridden */
void UserAudioStreamStop() __attribute__ ((weak));
void UserAudioStreamStop()
{
return;
}
void UserAudioStreamStart() __attribute__ ((weak));
void UserAudioStreamStart()
{
return;
}

View File

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

View File

@@ -0,0 +1,18 @@
#ifndef _CLOCKING_H_
#define _CLOCKING_H_
/** Clock generation and digital audio I/O handling.
*
* \param c_spdif_rx channel connected to S/PDIF receive thread
* \param c_adat_rx channel connect to ADAT receive thread
* \param p port to output clock signal to drive external frequency synthesizer
* \param c_audio channel connected to the audio() thread
* \param c_clk_ctl channel connected to Endpoint0() for configuration of the
* clock
* \param c_clk_int channel connected to the decouple() thread for clock
interrupts
*/
void clockGen (streaming chanend ?c_spdif_rx, chanend ?c_adat_rx, out port p, chanend c_audio, chanend c_clk_ctl, chanend c_clk_int);
#endif

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,16 @@
#ifndef _DSD_H_
#define _DSD_H_
#define DSD_MODE_OFF 0
#define DSD_MODE_DOP 1
#define DSD_MODE_NATIVE 2
/* DoP defines */
#define DSD_MARKER_1 0xFA
#define DSD_MARKER_2 0x05
#define DSD_MARKER_XOR 0xFF
#define DSD_EN_THRESH 32 /* Number of consecutive DSD markers before switching to DSD mode */
#define DSD_MASK(x) ((x >> 24) & 0xff)
#endif

View File

@@ -0,0 +1,19 @@
#ifndef _AUDIOREQUESTS_H_
#define _AUDIOREQUESTS_H_
#include <xccompat.h>
int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, REFERENCE_PARAM(USB_SetupPacket_t, sp), chanend c_audioControl,
NULLABLE_RESOURCE(chanend, c_mix_ctl), NULLABLE_RESOURCE(chanend, c_clk_ctl));
XUD_Result_t AudioClassRequests_1(XUD_ep ep0_out, XUD_ep ep0_in, REFERENCE_PARAM(USB_SetupPacket_t, sp), chanend c_audioControl,
NULLABLE_RESOURCE(chanend, c_mix_ctl), NULLABLE_RESOURCE(chanend, c_clk_ctl));
int AudioEndpointRequests_1(XUD_ep ep0_out, XUD_ep ep0_in, REFERENCE_PARAM(USB_SetupPacket_t, sp), chanend c_audioControl,
NULLABLE_RESOURCE(chanend, c_mix_ctl), NULLABLE_RESOURCE(chanend, c_clk_ctl));
void VendorAudioRequestsInit(chanend c_audioControl, NULLABLE_RESOURCE(chanend, c_mix_ctl), NULLABLE_RESOURCE(chanend, c_clk_ctl));
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,56 @@
def genstrings(outputChanCount, chanString, portString, structureString, adc_dac):
for i in range(1,outputChanCount):
print "#if (NUM_USB_CHAN_{c} > {i}-1)\n\
.{s}ChanStr_{i} = \"\"\n\
#if ({i} < I2S_CHANS_{adcdac}+1)\n\
\"Analogue {i}\"\n\
#endif\n\
#if (({i} < SPDIF_{p}_INDEX+2+1) && ({i} > SPDIF_{p}_INDEX)) && defined(SPDIF_{p})\n\
#if ({i} < I2S_CHANS_{adcdac}+1)\n\
\"/\"\n\
#endif\n\
#if({i} - SPDIF_{p}_INDEX == 1)\n\
\"SPDIF 1\"\n\
#elif({i} - SPDIF_{p}_INDEX == 2)\n\
\"SPDIF 2\"\n\
#endif\n\
#endif\n\
#if (({i} < ADAT_{p}_INDEX+8+1) && ({i} > ADAT_{p}_INDEX)) && defined(ADAT_{p})\n\
#if (({i} < SPDIF_{p}_INDEX+2+1) && ({i} > SPDIF_{p}_INDEX)) && defined(SPDIF_{p}) || ({i} < I2S_CHANS_{adcdac}+1)\n\
\"/\"\n\
#endif\n\
#if({i} - ADAT_TX_INDEX == 1)\n\
\"ADAT 1\"\n\
#elif({i} - ADAT_TX_INDEX == 2)\n\
\"ADAT 2\"\n\
#elif({i} - ADAT_TX_INDEX == 3)\n\
\"ADAT 3\"\n\
#elif({i} - ADAT_TX_INDEX == 4)\n\
\"ADAT 4\"\n\
#elif({i} - ADAT_TX_INDEX == 5)\n\
\"ADAT 5\"\n\
#elif({i} - ADAT_TX_INDEX == 6)\n\
\"ADAT 6\"\n\
#elif({i} - ADAT_TX_INDEX == 7)\n\
\"ADAT 7\"\n\
#elif({i} - ADAT_TX_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);
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 "/* Output Strings */\n\n"
genstrings(33, "OUT", "TX", "output", "DAC");
print "/* Input Strings */\n\n"
genstrings(33, "IN", "RX", "input", "ADC");

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -0,0 +1,61 @@
#ifndef __DESCRIPTOR_DEFS_H__
#define __DESCRIPTOR_DEFS_H__
#if (NUM_USB_CHAN_IN > 0) && (NUM_USB_CHAN_OUT > 0)
#define AUDIO_INTERFACE_COUNT 3
#elif (NUM_USB_CHAN_IN > 0) || (NUM_USB_CHAN_OUT > 0)
#define AUDIO_INTERFACE_COUNT 2
#else
#define AUDIO_INTERFACE_COUNT 1
#endif
/* Endpoint address defines */
#define ENDPOINT_ADDRESS_IN_CONTROL (ENDPOINT_NUMBER_IN_CONTROL | 0x80)
#define ENDPOINT_ADDRESS_IN_FEEDBACK (ENDPOINT_NUMBER_IN_FEEDBACK | 0x80)
#define ENDPOINT_ADDRESS_IN_AUDIO (ENDPOINT_NUMBER_IN_AUDIO | 0x80)
#define ENDPOINT_ADDRESS_IN_INTERRUPT (ENDPOINT_NUMBER_IN_INTERRUPT | 0x80)
#define ENDPOINT_ADDRESS_IN_MIDI (ENDPOINT_NUMBER_IN_MIDI | 0x80)
#define ENDPOINT_ADDRESS_IN_HID (ENDPOINT_NUMBER_IN_HID | 0x80)
#define ENDPOINT_ADDRESS_IN_IAP_INT (ENDPOINT_NUMBER_IN_IAP_INT | 0x80)
#define ENDPOINT_ADDRESS_IN_IAP (ENDPOINT_NUMBER_IN_IAP | 0x80)
#define ENDPOINT_ADDRESS_IN_IAP_EA_NATIVE_TRANS (ENDPOINT_NUMBER_IN_IAP_EA_NATIVE_TRANS | 0x80)
#define ENDPOINT_ADDRESS_OUT_CONTROL (ENDPOINT_NUMBER_OUT_CONTROL)
#define ENDPOINT_ADDRESS_OUT_AUDIO (ENDPOINT_NUMBER_OUT_AUDIO)
#define ENDPOINT_ADDRESS_OUT_MIDI (ENDPOINT_NUMBER_OUT_MIDI)
#define ENDPOINT_ADDRESS_OUT_IAP (ENDPOINT_NUMBER_OUT_IAP)
#define ENDPOINT_ADDRESS_OUT_IAP_EA_NATIVE_TRANS (ENDPOINT_NUMBER_OUT_IAP_EA_NATIVE_TRANS)
/* Interface numbers enum */
enum USBInterfaceNumber
{
INTERFACE_NUMBER_AUDIO_CONTROL = 0,
#if (NUM_USB_CHAN_OUT > 0)
INTERFACE_NUMBER_AUDIO_OUTPUT,
#endif
#if (NUM_USB_CHAN_IN > 0)
INTERFACE_NUMBER_AUDIO_INPUT,
#endif
#if defined(MIDI) && (MIDI != 0)
INTERFACE_NUMBER_MIDI_CONTROL,
INTERFACE_NUMBER_MIDI_STREAM,
#endif
#if defined(DFU) && (DFU != 0)
INTERFACE_NUMBER_DFU,
#endif
#if defined(IAP) && (IAP != 0)
INTERFACE_NUMBER_IAP,
#if defined(IAP_EA_NATIVE_TRANS) && (IAP_EA_NATIVE_TRANS != 0)
INTERFACE_NUMBER_IAP_EA_NATIVE_TRANS,
#endif
#endif
#if defined(HID_CONTROLS) && (HID_CONTROLS != 0)
INTERFACE_NUMBER_HID,
#endif
INTERFACE_COUNT /* End marker */
};
#endif

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -0,0 +1,802 @@
/**
* @brief Implements endpoint zero for an USB Audio 1.0/2.0 device
* @author Ross Owen, XMOS Semiconductor
*/
#include <xs1.h>
#include <safestring.h>
#include <stddef.h>
#ifndef NO_USB
#include "xud_device.h" /* Standard descriptor requests */
#include "usbaudio20.h" /* Defines from USB Audio 2.0 spec */
#include "devicedefines.h"
#include "descriptors.h" /* This devices descriptors */
#include "commands.h"
#include "audiostream.h"
#include "hostactive.h"
#include "vendorrequests.h"
#include "dfu_types.h"
#include "xc_ptr.h"
#include "audiorequests.h"
#ifdef HID_CONTROLS
#include "hid.h"
#endif
#if DSD_CHANS_DAC > 0
#include "dsd_support.h"
#endif
#ifndef __XC__
/* Support for xCORE channels in C */
#define null 0
#define outuint(c, x) asm ("out res[%0], %1" :: "r" (c), "r" (x))
#define chkct(c, x) asm ("chkct res[%0], %1" :: "r" (c), "r" (x))
#endif
/* Some warnings.... */
/* Windows does not have a built in DFU driver (windows will prompt), so warn that DFU will not be functional in Audio 1.0 mode */
#ifndef FORCE_UAC1_DFU
#if ((AUDIO_CLASS==1) || defined(AUDIO_CLASS_FALLBACK)) && defined(DFU)
#warning DFU will not be enabled in AUDIO 1.0 mode due to Windows requesting driver
#endif
#endif
/* MIDI not supported in Audio 1.0 mode */
#if ((AUDIO_CLASS==1) || defined(AUDIO_CLASS_FALLBACK)) && defined(MIDI)
#warning MIDI is currently not supported and will not be enabled in AUDIO 1.0 mode
#endif
/* If DFU_PID not defined, standard PID used.. this is probably what we want.. */
#ifndef DFU_PID
#warning DFU_PID not defined, Using PID_AUDIO_2. This is probably fine!
#endif
#ifdef DFU
#include "dfu.h"
#define DFU_IF_NUM INPUT_INTERFACES + OUTPUT_INTERFACES + MIDI_INTERFACES + 1
extern void device_reboot(chanend);
#endif
unsigned int DFU_mode_active = 0; // 0 - App active, 1 - DFU active
/* Global volume and mute tables */
int volsOut[NUM_USB_CHAN_OUT + 1];
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];
unsigned char channelMap[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT];
unsigned char channelMapAud[NUM_USB_CHAN_OUT];
unsigned char channelMapUsb[NUM_USB_CHAN_IN];
unsigned char mixSel[MAX_MIX_COUNT][MIX_INPUTS];
#endif
int min(int x, int y);
/* Global current device config var*/
extern unsigned char g_currentConfig;
/* Global endpoint status arrays - declared in usb_device.xc */
extern unsigned char g_interfaceAlt[];
/* We remember which streaming alt we were last using to avoid interrupting the I2S as best we can */
/* Note, we cannot simply use g_interfaceAlt[] since this also records using the zero bandwidth alt */
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;
/* Subslot */
const unsigned g_subSlot_Out_HS[OUTPUT_FORMAT_COUNT] = {HS_STREAM_FORMAT_OUTPUT_1_SUBSLOT_BYTES,
#if(OUTPUT_FORMAT_COUNT > 1)
HS_STREAM_FORMAT_OUTPUT_2_SUBSLOT_BYTES,
#endif
#if(OUTPUT_FORMAT_COUNT > 2)
HS_STREAM_FORMAT_OUTPUT_3_SUBSLOT_BYTES
#endif
};
const unsigned g_subSlot_Out_FS[OUTPUT_FORMAT_COUNT] = {FS_STREAM_FORMAT_OUTPUT_1_SUBSLOT_BYTES,
#if(OUTPUT_FORMAT_COUNT > 1)
FS_STREAM_FORMAT_OUTPUT_2_SUBSLOT_BYTES,
#endif
#if(OUTPUT_FORMAT_COUNT > 2)
FS_STREAM_FORMAT_OUTPUT_3_SUBSLOT_BYTES
#endif
};
const unsigned g_subSlot_In_HS[INPUT_FORMAT_COUNT] = {HS_STREAM_FORMAT_INPUT_1_SUBSLOT_BYTES,
#if(INPUT_FORMAT_COUNT > 1)
HS_STREAM_FORMAT_INPUT_2_SUBSLOT_BYTES,
#endif
#if(INPUT_FORMAT_COUNT > 2)
HS_STREAM_FORMAT_INPUT_3_SUBSLOT_BYTES
#endif
};
const unsigned g_subSlot_In_FS[INPUT_FORMAT_COUNT] = {FS_STREAM_FORMAT_INPUT_1_SUBSLOT_BYTES,
#if(INPUT_FORMAT_COUNT > 1)
FS_STREAM_FORMAT_INPUT_2_SUBSLOT_BYTES,
#endif
#if(INPUT_FORMAT_COUNT > 2)
FS_STREAM_FORMAT_INPUT_3_SUBSLOT_BYTES
#endif
};
/* Sample Resolution */
const unsigned g_sampRes_Out_HS[OUTPUT_FORMAT_COUNT] = {HS_STREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS,
#if(OUTPUT_FORMAT_COUNT > 1)
HS_STREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS,
#endif
#if(OUTPUT_FORMAT_COUNT > 2)
HS_STREAM_FORMAT_OUTPUT_3_RESOLUTION_BITS
#endif
};
const unsigned g_sampRes_Out_FS[OUTPUT_FORMAT_COUNT] = {FS_STREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS,
#if(OUTPUT_FORMAT_COUNT > 1)
FS_STREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS,
#endif
#if(OUTPUT_FORMAT_COUNT > 2)
FS_STREAM_FORMAT_OUTPUT_3_RESOLUTION_BITS
#endif
};
const unsigned g_sampRes_In_HS[INPUT_FORMAT_COUNT] = {HS_STREAM_FORMAT_INPUT_1_RESOLUTION_BITS,
#if(INPUT_FORMAT_COUNT > 1)
HS_STREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS,
#endif
#if(INPUT_FORMAT_COUNT > 2)
HS_STREAM_FORMAT_OUTPUT_3_RESOLUTION_BITS
#endif
};
const unsigned g_sampRes_In_FS[INPUT_FORMAT_COUNT] = {FS_STREAM_FORMAT_INPUT_1_RESOLUTION_BITS,
#if(INPUT_FORMAT_COUNT > 1)
FS_STREAM_FORMAT_INPUT_2_RESOLUTION_BITS,
#endif
#if(INPUT_FORMAT_COUNT > 2)
FS_STREAM_FORMAT_INPUT_3_RESOLUTION_BITS
#endif
};
/* Data Format (Note, this is shared over HS and FS */
const unsigned g_dataFormat_Out[OUTPUT_FORMAT_COUNT] = {STREAM_FORMAT_OUTPUT_1_DATAFORMAT,
#if(OUTPUT_FORMAT_COUNT > 1)
STREAM_FORMAT_OUTPUT_2_DATAFORMAT,
#endif
#if(OUTPUT_FORMAT_COUNT > 2)
STREAM_FORMAT_OUTPUT_3_DATAFORMAT
#endif
};
const unsigned g_dataFormat_In[INPUT_FORMAT_COUNT] = {STREAM_FORMAT_INPUT_1_DATAFORMAT,
#if(INPUT_FORMAT_COUNT > 1)
STREAM_FORMAT_INPUT_2_DATAFORMAT,
#endif
#if(INPUT_FORMAT_COUNT > 2)
STREAM_FORMAT_INPUT_3_DATAFORMAT
#endif
};
/* Channel count */
/* Note, currently only input changes.. */
const unsigned g_chanCount_In_HS[INPUT_FORMAT_COUNT] = {HS_STREAM_FORMAT_INPUT_1_CHAN_COUNT,
#if(INPUT_FORMAT_COUNT > 1)
HS_STREAM_FORMAT_INPUT_2_CHAN_COUNT,
#endif
#if(INPUT_FORMAT_COUNT > 2)
HS_STREAM_FORMAT_INPUT_3_CHAN_COUNT
#endif
};
/* Endpoint 0 function. Handles all requests to the device */
void Endpoint0(chanend c_ep0_out, chanend c_ep0_in, chanend c_audioControl,
chanend c_mix_ctl, chanend c_clk_ctl, chanend c_EANativeTransport_ctrl, CLIENT_INTERFACE(i_dfu, dfuInterface) VENDOR_REQUESTS_PARAMS_DEC_)
{
USB_SetupPacket_t sp;
XUD_ep ep0_out = XUD_InitEp(c_ep0_out);
XUD_ep ep0_in = XUD_InitEp(c_ep0_in);
#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
/* 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;
}
#endif
#ifdef VENDOR_AUDIO_REQS
VendorAudioRequestsInit(c_audioControl, c_mix_ctl, c_clk_ctl);
#endif
#ifdef DFU
/* Check if device has started in DFU mode */
if (DFUReportResetState(null))
{
/* Stop audio */
outuint(c_audioControl, SET_SAMPLE_FREQ);
outuint(c_audioControl, AUDIO_STOP_FOR_DFU);
/* No Handshake */
DFU_mode_active = 1;
}
#endif
while(1)
{
/* Returns XUD_RES_OKAY for success, XUD_RES_RST for bus reset */
XUD_Result_t result = USB_GetSetupPacket(ep0_out, ep0_in, &sp);
if (result == XUD_RES_OKAY)
{
result = XUD_RES_ERR;
/* Inspect Request type and Receipient and direction */
switch( (sp.bmRequestType.Direction << 7) | (sp.bmRequestType.Recipient ) | (sp.bmRequestType.Type << 5) )
{
case USB_BMREQ_H2D_STANDARD_INT:
/* Over-riding USB_StandardRequests implementation */
if(sp.bRequest == USB_SET_INTERFACE)
{
switch (sp.wIndex)
{
/* Check for audio stream from host start/stop */
#if (NUM_USB_CHAN_OUT > 0) && (AUDIO_CLASS == 2)
case INTERFACE_NUMBER_AUDIO_OUTPUT:
/* Check the alt is in range */
if(sp.wValue <= OUTPUT_FORMAT_COUNT)
{
/* Alt 0 is stream stop */
/* Only send change if we need to */
if((sp.wValue > 0) && (g_curStreamAlt_Out != sp.wValue))
{
g_curStreamAlt_Out = sp.wValue;
/* Send format of data onto buffering */
outuint(c_audioControl, SET_STREAM_FORMAT_OUT);
outuint(c_audioControl, g_dataFormat_Out[sp.wValue-1]); /* Data format (PCM/DSD) */
if(g_curUsbSpeed == XUD_SPEED_HS)
{
outuint(c_audioControl, NUM_USB_CHAN_OUT); /* Channel count */
outuint(c_audioControl, g_subSlot_Out_HS[sp.wValue-1]); /* Subslot */
outuint(c_audioControl, g_sampRes_Out_HS[sp.wValue-1]); /* Resolution */
}
else
{
outuint(c_audioControl, NUM_USB_CHAN_OUT_FS); /* Channel count */
outuint(c_audioControl, g_subSlot_Out_FS[sp.wValue-1]); /* Subslot */
outuint(c_audioControl, g_sampRes_Out_FS[sp.wValue-1]); /* Resolution */
}
/* Handshake */
chkct(c_audioControl, XS1_CT_END);
}
}
break;
#endif
#if (NUM_USB_CHAN_IN > 0) && (AUDIO_CLASS == 2)
case INTERFACE_NUMBER_AUDIO_INPUT:
/* Check the alt is in range */
if(sp.wValue <= INPUT_FORMAT_COUNT)
{
/* Alt 0 is stream stop */
/* Only send change if we need to */
if((sp.wValue > 0) && (g_curStreamAlt_In != sp.wValue))
{
g_curStreamAlt_In = sp.wValue;
/* Send format of data onto buffering */
outuint(c_audioControl, SET_STREAM_FORMAT_IN);
outuint(c_audioControl, g_dataFormat_In[sp.wValue-1]); /* Data format (PCM/DSD) */
if(g_curUsbSpeed == XUD_SPEED_HS)
{
outuint(c_audioControl, g_chanCount_In_HS[sp.wValue-1]); /* Channel count */
outuint(c_audioControl, g_subSlot_In_HS[sp.wValue-1]); /* Subslot */
outuint(c_audioControl, g_sampRes_In_HS[sp.wValue-1]); /* Resolution */
}
else
{
outuint(c_audioControl, NUM_USB_CHAN_IN_FS); /* Channel count */
outuint(c_audioControl, g_subSlot_In_FS[sp.wValue-1]); /* Subslot */
outuint(c_audioControl, g_sampRes_In_FS[sp.wValue-1]); /* Resolution */
}
/* Wait for handshake */
chkct(c_audioControl, XS1_CT_END);
}
}
break;
#endif
#ifdef IAP_EA_NATIVE_TRANS
case INTERFACE_NUMBER_IAP_EA_NATIVE_TRANS:
/* Check the alt is in range */
if (sp.wValue <= IAP_EA_NATIVE_TRANS_ALT_COUNT)
{
/* Reset all state of endpoints associated with this interface
* when changing an alternative setting. See USB 2.0 Spec 9.1.1.5 */
XUD_ResetEpStateByAddr(ENDPOINT_ADDRESS_IN_IAP_EA_NATIVE_TRANS);
XUD_ResetEpStateByAddr(ENDPOINT_ADDRESS_OUT_IAP_EA_NATIVE_TRANS);
/* Send selected Alt interface number onto EA Native EP manager */
outuint(c_EANativeTransport_ctrl, (unsigned)sp.wValue);
/* Wait for handshake */
chkct(c_EANativeTransport_ctrl, XS1_CT_END);
}
break;
#endif
default:
/* Unhandled interface */
break;
}
#if (NUM_USB_CHAN_OUT > 0) && (NUM_USB_CHAN_IN > 0)
if ((sp.wIndex == INTERFACE_NUMBER_AUDIO_OUTPUT) || (sp.wIndex == INTERFACE_NUMBER_AUDIO_INPUT))
{
/* Check for stream start stop on output and input audio interfaces */
if(sp.wValue && !g_interfaceAlt[INTERFACE_NUMBER_AUDIO_OUTPUT] && !g_interfaceAlt[INTERFACE_NUMBER_AUDIO_INPUT])
{
/* If start and input AND output not currently running */
UserAudioStreamStart();
}
else if(((sp.wIndex == 1) && (!sp.wValue)) && g_interfaceAlt[INTERFACE_NUMBER_AUDIO_OUTPUT] && (!g_interfaceAlt[INTERFACE_NUMBER_AUDIO_INPUT]))
{
/* if output stop and output running and input not running */
UserAudioStreamStop();
}
else if(((sp.wIndex == 2) && (!sp.wValue)) && g_interfaceAlt[INTERFACE_NUMBER_AUDIO_INPUT] && (!g_interfaceAlt[INTERFACE_NUMBER_AUDIO_OUTPUT]))
{
/* if input stop and input running and output not running */
UserAudioStreamStop();
}
}
#elif (NUM_USB_CHAN_OUT > 0)
if(sp.wIndex == INTERFACE_NUMBER_AUDIO_OUTPUT)
{
if(sp.wValue && (!g_interfaceAlt[INTERFACE_NUMBER_AUDIO_OUTPUT]))
{
/* if start and not currently running */
UserAudioStreamStart();
}
else if (!sp.wValue && g_interfaceAlt[INTERFACE_NUMBER_AUDIO_OUTPUT])
{
/* if stop and currently running */
UserAudioStreamStop();
}
}
#elif (NUM_USB_CHAN_IN > 0)
if(sp.wIndex == INTERFACE_NUMBER_AUDIO_INPUT)
{
if(sp.wValue && (!g_interfaceAlt[INTERFACE_NUMBER_AUDIO_INPUT]))
{
/* if start and not currently running */
UserAudioStreamStart();
}
else if (!sp.wValue && g_interfaceAlt[INTERFACE_NUMBER_AUDIO_INPUT])
{
/* if stop and currently running */
UserAudioStreamStop();
}
}
#endif
} /* if(sp.bRequest == SET_INTERFACE) */
break; /* BMREQ_H2D_STANDARD_INT */
case USB_BMREQ_D2H_STANDARD_INT:
switch(sp.bRequest)
{
#ifdef HID_CONTROLS
case USB_GET_DESCRIPTOR:
/* Check what inteface request is for */
if(sp.wIndex == INTERFACE_NUMBER_HID)
{
/* High byte of wValue is descriptor type */
unsigned descriptorType = sp.wValue & 0xff00;
switch (descriptorType)
{
case HID_HID:
/* 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;
}
}
break;
#endif
default:
break;
}
break;
/* Recipient: Device */
case USB_BMREQ_H2D_STANDARD_DEV:
/* Inspect for actual request */
switch( sp.bRequest )
{
/* Standard request: SetConfiguration */
/* Overriding implementation in USB_StandardRequests */
case USB_SET_CONFIGURATION:
//if(g_current_config == 1)
{
/* Consider host active with valid driver at this point */
UserHostActive(1);
}
/* We want to run USB_StandardsRequests() implementation also. Don't modify result
* and don't call XUD_DoSetRequestStatus() */
break;
default:
//Unknown device request"
break;
}
break;
/* Audio Class 1.0 Sampling Freqency Requests go to Endpoint */
case USB_BMREQ_H2D_CLASS_EP:
case USB_BMREQ_D2H_CLASS_EP:
{
unsigned epNum = sp.wIndex & 0xff;
if ((epNum == ENDPOINT_ADDRESS_OUT_AUDIO) || (epNum == ENDPOINT_ADDRESS_IN_AUDIO))
{
#if (AUDIO_CLASS == 2) && defined(AUDIO_CLASS_FALLBACK)
if(g_curUsbSpeed == XUD_SPEED_FS)
{
result = AudioEndpointRequests_1(ep0_out, ep0_in, &sp, c_audioControl, c_mix_ctl, c_clk_ctl);
}
#elif (AUDIO_CLASS==1)
result = AudioEndpointRequests_1(ep0_out, ep0_in, &sp, c_audioControl, c_mix_ctl, c_clk_ctl);
#endif
}
}
break;
case USB_BMREQ_H2D_CLASS_INT:
case USB_BMREQ_D2H_CLASS_INT:
{
unsigned interfaceNum = sp.wIndex & 0xff;
//unsigned request = (sp.bmRequestType.Recipient ) | (sp.bmRequestType.Type << 5);
/* TODO Check on return value retval = */
#ifdef DFU
unsigned DFU_IF = INTERFACE_NUMBER_DFU;
/* DFU interface number changes based on which mode we are currently running in */
if (DFU_mode_active)
{
DFU_IF = 0;
}
if (interfaceNum == DFU_IF)
{
int reset = 0;
/* If running in application mode stop audio */
/* Don't interupt audio for save and restore cmds */
if ((DFU_IF == INTERFACE_NUMBER_DFU) && (sp.bRequest != XMOS_DFU_SAVESTATE) &&
(sp.bRequest != XMOS_DFU_RESTORESTATE))
{
// Stop audio
outuint(c_audioControl, SET_SAMPLE_FREQ);
outuint(c_audioControl, AUDIO_STOP_FOR_DFU);
// Handshake
chkct(c_audioControl, XS1_CT_END);
}
/* This will return 1 if reset requested */
result = DFUDeviceRequests(ep0_out, &ep0_in, &sp, null, g_interfaceAlt[sp.wIndex], dfuInterface, &reset);
if(reset)
{
DFUDelay(50000000);
device_reboot(c_audioControl);
}
}
#endif
/* Check for: - Audio CONTROL interface request - always 0, note we check for DFU first
* - Audio STREAMING interface request (In or Out)
* - Audio endpoint request (Audio 1.0 Sampling freq requests are sent to the endpoint)
*/
if(((interfaceNum == 0) || (interfaceNum == 1) || (interfaceNum == 2))
#ifdef DFU
&& !DFU_mode_active
#endif
)
{
#if (AUDIO_CLASS == 2) && defined(AUDIO_CLASS_FALLBACK)
if(g_curUsbSpeed == XUD_SPEED_HS)
{
result = AudioClassRequests_2(ep0_out, ep0_in, &sp, c_audioControl, c_mix_ctl, c_clk_ctl);
}
else
{
result = AudioClassRequests_1(ep0_out, ep0_in, &sp, c_audioControl, c_mix_ctl, c_clk_ctl);
}
#elif (AUDIO_CLASS==2)
result = AudioClassRequests_2(ep0_out, ep0_in, &sp, c_audioControl, c_mix_ctl, c_clk_ctl);
#else
result = AudioClassRequests_1(ep0_out, ep0_in, &sp, c_audioControl, c_mix_ctl, c_clk_ctl);
#endif
#ifdef VENDOR_AUDIO_REQS
/* If result is ERR at this point, then request to audio interface not handled - handle vendor audio reqs */
if(result == XUD_RES_ERR)
{
result = VendorAudioRequests(ep0_out, ep0_in, sp.bRequest,
sp.wValue >> 8, sp.wValue & 0xff,
sp.wIndex >> 8, sp.bmRequestType.Direction,
c_audioControl, c_mix_ctl, c_clk_ctl);
}
#endif
}
}
break;
default:
break;
}
} /* if(result == XUD_RES_OKAY) */
{
if(result == XUD_RES_ERR)
{
/* Run vendor defined parsing/processing */
/* Note, an interface might seem ideal here but this *must* be executed on the same
* core sure to shared memory depandancy */
result = VendorRequests(ep0_out, ep0_in, &sp VENDOR_REQUESTS_PARAMS_);
}
}
if(result == XUD_RES_ERR)
{
#ifdef DFU
if (!DFU_mode_active)
{
#endif
#if defined(AUDIO_CLASS_FALLBACK) && (AUDIO_CLASS != 1)
/* Return Audio 2.0 Descriptors with Audio 1.0 as fallback */
result = USB_StandardRequests(ep0_out, ep0_in,
(unsigned char*)&devDesc_Audio2, sizeof(devDesc_Audio2),
(unsigned char*)&cfgDesc_Audio2, sizeof(cfgDesc_Audio2),
(unsigned char*)&devDesc_Audio1, sizeof(devDesc_Audio1),
cfgDesc_Audio1, sizeof(cfgDesc_Audio1),
(char**)&g_strTable, sizeof(g_strTable)/sizeof(char *),
&sp, g_curUsbSpeed);
#elif FULL_SPEED_AUDIO_2
/* Return Audio 2.0 Descriptors for high_speed and full-speed */
/* Unfortunately we need to munge the descriptors a bit between full and high-speed */
if(g_curUsbSpeed == XUD_SPEED_HS)
{
/* Modify Audio Class 2.0 Config descriptor for High-speed operation */
#if (NUM_USB_CHAN_OUT > 0)
cfgDesc_Audio2.Audio_CS_Control_Int.Audio_Out_InputTerminal.bNrChannels = NUM_USB_CHAN_OUT;
#if (NUM_USB_CHAN_OUT > 0)
cfgDesc_Audio2.Audio_Out_Format.bSubslotSize = HS_STREAM_FORMAT_OUTPUT_1_SUBSLOT_BYTES;
cfgDesc_Audio2.Audio_Out_Format.bBitResolution = HS_STREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS;
cfgDesc_Audio2.Audio_Out_Endpoint.wMaxPacketSize = HS_STREAM_FORMAT_OUTPUT_1_MAXPACKETSIZE;
cfgDesc_Audio2.Audio_Out_ClassStreamInterface.bNrChannels = NUM_USB_CHAN_OUT;
#endif
#if (OUTPUT_FORMAT_COUNT > 1)
cfgDesc_Audio2.Audio_Out_Format_2.bSubslotSize = HS_STREAM_FORMAT_OUTPUT_2_SUBSLOT_BYTES;
cfgDesc_Audio2.Audio_Out_Format_2.bBitResolution = HS_STREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS;
cfgDesc_Audio2.Audio_Out_Endpoint_2.wMaxPacketSize = HS_STREAM_FORMAT_OUTPUT_2_MAXPACKETSIZE;
cfgDesc_Audio2.Audio_Out_ClassStreamInterface_2.bNrChannels = NUM_USB_CHAN_OUT;
#endif
#if (OUTPUT_FORMAT_COUNT > 2)
cfgDesc_Audio2.Audio_Out_Format_3.bSubslotSize = HS_STREAM_FORMAT_OUTPUT_3_SUBSLOT_BYTES;
cfgDesc_Audio2.Audio_Out_Format_3.bBitResolution = HS_STREAM_FORMAT_OUTPUT_3_RESOLUTION_BITS;
cfgDesc_Audio2.Audio_Out_Endpoint_3.wMaxPacketSize = HS_STREAM_FORMAT_OUTPUT_3_MAXPACKETSIZE;
cfgDesc_Audio2.Audio_Out_ClassStreamInterface_3.bNrChannels = NUM_USB_CHAN_OUT;
#endif
#endif
#if (NUM_USB_CHAN_IN > 0)
cfgDesc_Audio2.Audio_CS_Control_Int.Audio_In_InputTerminal.bNrChannels = NUM_USB_CHAN_IN;
cfgDesc_Audio2.Audio_In_Format.bSubslotSize = HS_STREAM_FORMAT_INPUT_1_SUBSLOT_BYTES;
cfgDesc_Audio2.Audio_In_Format.bBitResolution = HS_STREAM_FORMAT_INPUT_1_RESOLUTION_BITS;
cfgDesc_Audio2.Audio_In_Endpoint.wMaxPacketSize = HS_STREAM_FORMAT_INPUT_1_MAXPACKETSIZE;
cfgDesc_Audio2.Audio_In_ClassStreamInterface.bNrChannels = NUM_USB_CHAN_IN;
#endif
}
else
{
/* Modify Audio Class 2.0 Config descriptor for Full-speed operation */
#if (NUM_USB_CHAN_OUT > 0)
cfgDesc_Audio2.Audio_CS_Control_Int.Audio_Out_InputTerminal.bNrChannels = NUM_USB_CHAN_OUT_FS;
#if (NUM_USB_CHAN_OUT > 0)
cfgDesc_Audio2.Audio_Out_Format.bSubslotSize = FS_STREAM_FORMAT_OUTPUT_1_SUBSLOT_BYTES;
cfgDesc_Audio2.Audio_Out_Format.bBitResolution = FS_STREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS;
cfgDesc_Audio2.Audio_Out_Endpoint.wMaxPacketSize = FS_STREAM_FORMAT_OUTPUT_1_MAXPACKETSIZE;
cfgDesc_Audio2.Audio_Out_ClassStreamInterface.bNrChannels = NUM_USB_CHAN_OUT_FS;
#endif
#if (OUTPUT_FORMAT_COUNT > 1)
cfgDesc_Audio2.Audio_Out_Format_2.bSubslotSize = FS_STREAM_FORMAT_OUTPUT_2_SUBSLOT_BYTES;
cfgDesc_Audio2.Audio_Out_Format_2.bBitResolution = FS_STREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS;
cfgDesc_Audio2.Audio_Out_Endpoint_2.wMaxPacketSize = FS_STREAM_FORMAT_OUTPUT_2_MAXPACKETSIZE;
cfgDesc_Audio2.Audio_Out_ClassStreamInterface_2.bNrChannels = NUM_USB_CHAN_OUT_FS;
#endif
#if (OUTPUT_FORMAT_COUNT > 2)
cfgDesc_Audio2.Audio_Out_Format_3.bSubslotSize = FS_STREAM_FORMAT_OUTPUT_3_SUBSLOT_BYTES;
cfgDesc_Audio2.Audio_Out_Format_3.bBitResolution = FS_STREAM_FORMAT_OUTPUT_3_RESOLUTION_BITS;
cfgDesc_Audio2.Audio_Out_Endpoint_3.wMaxPacketSize = FS_STREAM_FORMAT_OUTPUT_3_MAXPACKETSIZE;
cfgDesc_Audio2.Audio_Out_ClassStreamInterface_3.bNrChannels = NUM_USB_CHAN_OUT_FS;
#endif
#endif
#if (NUM_USB_CHAN_IN > 0)
cfgDesc_Audio2.Audio_CS_Control_Int.Audio_In_InputTerminal.bNrChannels = NUM_USB_CHAN_IN_FS;
cfgDesc_Audio2.Audio_In_Format.bSubslotSize = FS_STREAM_FORMAT_INPUT_1_SUBSLOT_BYTES;
cfgDesc_Audio2.Audio_In_Format.bBitResolution = FS_STREAM_FORMAT_INPUT_1_RESOLUTION_BITS;
cfgDesc_Audio2.Audio_In_Endpoint.wMaxPacketSize = FS_STREAM_FORMAT_INPUT_1_MAXPACKETSIZE;
cfgDesc_Audio2.Audio_In_ClassStreamInterface.bNrChannels = NUM_USB_CHAN_IN_FS;
#endif
}
result = USB_StandardRequests(ep0_out, ep0_in,
(unsigned char*)&devDesc_Audio2, sizeof(devDesc_Audio2),
(unsigned char*)&cfgDesc_Audio2, sizeof(cfgDesc_Audio2),
null, 0,
null, 0,
#ifdef __XC__
g_strTable, sizeof(g_strTable), sp, null, g_curUsbSpeed);
#else
(char**)&g_strTable, sizeof(g_strTable)/sizeof(char *), &sp, g_curUsbSpeed);
#endif
#elif (AUDIO_CLASS == 1)
/* Return Audio 1.0 Descriptors in FS, should never be in HS! */
result = USB_StandardRequests(ep0_out, ep0_in,
null, 0,
null, 0,
(unsigned char*)&devDesc_Audio1, sizeof(devDesc_Audio1),
cfgDesc_Audio1, sizeof(cfgDesc_Audio1),
(char**)&g_strTable, sizeof(g_strTable)/sizeof(char *), &sp, g_curUsbSpeed);
#else
/* Return Audio 2.0 Descriptors with Null device as fallback */
result = USB_StandardRequests(ep0_out, ep0_in,
(unsigned char*)&devDesc_Audio2, sizeof(devDesc_Audio2),
(unsigned char*)&cfgDesc_Audio2, sizeof(cfgDesc_Audio2),
devDesc_Null, sizeof(devDesc_Null),
cfgDesc_Null, sizeof(cfgDesc_Null),
(char**)&g_strTable, sizeof(g_strTable)/sizeof(char *), &sp, g_curUsbSpeed);
#endif
#ifdef DFU
}
else
{
/* Running in DFU mode - always return same descs for DFU whether HS or FS */
result = USB_StandardRequests(ep0_out, ep0_in,
DFUdevDesc, sizeof(DFUdevDesc),
DFUcfgDesc, sizeof(DFUcfgDesc),
null, 0, /* Used same descriptors for full and high-speed */
null, 0,
(char**)&g_strTable, sizeof(g_strTable)/sizeof(char *), &sp, g_curUsbSpeed);
}
#endif
}
if (result == XUD_RES_RST)
{
#ifdef __XC__
g_curUsbSpeed = XUD_ResetEndpoint(ep0_out, ep0_in);
#else
g_curUsbSpeed = XUD_ResetEndpoint(ep0_out, &ep0_in);
#endif
g_currentConfig = 0;
g_curStreamAlt_Out = 0;
g_curStreamAlt_In = 0;
#ifdef DFU
if (DFUReportResetState(null))
{
if (!DFU_mode_active)
{
DFU_mode_active = 1;
}
}
else
{
if (DFU_mode_active)
{
DFU_mode_active = 0;
/* Send reboot command */
DFUDelay(5000000);
device_reboot(c_audioControl);
}
}
#endif
}
}
}
#endif /* NO_USB */

View File

@@ -0,0 +1,29 @@
#ifndef _ENDPOINT0_H_
#define _ENDPOINT0_H_
#include "dfu_interface.h"
#include "devicedefines.h"
#include "vendorrequests.h"
/** Function implementing Endpoint 0 for enumeration, control and configuration
* of USB audio devices. It uses the descriptors defined in ``descriptors_2.h``.
*
* \param c_ep0_out Chanend connected to the XUD_Manager() out endpoint array
* \param c_ep0_in Chanend connected to the XUD_Manager() in endpoint array
* \param c_audioCtrl Chanend connected to the decouple thread for control
* audio (sample rate changes etc.)
* \param c_mix_ctl Optional chanend to be connected to the mixer thread if
* present
* \param c_clk_ctl Optional chanend to be connected to the clockgen thread if
* present.
* \param c_usb_test Optional chanend to be connected to XUD if test modes required.
*
* \param c_EANativeTransport_ctrl Optional chanend to be connected to EA Native
* endpoint manager if present
*/
void 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_);
#endif

View File

@@ -0,0 +1,34 @@
#ifndef NO_USB
#include "xud.h"
#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,
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,
NULLABLE_RESOURCE(chanend, c_mix_ctl),
NULLABLE_RESOURCE(chanend, c_clk_ctL))
{
return XUD_RES_ERR;
}
int VendorRequests(XUD_ep ep0_out, XUD_ep ep0_in, REFERENCE_PARAM(USB_SetupPacket_t, sp) VENDOR_REQUESTS_PARAMS_DEC_) __attribute__ ((weak));
int VendorRequests(XUD_ep ep0_out, XUD_ep ep0_in, REFERENCE_PARAM(USB_SetupPacket_t, sp) VENDOR_REQUESTS_PARAMS_DEC_)
{
return XUD_RES_ERR;
}
void VendorRequests_Init(VENDOR_REQUESTS_PARAMS_DEC) __attribute__ ((weak));
void VendorRequests_Init(VENDOR_REQUESTS_PARAMS_DEC)
{
}
#endif /* NO_USB */

View File

@@ -0,0 +1,44 @@
#ifndef _VENDORREQUESTS_H_
#define _VENDORREQUESTS_H_
#include <xccompat.h>
#include "devicedefines.h"
#include "xud_device.h"
/* Functions that handle vustomer vendor requests.
*
* THESE NEED IMPLEMENTING FOR A SPECIFIC DESIGN
*
* Should return 0 if handled sucessfully, else return 0 (-1 for passing up reset/suspend)
*
* */
#define PREPEND_COMMA(x) ,x
#ifndef VENDOR_REQUESTS_PARAMS
#define VENDOR_REQUESTS_PARAMS_
#define VENDOR_REQUESTS_PARAMS_DEC_
#else
#define VENDOR_REQUESTS_PARAMS_ PREPEND_COMMA(VENDOR_REQUESTS_PARAMS)
#define VENDOR_REQUESTS_PARAMS_DEC_ PREPEND_COMMA(VENDOR_REQUESTS_PARAMS_DEC)
#endif
#ifndef VENDOR_REQUESTS_PARAMS_DEC
#define VENDOR_REQUESTS_PARAMS_DEC
#endif
#ifndef VENDOR_REQUESTS_PARAMS
#define VENDOR_REQUESTS_PARAMS
#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,
NULLABLE_RESOURCE(chanend, c_mix_ctl),
NULLABLE_RESOURCE(chanend, c_clk_ctL));
int VendorRequests(XUD_ep ep0_out, XUD_ep ep0_in, REFERENCE_PARAM(USB_SetupPacket_t, sp) VENDOR_REQUESTS_PARAMS_DEC_);
void VendorRequests_Init(VENDOR_REQUESTS_PARAMS_DEC);
#endif

View File

@@ -0,0 +1,127 @@
#include "devicedefines.h"
#ifdef DFU
#include "uac_hwresources.h"
#include <xs1.h>
#include <xclib.h>
#ifdef QUAD_SPI_FLASH
#include <quadflashlib.h>
#else
#include <flashlib.h>
#endif
#include <print.h>
#define settw(a,b) {__asm__ __volatile__("settw res[%0], %1": : "r" (a) , "r" (b));}
#define setc(a,b) {__asm__ __volatile__("setc res[%0], %1": : "r" (a) , "r" (b));}
#define setclk(a,b) {__asm__ __volatile__("setclk res[%0], %1": : "r" (a) , "r" (b));}
#define portin(a,b) {__asm__ __volatile__("in %0, res[%1]": "=r" (b) : "r" (a));}
#define portout(a,b) {__asm__ __volatile__("out res[%0], %1": : "r" (a) , "r" (b));}
#ifdef DFU_FLASH_DEVICE
#ifdef QUAD_SPI_FLASH
/* Using specified flash device rather than all supported in tools */
fl_QuadDeviceSpec flash_devices[] = {DFU_FLASH_DEVICE};
#else
/* Using specified flash device rather than all supported in tools */
fl_DeviceSpec flash_devices[] = {DFU_FLASH_DEVICE};
#endif
#endif
#ifdef QUAD_SPI_FLASH
/*
typedef struct {
out port qspiCS;
out port qspiSCLK;
out buffered port:32 qspiSIO;
clock qspiClkblk;
} fl_QSPIPorts;
*/
fl_QSPIPorts p_qflash =
{
XS1_PORT_1B,
XS1_PORT_1C,
XS1_PORT_4B,
CLKBLK_FLASHLIB
};
#else
fl_PortHolderStruct p_flash =
{
XS1_PORT_1A,
XS1_PORT_1B,
XS1_PORT_1C,
XS1_PORT_1D,
CLKBLK_FLASHLIB
};
#endif
/* return 1 for opened ports successfully */
int flash_cmd_enable_ports()
{
int result = 0;
#ifdef QUAD_SPI_FLASH
/* Ports not shared */
#else
setc(p_flash.spiMISO, XS1_SETC_INUSE_OFF);
setc(p_flash.spiCLK, XS1_SETC_INUSE_OFF);
setc(p_flash.spiMOSI, XS1_SETC_INUSE_OFF);
setc(p_flash.spiSS, XS1_SETC_INUSE_OFF);
setc(p_flash.spiClkblk, XS1_SETC_INUSE_OFF);
setc(p_flash.spiMISO, XS1_SETC_INUSE_ON);
setc(p_flash.spiCLK, XS1_SETC_INUSE_ON);
setc(p_flash.spiMOSI, XS1_SETC_INUSE_ON);
setc(p_flash.spiSS, XS1_SETC_INUSE_ON);
setc(p_flash.spiClkblk, XS1_SETC_INUSE_ON);
setc(p_flash.spiClkblk, XS1_SETC_INUSE_ON);
setclk(p_flash.spiMISO, XS1_CLKBLK_REF);
setclk(p_flash.spiCLK, XS1_CLKBLK_REF);
setclk(p_flash.spiMOSI, XS1_CLKBLK_REF);
setclk(p_flash.spiSS, XS1_CLKBLK_REF);
setc(p_flash.spiMISO, XS1_SETC_BUF_BUFFERS);
setc(p_flash.spiMOSI, XS1_SETC_BUF_BUFFERS);
settw(p_flash.spiMISO, 8);
settw(p_flash.spiMOSI, 8);
#endif
#ifdef DFU_FLASH_DEVICE
#ifdef QUAD_SPI_FLASH
result = fl_connectToDevice(&p_qflash, flash_devices, 1);
#else
result = fl_connectToDevice(&p_flash, flash_devices, 1);
#endif
#else
/* Use default flash list */
#ifdef QUAD_SPI_FLASH
result = fl_connect(&p_qflash);
#else
result = fl_connect(&p_flash);
#endif
#endif
if (!result)
{
/* All okay.. */
return 1;
}
else
{
return 0;
}
}
int flash_cmd_disable_ports()
{
fl_disconnect();
#ifndef QUAD_SPI_FLASH
setc(p_flash.spiMISO, XS1_SETC_INUSE_OFF);
setc(p_flash.spiCLK, XS1_SETC_INUSE_OFF);
setc(p_flash.spiMOSI, XS1_SETC_INUSE_OFF);
setc(p_flash.spiSS, XS1_SETC_INUSE_OFF);
#endif
return 1;
}
#endif

View File

@@ -0,0 +1,7 @@
void UserHostActive(int active) __attribute__ ((weak));
void UserHostActive(int active)
{
return;
}

View File

@@ -0,0 +1,2 @@
void UserHostActive(int active);

View File

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

717
lib_xua/src/usb_audio/main.xc Executable file
View File

@@ -0,0 +1,717 @@
/**
* @file main.xc
* @brief Top level for XMOS USB 2.0 Audio 2.0 Reference Designs.
* @author Ross Owen, XMOS Semiconductor Ltd
*/
#include <syscall.h>
#include <platform.h>
#include <xs1.h>
#include <xclib.h>
#include <print.h>
#ifdef XSCOPE
#include <xscope.h>
#endif
#include "xud.h" /* XMOS USB Device Layer defines and functions */
#ifndef NO_USB
#include "endpoint0.h"
#endif
#include "devicedefines.h" /* Device specific defines */
#include "uac_hwresources.h"
#include "usb_buffer.h"
#include "decouple.h"
#ifdef MIDI
#include "usb_midi.h"
#endif
#include "xua_audio.h"
#ifdef IAP
#include "i2c_shared.h"
#include "iap.h"
#endif
#ifdef MIXER
#include "mixer.h"
#endif
#ifdef SPDIF_RX
#include "SpdifReceive.h"
#endif
#ifdef ADAT_RX
#include "adat_rx.h"
#endif
#include "clocking.h"
#if (NUM_PDM_MICS > 0)
#include "xua_pdm_mic.h"
#endif
#ifdef DFU
[[distributable]]
void DFUHandler(server interface i_dfu i, chanend ?c_user_cmd);
#endif
/* Audio I/O - Port declarations */
#if I2S_WIRES_DAC > 0
on tile[AUDIO_IO_TILE] : buffered out port:32 p_i2s_dac[I2S_WIRES_DAC] =
{PORT_I2S_DAC0,
#endif
#if I2S_WIRES_DAC > 1
PORT_I2S_DAC1,
#endif
#if I2S_WIRES_DAC > 2
PORT_I2S_DAC2,
#endif
#if I2S_WIRES_DAC > 3
PORT_I2S_DAC3,
#endif
#if I2S_WIRES_DAC > 4
PORT_I2S_DAC4,
#endif
#if I2S_WIRES_DAC > 5
PORT_I2S_DAC5,
#endif
#if I2S_WIRES_DAC > 6
PORT_I2S_DAC6,
#endif
#if I2S_WIRES_DAC > 7
#error I2S_WIRES_DAC value is too large!
#endif
#if I2S_WIRES_DAC > 0
};
#endif
#if I2S_WIRES_ADC > 0
on tile[AUDIO_IO_TILE] : buffered in port:32 p_i2s_adc[I2S_WIRES_ADC] =
{PORT_I2S_ADC0,
#endif
#if I2S_WIRES_ADC > 1
PORT_I2S_ADC1,
#endif
#if I2S_WIRES_ADC > 2
PORT_I2S_ADC2,
#endif
#if I2S_WIRES_ADC > 3
PORT_I2S_ADC3,
#endif
#if I2S_WIRES_ADC > 4
PORT_I2S_ADC4,
#endif
#if I2S_WIRES_ADC > 5
PORT_I2S_ADC5,
#endif
#if I2S_WIRES_ADC > 6
PORT_I2S_ADC6,
#endif
#if I2S_WIRES_ADC > 7
#error I2S_WIRES_ADC value is too large!
#endif
#if I2S_WIRES_ADC > 0
};
#endif
#ifndef CODEC_MASTER
on tile[AUDIO_IO_TILE] : buffered out port:32 p_lrclk = PORT_I2S_LRCLK;
on tile[AUDIO_IO_TILE] : buffered out port:32 p_bclk = PORT_I2S_BCLK;
#else
on tile[AUDIO_IO_TILE] : buffered in port:32 p_lrclk = PORT_I2S_LRCLK;
on tile[AUDIO_IO_TILE] : buffered in port:32 p_bclk = PORT_I2S_BCLK;
#endif
on tile[AUDIO_IO_TILE] : port p_mclk_in = PORT_MCLK_IN;
on tile[XUD_TILE] : in port p_for_mclk_count = PORT_MCLK_COUNT;
#ifdef SPDIF_TX
on tile[SPDIF_TX_TILE] : buffered out port:32 p_spdif_tx = PORT_SPDIF_OUT;
#endif
#ifdef ADAT_TX
on stdcore[AUDIO_IO_TILE] : buffered out port:32 p_adat_tx = PORT_ADAT_OUT;
#endif
#ifdef ADAT_RX
on stdcore[XUD_TILE] : buffered in port:32 p_adat_rx = PORT_ADAT_IN;
#endif
#ifdef SPDIF_RX
on tile[XUD_TILE] : buffered in port:4 p_spdif_rx = PORT_SPDIF_IN;
#endif
#if defined (SPDIF_RX) || defined (ADAT_RX)
/* Reference to external clock multiplier */
on tile[AUDIO_IO_TILE] : out port p_pll_clk = PORT_PLL_REF;
#endif
#ifdef MIDI
on tile[MIDI_TILE] : port p_midi_tx = PORT_MIDI_OUT;
#if(MIDI_RX_PORT_WIDTH == 4)
on tile[MIDI_TILE] : buffered in port:4 p_midi_rx = PORT_MIDI_IN;
#elif(MIDI_RX_PORT_WIDTH == 1)
on tile[MIDI_TILE] : buffered in port:1 p_midi_rx = PORT_MIDI_IN;
#endif
#endif
/* Clock blocks */
#ifdef MIDI
on tile[MIDI_TILE] : clock clk_midi = CLKBLK_MIDI;
#endif
#if defined(SPDIF_TX) || defined(ADAT_TX)
on tile[SPDIF_TX_TILE] : clock clk_mst_spd = CLKBLK_SPDIF_TX;
#endif
#ifdef SPDIF_RX
on tile[XUD_TILE] : clock clk_spd_rx = CLKBLK_SPDIF_RX;
#endif
#if(XUD_SERIES_SUPPORT == XUD_L_SERIES) && defined(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)
on tile[XUD_TILE] : clock clk_audio_mclk2 = CLKBLK_MCLK; /* Master clock */
on tile[XUD_TILE] : in port p_mclk_in2 = PORT_MCLK_IN2;
#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
#if (XUD_SERIES_SUPPORT != XUD_U_SERIES && XUD_SERIES_SUPPORT != XUD_X200_SERIES)
/* L Series also needs a clock block for this port */
on tile[XUD_TILE] : clock clk = CLKBLK_USB_RST;
#else
#define clk null
#endif
#ifdef IAP
/* I2C ports - in a struct for use with module_i2c_shared & module_i2c_simple/module_i2c_single_port */
#ifdef PORT_I2C
on tile [IAP_TILE] : struct r_i2c r_i2c = {PORT_I2C};
#else
on tile [IAP_TILE] : struct r_i2c r_i2c = {PORT_I2C_SCL, PORT_I2C_SDA};
#endif
#endif
#ifndef NO_USB
/* Endpoint type tables for XUD */
XUD_EpType epTypeTableOut[ENDPOINT_COUNT_OUT] = { XUD_EPTYPE_CTL | XUD_STATUS_ENABLE,
XUD_EPTYPE_ISO, /* Audio */
#ifdef MIDI
XUD_EPTYPE_BUL, /* MIDI */
#endif
#ifdef IAP
XUD_EPTYPE_BUL, /* iAP */
#ifdef IAP_EA_NATIVE_TRANS
XUD_EPTYPE_BUL, /* EA Native Transport */
#endif
#endif
};
XUD_EpType epTypeTableIn[ENDPOINT_COUNT_IN] = { XUD_EPTYPE_CTL | XUD_STATUS_ENABLE,
XUD_EPTYPE_ISO,
#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP)
XUD_EPTYPE_ISO, /* Async feedback endpoint */
#endif
#if defined (SPDIF_RX) || defined (ADAT_RX)
XUD_EPTYPE_BUL,
#endif
#ifdef MIDI
XUD_EPTYPE_BUL,
#endif
#ifdef HID_CONTROLS
XUD_EPTYPE_INT,
#endif
#ifdef IAP
XUD_EPTYPE_BUL | XUD_STATUS_ENABLE,
#ifdef IAP_INT_EP
XUD_EPTYPE_BUL | XUD_STATUS_ENABLE,
#endif
#ifdef IAP_EA_NATIVE_TRANS
XUD_EPTYPE_BUL | XUD_STATUS_ENABLE,
#endif
#endif
};
#endif /* NO_USB */
void thread_speed()
{
#ifdef FAST_MODE
#warning Building with fast mode enabled
set_thread_fast_mode_on();
#else
set_thread_fast_mode_off();
#endif
}
#ifdef XSCOPE
void xscope_user_init()
{
xscope_register(0, 0, "", 0, "");
xscope_config_io(XSCOPE_IO_BASIC);
}
#endif
#ifndef NO_USB
/* 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
#endif
#ifdef IAP
, chanend c_iap
#ifdef IAP_EA_NATIVE_TRANS
, chanend c_ea_data
#endif
#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_
)
{
chan c_sof;
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
#define c_mix_ctl null
#endif
#ifdef IAP_EA_NATIVE_TRANS
chan c_EANativeTransport_ctrl;
#else
#define c_EANativeTransport_ctrl null
#endif
par
{
{
#ifdef XUD_PRIORITY_HIGH
set_core_high_priority_on();
#endif
/* USB Interface Core */
#if (AUDIO_CLASS==2)
XUD_Main(c_xud_out, ENDPOINT_COUNT_OUT, c_xud_in, ENDPOINT_COUNT_IN,
c_sof, epTypeTableOut, epTypeTableIn, p_usb_rst,
clk, 1, XUD_SPEED_HS, XUD_PWR_CFG);
#else
XUD_Main(c_xud_out, ENDPOINT_COUNT_OUT, c_xud_in, ENDPOINT_COUNT_IN,
c_sof, epTypeTableOut, epTypeTableIn, p_usb_rst,
clk, 1, XUD_SPEED_FS, XUD_PWR_CFG);
#endif
}
/* USB Packet buffering Core */
{
unsigned x;
thread_speed();
/* Attach mclk count port to mclk clock-block (for feedback) */
//set_port_clock(p_for_mclk_count, clk_audio_mclk);
#if(AUDIO_IO_TILE != XUD_TILE)
set_clock_src(clk_audio_mclk2, p_mclk_in2);
set_port_clock(p_for_mclk_count, clk_audio_mclk2);
start_clock(clk_audio_mclk2);
#else
/* Uses same clock-block as I2S */
asm("ldw %0, dp[clk_audio_mclk]":"=r"(x));
asm("setclk res[%0], %1"::"r"(p_for_mclk_count), "r"(x));
#endif
//:buffer
buffer(c_xud_out[ENDPOINT_NUMBER_OUT_AUDIO], /* Audio Out*/
c_xud_in[ENDPOINT_NUMBER_IN_AUDIO], /* Audio In */
#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP)
c_xud_in[ENDPOINT_NUMBER_IN_FEEDBACK], /* Audio FB */
#endif
#ifdef MIDI
c_xud_out[ENDPOINT_NUMBER_OUT_MIDI], /* MIDI Out */ // 2
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 defined(SPDIF_RX) || defined(ADAT_RX)
/* 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
#ifdef HID_CONTROLS
, c_xud_in[ENDPOINT_NUMBER_IN_HID]
#endif
#ifdef CHAN_BUFF_CTRL
, c_buff_ctrl
#endif
);
//:
}
/* Endpoint 0 Core */
{
thread_speed();
Endpoint0( c_xud_out[0], c_xud_in[0], c_aud_ctl, c_mix_ctl, c_clk_ctl, c_EANativeTransport_ctrl, dfuInterface VENDOR_REQUESTS_PARAMS_);
}
/* Decoupling core */
{
thread_speed();
decouple(c_mix_out
#ifdef CHAN_BUFF_CTRL
, c_buff_ctrl
#endif
);
}
//:
}
}
#endif /* NO_USB */
void usb_audio_io(chanend ?c_aud_in, chanend ?c_adc,
#if defined(SPDIF_TX) && (SPDIF_TX_TILE != AUDIO_IO_TILE)
chanend c_spdif_tx,
#endif
#ifdef MIXER
chanend c_mix_ctl,
#endif
chanend ?c_aud_cfg,
streaming chanend ?c_spdif_rx,
chanend ?c_adat_rx,
chanend ?c_clk_ctl,
chanend ?c_clk_int
#if (XUD_TILE != 0) && (AUDIO_IO_TILE == 0)
, server interface i_dfu ?dfuInterface
#endif
#if (NUM_PDM_MICS > 0)
, chanend c_pdm_pcm
#endif
, client audManage_if i_audMan
)
{
#ifdef MIXER
chan c_mix_out;
#endif
#if defined(SPDIF_RX) || defined(ADAT_RX)
chan c_dig_rx;
#else
#define c_dig_rx null
#endif
par
{
#ifdef MIXER
/* Mixer cores(s) */
{
thread_speed();
mixer(c_aud_in, c_mix_out, c_mix_ctl);
}
#endif
/* Audio I/O Core (pars additional S/PDIF TX Core) */
{
thread_speed();
#ifdef MIXER
#define AUDIO_CHANNEL c_mix_out
#else
#define AUDIO_CHANNEL c_aud_in
#endif
audio(AUDIO_CHANNEL,
#if defined(SPDIF_TX) && (SPDIF_TX_TILE != AUDIO_IO_TILE)
c_spdif_tx,
#endif
#if defined(SPDIF_RX) || defined(ADAT_RX)
c_dig_rx,
#endif
c_aud_cfg, c_adc
#if (XUD_TILE != 0) && (AUDIO_IO_TILE == 0)
, dfuInterface
#endif
#if (NUM_PDM_MICS > 0)
, c_pdm_pcm
#endif
, i_audMan
);
}
#if defined(SPDIF_RX) || defined(ADAT_RX)
{
thread_speed();
clockGen(c_spdif_rx, c_adat_rx, p_pll_clk, c_dig_rx, c_clk_ctl, c_clk_int);
}
#endif
//:
}
}
#ifndef USER_MAIN_DECLARATIONS
#define USER_MAIN_DECLARATIONS
#endif
#ifndef USER_MAIN_CORES
#define USER_MAIN_CORES
#endif
#ifndef EXCLUDE_USB_AUDIO_MAIN
/* Main for USB Audio Applications */
int main()
{
#if NO_USB
#define c_mix_out null
#else
chan c_mix_out;
#endif
#ifdef MIDI
chan c_midi;
#endif
#ifdef IAP
chan c_iap;
#ifdef IAP_EA_NATIVE_TRANS
chan c_ea_data;
#endif
#endif
#ifdef SU1_ADC_ENABLE
chan c_adc;
#else
#define c_adc null
#endif
#ifdef MIXER
chan c_mix_ctl;
#endif
#ifdef AUDIO_CFG_CHAN
chan c_aud_cfg;
#else
#define c_aud_cfg null
#endif
#ifdef SPDIF_RX
streaming chan c_spdif_rx;
#else
#define c_spdif_rx null
#endif
#ifdef ADAT_RX
chan c_adat_rx;
#else
#define c_adat_rx null
#endif
#if defined(SPDIF_TX) && (SPDIF_TX_TILE != AUDIO_IO_TILE)
chan c_spdif_tx;
#endif
#if (defined (SPDIF_RX) || defined (ADAT_RX))
chan c_clk_ctl;
chan c_clk_int;
#else
#define c_clk_int null
#define c_clk_ctl null
#endif
#ifdef DFU
interface i_dfu dfuInterface;
#else
#define dfuInterface null
#endif
#if (NUM_PDM_MICS > 0)
chan c_pdm_pcm;
streaming chan c_ds_output[2];
#ifdef MIC_PROCESSING_USE_INTERFACE
interface mic_process_if i_mic_process;
#endif
#endif
interface audManage_if i_audMan;
USER_MAIN_DECLARATIONS
par
{
USER_MAIN_CORES
on tile[XUD_TILE]:
par
{
#if (XUD_TILE == 0)
/* Check if USB is on the flash tile (tile 0) */
#ifdef DFU
[[distribute]]
DFUHandler(dfuInterface, null);
#endif
#endif
#ifndef NO_USB
usb_audio_core(c_mix_out
#ifdef MIDI
, c_midi
#endif
#ifdef IAP
, c_iap
#ifdef IAP_EA_NATIVE_TRANS
, c_ea_data
#endif
#endif
#ifdef MIXER
, c_mix_ctl
#endif
, c_clk_int, c_clk_ctl, dfuInterface
VENDOR_REQUESTS_PARAMS_
);
#endif /* NO_USB */
}
on tile[AUDIO_IO_TILE]: usb_audio_io(c_mix_out, c_adc
#if defined(SPDIF_TX) && (SPDIF_TX_TILE != AUDIO_IO_TILE)
, c_spdif_tx
#endif
#ifdef MIXER
, c_mix_ctl
#endif
,c_aud_cfg, c_spdif_rx, c_adat_rx, c_clk_ctl, c_clk_int
#if (XUD_TILE != 0) && (AUDIO_IO_TILE == 0)
, dfuInterface
#endif
#if (NUM_PDM_MICS > 0)
, c_pdm_pcm
#endif
, i_audMan
);
#if defined(SPDIF_TX) && (SPDIF_TX_TILE != AUDIO_IO_TILE)
on tile[SPDIF_TX_TILE]:
{
thread_speed();
SpdifTxWrapper(c_spdif_tx);
}
#endif
#if defined(MIDI) && defined(IAP) && (IAP_TILE == MIDI_TILE)
/* MIDI and IAP share a core */
on tile[IAP_TILE]:
{
thread_speed();
usb_midi(p_midi_rx, p_midi_tx, clk_midi, c_midi, 0, c_iap, null, null, null);
}
#else
#if defined(MIDI)
/* MIDI core */
on tile[MIDI_TILE]:
{
thread_speed();
usb_midi(p_midi_rx, p_midi_tx, clk_midi, c_midi, 0, null, null, null, null);
}
#endif
#if defined(IAP)
on tile[IAP_TILE]:
{
thread_speed();
iAP(c_iap, null, null, null);
}
#endif
#endif
#ifdef SPDIF_RX
on tile[XUD_TILE]:
{
thread_speed();
SpdifReceive(p_spdif_rx, c_spdif_rx, 1, clk_spd_rx);
}
#endif
#ifdef ADAT_RX
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);
adatReceiver44100(p_adat_rx, c_adat_rx);
}
}
#endif
#ifndef NO_USB
#if (XUD_TILE != 0 ) && (AUDIO_IO_TILE != 0)
/* Run flash code on its own - hope it gets combined */
//#warning Running DFU flash code on its own
on stdcore[0]: DFUHandler(dfuInterface, null);
#endif
#endif
#ifndef PDM_RECORD
#if (NUM_PDM_MICS > 0)
on stdcore[PDM_TILE]: pdm_mic(c_ds_output);
#ifdef MIC_PROCESSING_USE_INTERFACE
on stdcore[PDM_TILE].core[0]: pdm_buffer(c_ds_output, c_pdm_pcm, i_mic_process);
#else
on stdcore[PDM_TILE].core[0]: pdm_buffer(c_ds_output, c_pdm_pcm);
#endif /*MIC_PROCESSING_USE_INTERFACE*/
#endif /*NUM_PDM_MICS > 0*/
#endif /*PDM_RECORD*/
#ifdef SU1_ADC_ENABLE
xs1_su_adc_service(c_adc);
#endif
}
return 0;
}
#endif

View File

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

View File

@@ -0,0 +1,834 @@
#include <xs1.h>
#include <print.h>
#include "mixer.h"
#include "devicedefines.h"
#include "xc_ptr.h"
#include "commands.h"
#include "dbcalc.h"
#ifdef MIXER
#define FAST_MIXER 1
#warning USING FAST MIXER
//#ifdef OUT_VOLUME_IN_MIXER
static unsigned int multOut_array[NUM_USB_CHAN_OUT + 1];
static xc_ptr multOut;
//#endif
//#ifdef IN_VOLUME_IN_MIXER
static unsigned int multIn_array[NUM_USB_CHAN_IN + 1];
static xc_ptr multIn;
//#endif
#if defined (LEVEL_METER_LEDS) || defined (LEVEL_METER_HOST)
static unsigned abs(int x)
{
#if 0
if (x < 0)
return x*-1;
return x;
#else
int const mask = x >> sizeof(int) * 8 - 1;
return (x + mask) ^ mask;
#endif
}
#endif
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;
unsafe
{
static int volatile * const unsafe ptr_samples = samples_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
#endif
/* 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;
#ifdef LEVEL_METER_LEDS
int samples_to_host_inputs_buff[NUM_USB_CHAN_IN]; /* Audio transmitted to host i.e. dev inputs */
xc_ptr samples_to_host_inputs_buff_ptr;
#endif
static int samples_from_host_streams[NUM_USB_CHAN_OUT]; /* Peak samples for audio stream from host */
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;
int y;
xc_ptr ptr;
x = abs(sample);
/* y = samples_mixer_outputs[i] */
read_via_xc_ptr_indexed(y, samples_mixer_outputs_ptr, i);
if(x > y)
{
/* samples_to_host_outputs[i] = x; */
write_via_xc_ptr_indexed(samples_mixer_outputs_ptr,i,x);
}
}
#endif
#ifdef FAST_MIXER
void setPtr(int src, int dst, int mix);
int doMix0(xc_ptr samples, xc_ptr mult);
int doMix1(xc_ptr samples, xc_ptr mult);
int doMix2(xc_ptr samples, xc_ptr mult);
int doMix3(xc_ptr samples, xc_ptr mult);
int doMix4(xc_ptr samples, xc_ptr mult);
int doMix5(xc_ptr samples, xc_ptr mult);
int doMix6(xc_ptr samples, xc_ptr mult);
int doMix7(xc_ptr samples, xc_ptr mult);
int doMix8(xc_ptr samples, xc_ptr mult);
#else
/* DO NOT inline, causes 10.4.2 tools to add extra loads in loop */
/* At 18 x 12dB we could get 64 x bigger */
#pragma unsafe arrays
static inline int doMix(xc_ptr samples, xc_ptr ptr, xc_ptr mult)
{
int h=0;
int l=0;
/* By breaking up the loop we keep things in the encoding for ldw (0-11) */
#pragma loop unroll
for (int i=0; i<MIX_INPUTS; i++)
{
int sample;
int index;
int m;
read_via_xc_ptr_indexed(index, ptr, i);
read_via_xc_ptr_indexed(sample,samples,index);
read_via_xc_ptr_indexed(m, mult, i);
{h,l} = macs(sample, m, h, l);
}
#if 1
/* Perform saturation */
l = sext(h, 25);
if(l != h)
{
//if(h < 0)
if(h>>32)
h = (0x80000000>>7);
else
h = (0x7fffff00>>7);
}
#endif
return h<<7;
}
#endif
#pragma unsafe arrays
static inline void GiveSamplesToHost(chanend c, xc_ptr ptr, xc_ptr multIn)
{
#if defined(IN_VOLUME_IN_MIXER) && defined(IN_VOLUME_AFTER_MIX)
int mult;
int h;
unsigned l;
#endif
#pragma loop unroll
for (int i=0; i<NUM_USB_CHAN_IN; i++)
{
int sample;
int index;
#if MAX_MIX_COUNT > 0
read_via_xc_ptr_indexed(index,ptr,i);
#else
index = i + NUM_USB_CHAN_OUT;
#endif
unsafe
{
//read_via_xc_ptr_indexed(sample,samples,index);
sample = ptr_samples[index];
}
#if defined(IN_VOLUME_IN_MIXER) && defined(IN_VOLUME_AFTER_MIX)
#warning IN Vols in mixer, AFTER mix & map
//asm("ldw %0, %1[%2]":"=r"(mult):"r"(multIn),"r"(i));
read_via_xc_ptr_indexed(mult, multIn, i);
{h, l} = macs(mult, sample, 0, 0);
//h <<= 3 done on other side */
outuint(c, h);
#else
outuint(c,sample);
#endif
}
}
#pragma unsafe arrays
static inline void GetSamplesFromHost(chanend c)
{
#if (NUM_USB_CHAN_OUT == 0)
inuint(c);
#else
{
#pragma loop unroll
for (int i=0; i<NUM_USB_CHAN_OUT; i++)
unsafe {
int sample, x;
#if defined(OUT_VOLUME_IN_MIXER) && !defined(OUT_VOLUME_AFTER_MIX)
int mult;
int h;
unsigned l;
#endif
/* Receive sample from decouple */
sample = inuint(c);
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
/* Compute peak level data */
x = abs(sample);
if(x > samples_from_host_streams[i])
{
samples_from_host_streams[i] = x;
}
#endif
#if defined(OUT_VOLUME_IN_MIXER) && !defined(OUT_VOLUME_AFTER_MIX)
#warning OUT Vols in mixer, BEFORE mix & map
read_via_xc_ptr_indexed(mult, multOut, i);
{h, l} = macs(mult, sample, 0, 0);
h<<=3;
#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
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)
{
{
#pragma loop unroll
for (int i=0; i<NUM_USB_CHAN_OUT; i++)
{
int sample, x;
#if defined(OUT_VOLUME_IN_MIXER) && defined(OUT_VOLUME_AFTER_MIX)
int mult;
int h;
unsigned l;
#endif
int index;
#if MAX_MIX_COUNT > 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);
#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 */
#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
#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, h);
#else
outuint(c, sample);
#endif
}
}
}
#pragma unsafe arrays
static inline void GetSamplesFromDevice(chanend c)
{
#if defined(IN_VOLUME_IN_MIXER) && !defined(IN_VOLUME_AFTER_MIX)
int mult;
int h;
unsigned l;
#endif
#pragma loop unroll
for (int i=0;i<NUM_USB_CHAN_IN;i++)
{
int sample;
int x;
int old_x;
sample = inuint(c);
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
/* Compute peak level data */
x = abs(sample);
// old_x = samples_to_host_inputs[i]
read_via_xc_ptr_indexed(old_x, samples_to_host_inputs_ptr, i);
if(x > old_x)
{
//samples_to_host_inputs[i] = x;
write_via_xc_ptr_indexed(samples_to_host_inputs_ptr, i, x);
}
#endif
#if defined(IN_VOLUME_IN_MIXER) && !defined(IN_VOLUME_AFTER_MIX)
/* Read relevant multiplier */
read_via_xc_ptr_indexed(mult, multIn, i);
/* Do the multiply */
{h, l} = macs(mult, sample, 0, 0);
h <<=3;
write_via_xc_ptr_indexed(samples_array, NUM_USB_CHAN_OUT+i, h);
#else
/* No volume processing */
unsafe
{
ptr_samples[NUM_USB_CHAN_OUT + i] = sample;
}
#endif
}
}
static int mixer1_mix2_flag = (DEFAULT_FREQ > 96000);
#pragma unsafe arrays
static void mixer1(chanend c_host, chanend c_mix_ctl, chanend c_mixer2)
{
#if (MAX_MIX_COUNT > 0)
int mixed;
#endif
unsigned cmd;
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);
/* Between request to decouple and response ~ 400nS latency for interrupt to fire */
select
{
case inuint_byref(c_mix_ctl, cmd):
{
int mix, index, val;
switch (cmd)
{
#if MAX_MIX_COUNT > 0
case SET_SAMPLES_TO_HOST_MAP:
index = inuint(c_mix_ctl);
val = inuint(c_mix_ctl);
inct(c_mix_ctl);
write_via_xc_ptr_indexed(samples_to_host_map,
index,
val);
break;
case SET_SAMPLES_TO_DEVICE_MAP:
index = inuint(c_mix_ctl);
val = inuint(c_mix_ctl);
inct(c_mix_ctl);
write_via_xc_ptr_indexed(samples_to_device_map,index,val);
break;
case SET_MIX_MULT:
mix = inuint(c_mix_ctl);
index = inuint(c_mix_ctl);
val = inuint(c_mix_ctl);
inct(c_mix_ctl);
write_word_to_mix_mult(mix, index, val);
break;
case SET_MIX_MAP:
mix = inuint(c_mix_ctl);
index = inuint(c_mix_ctl); /* mixer input */
val = inuint(c_mix_ctl); /* source */
inct(c_mix_ctl);
#ifdef FAST_MIXER
setPtr(index, val, mix);
#else
write_word_to_mix_map(mix, index, val);
#endif
break;
#endif /* if MAX_MIX_COUNT > 0 */
#ifdef IN_VOLUME_IN_MIXER
case SET_MIX_IN_VOL:
index = inuint(c_mix_ctl);
val = inuint(c_mix_ctl);
inct(c_mix_ctl);
write_via_xc_ptr_indexed(multIn, index, val);
break;
#endif
#ifdef OUT_VOLUME_IN_MIXER
case SET_MIX_OUT_VOL:
index = inuint(c_mix_ctl);
val = inuint(c_mix_ctl);
inct(c_mix_ctl);
write_via_xc_ptr_indexed(multOut, index, val);
break;
#endif
/* Peak samples of stream from host to device (via USB) */
case GET_STREAM_LEVELS:
index = inuint(c_mix_ctl);
chkct(c_mix_ctl, XS1_CT_END);
outuint(c_mix_ctl, samples_from_host_streams[index]);
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;
}
default:
/* Select default */
break;
}
/* Get response from decouple */
if(testct(c_host))
{
int sampFreq;
#pragma xta endpoint "mixer1_rate_change"
unsigned command = inct(c_host);
switch(command)
{
case SET_SAMPLE_FREQ:
sampFreq = inuint(c_host);
mixer1_mix2_flag = sampFreq > 96000;
/* Inform mixer2 (or audio()) about freq change */
outct(c_mixer2, command);
outuint(c_mixer2, sampFreq);
break;
case SET_STREAM_FORMAT_OUT:
case SET_STREAM_FORMAT_IN:
/* Inform mixer2 (or audio()) about format change */
outct(c_mixer2, command);
outuint(c_mixer2, inuint(c_host));
outuint(c_mixer2, inuint(c_host));
break;
default:
break;
}
#pragma loop unroll
/* Reset the mix values back to 0 */
for (int i=0;i<MAX_MIX_COUNT;i++)
{
//write_via_xc_ptr_indexed(samples, (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + i), 0);
unsafe
{
ptr_samples[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + i] = 0;
}
}
/* Wait for handshake and pass on */
chkct(c_mixer2, XS1_CT_END);
outct(c_host, XS1_CT_END);
}
else
{
#if MAX_MIX_COUNT > 0
outuint(c_mixer2, 0);
GiveSamplesToHost(c_host, samples_to_host_map, multIn);
outuint(c_mixer2, 0);
inuint(c_mixer2);
GetSamplesFromHost(c_host);
outuint(c_mixer2, 0);
inuint(c_mixer2);
#ifdef FAST_MIXER
mixed = doMix0(samples, mix_mult_slice(0));
#else
mixed = doMix(samples, mix_map_slice(0),mix_mult_slice(0));
#endif
write_via_xc_ptr_indexed(samples_array, (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 0), mixed);
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
ComputeMixerLevel(mixed, 0);
#endif
#if (MAX_FREQ > 96000)
if (!mixer1_mix2_flag)
#endif
{
#if MAX_MIX_COUNT > 2
#ifdef FAST_MIXER
mixed = doMix2(samples, mix_mult_slice(2));
#else
mixed = doMix(samples, mix_map_slice(2),mix_mult_slice(2));
#endif
write_via_xc_ptr_indexed(samples_array, (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 2), mixed);
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
ComputeMixerLevel(mixed, 2);
#endif
#endif
#if MAX_MIX_COUNT > 4
#ifdef FAST_MIXER
mixed = doMix4(samples, mix_mult_slice(4));
#else
mixed = doMix(samples, mix_map_slice(4),mix_mult_slice(4));
#endif
write_via_xc_ptr_indexed(samples_array, (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 4), mixed);
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
ComputeMixerLevel(mixed, 4);
#endif
#endif
#if MAX_MIX_COUNT > 6
#ifdef FAST_MIXER
mixed = doMix6(samples, mix_mult_slice(6));
#else
mixed = doMix(samples, mix_map_slice(6),mix_mult_slice(6));
#endif
write_via_xc_ptr_indexed(samples, (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 6), mixed);
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
ComputeMixerLevel(mixed, 6);
#endif
#endif
}
#else /* IF MAX_MIX_COUNT > 0 */
/* No mixes, this thread runs on its own doing just volume */
#if(NUM_USB_CHAN_OUT == 0)
outuint(c_mixer2, 0);
#endif
GiveSamplesToDevice(c_mixer2, samples_to_device_map, multOut);
GetSamplesFromDevice(c_mixer2);
GetSamplesFromHost(c_host);
GiveSamplesToHost(c_host, samples_to_host_map, multIn);
#endif
}
}
}
#if (MAX_MIX_COUNT > 0)
static int mixer2_mix2_flag = (DEFAULT_FREQ > 96000);
#pragma unsafe arrays
static void mixer2(chanend c_mixer1, chanend c_audio)
{
int mixed;
unsigned request;
while (1)
{
#pragma xta endpoint "mixer2_req"
request = inuint(c_audio);
/* Forward the request on */
outuint(c_mixer1, request);
if(testct(c_mixer1))
{
int sampFreq;
#pragma xta endpoint "mixer2_rate_change"
unsigned command = inct(c_mixer1);
switch(command)
{
case SET_SAMPLE_FREQ:
sampFreq = inuint(c_mixer1);
mixer2_mix2_flag = sampFreq > 96000;
/* Inform mixer2 (or audio()) about freq change */
outct(c_audio, command);
outuint(c_audio, sampFreq);
break;
case SET_STREAM_FORMAT_OUT:
case SET_STREAM_FORMAT_IN:
/* Inform mixer2 (or audio()) about format change */
outct(c_audio, command);
outuint(c_audio, inuint(c_mixer1));
outuint(c_audio, inuint(c_mixer1));
break;
default:
break;
}
for (int i=0;i<MAX_MIX_COUNT;i++)
{
write_via_xc_ptr_indexed(samples, (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + i), 0);
}
/* Inform audio thread about freq change */
//outct(c_audio, XS1_CT_END);
//outuint(c_audio, sampFreq);
/* Wait for handshake and pass on */
chkct(c_audio, XS1_CT_END);
outct(c_mixer1, XS1_CT_END);
}
else
{
(void) inuint(c_mixer1);
GiveSamplesToDevice(c_audio, samples_to_device_map, multOut);
inuint(c_mixer1);
outuint(c_mixer1, 0);
GetSamplesFromDevice(c_audio);
inuint(c_mixer1);
outuint(c_mixer1, 0);
#if MAX_MIX_COUNT > 1
#ifdef FAST_MIXER
mixed = doMix1(samples, mix_mult_slice(1));
#else
mixed = doMix(samples, mix_map_slice(1),mix_mult_slice(1));
#endif
write_via_xc_ptr_indexed(samples, (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 1), mixed);
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
ComputeMixerLevel(mixed, 1);
#endif
#endif
#if (MAX_FREQ > 96000)
if (!mixer2_mix2_flag)
#endif
{
#if MAX_MIX_COUNT > 3
#ifdef FAST_MIXER
mixed = doMix3(samples, mix_mult_slice(3));
#else
mixed = doMix(samples, mix_map_slice(3),mix_mult_slice(3));
#endif
write_via_xc_ptr_indexed(samples, (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 3), mixed);
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
ComputeMixerLevel(mixed, 3);
#endif
#endif
#if MAX_MIX_COUNT > 5
#ifdef FAST_MIXER
mixed = doMix5(samples, mix_mult_slice(5));
#else
mixed = doMix(samples, mix_map_slice(5),mix_mult_slice(5));
#endif
write_via_xc_ptr_indexed(samples, NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 5, mixed);
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
ComputeMixerLevel(mixed, 5);
#endif
#endif
#if MAX_MIX_COUNT > 7
#ifdef FAST_MIXER
mixed = doMix7(samples, mix_mult_slice(7));
#else
mixed = doMix(samples, mix_map_slice(7),mix_mult_slice(7));
#endif
write_via_xc_ptr_indexed(samples, NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 7, mixed);
#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS)
ComputeMixerLevel(mixed, 7);
#endif
#endif
}
}
}
}
#endif
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[]));
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<NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT;i++)
unsafe {
//write_via_xc_ptr_indexed(samples,i,0);
ptr_samples[i] = 0;
}
{
int num_mixes = DEFAULT_FREQ > 96000 ? 2 : MAX_MIX_COUNT;
for (int i=0;i<NUM_USB_CHAN_OUT;i++)
{
//samples_to_device_map_array[i] = i;
asm("stw %0, %1[%2]":: "r"(i), "r"(samples_to_device_map), "r"(i));
}
}
#ifdef OUT_VOLUME_IN_MIXER
for (int i=0;i<NUM_USB_CHAN_OUT;i++)
{
write_via_xc_ptr_indexed(multOut, i, MAX_VOL);
}
#endif
#ifdef IN_VOLUME_IN_MIXER
for (int i=0;i<NUM_USB_CHAN_IN;i++)
{
write_via_xc_ptr_indexed(multIn, i, MAX_VOL);
}
#endif
for (int i=0;i<NUM_USB_CHAN_IN;i++)
{
write_via_xc_ptr_indexed(samples_to_host_map, i, NUM_USB_CHAN_OUT + i);
}
#if MAX_MIX_COUNT> 0
for (int i=0;i<MAX_MIX_COUNT;i++)
for (int j=0;j<MIX_INPUTS;j++)
{
#ifndef FAST_MIXER
write_word_to_mix_map(i,j, j < 16 ? j : j + 2);
#endif
write_word_to_mix_mult(i,j, i==j ? db_to_mult(0, 8, 25) : 0);
}
#endif
par
{
#if (MAX_MIX_COUNT > 0)
mixer1(c_mix_in, c_mix_ctl, c);
mixer2(c, c_mix_out);
#else
mixer1(c_mix_in, c_mix_ctl, c_mix_out);
#endif
}
}
#endif

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,9 @@
#ifndef MIC_ARRAY_CONF_H_
#define MIC_ARRAY_CONF_H_
#include "customdefines.h"
#define MIC_ARRAY_MAX_FRAME_SIZE_LOG2 0
#define MIC_ARRAY_NUM_MICS (NUM_PDM_MICS)
#endif /* MIC_ARRAY_CONF_H_ */

View File

@@ -0,0 +1,237 @@
#include "devicedefines.h"
#if (NUM_PDM_MICS > 0)
/* This file includes an example integration of lib_array_mic into USB Audio */
#include <platform.h>
#include <xs1.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <xclib.h>
#include <stdint.h>
#include <assert.h>
#include "mic_array.h"
#include "xua_pdm_mic.h"
#define MAX_DECIMATION_FACTOR (96000/(MIN_FREQ/AUD_TO_MICS_RATIO))
/* Hardware resources */
in port p_pdm_clk = PORT_PDM_CLK;
in buffered port:32 p_pdm_mics = PORT_PDM_DATA;
in port p_mclk = PORT_PDM_MCLK;
clock pdmclk = on tile[PDM_TILE]: XS1_CLKBLK_3;
int mic_decimator_fir_data[8][THIRD_STAGE_COEFS_PER_STAGE * MAX_DECIMATION_FACTOR] = {{0}};
mic_array_frame_time_domain mic_audio[2];
#ifdef MIC_PROCESSING_USE_INTERFACE
[[combinable]]
#pragma unsafe arrays
void pdm_buffer(streaming chanend c_ds_output[2], chanend c_audio, client mic_process_if i_mic_process)
#else
#pragma unsafe arrays
[[combinable]]
void pdm_buffer(streaming chanend c_ds_output[2], chanend c_audio)
#endif
{
unsigned buffer;
unsigned samplerate;
int output[NUM_PDM_MICS];
#ifdef MIC_PROCESSING_USE_INTERFACE
i_mic_process.init();
#else
user_pdm_init();
#endif
#if NUM_PDM_MICS > 4
unsigned decimatorCount = 2;
#else
unsigned decimatorCount = 1;
#endif
mic_array_decimator_conf_common_t dcc;
const int * unsafe fir_coefs[7];
mic_array_frame_time_domain * unsafe current;
mic_array_decimator_config_t dc[2];
/* Get initial sample-rate and compute decimation factor */
c_audio :> samplerate;
unsigned decimationfactor = 96000/samplerate;
int fir_gain_compen[7];
unsafe
{
fir_gain_compen[0] = 0;
fir_gain_compen[1] = FIR_COMPENSATOR_DIV_2;
fir_gain_compen[2] = FIR_COMPENSATOR_DIV_4;
fir_gain_compen[3] = FIR_COMPENSATOR_DIV_6;
fir_gain_compen[4] = FIR_COMPENSATOR_DIV_8;
fir_gain_compen[5] = 0;
fir_gain_compen[6] = FIR_COMPENSATOR_DIV_12;
fir_coefs[0] = 0;
fir_coefs[1] = g_third_stage_div_2_fir;
fir_coefs[2] = g_third_stage_div_4_fir;
fir_coefs[3] = g_third_stage_div_6_fir;
fir_coefs[4] = g_third_stage_div_8_fir;
fir_coefs[5] = 0;
fir_coefs[6] = g_third_stage_div_12_fir;
//dcc = {MIC_ARRAY_MAX_FRAME_SIZE_LOG2, 1, 0, 0, decimationfactor, fir_coefs[decimationfactor/2], 0, 0, DECIMATOR_NO_FRAME_OVERLAP, 2};
dcc.frame_size_log2 = MIC_ARRAY_MAX_FRAME_SIZE_LOG2;
dcc.apply_dc_offset_removal = 1;
dcc.index_bit_reversal = 0;
dcc.windowing_function = null;
dcc.output_decimation_factor = decimationfactor;
dcc.coefs = fir_coefs[decimationfactor/2];
dcc.apply_mic_gain_compensation = 0;
dcc.fir_gain_compensation = fir_gain_compen[decimationfactor/2];
dcc.buffering_type = DECIMATOR_NO_FRAME_OVERLAP;
dcc.number_of_frame_buffers = 2;
//dc[2] = {{&dcc, mic_decimator_fir_data[0], {0, 0, 0, 0}, 4}, {&dcc, mic_decimator_fir_data[4], {0, 0, 0, 0}, 4}};
dc[0].dcc = &dcc;
dc[0].data = mic_decimator_fir_data[0];
dc[0].mic_gain_compensation[0]=0;
dc[0].mic_gain_compensation[1]=0;
dc[0].mic_gain_compensation[2]=0;
dc[0].mic_gain_compensation[3]=0;
dc[0].channel_count = 4;
dc[1].dcc = &dcc;
dc[1].data = mic_decimator_fir_data[4];
dc[1].mic_gain_compensation[0]=0;
dc[1].mic_gain_compensation[1]=0;
dc[1].mic_gain_compensation[2]=0;
dc[1].mic_gain_compensation[3]=0;
dc[1].channel_count = 4;
mic_array_decimator_configure(c_ds_output, decimatorCount, dc);
mic_array_init_time_domain_frame(c_ds_output, decimatorCount, buffer, mic_audio, dc);
/* Grab a first frame of mic data */
/* Note, loop is unrolled once - allows for while(1) select {} and thus combinable */
current = mic_array_get_next_time_domain_frame(c_ds_output, decimatorCount, buffer, mic_audio, dc);
}
/* Run user code */
/* TODO ideally processing done inplace - it then doesn't matter if it is run or not */
#ifdef MIC_PROCESSING_USE_INTERFACE
i_mic_process.transfer_buffers(current);
#else
user_pdm_process(current);
#endif
int req;
while(1)
{
select
{
case c_audio :> req:
/* Audio IO core requests samples */
if(req)
unsafe{
slave
{
/* We store an additional buffer so we can reply immediately */
#pragma loop unroll
for(int i = 0; i < NUM_PDM_MICS; i++)
{
c_audio <: output[i];
}
}
/* Get a new frame of mic data */
mic_array_frame_time_domain * unsafe current = mic_array_get_next_time_domain_frame(c_ds_output, decimatorCount, buffer, mic_audio, dc);
/* Run user code */
#ifdef MIC_PROCESSING_USE_INTERFACE
i_mic_process.transfer_buffers(current);
#else
user_pdm_process(current);
#endif
/* Buffer up next mic data */
#pragma loop unroll
for(int i = 0; i < NUM_PDM_MICS; i++)
{
output[i] = current->data[i][0];
}
}
else
unsafe{
/* Sample rate change */
c_audio :> samplerate;
/* Re-config the mic decimators for the new sample-rate */
decimationfactor = 96000/samplerate;
dcc.output_decimation_factor = decimationfactor;
dcc.coefs=fir_coefs[decimationfactor/2];
dcc.fir_gain_compensation = fir_gain_compen[decimationfactor/2];
mic_array_decimator_configure(c_ds_output, decimatorCount, dc);
mic_array_init_time_domain_frame(c_ds_output, decimatorCount, buffer, mic_audio, dc);
/* Get a new mic data frame */
mic_array_frame_time_domain * unsafe current = mic_array_get_next_time_domain_frame(c_ds_output, decimatorCount, buffer, mic_audio, dc);
/* Run user code */
#ifdef MIC_PROCESSING_USE_INTERFACE
i_mic_process.transfer_buffers(current);
#else
user_pdm_process(current);
#endif
#pragma loop unroll
for(int i = 0; i < NUM_PDM_MICS; i++)
{
output[i] = current->data[i][0];
}
}
break;
} /* select */
} /* while(1) */
}
#if MAX_FREQ > 48000
#error MAX_FREQ > 48000 NOT CURRENTLY SUPPORTED
#endif
void pdm_mic(streaming chanend c_ds_output[2])
{
streaming chan c_4x_pdm_mic_0;
#if (NUM_PDM_MICS > 4)
streaming chan c_4x_pdm_mic_1;
#else
#define c_4x_pdm_mic_1 null
#endif
/* Mics expect a clock in the 3Mhz range, calculate the divide based on mclk */
/* e.g. For a 48kHz range mclk we expect a 3072000Hz mic clock */
/* e.g. For a 44.1kHz range mclk we expect a 2822400Hz mic clock */
/* Note, codebase currently does not handle a different divide for each clock */
assert((MCLK_48 / 3072000) == (MCLK_441 / 2822400));
unsigned micDiv = MCLK_48/3072000;
configure_clock_src_divide(pdmclk, p_mclk, micDiv/2);
configure_port_clock_output(p_pdm_clk, pdmclk);
configure_in_port(p_pdm_mics, pdmclk);
start_clock(pdmclk);
par
{
mic_array_pdm_rx(p_pdm_mics, c_4x_pdm_mic_0, c_4x_pdm_mic_1);
mic_array_decimate_to_pcm_4ch(c_4x_pdm_mic_0, c_ds_output[0], MIC_ARRAY_NO_INTERNAL_CHANS);
#if (NUM_PDM_MICS > 4)
mic_array_decimate_to_pcm_4ch(c_4x_pdm_mic_1, c_ds_output[1], MIC_ARRAY_NO_INTERNAL_CHANS);
#endif
}
}
#endif

View File

@@ -0,0 +1,22 @@
#include "devicedefines.h"
#if (NUM_PDM_MICS > 0) && !defined(MIC_PROCESSING_USE_INTERFACE)
#include "mic_array_frame.h"
/* Deafult implementations of user_pdm_init() and user_pdm_process(). Both can be over-ridden */
void user_pdm_init() __attribute__ ((weak));
void user_pdm_init()
{
return;
}
void user_pdm_process() __attribute__ ((weak));
void user_pdm_process(mic_array_frame_time_domain * audio)
{
return;
}
#endif

View File

@@ -0,0 +1,41 @@
#include "mic_array.h"
#ifdef MIC_PROCESSING_USE_INTERFACE
/* Interface based user processing */
typedef interface mic_process_if
{
void transfer_buffers(mic_array_frame_time_domain * unsafe audio);
void init();
} mic_process_if;
[[combinable]]
void pdm_buffer(streaming chanend c_ds_output[2], chanend c_audio
#ifdef MIC_PROCESSING_USE_INTERFACE
, client mic_process_if i_mic_process
#endif
);
[[combinable]]
void user_pdm_process(server mic_process_if i_mic_data);
/* PDM interface and decimation cores */
void pdm_mic(streaming chanend c_ds_output[2]);
#else
/* Simple user hooks/call-backs */
void user_pdm_process(mic_array_frame_time_domain * unsafe audio);
void user_pdm_init();
/* PDM interface and decimation cores */
[[combinable]]
void pdm_buffer(streaming chanend c_ds_output[2], chanend c_audio);
/* PDM interface and decimation cores */
void pdm_mic(streaming chanend c_ds_output[2]);
#endif

View File

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

View File

@@ -0,0 +1,60 @@
#include <xs1.h>
#define __ASSEMBLER__ // Work around for bug #14118
#include <platform.h>
#undef __ASSEMBLER__
#include "devicedefines.h"
#include "audioports.h"
/* Note since DSD ports could be reused for I2S ports we do all the setup manually in C */
#if DSD_CHANS_DAC > 0
port p_dsd_dac[DSD_CHANS_DAC] = {
PORT_DSD_DAC0,
#endif
#if DSD_CHANS_DAC > 1
PORT_DSD_DAC1,
#endif
#if DSD_CHANS_DAC > 2
#error > 2 DSD chans currently not supported
#endif
#if DSD_CHANS_DAC > 0
};
port p_dsd_clk = PORT_DSD_CLK;
#endif
void EnableBufferedPort(port p, unsigned transferWidth)
{
asm volatile("setc res[%0], %1"::"r"(p), "r"(XS1_SETC_INUSE_ON));
asm volatile("setc res[%0], %1"::"r"(p), "r"(XS1_SETC_BUF_BUFFERS));
asm volatile("settw res[%0], %1"::"r"(p),"r"(transferWidth));
}
/* C wrapper for ConfigAudioPorts() to handle DSD ports */
void ConfigAudioPortsWrapper(
#if (I2S_CHANS_DAC != 0) || (DSD_CHANS_DAC != 0)
port p_dac[], int numPortsDac,
#endif
#if (I2S_CHANS_ADC != 0)
port p_adc[], int numPortsAdc,
#endif
#if (I2S_CHANS_DAC != 0) || (I2S_CHANS_ADC != 0)
port p_lrclk,
port p_bclk,
#endif
unsigned int divide, unsigned curSamFreq, unsigned int dsdMode)
{
ConfigAudioPorts(
#if (I2S_CHANS_DAC != 0) || (DSD_CHANS_DAC != 0)
p_dac,
numPortsDac,
#endif
#if (I2S_CHANS_ADC != 0)
p_adc,
numPortsAdc,
#endif
p_lrclk,
p_bclk,
divide, curSamFreq);
}

View File

@@ -0,0 +1,104 @@
#ifndef _AUDIOPORTS_H_
#define _AUDIOPORTS_H_
#include <xccompat.h>
#include "devicedefines.h"
#ifdef __XC__
void ConfigAudioPorts(
#if (I2S_CHANS_DAC != 0) || (DSD_CHANS_DAC != 0)
buffered out port:32 p_i2s_dac[],
int numDacPorts,
#endif
#if (I2S_CHANS_ADC != 0)
buffered in port:32 p_i2s_adc[],
int numAdcPorts,
#endif
#if (I2S_CHANS_DAC != 0) || (I2S_CHANS_ADC != 0)
#ifndef CODEC_MASTER
buffered out port:32 ?p_lrclk,
buffered out port:32 p_bclk,
#else
in port ?p_lrclk,
in port p_bclk,
#endif
#endif
unsigned int divide, unsigned int curSamFreq);
#else
void ConfigAudioPorts(
#if (I2S_CHANS_DAC != 0) || (DSD_CHANS_DAC != 0)
port p_i2s_dac[],
int numDacPorts,
#endif
#if (I2S_CHANS_ADC != 0)
port p_i2s_adc[],
int numAdcPorts,
#endif
#if (I2S_CHANS_DAC != 0) || (I2S_CHANS_ADC != 0)
#ifndef CODEC_MASTER
port p_lrclk,
port p_bclk,
#else
port p_lrclk,
port p_bclk,
#endif
#endif
unsigned int divide, unsigned int curSamFreq);
#endif /* __XC__*/
#ifdef __XC__
void ConfigAudioPortsWrapper(
#if (I2S_CHANS_DAC != 0) || (DSD_CHANS_DAC != 0)
buffered out port:32 p_i2s_dac[], int numPortsDAC,
#endif
#if (I2S_CHANS_ADC != 0)
buffered in port:32 p_i2s_adc[], int numPortsADC,
#endif
#if (I2S_CHANS_DAC != 0) || (I2S_CHANS_ADC != 0)
#ifndef CODEC_MASTER
buffered out port:32 ?p_lrclk,
buffered out port:32 p_bclk,
#else
buffered in port:32 ?p_lrclk,
buffered in port:32 p_bclk,
#endif
#endif
unsigned int divide, unsigned curSamFreq, unsigned int dsdMode);
#else
void ConfigAudioPortsWrapper(
#if (I2S_CHANS_DAC != 0) || (DSD_CHANS_DAC != 0)
port p_i2s_dac[], int numPortsDAC,
#endif
#if (I2S_CHANS_ADC != 0)
port p_i2s_adc[], int numPortsADC,
#endif
#if (I2S_CHANS_DAC != 0) || (I2S_CHANS_ADC != 0)
port p_lrclk,
port p_bclk,
#endif
unsigned int divide, unsigned curSamFreq, unsigned int dsdMode);
#endif /* __XC__*/
#ifdef __XC__
void EnableBufferedPort(buffered out port:32 p, unsigned transferWidth);
#else
void EnableBufferedPort(port p, unsigned transferWidth);
#endif
#endif /* _AUDIOPORTS_H_ */

View File

@@ -0,0 +1,152 @@
#include <xs1.h>
#include <platform.h>
#include <print.h>
#include "devicedefines.h"
#include "audioports.h"
extern 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)
buffered out port:32 p_i2s_dac[],
int numPortsDac,
#endif
#if (I2S_CHANS_ADC != 0)
buffered in port:32 p_i2s_adc[],
int numPortsAdc,
#endif
#if (I2S_CHANS_DAC != 0) || (I2S_CHANS_ADC != 0)
#if !defined(CODEC_MASTER)
buffered out port:32 ?p_lrclk,
buffered out port:32 p_bclk,
#else
in port ?p_lrclk,
in port p_bclk,
#endif
#endif
unsigned int divide, unsigned curSamFreq)
{
#if !defined(CODEC_MASTER)
/* 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);
}
clearbuf(p_bclk);
#if (I2S_CHANS_ADC != 0)
for(int i = 0; i < numPortsAdc; i++)
{
clearbuf(p_i2s_adc[i]);
}
#endif
#if (I2S_CHANS_DAC != 0)
for(int i = 0; i < numPortsDac; i++)
{
clearbuf(p_i2s_dac[i]);
}
#endif
#if defined(__XS2A__)
/* Clock bitclock clock block from master clock pin (divided) */
configure_clock_src_divide(clk_audio_bclk, p_mclk_in, (divide/2));
configure_port_clock_output(p_bclk, clk_audio_bclk);
#else
/* 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))
{
/* Clock LR clock from bit clock-block */
configure_out_port_no_ready(p_lrclk, clk_audio_bclk, 0);
}
#if (I2S_CHANS_DAC != 0)
/* Clock I2S output data ports from clock block */
for(int i = 0; i < numPortsDac; 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 < numPortsAdc; i++)
{
configure_in_port_no_ready(p_i2s_adc[i], clk_audio_bclk);
}
#endif
/* 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
}

View File

@@ -0,0 +1,9 @@
#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_*/

View File

@@ -0,0 +1,68 @@
#if (XUD_SERIES_SUPPORT==1)
#include "archU_powerSaving.h"
#include <xs1.h>
#include <xs1_l_registers.h>
#include <xs1_su_registers.h>
#include <platform.h>
#if (XCC_MAJOR_VERSION >= 1300)
#include <hwtimer.h>
#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

View File

@@ -0,0 +1,75 @@
#include <xs1.h>
#include <platform.h>
#include <xs1_su.h>
#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 0x7FFFFFFF
#else
#define PLL_MASK 0xFFFFFFFF
#endif
/* Note, this function is prototyped in xs1.h only from 13 tools onwards */
unsigned get_tile_id(tileref);
extern tileref tile[];
void device_reboot_aux(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 pllVal;
unsigned int localTileId = get_local_tile_id();
unsigned int tileId;
unsigned int tileArrayLength;
#if (XUD_SERIES_SUPPORT == 4)
/* 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);
#endif
/* Find size of tile array - note in future tools versions this will be available from platform.h */
asm volatile ("ldc %0, tile.globound":"=r"(tileArrayLength));
/* Reset all remote tiles */
for(int i = 0; i< tileArrayLength; i++)
{
/* Cannot cast tileref to unsigned! */
tileId = get_tile_id(tile[i]);
/* Do not reboot local tile yet! */
if(localTileId != tileId)
{
read_sswitch_reg(tileId, 6, pllVal);
pllVal &= PLL_MASK;
write_sswitch_reg_no_ack(tileId, 6, pllVal);
}
}
/* Finally reboot this tile! */
read_sswitch_reg(localTileId, 6, pllVal);
pllVal &= PLL_MASK;
write_sswitch_reg_no_ack(localTileId, 6, pllVal);
#endif
}
/* Reboots XMOS device by writing to the PLL config register */
void device_reboot(chanend spare)
{
device_reboot_aux();
while(1);
}

View File

@@ -0,0 +1,47 @@
#ifndef _UAC_HWRESOURCES_H_
#define _UAC_HWRESOURCES_H_
#ifndef NO_USB
#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_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_I2S_BIT XS1_CLKBLK_3
#endif
#endif /* _UAC_HWRESOURCES_H_ */

View File

@@ -0,0 +1,16 @@
#ifndef __DECOUPLE_H__
#define __DECOUPLE_H__
/** Manage the data transfer between the USB audio buffer and the
* Audio I/O driver.
*
* \param c_audio_out Channel connected to the audio() or mixer() threads
*/
void decouple(chanend c_audio_out
#ifdef CHAN_BUFF_CTRL
, chanend c_buff_ctrl
#endif
);
#endif // __DECOUPLE_H__

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
#ifndef NO_USB
#include <interrupt.h>
register_interrupt_handler(handle_audio_request, 1, 200)
#endif

View File

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

View File

@@ -0,0 +1,119 @@
#ifndef __interrupt_h__
#define __interrupt_h__
#define store_args0(c) \
asm("kentsp 20; stw %0, sp[1]; krestsp 20"::"r"(c));
#define store_args1(c,x) \
asm("kentsp 20; stw %0, sp[1]; stw %1, sp[2]; krestsp 20"::"r"(c),"r"(x));
#define store_args2(c,x0,x1) \
asm("kentsp 22; stw %0, sp[1];" \
"stw %1, sp[2];" \
"stw %2, sp[3];" \
" krestsp 22"::"r"(c),"r"(x0),"r"(x1));
#define store_args3(c,x0,x1,x2) \
asm("kentsp 22; stw %0, sp[1];" \
"stw %1, sp[2];" \
"stw %2, sp[3];" \
"stw %3, sp[4];" \
" krestsp 22"::"r"(c),"r"(x0),"r"(x1),"r"(x2));
#define load_args0(f) \
"ldw r0, sp[1]\n"
#define load_args1(f)\
"ldw r0, sp[1]\n" \
"ldw r1, sp[2]\n"
#define load_args2(f)\
"ldw r0, sp[1]\n" \
"ldw r1, sp[2]\n" \
"ldw r2, sp[3]\n"
#define load_args3(f)\
"ldw r0, sp[1]\n" \
"ldw r1, sp[2]\n" \
"ldw r2, sp[3]\n" \
"ldw r3, sp[4]\n"
#define save_state(f,args) \
".linkset __"#f"_handler_r0_save, "#args"+12\n" \
"stw r0, sp[" "__"#f"_handler_r0_save" "]\n" \
".linkset __"#f"_handler_r1_save, "#args"+13\n" \
"stw r1, sp[" "__"#f"_handler_r1_save" "]\n" \
".linkset __"#f"_handler_r2_save, "#args"+2\n" \
"stw r2, sp[" "__"#f"_handler_r2_save" "]\n" \
".linkset __"#f"_handler_r3_save, "#args"+3\n" \
"stw r3, sp[" "__"#f"_handler_r3_save" "]\n" \
".linkset __"#f"_handler_r11_save, "#args"+11\n" \
"stw r11, sp[" "__"#f"_handler_r11_save" "]\n" \
".linkset __"#f"_handler_lr_save, "#args"+14\n" \
"stw lr, sp[" "__"#f"_handler_lr_save" "]\n"
#define restore_state(f,args) \
"ldw r0, sp[" "__"#f"_handler_r0_save" "]\n" \
"ldw r1, sp[" "__"#f"_handler_r1_save" "]\n" \
"ldw r2, sp[" "__"#f"_handler_r2_save" "]\n" \
"ldw r3, sp[" "__"#f"_handler_r3_save" "]\n" \
"ldw r11, sp[" "__"#f"_handler_r11_save" "]\n" \
"ldw lr, sp[" "__"#f"_handler_lr_save" "]\n"
#define STRINGIFY0(x) #x
#define STRINGIFY(x) STRINGIFY0(x)
#define ENABLE_INTERRUPTS() asm("setsr " STRINGIFY(XS1_SR_IEBLE_SET(0, 1)))
#define DISABLE_INTERRUPTS() asm("clrsr " STRINGIFY(XS1_SR_IEBLE_SET(0, 1)))
//int ksp_enter, ksp_exit, r11_store;
#ifdef __XS2A__
#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\
".align 4\n" \
".cc_top __"#f"_handler.function,__"#f"_handler\n" \
"__" #f "_handler:\n" \
"ENTSP_lu6 0\n" \
"kentsp " #args "/2*2 + 20\n" \
"__kent:" \
save_state(f,args) \
load_args ## args (f) \
"bl " #f "\n" \
restore_state(f,args) \
"krestsp " #args "/2*2 + 20 \n" \
"__kret:\n" \
"kret\n" \
".cc_bottom __"#f"_handler.function");
#define register_interrupt_handler(f, args, nstackwords) \
asm (" .section .dp.data, \"adw\", @progbits\n" \
" .globl __" #f "_kernel_stack_end\n" \
" .globl __" #f "_handler\n" \
" .align 8\n" \
"__" #f "_kernel_stack:\n" \
" .space " #nstackwords ", 0\n" \
"__" #f "_kernel_stack_end:\n" \
" .space 4\n"\
" .text\n"); \
do_interrupt_handler(f, args)
#define set_interrupt_handler(f, args, c, ...) \
asm("ldaw r11, dp[__" #f "_kernel_stack_end];ldaw r10, sp[0]; " \
"set sp,r11;stw r10, sp[0]; krestsp 0":::"r10","r11"); \
store_args ## args(c, __VA_ARGS__) \
asm("ldap r11, __" #f "_handler; setv res[%0],r11"::"r"(c):"r11"); \
asm("setc res[%0], 0xa; eeu res[%0]"::"r"(c)); \
asm("setsr (((0) & ~(((1 << 0x1) - 1) << 0x1)) | (((1) << 0x1) & (((1 << 0x1) - 1) << 0x1)))");
#endif

View File

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

View File

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

View File

@@ -0,0 +1,60 @@
#ifndef __USB_BUFFER_H__
#define __USB_BUFFER_H__
/** USB Audio Buffering Thread.
*
* This function buffers USB audio data between the XUD layer and the decouple
* thread. Most of the chanend parameters to the function should be connected to
* XUD_Manager()
*
* \param c_aud_out Audio OUT endpoint channel connected to the XUD
* \param c_aud_in Audio IN endpoint channel connected to the XUD
* \param c_aud_fb Audio feedback endpoint channel connected to the XUD
* \param c_midi_from_host MIDI OUT endpoint channel connected to the XUD
* \param c_midi_to_host MIDI IN endpoint channel connected to the XUD
* \param c_int Audio clocking interrupt endpoint channel connected to the XUD
* \param c_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)
*/
#include "devicedefines.h"
void buffer(chanend c_aud_out,
chanend c_aud_in,
#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,
#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 defined(SPDIF_RX) || defined(ADAT_RX)
chanend ?c_int,
chanend ?c_clk_int,
#endif
chanend c_sof,
chanend c_aud_ctl,
in port p_off_mclk
#ifdef HID_CONTROLS
, chanend c_hid
#endif
#ifdef CHAN_BUFF_CTRL
, chanend c_buff_ctrl
#endif
);
#endif

View File

@@ -0,0 +1,962 @@
#ifndef NO_USB
#include <xs1.h>
#include <print.h>
#include "devicedefines.h"
#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 "commands.h"
#include "xud.h"
#include "testct_byref.h"
#ifdef HID_CONTROLS
#include "user_hid.h"
unsigned char g_hidData[1] = {0};
#endif
void GetADCCounts(unsigned samFreq, int &min, int &mid, int &max);
#define BUFFER_SIZE_OUT (1028 >> 2)
#define BUFFER_SIZE_IN (1028 >> 2)
/* Packet nuffers for audio data */
extern unsigned int g_curSamFreqMultiplier;
#ifdef CHAN_BUFF_CTRL
#define SET_SHARED_GLOBAL0(x,y) SET_SHARED_GLOBAL(x,y); outuchar(c_buff_ctrl, 0);
#else
#define SET_SHARED_GLOBAL0(x,y) SET_SHARED_GLOBAL(x,y)
#endif
/* Global var for speed. Related to feedback. Used by input stream to determine IN packet size */
unsigned g_speed;
unsigned g_freqChange = 0;
#if defined (SPDIF_RX) || defined (ADAT_RX)
/* 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] =
{
0, // Class-specific, caused by interface
1, // attribute: CUR
0, // CN/ MCN
0, // CS
0, // interface
0, // ID of entity causing interrupt - this will get modified;
0, // Spare
0, // Spare
};
unsigned g_intFlag = 0;
#endif
#if defined (MIDI) || defined(IAP)
static inline void swap(xc_ptr &a, xc_ptr &b)
{
xc_ptr tmp;
tmp = a;
a = b;
b = tmp;
return;
}
#endif
#ifdef MIDI
static unsigned int g_midi_to_host_buffer_A[MIDI_USB_BUFFER_TO_HOST_SIZE/4];
static unsigned int g_midi_to_host_buffer_B[MIDI_USB_BUFFER_TO_HOST_SIZE/4];
static unsigned int g_midi_from_host_buffer[MAX_USB_MIDI_PACKET_SIZE/4];
#endif
#ifdef IAP
unsigned char gc_zero_buffer[4];
#endif
unsigned int fb_clocks[4];
//#define FB_TOLERANCE_TEST
#define FB_TOLERANCE 0x100
//extern unsigned inZeroBuff[];
/**
* Buffers data from audio endpoints
* @param c_aud_out chanend for audio from xud
* @param c_aud_in chanend for audio to xud
* @param c_aud_fb chanend for feeback to xud
* @return void
*/
void buffer(register chanend c_aud_out, register chanend c_aud_in,
#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,
#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 defined(SPDIF_RX) || defined(ADAT_RX)
chanend ?c_ep_int,
chanend ?c_clk_int,
#endif
chanend c_sof,
chanend c_aud_ctl,
in port p_off_mclk
#ifdef HID_CONTROLS
, chanend c_hid
#endif
#ifdef CHAN_BUFF_CTRL
, chanend c_buff_ctrl
#endif
)
{
XUD_ep ep_aud_out = XUD_InitEp(c_aud_out);
XUD_ep ep_aud_in = XUD_InitEp(c_aud_in);
#if (NUM_USB_CHAN_IN == 0) || defined (UAC_FORCE_FEEDBACK_EP)
XUD_ep ep_aud_fb = XUD_InitEp(c_aud_fb);
#endif
#ifdef MIDI
XUD_ep ep_midi_from_host = XUD_InitEp(c_midi_from_host);
XUD_ep ep_midi_to_host = XUD_InitEp(c_midi_to_host);
#endif
#ifdef IAP
XUD_ep ep_iap_from_host = XUD_InitEp(c_iap_from_host);
XUD_ep ep_iap_to_host = XUD_InitEp(c_iap_to_host);
#ifdef IAP_INT_EP
XUD_ep ep_iap_to_host_int = XUD_InitEp(c_iap_to_host_int);
#endif
#ifdef IAP_EA_NATIVE_TRANS
XUD_ep ep_iap_ea_native_out = XUD_InitEp(c_iap_ea_native_out);
XUD_ep ep_iap_ea_native_in = XUD_InitEp(c_iap_ea_native_in);
#endif
#endif
#if defined(SPDIF_RX) || defined(ADAT_RX)
XUD_ep ep_int = XUD_InitEp(c_ep_int);
#endif
#ifdef HID_CONTROLS
XUD_ep ep_hid = XUD_InitEp(c_hid);
#endif
unsigned u_tmp;
unsigned sampleFreq = DEFAULT_FREQ;
unsigned masterClockFreq = DEFAULT_MCLK_FREQ;
unsigned lastClock = 0;
unsigned clocks = 0;
long long clockcounter = 0;
#if (NUM_USB_CHAN_IN > 0)
unsigned bufferIn = 1;
#endif
//unsigned remnant = 0;
unsigned sofCount = 0;
unsigned freqChange = 0;
unsigned mod_from_last_time = 0;
#ifdef FB_TOLERANCE_TEST
unsigned expected_fb = 0;
#endif
#if (NUM_USB_CHAN_OUT > 0)
xc_ptr aud_from_host_buffer = 0;
#endif
#ifdef MIDI
xc_ptr midi_from_host_buffer = array_to_xc_ptr(g_midi_from_host_buffer);
xc_ptr midi_from_host_rdptr;
xc_ptr midi_to_host_buffer_being_sent = array_to_xc_ptr(g_midi_to_host_buffer_A);
xc_ptr midi_to_host_buffer_being_collected = array_to_xc_ptr(g_midi_to_host_buffer_B);
int is_ack;
unsigned int datum;
int midi_data_remaining_to_device = 0;
int midi_data_collected_from_device = 0;
int midi_waiting_on_send_to_host = 0;
#endif
#ifdef IAP
xc_ptr iap_from_host_rdptr;
unsigned char iap_from_host_buffer[IAP_MAX_PACKET_SIZE+4];
unsigned char iap_to_host_buffer[IAP_MAX_PACKET_SIZE];
int is_ack_iap;
int is_reset;
unsigned int datum_iap;
int iap_data_remaining_to_device = 0;
int iap_data_collected_from_device = 0;
int iap_expected_data_length = 0;
int iap_draining_chan = 0;
#ifdef IAP_EA_NATIVE_TRANS
unsigned char iap_ea_native_control_flag;
unsigned char iap_ea_native_rx_buffer[IAP2_EA_NATIVE_TRANS_MAX_PACKET_SIZE];
unsigned char iap_ea_native_tx_buffer[IAP2_EA_NATIVE_TRANS_MAX_PACKET_SIZE];
unsigned iap_ea_native_rx_length = 0;
unsigned iap_ea_native_tx_length = 0;
unsigned iap_ea_native_interface_alt_setting = 0;
unsigned iap_ea_native_control_to_send = 0;
unsigned iap_ea_native_incoming = 0;
#endif
#endif
/* Store EP's to globals so that decouple() can access them */
asm("stw %0, dp[aud_from_host_usb_ep]"::"r"(ep_aud_out));
asm("stw %0, dp[aud_to_host_usb_ep]"::"r"(ep_aud_in));
asm("stw %0, dp[buffer_aud_ctl_chan]"::"r"(c_aud_ctl));
#ifdef FB_TOLERANCE_TEST
expected_fb = ((DEFAULT_FREQ * 0x2000) / 1000);
#endif
#if (NUM_USB_CHAN_OUT > 0)
SET_SHARED_GLOBAL(g_aud_from_host_flag, 1);
#endif
#if (NUM_USB_CHAN_IN > 0)
SET_SHARED_GLOBAL(g_aud_to_host_flag, 1);
#endif
fb_clocks[0] = 0;
/* Mark OUT endpoints ready to receive data from host */
#ifdef MIDI
XUD_SetReady_OutPtr(ep_midi_from_host, midi_from_host_buffer);
#endif
#ifdef IAP
XUD_SetReady_Out(ep_iap_from_host, iap_from_host_buffer);
#ifdef IAP_EA_NATIVE_TRANS
XUD_SetReady_Out(ep_iap_ea_native_out, iap_ea_native_rx_buffer);
#endif
#endif
#ifdef HID_CONTROLS
XUD_SetReady_In(ep_hid, g_hidData, 1);
#endif
#if (AUDIO_CLASS == 1)
#if (NUM_USB_CHAN_IN == 0) || defined (UAC_FORCE_FEEDBACK_EP)
/* In UAC1 we dont use a stream start event (and we are always FS) so mark FB EP ready now */
XUD_SetReady_In(ep_aud_fb, (fb_clocks, unsigned char[]), 3);
#endif
#endif
while(1)
{
XUD_Result_t result;
unsigned length;
/* Wait for response from XUD and service relevant EP */
select
{
#if defined(SPDIF_RX) || defined(ADAT_RX)
/* Clocking thread wants to produce an interrupt... */
case inuint_byref(c_clk_int, u_tmp):
chkct(c_clk_int, XS1_CT_END);
/* Check if we have interrupt pending.
* Note, this his means we can loose interrupts... */
if(!g_intFlag)
{
g_intFlag = 1;
/* Append Unit ID onto packet */
g_intData[5] = u_tmp;
XUD_SetReady_In(ep_int, g_intData, 6);
}
break;
/* Interrupt EP data sent, clear flag */
case XUD_SetData_Select(c_ep_int, ep_int, result):
{
g_intFlag = 0;
break;
}
#endif
/* Sample Freq or chan count update from Endpoint 0 core */
case testct_byref(c_aud_ctl, u_tmp):
{
if (u_tmp)
{
// is a control token sent by reboot_device
inct(c_aud_ctl);
outct(c_aud_ctl, XS1_CT_END);
while(1) {};
}
else
{
unsigned cmd = inuint(c_aud_ctl);
if(cmd == SET_SAMPLE_FREQ)
{
unsigned receivedSampleFreq = inuint(c_aud_ctl);
#if (MAX_FREQ != MIN_FREQ)
/* Don't update things for DFU command.. */
if(receivedSampleFreq != AUDIO_STOP_FOR_DFU)
{
sampleFreq = receivedSampleFreq;
#ifdef FB_TOLERANCE_TEST
expected_fb = ((sampleFreq * 0x2000) / frameTime);
#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) */
sofCount = 1;
clocks = 0;
//remnant = 0;
clockcounter = 0;
mod_from_last_time = 0;
/* Set g_speed to something sensible. We expect it to get over-written before stream time */
int min, mid, max;
GetADCCounts(sampleFreq, min, mid, max);
g_speed = mid<<16;
if((MCLK_48 % sampleFreq) == 0)
{
masterClockFreq = MCLK_48;
}
else
{
masterClockFreq = MCLK_441;
}
}
#endif
/* Ideally we want to wait for handshake (and pass back up) here. But we cannot keep this
* core locked, it must stay responsive to packets (MIDI etc) and SOFs. So, set a flag and check for
* handshake elsewhere */
SET_SHARED_GLOBAL(g_freqChange_sampFreq, receivedSampleFreq);
}
#if (AUDIO_CLASS == 2)
else
if(cmd == SET_STREAM_FORMAT_IN)
{
unsigned formatChange_DataFormat = inuint(c_aud_ctl);
unsigned formatChange_NumChans = inuint(c_aud_ctl);
unsigned formatChange_SubSlot = inuint(c_aud_ctl);
unsigned formatChange_SampRes = inuint(c_aud_ctl);
SET_SHARED_GLOBAL(g_formatChange_NumChans, formatChange_NumChans);
SET_SHARED_GLOBAL(g_formatChange_SubSlot, formatChange_SubSlot);
SET_SHARED_GLOBAL(g_formatChange_DataFormat, formatChange_DataFormat);
SET_SHARED_GLOBAL(g_formatChange_SampRes, formatChange_SampRes);
}
/* FIXME when FB EP is enabled there is no inital XUD_SetReady */
else if (cmd == SET_STREAM_FORMAT_OUT)
{
XUD_BusSpeed_t busSpeed;
unsigned formatChange_DataFormat = inuint(c_aud_ctl);
unsigned formatChange_NumChans = inuint(c_aud_ctl);
unsigned formatChange_SubSlot = inuint(c_aud_ctl);
unsigned formatChange_SampRes = inuint(c_aud_ctl);
SET_SHARED_GLOBAL(g_formatChange_NumChans, formatChange_NumChans);
SET_SHARED_GLOBAL(g_formatChange_SubSlot, formatChange_SubSlot);
SET_SHARED_GLOBAL(g_formatChange_DataFormat, formatChange_DataFormat);
SET_SHARED_GLOBAL(g_formatChange_SampRes, formatChange_SampRes);
#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP)
/* Host is starting up the output stream. Setup (or potentially resize) feedback packet based on bus-speed
* This is only really important on inital start up (when bus-speed
was unknown) and when changing bus-speeds */
GET_SHARED_GLOBAL(busSpeed, g_curUsbSpeed);
if (busSpeed == XUD_SPEED_HS)
{
XUD_SetReady_In(ep_aud_fb, (fb_clocks, unsigned char[]), 4);
}
else
{
XUD_SetReady_In(ep_aud_fb, (fb_clocks, unsigned char[]), 3);
}
#endif
}
#endif
/* Pass on sample freq change to decouple() via global flag (saves a chanend) */
/* Note: freqChange flags now used to communicate other commands also */
SET_SHARED_GLOBAL0(g_freqChange, cmd); /* Set command */
SET_SHARED_GLOBAL(g_freqChange_flag, cmd); /* Set Flag */
}
break;
}
#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 */
/* (previously used 63 instead of 127) */
/* SOF notifcation from XUD_Manager() */
case inuint_byref(c_sof, u_tmp):
/* NOTE our feedback will be wrong for a couple of SOF's after a SF change due to
* lastClock being incorrect */
/* Get MCLK count */
asm volatile(" getts %0, res[%1]" : "=r" (u_tmp) : "r" (p_off_mclk));
GET_SHARED_GLOBAL(freqChange, g_freqChange);
if(freqChange == SET_SAMPLE_FREQ)
{
/* Keep getting MCLK counts */
lastClock = u_tmp;
}
else
{
unsigned usb_speed;
GET_SHARED_GLOBAL(usb_speed, g_curUsbSpeed);
#if 0
unsigned mask = MASK_16_13;
/* Original feedback implementation */
if(usb_speed != XUD_SPEED_HS)
mask = MASK_16_10;
/* Number of MCLKS this SOF, approx 125 * 24 (3000), sample by sample rate */
GET_SHARED_GLOBAL(cycles, g_curSamFreqMultiplier);
cycles = ((int)((short)(u_tmp - lastClock))) * cycles;
/* Any odd bits (lower than 16.23) have to be kept seperate */
remnant += cycles & mask;
/* Add 16.13 bits into clock count */
clocks += (cycles & ~mask) + (remnant & ~mask);
/* and overflow from odd bits. Remove overflow from odd bits. */
remnant &= mask;
/* Store MCLK for next time around... */
lastClock = u_tmp;
/* Reset counts based on SOF counting. Expect 16ms (128 HS SOFs/16 FS SOFS) per feedback poll
* We always count 128 SOFs, so 16ms @ HS, 128ms @ FS */
if(sofCount == 128)
{
sofCount = 0;
#ifdef FB_TOLERANCE_TEST
if (clocks > (expected_fb - FB_TOLERANCE) &&
clocks < (expected_fb + FB_TOLERANCE))
#endif
{
int usb_speed;
asm volatile("stw %0, dp[g_speed]"::"r"(clocks)); // g_speed = clocks
GET_SHARED_GLOBAL(usb_speed, g_curUsbSpeed);
if (usb_speed == XUD_SPEED_HS)
{
fb_clocks[0] = clocks;
}
else
{
fb_clocks[0] = clocks>>2;
}
}
#ifdef FB_TOLERANCE_TEST
else
{
}
#endif
clocks = 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.
* Average over 128 SOFs - 128 x 3072 = 0x60000.
*/
unsigned long long feedbackMul = 64ULL;
if(usb_speed != XUD_SPEED_HS)
feedbackMul = 8ULL; /* TODO Use 4 instead of 8 to avoid windows LSB issues? */
/* Number of MCLK ticks in this SOF period (E.g = 125 * 24.576 = 3072) */
int count = (int) ((short)(u_tmp - lastClock));
unsigned long long full_result = count * feedbackMul * sampleFreq;
clockcounter += full_result;
/* Store MCLK for next time around... */
lastClock = u_tmp;
/* Reset counts based on SOF counting. Expect 16ms (128 HS SOFs/16 FS SOFS) per feedback poll
* We always count 128 SOFs, so 16ms @ HS, 128ms @ FS */
if(sofCount == 128)
{
sofCount = 0;
clockcounter += mod_from_last_time;
clocks = clockcounter / masterClockFreq;
mod_from_last_time = clockcounter % masterClockFreq;
if(usb_speed == XUD_SPEED_HS)
{
clocks <<= 3;
}
else
{
clocks <<= 6;
}
#ifdef FB_TOLERANCE_TEST
if (clocks > (expected_fb - FB_TOLERANCE) &&
clocks < (expected_fb + FB_TOLERANCE))
#endif
{
int usb_speed;
asm volatile("stw %0, dp[g_speed]"::"r"(clocks)); // g_speed = clocks
GET_SHARED_GLOBAL(usb_speed, g_curUsbSpeed);
if (usb_speed == XUD_SPEED_HS)
{
fb_clocks[0] = clocks;
}
else
{
fb_clocks[0] = clocks >> 2;
}
}
#ifdef FB_TOLERANCE_TEST
else
{
}
#endif
clockcounter = 0;
}
#endif
sofCount++;
}
break;
#if (NUM_USB_CHAN_IN > 0)
/* Sent audio packet DEVICE -> HOST */
case XUD_SetData_Select(c_aud_in, ep_aud_in, result):
{
/* Inform stream that buffer sent */
SET_SHARED_GLOBAL0(g_aud_to_host_flag, bufferIn+1);
}
break;
#endif
#if (NUM_USB_CHAN_OUT > 0)
#if (NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP)
/* Feedback Pipe */
case XUD_SetData_Select(c_aud_fb, ep_aud_fb, result):
{
XUD_BusSpeed_t busSpeed;
GET_SHARED_GLOBAL(busSpeed, g_curUsbSpeed);
if (busSpeed == XUD_SPEED_HS)
{
XUD_SetReady_In(ep_aud_fb, (fb_clocks, unsigned char[]), 4);
}
else
{
XUD_SetReady_In(ep_aud_fb, (fb_clocks, unsigned char[]), 3);
}
}
break;
#endif
/* Received Audio packet HOST -> DEVICE. Datalength written to length */
case XUD_GetData_Select(c_aud_out, ep_aud_out, length, result):
{
GET_SHARED_GLOBAL(aud_from_host_buffer, g_aud_from_host_buffer);
write_via_xc_ptr(aud_from_host_buffer, length);
/* 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):
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)
{
read_via_xc_ptr(datum, midi_from_host_rdptr);
outuint(c_midi, datum);
midi_from_host_rdptr += 4;
midi_data_remaining_to_device -= 4;
}
}
break;
/* MIDI IN to host */
case XUD_SetData_Select(c_midi_to_host, ep_midi_to_host, 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);
/* 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;
#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):
if((result == XUD_RES_OKAY) && (length > 0))
{
iap_data_remaining_to_device = length;
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);
/* 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;
/* 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);
#ifdef IAP_INT_EP
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 */
#ifdef IAP_INT_EP
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;
#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 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;
//::
#endif
#endif
#ifdef HID_CONTROLS
/* HID Report Data */
case XUD_SetData_Select(c_hid, ep_hid, result):
{
g_hidData[0]=0;
UserReadHIDButtons(g_hidData);
XUD_SetReady_In(ep_hid, g_hidData, 1);
}
break;
#endif
#ifdef MIDI
/* Received word from MIDI thread - Check for ACK or Data */
case midi_get_ack_or_data(c_midi, is_ack, datum):
if (is_ack)
{
/* An ack from the midi/uart thread means it has accepted some data we sent it
* we are okay to send another word */
if (midi_data_remaining_to_device <= 0)
{
/* We have read an entire packet - Mark ready to receive another */
int reset = XUD_SetReady_OutPtr(ep_midi_from_host, midi_from_host_buffer);
}
else
{
/* Read another word from the fifo and output it to MIDI thread */
read_via_xc_ptr(datum, midi_from_host_rdptr);
outuint(c_midi, datum);
midi_from_host_rdptr += 4;
midi_data_remaining_to_device -= 4;
}
}
else
{
/* The midi/uart thread has sent us some data - handshake back */
midi_send_ack(c_midi);
if (midi_data_collected_from_device < MIDI_USB_BUFFER_TO_HOST_SIZE)
{
/* There is room in the collecting buffer for the data */
xc_ptr p = midi_to_host_buffer_being_collected + midi_data_collected_from_device;
// Add data to the buffer
write_via_xc_ptr(p, datum);
midi_data_collected_from_device += 4;
}
else
{
// Too many events from device - drop
}
// If we are not sending data to the host then initiate it
if (!midi_waiting_on_send_to_host)
{
swap(midi_to_host_buffer_being_collected, midi_to_host_buffer_being_sent);
// Signal other side to swap
XUD_SetReady_InPtr(ep_midi_to_host, midi_to_host_buffer_being_sent, midi_data_collected_from_device);
midi_data_collected_from_device = 0;
midi_waiting_on_send_to_host = 1;
}
}
break;
#endif /* ifdef MIDI */
#ifdef IAP
/* Received word from iap thread - Check for ACK or Data */
case iap_get_ack_or_reset_or_data(c_iap, is_ack_iap, is_reset, datum_iap):
if (iap_draining_chan)
{
/* As we're draining the iAP channel now, ignore ACKs and data */
if (is_reset)
{
// The iAP core has returned a reset token, so we can stop draining the iAP channel now
iap_draining_chan = 0;
}
}
else
{
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 */
if (iap_data_remaining_to_device == 0)
{
/* We have read an entire packet - Mark ready to receive another */
XUD_SetReady_Out(ep_iap_from_host, iap_from_host_buffer);
}
else
{
/* Read another byte from the fifo and output it to iap thread */
datum_iap = iap_from_host_buffer[iap_from_host_rdptr];
outuint(c_iap, datum_iap);
iap_from_host_rdptr += 1;
iap_data_remaining_to_device -= 1;
}
}
else if (!is_reset)
{
if (iap_expected_data_length == 0)
{
/* Expect a length from iAP core */
iap_send_ack(c_iap);
iap_expected_data_length = datum_iap;
}
else
{
if (iap_data_collected_from_device < IAP_MAX_PACKET_SIZE)
{
/* There is room in the collecting buffer for the data.. */
iap_to_host_buffer[iap_data_collected_from_device] = datum_iap;
iap_data_collected_from_device += 1;
}
else
{
// Too many events from device - drop
}
/* Once we have the whole message, sent it to host */
/* Note we don't ack the last byte yet... */
if (iap_data_collected_from_device == iap_expected_data_length)
{
XUD_Result_t result1 = XUD_RES_OKAY, result2;
#ifdef IAP_INT_EP
result1 = XUD_SetReady_In(ep_iap_to_host_int, gc_zero_buffer, 0);
#endif
result2 = XUD_SetReady_In(ep_iap_to_host, iap_to_host_buffer, iap_data_collected_from_device);
if((result1 == XUD_RES_RST) || (result2 == XUD_RES_RST))
{
#ifdef IAP_INT_EP
XUD_ResetEndpoint(ep_iap_to_host_int, null);
#endif
XUD_ResetEndpoint(ep_iap_to_host, null);
iap_send_reset(c_iap);
iap_draining_chan = 1; // Drain c_iap until a reset is sent back
iap_data_remaining_to_device = -1;
iap_from_host_rdptr = 0;
}
iap_data_collected_from_device = 0;
iap_expected_data_length = 0;
}
else
{
/* The iap/uart thread has sent us some data - handshake back */
iap_send_ack(c_iap);
}
}
}
}
break;
# 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 */
outct(c_iap_ea_native_ctrl, XS1_CT_END);
if (iap_ea_native_interface_alt_setting == 0) // EA Protocol session closed by Apple device
{
// 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_DISCONNECTED;
iap_ea_native_control_to_send = 1;
}
else if (iap_ea_native_interface_alt_setting == 1) // EA Protocol session opened by Apple device
{
// 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_CONNECTED;
iap_ea_native_control_to_send = 1;
}
break;
/* Receive data from the EA Protocol user core */
case c_iap_ea_native_data :> iap_ea_native_incoming:
// Check if this is a ready flag or unsolicited data
switch (iap_ea_native_incoming)
{
case EA_NATIVE_RECEIVER_READY: // EA Protocol user core ready to receive data
// Check if we are sending a control flag, or OUT data
if (iap_ea_native_control_to_send)
{
unsigned char ea_control[] = {iap_ea_native_control_flag};
iAP2_EANativeTransport_writeToChan_data(c_iap_ea_native_data,
ea_control,
1);
iap_ea_native_control_to_send = 0;
}
else
{
iAP2_EANativeTransport_writeToChan_data(c_iap_ea_native_data,
iap_ea_native_rx_buffer,
iap_ea_native_rx_length);
// Mark the OUT EP as ready again now we have sent all the data
XUD_SetReady_Out(ep_iap_ea_native_out, iap_ea_native_rx_buffer);
}
break;
case EA_NATIVE_SEND_DATA: // Unsolicited data from user core for IN ep
iAP2_EANativeTransport_readFromChan_data(c_iap_ea_native_data,
iap_ea_native_tx_buffer,
iap_ea_native_tx_length);
// Mark the IN EP as ready now we have all the data
XUD_SetReady_In(ep_iap_ea_native_in, iap_ea_native_tx_buffer, iap_ea_native_tx_length);
break;
}
break;
//::
#endif
#endif
}
}
}
#endif /* NO_USB */

View File

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

View File

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

View File

@@ -0,0 +1,12 @@
/* These defines relate to the HID report desc - do not mod */
#define HID_CONTROL_PLAYPAUSE_SHIFT 0x00
#define HID_CONTROL_NEXT_SHIFT 0x01
#define HID_CONTROL_PREV_SHIFT 0x02
#define HID_CONTROL_VOLUP_SHIFT 0x03
#define HID_CONTROL_VOLDN_SHIFT 0x04
#define HID_CONTROL_MUTE_SHIFT 0x05
void UserReadHIDButtons(unsigned char hidData[]);

View File

@@ -0,0 +1,77 @@
/*
Warnings relating to configuration defines located in this XC source file rather than the devicedefines.h header file in order to avoid multiple warnings being issued when the devicedefines.h header file is included in multiple files.
*/
#include "customdefines.h"
#include "customdefines.h"
#ifndef NO_USB
#ifndef DEFAULT_FREQ
#warning DEFAULT_FREQ not defined. Using MIN_FREQ
#endif
#ifndef MIN_FREQ
#warning MIN_FREQ not defined. Using 44100
#endif
#ifndef MAX_FREQ
#warning MAX_FREQ not defined. Using 192000
#endif
#ifdef SPDIF_TX
#ifndef SPDIF_TX_INDEX
#warning SPDIF_TX_INDEX not defined! Using 0
#endif
#endif
#ifndef VENDOR_STR
#warning VENDOR_STR not defined. Using "XMOS"
#endif
#ifndef VENDOR_ID
#warning VENDOR_ID not defined. Using XMOS vendor ID (0x20B1)
#endif
#ifndef PRODUCT_STR_A2
#warning PRODUCT_STR_A2 not defined. Using default XMOS string
#endif
#ifndef PRODUCT_STR_A1
#warning PRODUCT_STR_A1 not defined. Using default XMOS string
#endif
#ifndef BCD_DEVICE
#warning BCD_DEVICE not defined. Using XMOS release version number
#endif
#if (AUDIO_CLASS==1) || defined(AUDIO_CLASS_FALLBACK)
#ifndef PID_AUDIO_1
#warning PID_AUDIO_1 not defined. Using 0x0003
#endif
#endif
#ifndef PID_AUDIO_2
#warning PID_AUDIO_2 not defined. Using 0x0002
#endif
#ifndef AUDIO_CLASS
#warning AUDIO_CLASS not defined, using 2
#endif
#ifndef AUDIO_CLASS_FALLBACK
#warning AUDIO_CLASS_FALLBACK not defined, using 0 (i.e. disabled)
#endif
/* Sanity check on FS channel counts */
#if (NUM_USB_CHAN_OUT_FS > NUM_USB_CHAN_OUT)
#error NUM_USB_CHAN_OUT_FS expected to be less than or equal to NUM_USB_CHAN_OUT
#endif
#if (NUM_USB_CHAN_IN_FS > NUM_USB_CHAN_IN)
#error NUM_USB_CHAN_IN_FS expected to be less than or equal to NUM_USB_CHAN_IN
#endif
#endif

View File

@@ -0,0 +1,9 @@
def use_module(bld):
# Set file specific flags
for item in ('endpoint0.c', 'dbcalc.xc', 'audiorequests.xc',
'flashlib_user.c', 'audioports.c', 'audioports.xc'):
bld.env['XCC_FLAGS_{}'.format(item)] = ['-Os', '-mno-dual-issue'
] + bld.env.XCC_FLAGS
sources = bld.path.ant_glob(['**/*.xc', '**/*.c', '**/*.S'])
tgen = bld.module(source=sources, includes=['.', 'audio_io'])

View File

@@ -0,0 +1,25 @@
#ifndef NO_USB
#include "devicedefines.h"
#include "hostactive.h"
#include "audiostream.h"
void XUD_UserSuspend(void) __attribute__ ((weak));
void XUD_UserSuspend(void)
{
UserAudioStreamStop();
UserHostActive(0);
}
void XUD_UserResume(void) __attribute__ ((weak));
void XUD_UserResume(void)
{
unsigned config;
asm("ldw %0, dp[g_currentConfig]" : "=r" (config):);
if(config == 1)
{
UserHostActive(1);
}
}
#endif /* NO_USB */

View File

@@ -0,0 +1 @@
module README.

View File

@@ -0,0 +1,9 @@
MIDI Module for USB Audio Framework
===================================
:scope: General Use
:description: MIDI
:keywords: MIDI
:boards:

View File

@@ -0,0 +1,12 @@
# You can set flags specifically for your module by using the MODULE_XCC_FLAGS
# variable. So the following
#
# MODULE_XCC_FLAGS = $(XCC_FLAGS) -O3
#
# specifies that everything in the modules should have the application
# build flags with -O3 appended (so the files will build at
# optimization level -O3).
#
# You can also set MODULE_XCC_C_FLAGS, MODULE_XCC_XC_FLAGS etc..
DEPENDENT_MODULES = module_queue

View File

@@ -0,0 +1 @@
One line module description.

View File

@@ -0,0 +1,120 @@
/* This file contains the MID device descriptor
It is intended to be included in the main device descriptor definition */
/* MIDI Descriptors */
/* Table B-3: MIDI Adapter Standard AC Interface Descriptor */
0x09, /* 0 bLength : Size of this descriptor, in bytes. (field size 1 bytes) */
0x04, /* 1 bDescriptorType : INTERFACE descriptor. (field size 1 bytes) */
MIDI_INTERFACE_1, /* 2 bInterfaceNumber : Index of this interface. (field size 1 bytes) */
0x00, /* 3 bAlternateSetting : Index of this setting. (field size 1 bytes) */
0x00, /* 4 bNumEndpoints : 0 endpoints. (field size 1 bytes) */
0x01, /* 5 bInterfaceClass : AUDIO. (field size 1 bytes) */
0x01, /* 6 bInterfaceSubclass : AUDIO_CONTROL. (field size 1 bytes) */
0x00, /* 7 bInterfaceProtocol : Unused. (field size 1 bytes) */
0x00, /* 8 iInterface : Unused. (field size 1 bytes) */
// 9
/* Table B-4: MIDI Adapter Class-specific AC Interface Descriptor */
0x09, /* 0 bLength : Size of this descriptor, in bytes. (field size 1 bytes) */
0x24, /* 1 bDescriptorType : 0x24. (field size 1 bytes) */
0x01, /* 2 bDescriptorSubtype : HEADER subtype. (field size 1 bytes) */
0x00, /* 3 bcdADC : Revision of class specification - 1.0 (field size 2 bytes) */
0x01, /* 4 bcdADC */
0x09, /* 5 wTotalLength : Total size of class specific descriptors. (field size 2 bytes) */
0x00, /* 6 wTotalLength */
0x01, /* 7 bInCollection : Number of streaming interfaces. (field size 1 bytes) */
0x01, /* 8 baInterfaceNr(1) : MIDIStreaming interface 1 belongs to this AudioControl interface */
//9
/* Table B-5: MIDI Adapter Standard MS Interface Descriptor */
0x09, /* 0 bLength : Size of this descriptor, in bytes. (field size 1 bytes) */
0x04, /* 1 bDescriptorType : INTERFACE descriptor. (field size 1 bytes) */
MIDI_INTERFACE_2, /* 2 bInterfaceNumber : Index of this interface. (field size 1 bytes) */
0x00, /* 3 bAlternateSetting : Index of this alternate setting. (field size 1 bytes) */
0x02, /* 4 bNumEndpoints : 2 endpoints. (field size 1 bytes) */
0x01, /* 5 bInterfaceClass : AUDIO. (field size 1 bytes) */
0x03, /* 6 bInterfaceSubclass : MIDISTREAMING. (field size 1 bytes) */
0x00, /* 7 bInterfaceProtocol : Unused. (field size 1 bytes) */
0x00, /* 8 iInterface : Unused. (field size 1 bytes) */
//9
/* Table B-6: MIDI Adapter Class-specific MS Interface Descriptor */
0x07, /* 0 bLength : Size of this descriptor, in bytes. (field size 1 bytes) */
0x24, /* 1 bDescriptorType : CS_INTERFACE. (field size 1 bytes) */
0x01, /* 2 bDescriptorSubtype : MS_HEADER subtype. (field size 1 bytes) */
0x00, /* 3 BcdADC : Revision of this class specification. (field size 2 bytes) */
0x01, /* 4 BcdADC */
0x41, /* 5 wTotalLength : Total size of class-specific descriptors. (field size 2 bytes) */
0x00, /* 6 wTotalLength */
//7
/* Table B-7: MIDI Adapter MIDI IN Jack Descriptor (Embedded) */
0x06, /* 0 bLength : Size of this descriptor, in bytes. (field size 1 bytes) */
0x24, /* 1 bDescriptorType : CS_INTERFACE. (field size 1 bytes) */
0x02, /* 2 bDescriptorSubtype : MIDI_IN_JACK subtype. (field size 1 bytes) */
0x01, /* 3 bJackType : EMBEDDED. (field size 1 bytes) */
0x01, /* 4 bJackID : ID of this Jack. (field size 1 bytes) */
0x00, /* 5 iJack : Unused. (field size 1 bytes) */
//6
/* Table B-8: MIDI Adapter MIDI IN Jack Descriptor (External) */
0x06, /* 0 bLength : Size of this descriptor, in bytes. (field size 1 bytes) */
0x24, /* 1 bDescriptorType : CS_INTERFACE. (field size 1 bytes) */
0x02, /* 2 bDescriptorSubtype : MIDI_IN_JACK subtype. (field size 1 bytes) */
0x02, /* 3 bJackType : EXTERNAL. (field size 1 bytes) */
0x02, /* 4 bJackID : ID of this Jack. (field size 1 bytes) */
0x00, /* 5 iJack : Unused. (field size 1 bytes) */
//6
/* Table B-9: MIDI Adapter MIDI OUT Jack Descriptor (Embedded) */
0x09, /* 0 bLength : Size of this descriptor, in bytes. (field size 1 bytes) */
0x24, /* 1 bDescriptorType : CS_INTERFACE. (field size 1 bytes) */
0x03, /* 2 bDescriptorSubtype : MIDI_OUT_JACK subtype. (field size 1 bytes) */
0x01, /* 3 bJackType : EMBEDDED. (field size 1 bytes) */
0x03, /* 4 bJackID : ID of this Jack. (field size 1 bytes) */
0x01, /* 5 bNrInputPins : Number of Input Pins of this Jack. (field size 1 bytes) */
0x02, /* 6 BaSourceID(1) : ID of the Entity to which this Pin is connected. (field size 1 bytes) */
0x01, /* 7 BaSourcePin(1) : Output Pin number of the Entityt o which this Input Pin is connected. */
0x00, /* 8 iJack : Unused. (field size 1 bytes) */
//9
/* Table B-10: MIDI Adapter MIDI OUT Jack Descriptor (External) */
0x09, /* 0 bLength : Size of this descriptor, in bytes. (field size 1 bytes) */
0x24, /* 1 bDescriptorType : CS_INTERFACE. (field size 1 bytes) */
0x03, /* 2 bDescriptorSubtype : MIDI_OUT_JACK subtype. (field size 1 bytes) */
0x02, /* 3 bJackType : EXTERNAL. (field size 1 bytes) */
0x04, /* 4 bJackID : ID of this Jack. (field size 1 bytes) */
0x01, /* 5 bNrInputPins : Number of Input Pins of this Jack. (field size 1 bytes) */
0x01, /* 6 BaSourceID(1) : ID of the Entity to which this Pin is connected. (field size 1 bytes) */
0x01, /* 7 BaSourcePin(1) : Output Pin number of the Entity to which this Input Pin is connected. */
0x00, /* 8 iJack : Unused. (field size 1 bytes) */
//9
/* Table B-11: MIDI Adapter Standard Bulk OUT Endpoint Descriptor */
0x09, /* 0 bLength : Size of this descriptor, in bytes. (field size 1 bytes) */
0x05, /* 1 bDescriptorType : ENDPOINT descriptor. (field size 1 bytes) */
0x04, /* 2 bEndpointAddress : OUT Endpoint 4. (field size 1 bytes) */
0x02, /* 3 bmAttributes : Bulk, not shared. (field size 1 bytes) */
0x00, /* 4 wMaxPacketSize : 64 bytes per packet. (field size 2 bytes) */
0x02, /* 5 wMaxPacketSize */
0x00, /* 6 bInterval : Ignored for Bulk. Set to zero. (field size 1 bytes) */
0x00, /* 7 bRefresh : Unused. (field size 1 bytes) */
0x00, /* 8 bSynchAddress : Unused. (field size 1 bytes) */
//9
/* Table B-12: MIDI Adapter Class-specific Bulk OUT Endpoint Descriptor */
0x05, /* 0 bLength : Size of this descriptor, in bytes. (field size 1 bytes) */
0x25, /* 1 bDescriptorType : CS_ENDPOINT descriptor (field size 1 bytes) */
0x01, /* 2 bDescriptorSubtype : MS_GENERAL subtype. (field size 1 bytes) */
0x01, /* 3 bNumEmbMIDIJack : Number of embedded MIDI IN Jacks. (field size 1 bytes) */
0x01, /* 4 BaAssocJackID(1) : ID of the Embedded MIDI IN Jack. (field size 1 bytes) */
//5
/* Table B-13: MIDI Adapter Standard Bulk IN Endpoint Descriptor */
0x09, /* 0 bLength : Size of this descriptor, in bytes. (field size 1 bytes) */
0x05, /* 1 bDescriptorType : ENDPOINT descriptor. (field size 1 bytes) */
0x85, /* 2 bEndpointAddress : IN Endpoint 5. (field size 1 bytes) */
0x02, /* 3 bmAttributes : Bulk, not shared. (field size 1 bytes) */
0x00, /* 4 wMaxPacketSize : 64 bytes per packet. (field size 2 bytes) */
0x02, /* 5 wMaxPacketSize */
0x00, /* 6 bInterval : Ignored for Bulk. Set to zero. (field size 1 bytes) */
0x00, /* 7 bRefresh : Unused. (field size 1 bytes) */
0x00, /* 8 bSynchAddress : Unused. (field size 1 bytes) */
//9
/* Table B-14: MIDI Adapter Class-specific Bulk IN Endpoint Descriptor */
0x05, /* 0 bLength : Size of this descriptor, in bytes. (field size 1 bytes) */
0x25, /* 1 bDescriptorType : CS_ENDPOINT descriptor (field size 1 bytes) */
0x01, /* 2 bDescriptorSubtype : MS_GENERAL subtype. (field size 1 bytes) */
0x01, /* 3 bNumEmbMIDIJack : Number of embedded MIDI OUT Jacks. (field size 1 bytes) */
0x03, /* 4 BaAssocJackID(1) : ID of the Embedded MIDI OUT Jack. (field size 1 bytes) */
//5

View File

@@ -0,0 +1,24 @@
#ifndef MIDIINPARSE_XH
#define MIDIINPARSE_XH
#define INITIAL 0
#define INCHANNEL_MSG 1
#define INSYSCOMMON_MSG 2
#define INSYSEX_MSG 3
struct midi_in_parse_state {
// State for the parser
unsigned expect_msg_len;
unsigned msg_type;
unsigned receivebuffer[3];
unsigned received;
unsigned codeIndexNumber;
};
void dump_midi_in_parse_state(struct midi_in_parse_state &s);
void reset_midi_state(struct midi_in_parse_state &mips);
{unsigned int , unsigned int} midi_in_parse(struct midi_in_parse_state &mips, unsigned cable_number, unsigned char b);
#endif

View File

@@ -0,0 +1,216 @@
/**
* @file midiinparse.xc
* @brief Generates USB MIDI events from MIDI events
* @author Russell Gallop, XMOS Semiconductor
*/
#include <print.h>
#include "midiinparse.h"
/**
* @brief Report state of the MIDI in parser (should be removed by deadcode elimination)
*
*/
void dump_midi_in_parse_state(struct midi_in_parse_state &s) {
printstr("expect_msg_len: 0x"); printhexln(s.expect_msg_len);
printstr("msg_type: 0x"); printhexln(s.msg_type);
printstr("receivebuffer: 0x"); printhex(s.receivebuffer[0]);
printstr(", 0x"); printhex(s.receivebuffer[1]);
printstr(", 0x"); printhexln(s.receivebuffer[2]);
printstr("received: 0x"); printhexln(s.received);
printstr("codeIndexNumber: 0x"); printhexln(s.codeIndexNumber);
}
/**
* @brief Reset state of MIDI parser
*
*/
void reset_midi_state(struct midi_in_parse_state &mips) {
mips.expect_msg_len = 0;
mips.msg_type = 0;
mips.receivebuffer[0] = 0;
mips.receivebuffer[1] = 0;
mips.receivebuffer[2] = 0;
mips.received = 0;
mips.codeIndexNumber = 0;
}
/**
* @brief Construct USB MIDI event
*
*/
static unsigned makeEvent(unsigned cable_number, unsigned codeIndexNumber, unsigned midi0, unsigned midi1, unsigned midi2) {
unsigned event = (cable_number << 28);
event |= (codeIndexNumber << 24);
event |= (midi0 << 16);
event |= (midi1 << 8);
event |= (midi2 << 0);
return event;
}
/**
* @ brief MIDI input parser
*
*/
{unsigned int , unsigned int} midi_in_parse(struct midi_in_parse_state &state, unsigned cable_number, unsigned char b) {
unsigned valid = 0;
unsigned data = 0xBADDF00D; // should never be returned along with valid = 1
unsigned highNibble = (b & 0xF0) >> 4;
unsigned lowNibble = (b & 0xF);
/*
assert(!(state.expect_msg_len && state.msg_type == INSYSEX_MSG));
assert((state.received >= 1) || (state.receivebuffer[0] == 0));
assert((state.received >= 2) || (state.receivebuffer[1] == 0));
assert((state.received == 3) || (state.receivebuffer[2] == 0));
assert(state.received < 3);
*/
if (b & 0x80) { // Is status byte
if (highNibble == 0xF) { // System message
if (lowNibble & 0x8) { // System real time
// System Real-Time Messages (can interleave system exclusive and between header and data (page 30 of complete MIDI spec))
//case 0x8: // Timing tick
//case 0x9: // Reserved
//case 0xA: // Start song
//case 0xB: // Continue song
//case 0xC: // Stop song
//case 0xD: // Reserved
//case 0xE: // Active sensing
//case 0xF: // System reset
// Have complete event, send out
valid = 1;
data = makeEvent(cable_number, highNibble, b, 0, 0);
} else {
if (b == 0xF7) { // End of SysEx
state.receivebuffer[state.received] = b;
state.received++;
// Compose sysex bytes that we've got and send them out.
// This will depend how many we have.
state.codeIndexNumber = state.received + 0x4;
valid = 1;
data = makeEvent(cable_number, state.codeIndexNumber,
state.receivebuffer[0], state.receivebuffer[1], state.receivebuffer[2]);
reset_midi_state(state);
} else {
reset_midi_state(state);
state.receivebuffer[state.received] = b;
state.received++;
switch (lowNibble)
{
case 0x2: // Song Position Pointer (3 byte system common)
{
state.msg_type = INSYSCOMMON_MSG;
state.expect_msg_len = 3;
state.codeIndexNumber = 3;
break;
}
case 0x1: // MIDI Time Code (2 byte system common)
case 0x3: // Song Select (2 byte system common)
{
state.msg_type = INSYSCOMMON_MSG;
state.expect_msg_len = 2;
state.codeIndexNumber = 2;
break;
}
case 0x6: // Tune request (1 byte system common)
state.codeIndexNumber = 5;
valid = 1;
data = makeEvent(cable_number, state.codeIndexNumber,
state.receivebuffer[0], state.receivebuffer[1], state.receivebuffer[2]);
break;
case 0x0: // Sysex start byte, never send based on just this
state.msg_type = INSYSEX_MSG;
break;
default:
// Could happen with unrecognised headers, e.g. 0xF4, 0xF5
// Just pass on
valid = 1;
data = makeEvent(cable_number, 0x0f, b, 0, 0);
reset_midi_state(state);
break;
}
}
}
} else { // Channel message
reset_midi_state(state);
state.receivebuffer[state.received] = b;
state.received++;
// code index number is always the high nibble for channel messages
state.codeIndexNumber = highNibble;
switch (highNibble)
{
case 0x8: // Note-off
case 0x9: // Note-on
case 0xA: // Poly-KeyPress
case 0xB: // Control Change
case 0xE: // PitchBend Change
{
state.msg_type = INCHANNEL_MSG;
state.expect_msg_len = 3;
break;
}
case 0xC: // Program Change
case 0xD: // Channel Pressure
{
state.msg_type = INCHANNEL_MSG;
state.expect_msg_len = 2;
break;
}
}
}
} else { // data byte
state.receivebuffer[state.received] = b;
state.received++;
switch (state.msg_type) {
case INCHANNEL_MSG:
case INSYSCOMMON_MSG:
{
if (state.received == state.expect_msg_len) {
valid = 1;
data = makeEvent(cable_number, state.codeIndexNumber,
state.receivebuffer[0], state.receivebuffer[1], state.receivebuffer[2]);
if (state.msg_type == INSYSCOMMON_MSG) {
// No running status on system common messages
reset_midi_state(state);
} else {
// Keep the first byte on channel messages, already received 1 byte
state.received = 1;
state.receivebuffer[1] = 0;
state.receivebuffer[2] = 0;
}
}
break;
}
case INSYSEX_MSG:
{
if ((state.received == 3)) {
// Output if have 3 using the SysEx starts or continues
state.codeIndexNumber = 0x4;
valid = 1;
data = makeEvent(cable_number, state.codeIndexNumber,
state.receivebuffer[0], state.receivebuffer[1], state.receivebuffer[2]);
// reset buffer but not msg_type
state.received = 0;
state.receivebuffer[0] = 0;
state.receivebuffer[1] = 0;
state.receivebuffer[2] = 0;
}
break;
}
default:
{
// Else data byte with no status so just send as single byte without parsing.
valid = 1;
data = makeEvent(cable_number, 0x0f, b, 0, 0);
reset_midi_state(state);
break;
}
}
}
return {valid, data};
}

View File

@@ -0,0 +1,6 @@
#ifndef MIDIOUTPARSE_XH
#define MIDIOUTPARSE_XH
{unsigned, unsigned, unsigned, unsigned} midi_out_parse(unsigned event);
#endif

View File

@@ -0,0 +1,71 @@
/**
* @file midioutparse.xc
* @brief Parses USB-MIDI events into set of MIDI bytes
* @author Russell Gallop, XMOS Semiconductor
*/
#include "midioutparse.h"
/**
* @brief Breaks a USB-MIDI event into it's constituant fields
*
* @param[in] ev USB-MIDI event
*/
#if 1
{unsigned, unsigned, unsigned, unsigned, unsigned} static breakEvent(unsigned ev) {
unsigned cable_number = (ev >> 28) & 0xf;
unsigned codeIndexNumber = (ev >> 24) & 0xf;
unsigned midi0 = (ev >> 16) & 0xff;
unsigned midi1 = (ev >> 8) & 0xff;
unsigned midi2 = (ev >> 0) & 0xff;
return {cable_number, codeIndexNumber, midi0, midi1, midi2};
}
#endif
/**
* @brief Parse a USB-MIDI event into the MIDI bytes and a length field
*
* @param[in] ev USB-MIDI event
*/
{unsigned, unsigned, unsigned, unsigned} midi_out_parse(unsigned event) {
unsigned cable_number; // ignore this for now!
unsigned codeIndexNumber;
unsigned midi[3];
unsigned size = 0;
{cable_number, codeIndexNumber, midi[0], midi[1], midi[2]} = breakEvent(event);
// Not doing anything with cable number
switch (codeIndexNumber) {
case 0x3: // Three-byte system Common messages like SPP, etc.
case 0x4: // SysEx starts or continues
case 0x7: // SysEx ends with the following three bytes
case 0x8: // Note-off
case 0x9: // Note-on
case 0xA: // Poly-KeyPress
case 0xB: // Control Change
case 0xE: // PitchBend Change
{
size = 3;
break;
}
case 0x2: // Two-byte system Common messages like MTC, SongSelect, etc.
case 0x6: // SysEx ends with the following two bytes
case 0xC: // Program Change
case 0xD: // Channel Pressure
{
size = 2;
break;
}
case 0x5: // Single-byte System Common Message or SysEx ends with following single byte.
case 0xF: // Single byte
{
size = 1;
break;
}
default:
break;
}
return {midi[0], midi[1], midi[2], size};
}

View File

@@ -0,0 +1,73 @@
#ifndef __usb_midi_h__
#define __usb_midi_h__
#include "devicedefines.h"
/** USB MIDI I/O thread.
*
* This function passes MIDI data from USB to 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.
* This should be set to 0.
**/
void usb_midi(
#if (MIDI_RX_PORT_WIDTH == 4)
buffered in port:4 ?p_midi_in,
#else
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
);
#define MAX_USB_MIDI_PACKET_SIZE 1024
#define MIDI_USB_BUFFER_FROM_HOST_FIFO_SIZE (512+1024)
#define MIDI_USB_BUFFER_TO_HOST_SIZE (256)
#define MIDI_ACK 20
#define USB_MIDI_DEVICE_OUT_FIFO_SIZE (1024)
#ifdef __MIDI_IMPL
#define INLINE
#else
#define INLINE inline
#endif
#ifdef NO_INLINE_MIDI_SELECT_HANDLER
#pragma select handler
void midi_get_ack_or_data(chanend c, int &is_ack, unsigned int &datum);
#else
#pragma select handler
INLINE void midi_get_ack_or_data(chanend c, int &is_ack, unsigned int &datum) {
if (testct(c)) {
is_ack = 1;
(void) inct(c); // read 1-bytes control token
(void) inuchar(c);
(void) inuchar(c);
(void) inuchar(c);
}
else {
is_ack = 0;
datum = inuint(c);
}
}
#endif
INLINE void midi_send_ack(chanend c) {
outct(c, MIDI_ACK);
outuchar(c, 0);
outuchar(c, 0);
outuchar(c, 0);
}
#define MIDI_RATE (31250)
#define MIDI_BITTIME (XS1_TIMER_MHZ * 1000000 / MIDI_RATE)
#define MIDI_BITTIME_2 (MIDI_BITTIME>>1)
#endif // __usb_midi_h__

View File

@@ -0,0 +1,373 @@
#include <xs1.h>
#include <xclib.h>
#include <print.h>
#include <stdint.h>
#include "usb_midi.h"
#include "midiinparse.h"
#include "midioutparse.h"
#include "queue.h"
#ifdef IAP
#include "iap.h"
#include "iap_user.h"
#include "coprocessor_user.h"
#endif
//#define MIDI_LOOPBACK 1
int icount = 0;
static unsigned makeSymbol(unsigned data)
{
// Start and stop bits to the data packet
// like 10'b1dddddddd0
return (data << 1) | 0x200;
}
#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;
// For debugging
int mr_count = 0; // MIDI received (from HOST)
int th_count = 0; // MIDI sent (To Host)
int uout_count = 0; // UART bytes out
int uin_count = 0; // UART bytes in
// state for iAP
#ifdef IAP
extern unsigned authenticating;
extern iap_in_buf iap_incoming_buffer;
extern iap_out_buf iap_outgoing_buffer;
#else
unsigned authenticating = 0;
#endif
// state for auto-selecting dock or USB B
extern unsigned polltime;
#ifdef IAP
timer iAPTimer;
#endif
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
void usb_midi(
#if (MIDI_RX_PORT_WIDTH == 4)
buffered in port:4 ?p_midi_in,
#else
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
)
{
unsigned symbol = 0x0; // Symbol in progress of being sent out
unsigned isTX = 0; // Guard when outputting data
unsigned txT; // Timer value used for outputting
//unsigned inputPortState, newInputPortState;
int waiting_for_ack = 0;
// Receiver
unsigned rxByte;
int rxI;
int rxT;
int isRX = 0; // Guard when receiving data
timer t;
timer t2;
// One place buffer for data going out to host
queue_t midi_to_host_fifo;
unsigned midi_to_host_fifo_arr[1]; // Used for 32bit USB MIDI events
unsigned outputting_symbol, outputted_symbol;
struct midi_in_parse_state mips;
// the symbol fifo (to go out of uart)
queue_t symbol_fifo;
unsigned symbol_fifo_arr[USB_MIDI_DEVICE_OUT_FIFO_SIZE]; // Used for 32bit USB MIDI events
unsigned rxPT, txPT;
int midi_from_host_overflow = 0;
//configure_clock_rate(clk_midi, 100, 1);
queue_init(symbol_fifo, ARRAY_SIZE(symbol_fifo_arr));
queue_init(midi_to_host_fifo, ARRAY_SIZE(midi_to_host_fifo_arr));
configure_out_port(p_midi_out, clk_midi, 1<<MIDI_SHIFT_TX);
configure_in_port(p_midi_in, clk_midi);
/* Just in case not using CLKBLK_REF */
start_clock(clk_midi);
reset_midi_state(mips);
t :> txT;
t2 :> rxT;
#ifdef IAP
CoProcessorDisable();
#endif
// p_midi_out <: 1 << MIDI_SHIFT_TX; // Start with high bit.
#ifdef IAP
CoProcessorEnable();
#endif
#ifdef IAP
/* Check for special case where MIDI and i2c ports are shared... */
if(isnull(c_i2c) && isnull(p_scl) && isnull(p_sda))
{
init_iAP(iap_incoming_buffer, iap_outgoing_buffer, null, null, null); // uses timer for i2c initialisation pause..
}
else
{
init_iAP(iap_incoming_buffer, iap_outgoing_buffer, c_i2c, p_scl, p_sda); // uses timer for i2c initialisation pause..
}
#endif
{
#ifdef IAP
iAPTimer :> polltime;
polltime += XS1_TIMER_HZ / 2;
SelectUSBPc(); // Select the PC connector to begin with, as we cannot actively detect connections to the USB B
#endif
while (1)
{
int is_ack;
int is_reset;
unsigned int datum;
select
{
// Input to read the start bit
#ifndef MIDI_LOOPBACK
case (!authenticating && !isRX) => p_midi_in when pinseq(0) :> void @ rxPT:
isRX = 1;
t2 :> rxT;
rxT += (bit_time + bit_time_2);
rxPT += (bit_time + bit_time_2); // absorb start bit and set to halfway through the next bit
rxI = 0;
asm("setc res[%0],1"::"r"(p_midi_in));
asm("setpt res[%0],%1"::"r"(p_midi_in),"r"(rxPT));
break;
// Input to read the remaining bits
case (!authenticating && isRX) => t2 when timerafter(rxT) :> int _ :
{
unsigned bit;
p_midi_in :> bit;
if (rxI++ < 8)
{
// shift in bits into the high end of a word
rxByte = (bit << 31) | (rxByte >> 1);
rxT += bit_time;
rxPT += bit_time;
asm("setpt res[%0],%1"::"r"(p_midi_in),"r"(rxPT));
}
else
{
// rcv and check stop bit
if ((bit & 0x1) == 1)
{
unsigned valid = 0;
unsigned event = 0;
uin_count++;
rxByte >>= 24;
#if 0
// Loopback check
if ((rxByte != outputted_symbol))
{
printhexln(rxByte);
printhexln(outputted_symbol);
}
#endif
{valid, event} = midi_in_parse(mips, cable_number, rxByte);
if (valid && queue_is_empty(midi_to_host_fifo))
{
event = byterev(event);
// data to send to host - add to fifo
if (!waiting_for_ack)
{
// send data
// printstr("uart->decouple: ");
outuint(c_midi, event);
waiting_for_ack = 1;
th_count++;
}
else
{
queue_push_word(midi_to_host_fifo, midi_to_host_fifo_arr, event);
}
}
else if (valid)
{
//printstr("g");
}
}
isRX = 0;
}
break;
}
// Output
// If isTX then feed the bits out one at a time
// until symbol is zero expect pattern like 10'b1dddddddd0
// This code will leave the output high afterwards due to the stop bit added with makeSymbol
case (!authenticating && isTX) => t when timerafter(txT) :> int _:
if (symbol == 0)
{
// Got something to output but not mid-symbol.
// Start sending symbol.
// This case is reached when a symbol has been received from the host but not started AND
// When it has just finished sending a symbol
// Take from FIFO
outputting_symbol = queue_pop_word(symbol_fifo, symbol_fifo_arr);
symbol = makeSymbol(outputting_symbol);
if (queue_space(symbol_fifo) > 3 && midi_from_host_overflow)
{
midi_from_host_overflow = 0;
midi_send_ack(c_midi);
}
p_midi_out <: (1<<MIDI_SHIFT_TX) @ txPT;
// printstr("mout1\n");
t :> txT;
txT += bit_time;
txPT += bit_time;
isTX = 1;
}
else
{
// Mid-symbol
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)<<MIDI_SHIFT_TX);
// printstr("mout2\n");
symbol >>= 1;
if (symbol == 0)
{
// Finished sending byte
uout_count++;
outputted_symbol = outputting_symbol;
if (queue_is_empty(symbol_fifo))
{ // FIFO empty
isTX = 0;
}
}
}
break;
#endif
case !authenticating => midi_get_ack_or_data(c_midi, is_ack, datum):
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++;
}
else
{
waiting_for_ack = 0;
}
}
else
{
unsigned midi[3];
unsigned size;
// received data from host
int event = byterev(datum);
mr_count++;
#ifdef MIDI_LOOPBACK
if (queue_is_empty(midi_to_host_fifo))
{
// data to send to host
if (!waiting_for_ack)
{
// send data
event = byterev(event);
outuint(c_midi, event);
th_count++;
waiting_for_ack = 1;
}
else
{
event = byterev(event);
queue_push_word(midi_to_host_fifo, midi_to_host_fifo_arr, event);
}
midi_send_ack(c_midi);
}
else
{
//printstr("DROP\n");
}
#else
{midi[0], midi[1], midi[2], size} = midi_out_parse(event);
for (int i = 0; i != size; i++)
{
// add symbol to fifo
queue_push_word(symbol_fifo, symbol_fifo_arr, midi[i]);
}
if (queue_space(symbol_fifo) > 3)
{
midi_send_ack(c_midi);
}
else
{
midi_from_host_overflow = 1;
}
// Drop through to the isTX guarded case
if (!isTX)
{
t :> txT; // Should be enough to trigger the other case
isTX = 1;
}
#endif
}
break;
#ifdef IAP
case !(isTX || isRX) => iap_get_ack_or_reset_or_data(c_iap, is_ack, is_reset, datum):
/* Check for special case where MIDI ports are shared with i2c ports */
if(isnull(c_i2c) && isnull(p_scl) && isnull(p_sda))
{
iap_handle_ack_or_reset_or_data(iap_incoming_buffer, iap_outgoing_buffer, is_ack, is_reset, datum, c_iap, null, null, null);
}
else
{
iap_handle_ack_or_reset_or_data(iap_incoming_buffer, iap_outgoing_buffer, is_ack, is_reset, datum, c_iap, c_i2c, p_scl, p_sda);
}
if (!authenticating)
{
// printstrln("Completed authentication");
p_midi_in :> void; // Change port around to input again after authenticating (unique to midi+iAP case)
}
break;
/* Slow timer looking for IDevice plug/unplug event */
case iAPTimer when timerafter(polltime) :> void:
if (!iap_handle_poll_dev_det(iap_incoming_buffer, iap_outgoing_buffer))
{
check_iAP_timeout(iap_outgoing_buffer, c_iap);
}
break;
#endif
}
}
}
}

View File

@@ -0,0 +1,3 @@
#include <xs1.h>
#define __MIDI_IMPL
#include "usb_midi.h"

Some files were not shown because too many files have changed in this diff Show More