commit 02b07ed9f3d3c61fb1ad00874d75d931e212be0e Author: Dave Lacey Date: Thu Jul 7 20:15:51 2011 +0100 First commit diff --git a/.cproject b/.cproject new file mode 100644 index 00000000..3ddfeef5 --- /dev/null +++ b/.cproject @@ -0,0 +1,172 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +xmake +-f .makefile +all +true +false +true + + +xmake +-f Makefile +app_uart_test.all +true +true +true + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..1dce509a --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +*/.build_*/* +*/bin/* +*.o +*.xe +*.vcd +*.s +*.xi +*.i +*.a diff --git a/.project b/.project new file mode 100644 index 00000000..ad56f8b6 --- /dev/null +++ b/.project @@ -0,0 +1,81 @@ + + + sc_usb_audio + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + ?name? + + + + org.eclipse.cdt.make.core.append_environment + true + + + org.eclipse.cdt.make.core.autoBuildTarget + all + + + org.eclipse.cdt.make.core.buildArguments + -f Makefile + + + org.eclipse.cdt.make.core.buildCommand + xmake + + + org.eclipse.cdt.make.core.buildLocation + ${workspace_loc:/sc_usb_audio} + + + org.eclipse.cdt.make.core.cleanBuildTarget + clean + + + org.eclipse.cdt.make.core.contents + org.eclipse.cdt.make.core.activeConfigSettings + + + org.eclipse.cdt.make.core.enableAutoBuild + false + + + org.eclipse.cdt.make.core.enableCleanBuild + true + + + org.eclipse.cdt.make.core.enableFullBuild + true + + + org.eclipse.cdt.make.core.fullBuildTarget + all + + + org.eclipse.cdt.make.core.stopOnError + true + + + org.eclipse.cdt.make.core.useDefaultBuildCmd + false + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.core.cnature + + diff --git a/README.rst b/README.rst new file mode 100644 index 00000000..aff6e5e3 --- /dev/null +++ b/README.rst @@ -0,0 +1,11 @@ +USB Audio Shared +................ + +:Stable release: unreleased (apart from within usb audio ref designs) + +:Status: Feature complete, mature + +:Maintainer: XMOS + +:Description: USB Audio Shared Components + diff --git a/module_dfu/README b/module_dfu/README new file mode 100644 index 00000000..248e6492 --- /dev/null +++ b/module_dfu/README @@ -0,0 +1 @@ +Code providing firmware upgrade over USB. diff --git a/module_dfu/module_build_info b/module_dfu/module_build_info new file mode 100644 index 00000000..be27f1e8 --- /dev/null +++ b/module_dfu/module_build_info @@ -0,0 +1,46 @@ +# 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) + +# 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) + +ifeq ($(ARCH_G),) +ARCH_FLAG = -D ARCH_L +else +ARCH_FLAG = -D ARCH_G +endif + +$(call SET_XCC_C_FLAGS, $(MODULE_DIRS) $(XCC_FLAGS) $(ARCH_FLAG) -g -O3) +$(call SET_XCC_XC_FLAGS, $(MODULE_DIRS), $(XCC_FLAGS) $(ARCH_FLAG) -fsubword-select -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 += myfile.xc +# diff --git a/module_dfu/module_description b/module_dfu/module_description new file mode 100644 index 00000000..248e6492 --- /dev/null +++ b/module_dfu/module_description @@ -0,0 +1 @@ +Code providing firmware upgrade over USB. diff --git a/module_dfu/src/dfu.h b/module_dfu/src/dfu.h new file mode 100644 index 00000000..cecb9e36 --- /dev/null +++ b/module_dfu/src/dfu.h @@ -0,0 +1,188 @@ +#ifndef DFU_VENDOR_ID +#define DFU_VENDOR_ID 0x20B1 +#endif + +//#ifndef DFU_PRODUCT_ID +//#define DFU_PRODUCT_ID 0x0003 +//#error DFU_PRODUCT_ID not defined! +//#endif + +#ifndef PID_DFU +#error PID_DFU not defined! +#endif + +#ifndef DFU_BCD_DEVICE +#warning DFU_BCD_DEVICE NOT DEFINED +#define DFU_BCD_DEVICE 0x0000 +#endif + +#ifndef DFU_SERIAL_STR_INDEX +#error DFU_SERIAL_STR_INDEX NOT DEFINED! +//#define DFU_SERIAL_STR_INDEX 0x00 +#endif + +#ifndef DFU_PRODUCT_INDEX +#error DFU_PROFUCT_INDEX NOT DEFINED! +//#define DFU_PRODUCT_INDEX 0x1 +#endif + +#ifndef DFU_MANUFACTURER_INDEX +#error DFU_MANUFACTURING_INDEX NOT DEFINED! +//#define DFU_MANUFACTURER_INDEX 0x2 +#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 */ + (PID_DFU & 0xFF), /* 10 idProduct */ + (PID_DFU >> 8), /* 11 idProduct */ + (DFU_BCD_DEVICE & 0xFF), /* 12 bcdDevice : Device release number */ + (DFU_BCD_DEVICE >> 8), /* 13 bcdDevice : Device release number */ + DFU_MANUFACTURER_INDEX, /* 14 iManufacturer : Index of manufacturer string */ + DFU_PRODUCT_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 */ + 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) */ + +#if 0 + /* DFU 1.0 Standard DFU class functional descriptor */ + 0x07, + 0x21, + 0x07, + 0xFA, + 0x00, + 0x40, + 0x00 +#else + /* 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, /* 7 bcdDFUVersion */ +#endif + + +}; + +unsigned char DFUoSpeedCfgDesc[] = +{ + /* Standard USB device descriptor */ + 0x09, /* 0 bLength */ + OTHER_SPEED_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, /* 7 bcdDFUVersion */ +}; + +unsigned char DFUdevQualDesc[] = +{ + 10, /* 0 bLength : Size of descriptor in Bytes (18 Bytes) */ + DEVICE_QUALIFIER, /* 1 bdescriptorType */ + 0, /* 2 bcdUSB */ + 2, /* 3 bcdUSB */ + 0xfe, /* 4 bDeviceClass */ + 1, /* 5 bDeviceSubClass */ + 0, /* 6 bDeviceProtocol */ + 64, /* 7 bMaxPacketSize */ + 0x01, /* 8 bNumConfigurations : Number of possible configs */ \ + 0x00 /* 9 bReserved (must be zero) */ \ +}; + +int DFUReportResetState(chanend ?c_user_cmd); +int DFUDeviceRequests(XUD_ep c_ep0_out, XUD_ep &?ep0_in, SetupPacket &sp, chanend ?c_user_cmd, unsigned int altInterface, unsigned int user_reset); + +// Overload the USB user command handler if present +extern unsigned int flash_programmer(unsigned int cmd, unsigned int request_data[16]); +void user_cmd_handler(unsigned int cmd, chanend ?c_user_cmd) { + + if (!isnull(c_user_cmd)) { + unsigned int request_data_len = 0; + unsigned int request_data[16]; + unsigned int return_data_len = 0; + int i = 0; + + // Read request data length + request_data_len = inuint(c_user_cmd); + + // Read request data + for (i = 0; i < request_data_len; i++) { + request_data[i] = inuint(c_user_cmd); + } + + return_data_len = flash_programmer(cmd, request_data); + + outuint(c_user_cmd, return_data_len); + + if (return_data_len) { + for (i = 0; i < return_data_len/4; i++) { + outuint(c_user_cmd, request_data[i]); + } + } + } +} + + + + diff --git a/module_dfu/src/dfu.xc b/module_dfu/src/dfu.xc new file mode 100644 index 00000000..dc012c08 --- /dev/null +++ b/module_dfu/src/dfu.xc @@ -0,0 +1,460 @@ +#include +#include +#include + +#include "xud.h" +#ifdef ARCH_G +#include "XUD_USB_Defines.h" +#else +#include "usb.h" +#endif + +#include "dfu_types.h" +#include "flash_programmer.h" + +static int 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 int DFU_reset_override; + +void temp() { + asm(".linkset DFU_reset_override, _edp.bss"); + asm(".globl DFU_reset_override"); +} + +static int DFU_OpenFlash(chanend ?c_user_cmd) +{ + if (!DFU_flash_connected) + { + unsigned int cmd_data[16]; + HandleUserDeviceRequest(FLASH_CMD_INIT, 1, 0, cmd_data, c_user_cmd); + DFU_flash_connected = 1; + } + + return 0; +} + +static int DFU_CloseFlash(chanend ?c_user_cmd) { + if (DFU_flash_connected) { + unsigned int cmd_data[16]; + HandleUserDeviceRequest(FLASH_CMD_DEINIT, 1, 0, cmd_data, c_user_cmd); + DFU_flash_connected = 0; + } + return 0; +} + +static int DFU_Detach(unsigned int timeout, chanend ?c_user_cmd) { + if (DFU_state == STATE_APP_IDLE) { + + DFU_state = STATE_APP_DETACH; + + DFU_OpenFlash(c_user_cmd); + + // 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, unsigned int request_data[16], chanend ?c_user_cmd) { + unsigned int fromDfuIdle = 0; + + // 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 + + DFU_OpenFlash(c_user_cmd); + + switch (DFU_state) { + case STATE_DFU_IDLE: + case STATE_DFU_DOWNLOAD_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) { + 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++) { + HandleUserDeviceRequest(FLASH_CMD_WRITE_PAGE_DATA, 1, 64, subPagePad, c_user_cmd); + } + } + + cmd_data[0] = 2; // Terminate write + HandleUserDeviceRequest(FLASH_CMD_WRITE_PAGE, 1, 4, cmd_data, c_user_cmd); + + 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; + +#if 0 + /* Flash lib does erase on add image */ + // Erase flash on first block + HandleUserDeviceRequest(FLASH_CMD_ERASE_ALL, 1, 0, cmd_data, c_user_cmd); +#endif + +#if 0 + /* Delay should not be required. Erase now blocking call */ + DFUTimer :> s; + DFUTimer when timerafter(s + 25000000) :> s; // Wait for flash erase +#endif + } + + // 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. + HandleUserDeviceRequest(FLASH_CMD_WRITE_PAGE, 1, 4, cmd_data, c_user_cmd); + subPagesLeft = 4; + } + + for (i = 0; i < 16; i++) { + cmd_data[i] = request_data[i]; + } + + HandleUserDeviceRequest(FLASH_CMD_WRITE_PAGE_DATA, 1, 64, cmd_data, c_user_cmd); + subPagesLeft--; + + DFU_state = STATE_DFU_DOWNLOAD_SYNC; + } + + return 0; +} + + +static int DFU_Upload(unsigned int request_len, unsigned int block_num, unsigned int request_data[16], chanend ?c_user_cmd) { + unsigned int cmd_data[16]; + 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(c_user_cmd); + + switch (DFU_state) { + case STATE_DFU_IDLE: + case STATE_DFU_UPLOAD_IDLE: + break; + default: + DFU_state = STATE_DFU_ERROR; + return 0; + } + + //printintln(request_len); + + 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 page + HandleUserDeviceRequest(FLASH_CMD_READ_PAGE, 0, 4, cmd_data, c_user_cmd); + 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; + } + } + + // Read page data + HandleUserDeviceRequest(FLASH_CMD_READ_PAGE_DATA, 0, 0, request_data, c_user_cmd); + + subPagesLeft--; + + DFU_state = STATE_DFU_UPLOAD_IDLE; + + return 64; +} + +static int DFU_GetStatus(unsigned int request_len, unsigned int request_data[16], chanend ?c_user_cmd) { + unsigned int timeout = 0; + + request_data[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; + } + + request_data[1] = DFU_state; + + return 6; +} + +static int DFU_ClrStatus(void) { + 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) { + + 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(void) { + 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; + + if (DFU_reset_override == 0x11042011) { + unsigned int cmd_data[16]; + inDFU = 1; + DFU_state = STATE_DFU_IDLE; + return inDFU; + } + + switch(DFU_state) { + case STATE_APP_DETACH: + case STATE_DFU_IDLE: + DFU_state = STATE_DFU_IDLE; + + DFUTimer :> currentTime; + if (currentTime - DFUTimerStart > DFUResetTimeout) { + DFU_state = STATE_APP_IDLE; + //printintln(currentTime - DFUTimerStart); + //printintln(DFUResetTimeout); + 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; + DFU_state = STATE_APP_IDLE; + break; + default: + DFU_state = STATE_DFU_ERROR; + inDFU = 1; + break; + } + + if (!inDFU) { + DFU_CloseFlash(c_user_cmd); + } + + return inDFU; +} + +int XMOS_DFU_RevertFactory(chanend ?c_user_cmd) { + unsigned int cmd_data[16]; + unsigned s = 0; + + DFU_OpenFlash(c_user_cmd); + + HandleUserDeviceRequest(FLASH_CMD_ERASE_ALL, 1, 0, cmd_data, c_user_cmd); + + DFUTimer :> s; + DFUTimer when timerafter(s + 25000000) :> s; // Wait for flash erase + + return 0; +} + +int XMOS_DFU_SelectImage(unsigned int index, chanend ?c_user_cmd) { + + // Select the image index for firmware update + + return 0; + +} + + +int XMOS_DFU_SaveState() +{ + return 0; +} + +int XMOS_DFU_LoadState() +{ + return 0; +} + +int DFUDeviceRequests(XUD_ep ep0_out, XUD_ep &?ep0_in, SetupPacket &sp, chanend ?c_user_cmd, unsigned int altInterface, unsigned int user_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; + + if(sp.bmRequestType.Direction == BM_REQTYPE_DIRECTION_OUT) + { // Host to device + if (sp.wLength) +#ifdef ARCH_G + data_buffer_len = XUD_GetBuffer_(ep0_out, 0, (data_buffer, unsigned char[])); +#else + data_buffer_len = XUD_GetBuffer(ep0_out, (data_buffer, unsigned char[])); +#endif + } + + // Map Standard DFU commands onto device level firmware upgrade mechanism + switch (sp.bRequest) { + case DFU_DETACH: + return_data_len = DFU_Detach(sp.wValue, c_user_cmd); + break; + case DFU_DNLOAD: + return_data_len = DFU_Dnload(sp.wLength, sp.wValue, data_buffer, c_user_cmd); + break; + case DFU_UPLOAD: + return_data_len = DFU_Upload(sp.wLength, sp.wValue, data_buffer, c_user_cmd); + break; + case DFU_GETSTATUS: + return_data_len = DFU_GetStatus(sp.wLength, data_buffer, c_user_cmd); + break; + case DFU_CLRSTATUS: + return_data_len = DFU_ClrStatus(); + break; + case DFU_GETSTATE: + return_data_len = DFU_GetState(sp.wLength, data_buffer, c_user_cmd); + break; + case DFU_ABORT: + return_data_len = DFU_Abort(); + 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; + } + + if (sp.bmRequestType.Direction == BM_REQTYPE_DIRECTION_IN && sp.wLength != 0) { // Device to host +#ifdef ARCH_G + XUD_DoGetRequest(ep0_out, 0, (data_buffer, unsigned char[]), return_data_len, return_data_len); +#else + XUD_DoGetRequest(ep0_out, ep0_in, (data_buffer, unsigned char[]), return_data_len, return_data_len); +#endif + } else { +#ifdef ARCH_G + XUD_DoSetRequestStatus(ep0_out, 0); +#else + XUD_DoSetRequestStatus(ep0_in, 0); +#endif + } + + // If device reset requested, handle after command acknowledgement + if (reset_device_after_ack) + { + if (!user_reset) + { + unsigned int cmd_data[16]; + HandleUserDeviceRequest(FLASH_CMD_REBOOT, 1, 0, cmd_data, c_user_cmd); + } + else + { + return 1; + } + } + + return 0; +} diff --git a/module_dfu/src/dfu_types.h b/module_dfu/src/dfu_types.h new file mode 100644 index 00000000..6ab5d178 --- /dev/null +++ b/module_dfu/src/dfu_types.h @@ -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. diff --git a/module_dfu/src/flash_interface.c b/module_dfu/src/flash_interface.c new file mode 100755 index 00000000..41447829 --- /dev/null +++ b/module_dfu/src/flash_interface.c @@ -0,0 +1,218 @@ +#include +#include +#include +#include +#include +#include + +#ifndef FLASH_MAX_UPGRADE_SIZE +#ifdef ARCH_G +#define FLASH_MAX_UPGRADE_SIZE 128 * 1024 // 128K default +#else +//#define FLASH_MAX_UPGRADE_SIZE 64 * 1024 // 64K default +#define FLASH_MAX_UPGRADE_SIZE 128 * 1024 // 64K default +#endif +#endif + +#if 1 +#define FLASH_ERROR() do { printstr("Error: line: "); printintln(__LINE__); __builtin_trap(); } while(0) +#else +#define FLASH_ERROR() do {} while(0) +#endif + +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; +} + +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 0; + + // Disable flash protection + fl_setProtection(0); + + if (fl_getFactoryImage(&image) != 0) { + return 0; + } + 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_readImageRead(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 = ¤t_flash_page_data[current_flash_subpage_index * 64]; + memcpy(data, page_data_ptr, 64); + + current_flash_subpage_index++; + + return 64; +} + +#if 0 +static int roundUpToSectorBoundary(unsigned address) +{ + unsigned curAddress = 0; + unsigned numSectors = fl_getNumSectors(); + unsigned i; + for (i = 0; i < numSectors; i++) { + curAddress += fl_getSectorSize(i); + if (curAddress >= address) + return curAddress; + } + return address; +} +#endif + +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; + //printstr("flash_cmd_write_page("); + //printint(flag); + //printstr(")\n"); + + 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; +} + +static int isAllOnes(unsigned char page[256]) +{ + unsigned i; + for (i = 0; i < 256; i++) { + if (page[i] != 0xff) + return 0; + } + return 1; +} + +int flash_cmd_write_page_data(unsigned char *data) { + unsigned char *page_data_ptr = ¤t_flash_page_data[current_flash_subpage_index * 64]; + //printstr("flash_cmd_write_page_data()\n"); + + 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 (isAllOnes(data)) + FLASH_ERROR(); + if (fl_writeImagePage(current_flash_page_data) != 0) + FLASH_ERROR(); + pages_written++; + } + + return 0; +} + +int flash_cmd_erase_all(void) { + if (upgrade_image_valid) { + if (fl_deleteImage(&upgrade_image) != 0) + FLASH_ERROR(); + upgrade_image_valid = 0; + } + return 0; +} + +int flash_cmd_reboot(void) { + unsigned int pllVal; + read_sswitch_reg(0, 6, &pllVal); + write_sswitch_reg(0, 6, pllVal); + return 0; +} diff --git a/module_dfu/src/flash_interface.h b/module_dfu/src/flash_interface.h new file mode 100755 index 00000000..3bf0f7c9 --- /dev/null +++ b/module_dfu/src/flash_interface.h @@ -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_*/ diff --git a/module_dfu/src/flash_programmer.h b/module_dfu/src/flash_programmer.h new file mode 100755 index 00000000..642c81ce --- /dev/null +++ b/module_dfu/src/flash_programmer.h @@ -0,0 +1,14 @@ +#define FLASH_CMD_WRITE_PAGE 0xf1 +#define FLASH_CMD_READ_PAGE 0xf2 +#define FLASH_CMD_WRITE_PAGE_DATA 0xf3 +#define FLASH_CMD_READ_PAGE_DATA 0xf4 +#define FLASH_CMD_ERASE_ALL 0xf5 +#define FLASH_CMD_REBOOT 0xf6 +#define FLASH_CMD_INIT 0xf7 +#define FLASH_CMD_DEINIT 0xf8 + +unsigned int flash_programmer(unsigned int cmd, unsigned int request_data[16]); +int HandleUserDeviceRequest(unsigned int cmd, unsigned int to_device, + unsigned int request_size, unsigned int request_data[16], + chanend ?c_user_cmd); + diff --git a/module_dfu/src/flash_programmer.xc b/module_dfu/src/flash_programmer.xc new file mode 100755 index 00000000..afa33639 --- /dev/null +++ b/module_dfu/src/flash_programmer.xc @@ -0,0 +1,82 @@ +#include +#include +#include "flash_interface.h" +#include "flash_programmer.h" + +unsigned int flash_programmer(unsigned int cmd, unsigned int request_data[16]) { + unsigned int return_data_len = 0; + + switch (cmd) { + case FLASH_CMD_WRITE_PAGE: + return_data_len = flash_cmd_write_page((request_data, unsigned char[64])); + break; + + case FLASH_CMD_READ_PAGE: + return_data_len = flash_cmd_read_page((request_data, unsigned char[64])); + break; + + case FLASH_CMD_WRITE_PAGE_DATA: + return_data_len = flash_cmd_write_page_data((request_data, unsigned char[64])); + break; + + case FLASH_CMD_READ_PAGE_DATA: + return_data_len = flash_cmd_read_page_data((request_data, unsigned char[64])); + break; + + case FLASH_CMD_ERASE_ALL: + return_data_len = flash_cmd_erase_all(); + break; + + case FLASH_CMD_REBOOT: + return_data_len = flash_cmd_reboot(); + break; + + case FLASH_CMD_INIT: + return_data_len = flash_cmd_init(); + break; + + case FLASH_CMD_DEINIT: + return_data_len = flash_cmd_deinit(); + break; + + default: + break; + } + + return return_data_len; +} + +int HandleUserDeviceRequest(unsigned int cmd, unsigned int to_device, + unsigned int request_size, unsigned int request_data[16], + chanend ?c_user_cmd) { + + unsigned int return_data_len = 0; + + if (isnull(c_user_cmd)) { + return_data_len = flash_programmer(cmd, request_data); + } else { + int i = 0; + + outuint(c_user_cmd, cmd); + outuint(c_user_cmd, request_size/4); + + if (request_size) { + // Send request data to user cmd handler + for (i = 0; i < request_size/4; i++) { + outuint(c_user_cmd, request_data[i]); + } + } + + return_data_len = inuint(c_user_cmd); + + if (return_data_len) { + // Get response data from user cmd handler + for (i = 0; i < return_data_len/4; i++) { + request_data[i] = inuint(c_user_cmd); + } + } + } + + return return_data_len; +} + diff --git a/module_usb_aud_shared/README b/module_usb_aud_shared/README new file mode 100644 index 00000000..b5e23a40 --- /dev/null +++ b/module_usb_aud_shared/README @@ -0,0 +1 @@ +Shared USB Audio Files diff --git a/module_usb_aud_shared/audio.h b/module_usb_aud_shared/audio.h new file mode 100644 index 00000000..8fc0568d --- /dev/null +++ b/module_usb_aud_shared/audio.h @@ -0,0 +1,18 @@ +#ifndef __audio_h__ +#define __audio_h__ + +/** 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, chanend ?c_dig, chanend ?c_config); + +#endif // __audio_h__ diff --git a/module_usb_aud_shared/audio.xc b/module_usb_aud_shared/audio.xc new file mode 100755 index 00000000..42cbd908 --- /dev/null +++ b/module_usb_aud_shared/audio.xc @@ -0,0 +1,621 @@ +/** + * @file audio.xc + * @brief XMOS L1/L2 USB 2,0 Audio Reference Design. Audio Functions. + * @author Ross Owen, XMOS Semiconductor Ltd + * + * This thread handles I2S and pars an additional SPDIF Tx thread. It forwards samples to the SPDIF Tx thread. + * Additionally this thread handles clocking and CODEC/DAC/ADC config. + **/ + +#include +#include +#include +#include +#include + +#include "clocking.h" +#include "audioports.h" +#include "codec.h" +#include "devicedefines.h" +#include "SpdifTransmit.h" + +//#define RAMP_CHECK 1 + +//#pragma xta command "analyse path i2s_output_l i2s_output_r" +//#pragma xta command "set required - 2000 ns" + +//#pragma xta command "analyse path i2s_output_r i2s_output_l" +//#pragma xta command "set required - 2000 ns" + +/* I2S Data I/O*/ +#if (I2S_CHANS_DAC != 0) +extern buffered out port:32 p_i2s_dac[I2S_WIRES_DAC]; +#endif + +#if (I2S_CHANS_ADC != 0) +extern buffered in port:32 p_i2s_adc[I2S_WIRES_ADC]; +#endif + +/* I2S LR/Bit clock I/O */ +#ifdef CODEC_SLAVE +extern buffered out port:32 p_lrclk; +extern buffered out port:32 p_bclk; +#else +extern in port p_lrclk; +extern in port p_bclk; +#endif + +/* Master clock input */ +extern port p_mclk; + +#ifdef SPDIF +extern buffered out port:32 p_spdif_tx; +#endif + +extern clock clk_audio_mclk; +extern clock clk_audio_bclk; +extern clock clk_mst_spd; + +extern void device_reboot(void); + +/* I2S delivery thread */ +#pragma unsafe arrays +unsigned deliver(chanend c_out, chanend c_spd_out, unsigned divide, chanend ?c_dig_rx) +{ + unsigned sample; +#if NUM_USB_CHAN_OUT > 0 + unsigned samplesOut[NUM_USB_CHAN_OUT]; +#endif +#if NUM_USB_CHAN_IN > 0 + unsigned samplesIn[NUM_USB_CHAN_IN]; + unsigned samplesInPrev[NUM_USB_CHAN_IN]; +#endif + unsigned tmp; + unsigned index; +#ifdef RAMP_CHECK + unsigned prev=0; + int started = 0; +#endif + +#if NUM_USB_CHAN_IN > 0 + for (int i=0;i 0 +#pragma loop unroll + for(int i = 0; i < NUM_USB_CHAN_IN; i++) + { + outuint(c_out, samplesIn[i]); + } +#endif + +#if NUM_USB_CHAN_OUT > 0 +#pragma loop unroll + for(int i = 0; i < NUM_USB_CHAN_OUT; i++) + { + samplesOut[i] = inuint(c_out); + } +#endif +#else +#pragma loop unroll + for(int i = 0; i < NUM_USB_CHAN_OUT; i++) + { + int tmp = inuint(c_out); +#if defined(OUT_VOLUME_IN_MIXER) && defined(OUT_VOLUME_AFTER_MIX) + tmp<<=3; +#endif + samplesOut[i] = tmp; + } + +#pragma loop unroll + for(int i = 0; i < NUM_USB_CHAN_IN; i++) + { + outuint(c_out, samplesIn[i]); + } +#endif + } + +#ifdef CODEC_SLAVE + /* Clear I2S port buffers */ + clearbuf(p_lrclk); + +#if (I2S_CHANS_DAC != 0) + for(int i = 0; i < I2S_WIRES_DAC; i++) + { + clearbuf(p_i2s_dac[i]); + } +#endif + +#if (I2S_CHANS_ADC != 0) + for(int i = 0; i < I2S_WIRES_ADC; i++) + { + clearbuf(p_i2s_adc[i]); + } +#endif + + if(divide == 1) + { + p_lrclk <: 0 @ tmp; + tmp += 30; + + /* Prefill the ports so data starts to be input */ +#if (I2S_CHANS_DAC != 0) +#pragma loop unroll + for(int i = 0; i < I2S_WIRES_DAC; i++) + { + p_i2s_dac[i] @ tmp <: 0; + } +#endif + + p_lrclk @ tmp <: 0x7FFFFFFF; + + +#if (I2S_CHANS_ADC != 0) + p_i2s_adc[0] @ (tmp - 1) :> void; +#endif + + +#if (I2S_CHANS_ADC != 0) +#pragma loop unroll + for(int i = 0; i < I2S_WIRES_ADC; i++) + { + clearbuf(p_i2s_adc[i]); + } +#endif + } + else + { + clearbuf(p_bclk); + + +#if (I2S_CHANS_DAC != 0) + /* Prefill the ports so data is input in advance */ + for(int i = 0; i < I2S_WIRES_DAC; i++) + { + p_i2s_dac[i] <: 0; + } +#endif + + p_lrclk <: 0x7FFFFFFF; + p_bclk <: 0xAAAAAAAA; + p_bclk <: 0xAAAAAAAA; + } +#else + /* CODEC is master */ + /* Wait for LRCLK edge */ + p_lrclk when pinseq(0) :> void; + p_lrclk when pinseq(1) :> void; + p_lrclk when pinseq(0) :> void; + p_lrclk when pinseq(1) :> void; + p_lrclk when pinseq(0) :> void @ tmp; + tmp += 33; + +#if (I2S_CHANS_DAC != 0) +#pragma loop unroll + for(int i = 0; i < I2S_WIRES_DAC; i++) + { + p_i2s_dac[i] @ tmp <: 0; + } +#endif + + p_i2s_adc[0] @ tmp - 1 :> void; + +#pragma loop unroll + for(int i = 0; i < I2S_WIRES_ADC; i++) + { + clearbuf(p_i2s_adc[i]); + } + + /* TODO In master mode, the i/o loop assumes L/RCLK = 32bit clocks. We should check this every interation + * and resync if we got a bclk glitch */ + +#endif + + /* Main Audio I/O loop */ + while (1) + { + outuint(c_out, 0); + + /* Check for sample freq change or new samples from mixer*/ + if(testct(c_out)) + { + inct(c_out); + return inuint(c_out); + + } + else + { +#ifndef MIXER // Interfaces straight to decouple() + (void) inuint(c_out); + +#if NUM_USB_CHAN_IN > 0 +#pragma loop unroll + for(int i = 0; i < NUM_USB_CHAN_IN; i++) + { + outuint(c_out, samplesIn[i]); + } +#endif + +#if NUM_USB_CHAN_OUT > 0 +#pragma loop unroll + for(int i = 0; i < NUM_USB_CHAN_OUT; i++) + { + samplesOut[i] = inuint(c_out); + } +#endif +#else +#pragma loop unroll + for(int i = 0; i < NUM_USB_CHAN_OUT; i++) + { + int tmp = inuint(c_out); +#if defined(OUT_VOLUME_IN_MIXER) && defined(OUT_VOLUME_AFTER_MIX) + tmp<<=3; +#endif + samplesOut[i] = tmp; + } + +#pragma loop unroll + for(int i = 0; i < NUM_USB_CHAN_IN; i++) + { + outuint(c_out, samplesIn[i]); + } +#endif + } + +#if defined(SPDIF_RX) || defined(ADAT_RX) + inuint(c_dig_rx); +#endif +#ifdef SPDIF_RX + asm("ldw %0, dp[g_digData]":"=r"(samplesIn[SPDIF_RX_INDEX + 0])); + asm("ldw %0, dp[g_digData+4]":"=r"(samplesIn[SPDIF_RX_INDEX + 1])); + +#endif +#ifdef ADAT_RX + asm("ldw %0, dp[g_digData+8]":"=r"(samplesIn[ADAT_RX_INDEX + 0])); + asm("ldw %0, dp[g_digData+12]":"=r"(samplesIn[ADAT_RX_INDEX+ 1])); + asm("ldw %0, dp[g_digData+16]":"=r"(samplesIn[ADAT_RX_INDEX + 2])); + asm("ldw %0, dp[g_digData+20]":"=r"(samplesIn[ADAT_RX_INDEX + 3])); + asm("ldw %0, dp[g_digData+24]":"=r"(samplesIn[ADAT_RX_INDEX + 4])); + asm("ldw %0, dp[g_digData+28]":"=r"(samplesIn[ADAT_RX_INDEX + 5])); + asm("ldw %0, dp[g_digData+32]":"=r"(samplesIn[ADAT_RX_INDEX + 6])); + asm("ldw %0, dp[g_digData+36]":"=r"(samplesIn[ADAT_RX_INDEX + 7])); +#endif + +#if defined(SPDIF_RX) || defined(ADAT_RX) + /* Request digital data (with prefill) */ + outuint(c_dig_rx, 0); +#endif + + + + tmp = 0; + +#pragma xta endpoint "i2s_output_l" + +#if (I2S_CHANS_DAC != 0) && (NUM_USB_CHAN_OUT != 0) +#pragma loop unroll + for(int i = 0; i < I2S_CHANS_DAC; i+=2) + { + p_i2s_dac[tmp++] <: bitrev(samplesOut[i]); /* Output LEFT sample to DAC */ + } +#endif + +#ifdef CODEC_SLAVE + /* Generate clocks LR Clock low - LEFT */ + switch (divide) + { + case 8: + + /* LR clock delayed by one clock, This is so MSB is output on the falling edge of BCLK + * after the falling edge on which LRCLK was toggled. (see I2S spec) */ + p_lrclk <: 0x80000000; + + p_bclk <: 0xF0F0F0F0; + p_bclk <: 0xF0F0F0F0; + p_bclk <: 0xF0F0F0F0; + p_bclk <: 0xF0F0F0F0; + p_bclk <: 0xF0F0F0F0; + p_bclk <: 0xF0F0F0F0; + p_bclk <: 0xF0F0F0F0; + p_bclk <: 0xF0F0F0F0; + break; + + case 4: + p_lrclk <: 0x80000000; + + p_bclk <: 0xCCCCCCCC; + p_bclk <: 0xCCCCCCCC; + p_bclk <: 0xCCCCCCCC; + p_bclk <: 0xCCCCCCCC; + break; + + case 2: + p_lrclk <: 0x80000000; + + p_bclk <: 0xAAAAAAAA; + p_bclk <: 0xAAAAAAAA; + break; + + case 1: + p_lrclk <: 0x80000000; + break; + } +#endif + + +#if (I2S_CHANS_ADC != 0) + /* Input prevous R sample into R in buffer */ + index = 0; +#pragma loop unroll + for(int i = 1; i < I2S_CHANS_ADC; i += 2) + { + p_i2s_adc[index++] :> sample; +#if NUM_USB_CHAN_IN > 0 + samplesIn[i] = bitrev(sample); + + /* Store the previous left in left */ + samplesIn[i-1] = samplesInPrev[i]; +#endif + } +#endif + +#if defined(SPDIF) && (NUM_USB_CHAN_OUT > 0) + outuint(c_spd_out, samplesOut[SPDIF_TX_INDEX]); /* Forward sample to SPDIF txt thread */ + sample = samplesOut[SPDIF_TX_INDEX + 1]; + outuint(c_spd_out, sample); /* Forward sample to SPDIF txt thread */ +#ifdef RAMP_CHECK + sample >>= 8; + if (started<10000) { + if (sample == prev+1) + started++; + } + else + if (sample != prev+1 && sample != 0) { + printintln(prev); + printintln(sample); + printintln(prev-sample+1); + } + prev = sample; +#endif + +#endif + + tmp = 0; +#pragma xta endpoint "i2s_output_r" +#if (I2S_CHANS_DAC != 0) && (NUM_USB_CHAN_OUT != 0) +#pragma loop unroll + for(int i = 1; i < I2S_CHANS_DAC; i+=2) + { + p_i2s_dac[tmp++] <: bitrev(samplesOut[i]); /* Output RIGHT sample to DAC */ + } +#endif + +#ifdef CODEC_SLAVE + /* Clock out data (and LR clock) */ + switch (divide) + { + case 8: + p_lrclk <: 0x7FFFFFFF; + + p_bclk <: 0xF0F0F0F0; + p_bclk <: 0xF0F0F0F0; + p_bclk <: 0xF0F0F0F0; + p_bclk <: 0xF0F0F0F0; + p_bclk <: 0xF0F0F0F0; + p_bclk <: 0xF0F0F0F0; + p_bclk <: 0xF0F0F0F0; + p_bclk <: 0xF0F0F0F0; + break; + + case 4: + p_lrclk <: 0x7FFFFFFF; + + p_bclk <: 0xCCCCCCCC; + p_bclk <: 0xCCCCCCCC; + p_bclk <: 0xCCCCCCCC; + p_bclk <: 0xCCCCCCCC; + break; + + case 2: + p_lrclk <: 0x7FFFFFFF; + + p_bclk <: 0xAAAAAAAA; + p_bclk <: 0xAAAAAAAA; + break; + + case 1: + p_lrclk <: 0x7FFFFFFF; + break; + } +#endif + + +#if (I2S_CHANS_ADC != 0) + /* Input previous L ADC sample */ + index = 0; +#pragma loop unroll + for(int i = 1; i < I2S_CHANS_ADC; i += 2) + { + p_i2s_adc[index++] :> sample; + +#if NUM_USB_CHAN_IN > 0 + samplesInPrev[i] = bitrev(sample); +#endif + } + + +#endif + } + return 0; +} + +/* This function is a dummy version of the deliver thread that does not + connect to the codec ports. It is used during DFU reset. */ +static unsigned dummy_deliver(chanend c_out) { + while (1) + { + outuint(c_out, 0); + + /* Check for sample freq change or new samples from mixer*/ + if(testct(c_out)) + { + inct(c_out); + return inuint(c_out); + + } + else + { +#ifndef MIXER // Interfaces straight to decouple() + (void) inuint(c_out); +#pragma loop unroll + for(int i = 0; i < NUM_USB_CHAN_IN; i++) + { + outuint(c_out, 0); + } + +#pragma loop unroll + for(int i = 0; i < NUM_USB_CHAN_OUT; i++) + { + (void) inuint(c_out); + } +#else +#pragma loop unroll + for(int i = 0; i < NUM_USB_CHAN_OUT; i++) + { + (void) inuint(c_out); + } + +#pragma loop unroll + for(int i = 0; i < NUM_USB_CHAN_IN; i++) + { + outuint(c_out, 0); + } +#endif + } + } + return 0; +} + +void audio(chanend c_mix_out, chanend ?c_dig_rx, chanend ?c_config) +{ + chan c_spdif_out; + unsigned curSamFreq = DEFAULT_FREQ; + unsigned mClk; + unsigned divide; + unsigned firstRun = 1; + +#ifdef SPDIF + SpdifTransmitPortConfig(p_spdif_tx, clk_mst_spd, p_mclk); +#endif + + /* Initialise master clock generation */ + ClockingInit(); + + /* Perform required CODEC/ADC/DAC initialisation */ + CodecInit(c_config); + + while(1) + { + + /* Calculate what master clock we should be using */ + if ((curSamFreq % 22050) == 0) + { + mClk = MCLK_441; + } + else if ((curSamFreq % 24000) == 0) + { + mClk = MCLK_48; + } + + /* Calculate divide required for bit clock e.g. 11.289600 / (176400 * 64) = 1 */ + divide = mClk / ( curSamFreq * 64 ); + + /* Configure clocking for required master clock */ + ClockingConfig(mClk); + + if(!firstRun) + { + /* TODO wait for good mclk instead of delay */ + /* No delay for DFU modes */ + if ((curSamFreq != AUDIO_REBOOT_FROM_DFU) && (curSamFreq != AUDIO_STOP_FOR_DFU)) + { + timer t; + unsigned time; + t :> time; + t when timerafter(time+AUDIO_PLL_LOCK_DELAY) :> void; + + /* Handshake back */ + outct(c_mix_out, XS1_CT_END); + } + } + firstRun = 0; + + /* Configure CODEC/DAC/ADC for SampleFreq/MClk */ + CodecConfig(curSamFreq, mClk, c_config); + + /* Configure audio ports */ + ConfigAudioPorts(divide); + + par + { + +#ifdef SPDIF + { //set_thread_fast_mode_on(); + SpdifTransmit(p_spdif_tx, c_spdif_out); + } +#endif + + { +#ifdef SPDIF + /* Communicate master clock and sample freq to S/PDIF thread */ + outuint(c_spdif_out, curSamFreq); + outuint(c_spdif_out, mClk); +#endif + + curSamFreq = deliver(c_mix_out, c_spdif_out, divide, c_dig_rx); + + // Currently no more audio will happen after this point + if (curSamFreq == AUDIO_STOP_FOR_DFU) + { + outct(c_mix_out, XS1_CT_END); + + while (1) + { + + curSamFreq = dummy_deliver(c_mix_out); + + if (curSamFreq == AUDIO_START_FROM_DFU) + { + outct(c_mix_out, XS1_CT_END); + break; + } + } + } + + + +#ifdef SPDIF + /* Notify S/PDIF thread of impending new freq... */ + outct(c_spdif_out, XS1_CT_END); +#endif + } + } + } +} diff --git a/module_usb_aud_shared/audiostream/audiostream.h b/module_usb_aud_shared/audiostream/audiostream.h new file mode 100644 index 00000000..8c9862a7 --- /dev/null +++ b/module_usb_aud_shared/audiostream/audiostream.h @@ -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 AudioStreamStart(void); + +/* Any actions required on stream stop e.g. DAC mute - run every steam stop */ +void AudioStreamStop(void); + +#endif + diff --git a/module_usb_aud_shared/clockcmds.h b/module_usb_aud_shared/clockcmds.h new file mode 100644 index 00000000..37ec1e12 --- /dev/null +++ b/module_usb_aud_shared/clockcmds.h @@ -0,0 +1,30 @@ + +#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_CHAN_COUNT_IN 5 +#define SET_CHAN_COUNT_OUT 6 + + + diff --git a/module_usb_aud_shared/clocking/clockgen.xc b/module_usb_aud_shared/clocking/clockgen.xc new file mode 100644 index 00000000..a2d12bce --- /dev/null +++ b/module_usb_aud_shared/clocking/clockgen.xc @@ -0,0 +1,797 @@ + +#include +#include +#include + +#include "devicedefines.h" +#include "clockcmds.h" +#include "SpdifReceive.h" + + +#ifdef SPDIF_RX +#pragma xta command "analyse path digital_out digital_out" +#pragma xta command "set required - 5200 ns" /* 192kHz */ +#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]; + +int abs(int x) +{ + if (x < 0) return -x; + return x; +} + +int channelContainsControlToken(chanend x) +{ + unsigned char tmpc; + + select + { + case inct_byref(x, tmpc): + return 1; + default: + return 0; + } + +} + +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); + } +} + +#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]; /* Audio transmitted to host i.e. dev inputs */ +#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_EXT; +#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; + //g_inputLevelData[i] = samples_to_host_inputs[i]; + asm("ldw %0, %1[%2]":"=r"(tmp):"r"(samples_to_host_inputs),"r"(i)); + g_inputLevelData[i] = tmp; + + //samples_to_host_inputs[i] = 0; + asm("stw %0, %1[%2]"::"r"(0),"r"(samples_to_host_inputs),"r"(i)); + + /* Guard against host polling slower than timer and missing peaks */ + if(g_inputLevelData[i] > samples_to_host_inputs_buff[i]) + { + samples_to_host_inputs_buff[i] = g_inputLevelData[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]) + { + if(smux) + { + + adatSamples[adatWr + 0] = adatFrame[0]; + adatSamples[adatWr + 4] = adatFrame[1]; + adatSamples[adatWr + 1] = adatFrame[2]; + adatSamples[adatWr + 5] = adatFrame[3]; + adatSamples[adatWr + 2] = adatFrame[4]; + adatSamples[adatWr + 6] = adatFrame[5]; + adatSamples[adatWr + 3] = adatFrame[6]; + 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); + +#pragma xta endpoint "digital_out" + + 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 + { + /* TODO SMUX II mode */ + /* read out samples from the ADAT buffer and send */ + /* always return 8 samples */ + 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 + } + + } +} + + diff --git a/module_usb_aud_shared/clocking/clocking.h b/module_usb_aud_shared/clocking/clocking.h new file mode 100644 index 00000000..88fc786b --- /dev/null +++ b/module_usb_aud_shared/clocking/clocking.h @@ -0,0 +1,27 @@ + +#ifndef _CLOCKING_H_ +#define _CLOCKING_H_ + +/* Functions that handle master clock generation. These need modifying for an existing design */ + +/* Any initialisation required for master clock generation - run once at start up */ +void ClockingInit(void); + +/* Configuration for a specific master clock frequency - run every sample frequency change */ +void ClockingConfig(unsigned mClkFreq); + + +/** 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 + diff --git a/module_usb_aud_shared/codec/codec.h b/module_usb_aud_shared/codec/codec.h new file mode 100644 index 00000000..452ac900 --- /dev/null +++ b/module_usb_aud_shared/codec/codec.h @@ -0,0 +1,14 @@ +#ifndef _CODEC_H_ +#define _CODEC_H_ + +/* These functions must be implemented for the CODEC/ADC/DAC arrangement of a specific design */ + +/* TODO Are the channel args required? */ + +/* Any required CODEC initialisation - run once at start up */ +void CodecInit(chanend ?c_codec); + +/* Configure condec for a specific mClk/Sample frquency - run on every sample frequency change */ +void CodecConfig(unsigned samFreq, unsigned mClk, chanend ?c_codec); + +#endif diff --git a/module_usb_aud_shared/dbtable.h b/module_usb_aud_shared/dbtable.h new file mode 100644 index 00000000..e760ae2e --- /dev/null +++ b/module_usb_aud_shared/dbtable.h @@ -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 diff --git a/module_usb_aud_shared/devicedefines.h b/module_usb_aud_shared/devicedefines.h new file mode 100644 index 00000000..04a0e3c3 --- /dev/null +++ b/module_usb_aud_shared/devicedefines.h @@ -0,0 +1,343 @@ +/** + * @file internaldefines.h + * @brief Defines relating to device configuration and customisation. + * @author Ross Owen, XMOS Limited + */ + +#ifndef _DEVICEDEFINES_H_ +#define _DEVICEDEFINES_H_ + +#include "customdefines.h" + +/* Tidy up historical INPUT/OUTPUT defines. INPUT/OUTPUT now enabled based on channel count defines */ +#if !defined(NUM_USB_CHAN_IN) + #error NUM_USB_CHAN_IN must be defined! +#else + #if (NUM_USB_CHAN_IN == 0) + #undef INPUT + #else + #define INPUT 1 + #endif +#endif + +#if !defined(NUM_USB_CHAN_OUT) + #error NUM_USB_CHAN_OUT must be defined! +#else + #if (NUM_USB_CHAN_OUT == 0) + #undef OUTPUT + #else + #define OUTPUT 1 + #endif +#endif + +#if defined(MIDI) && (MIDI == 0) +#undef MIDI +#endif + +#if defined(SPDIF) && (SPDIF == 0) +#undef SPDIF +#endif + +#if defined(INPUT) && (INPUT == 0) +#undef INPUT +#endif + +#if defined(OUTPUT) && (OUTPUT == 0) +#undef OUTPUT +#endif + +#if defined(SPDIF_RX) && (SPDIF_RX == 0) +#undef SPDIF_RX +#endif + +#if defined(ADAT_RX) && (ADAT_RX == 0) +#undef ADAT_RX +#endif + +#if defined(DFU) && (DFU == 0) +#undef DFU +#endif + +#if defined(CODEC_SLAVE) && (CODEC_SLAVE == 0) +#undef CODEC_SLAVE +#endif + +#if defined(LEVEL_METER_LEDS) && !defined(LEVEL_UPDATE_RATE) +#define LEVEL_UPDATE_RATE 400000 +#endif + +#if(AUDIO_CLASS != 1) && (AUDIO_CLASS != 2) +#warning AUDIO_CLASS not defined, using 2 +#define AUDIO_CLASS 2 +#endif + +/* Number of IS2 chans to DAC */ +#ifndef I2S_CHANS_DAC +#error I2S_CHANS_DAC not defined +#else +#define I2S_WIRES_DAC (I2S_CHANS_DAC >> 1) +#endif + +/* Number of I2S chans from ADC */ +#ifndef I2S_CHANS_ADC +#error I2S_CHANS_ADC not defined +#else +#define I2S_WIRES_ADC (I2S_CHANS_ADC >> 1) +#endif + +/* SPDIF and ADAT first input chan indices */ +#ifdef SPDIF_RX +#ifndef SPDIF_RX_INDEX +#error SPDIF_RX_INDEX not defined and SPDIF_RX defined +#endif +#endif + +#ifdef ADAT_RX +#ifndef ADAT_RX_INDEX +#error ADAT_RX_INDEX not defined and ADAT_RX defined +#endif +#endif + +#ifndef SPDIF_TX_INDEX +#warning SPDIF_TX_INDEX not defined! Using 0 +#define SPDIF_TX_INDEX (0) +#endif + +/* Max supported freq for device */ +#ifndef MAX_FREQ +#warning MAX_FREQ not defined! Using 48000 +#define MAX_FREQ (48000) +#endif + +/* Default device freq */ +#ifndef DEFAULT_FREQ +#warning DEFAULT not defined! Using MAX_FREQ +#define DEFAULT_FREQ (MAX_FREQ) +#endif + +/* Master clock defines (in Hz) */ +#ifndef MCLK_441 +#error MCLK_441 not defined +#endif + +#ifndef MCLK_48 +#error MCLK_441 not defined +#endif + +/* The number of clock ticks to wait for the audio PLL to lock */ +#define AUDIO_PLL_LOCK_DELAY (40000000) + +/* Vendor/Product strings */ +#ifndef VENDOR_STR +#define VENDOR_STR "XMOS " +//#warning VENDOR_STR not defined. Using XMOS +#endif + +#ifndef VENDOR_ID +#warning VENDOR_ID not defined. Using XMOS vendor ID +#define VENDOR_ID (0x20B1) /* XMOS VID */ +#endif + +#ifndef PID_AUDIO_1 +#define PID_AUDIO_1 (0x0001) +#warning PRODUCT_ID not defined. Using 0x0001 +#endif + +#ifndef PID_AUDIO_2 +#define PID_AUDIO_2 (0x0001) +#warning PRODUCT_ID not defined. Using 0x0001 +#endif + +/* Device release number in BCD: 0xJJMNi */ +#ifndef BCD_DEVICE +#define BCD_DEVICE (0x0000) +#warning BCD_DEVICE not defined. Using 0x0000 +#endif + +/* Addition interfaces based on defines */ +#if defined(DFU) && DFU != 0 +#define DFU_INTERFACES (1) /* DFU interface count */ +#else +#define DFU_INTERFACES (0) +#endif + +#ifdef INPUT +#define INPUT_INTERFACES (1) +#else +#define INPUT_INTERFACES (0) +#endif + +#if defined(OUTPUT) && OUTPUT != 0 +#define OUTPUT_INTERFACES (1) +#else +#define OUTPUT_INTERFACES (0) +#endif + + +#if defined(MIDI) +#define MIDI_INTERFACES (2) +#else +#define MIDI_INTERFACES (0) +#endif + +#define AUDIO_STOP_FOR_DFU (0x12345678) +#define AUDIO_START_FROM_DFU (0x87654321) +#define AUDIO_REBOOT_FROM_DFU (0xa5a5a5a5) + + +#define MAX_VOL (0x20000000) + +#define NUM_EP_OUT 3 /* Max number of device endpoints used */ +#define NUM_EP_IN 5 +/* Length of clock unit/clock-selector units */ +#if defined(SPDIF_RX) && defined(ADAT_RX) +#define NUM_CLOCKS 3 +#elif defined(SPDIF_RX) || defined(ADAT_RX) +#define NUM_CLOCKS 2 +#else +#define NUM_CLOCKS 1 +#endif + + +/* Total number of USB interfaces this device implements (+1 for required control interface) */ +#define NUM_INTERFACES INPUT_INTERFACES + OUTPUT_INTERFACES + DFU_INTERFACES + MIDI_INTERFACES + 1 +/* Audio Unit ID defines */ +#define FU_USBIN 11 /* Feature Unit: USB Audio device -> host */ +#define FU_USBOUT 10 /* Feature Unit: USB Audio host -> device*/ +#define ID_IT_USB 2 /* Input terminal: USB streaming */ +#define ID_IT_AUD 1 /* Input terminal: Analogue input */ +#define ID_OT_USB 22 /* Output terminal: USB streaming */ +#define ID_OT_AUD 20 /* Output terminal: Analogue output */ + +#define ID_CLKSEL 40 /* Clock selector ID */ +#define ID_CLKSRC_INT 41 /* Clock source ID (internal) */ +#define ID_CLKSRC_EXT 42 /* Clock source ID (external) */ +#define ID_CLKSRC_ADAT 43 /* Clock source ID (external) */ + +#define ID_XU_MIXSEL 50 +#define ID_XU_OUT 51 +#define ID_XU_IN 52 + +#define ID_MIXER_1 60 + +#ifndef SERIAL_STR +#define SERIAL_STR "0000" /* Serial number string */ +#endif + +#define SERIAL_STR_INDEX 0x03 +#define MANUFACTURER_STR_INDEX 0x01 +#define PRODUCT_STR_INDEX 0x02 + +/* Mixer defines */ +#ifndef MIX_INPUTS +#define MIX_INPUTS 18 +#endif + +#ifndef MAX_MIX_COUNT +#define MAX_MIX_COUNT 8 +#endif + + +/* Volume defines */ + +#ifndef MIN_VOLUME +/* The minimum volume setting above -inf. This is a signed 8.8 fixed point + number that must be strictly greater than -128 (0x8000) */ +/* Default min volume is -127db */ +#define MIN_VOLUME (0x8100) +#endif + +#ifndef MAX_VOLUME +/* The maximum volume setting. This is a signed 8.8 fixed point number. */ +/* Default max volume is 0db */ +#define MAX_VOLUME (0x0000) +#endif + +#ifndef VOLUME_RES +/* The resolution of the volume control in db as a 8.8 fixed point number */ +/* Default volume resolution 1db */ +#define VOLUME_RES (0x100) +#endif + + +#ifndef MIN_MIXER_VOLUME +/* The minimum volume setting for the mixer unit above -inf. + This is a signed 8.8 fixed point + number that must be strictly greater than -128 (0x8000) */ +/* Default min volume is -127db */ +#define MIN_MIXER_VOLUME (0x8100) +#endif + +#ifndef MAX_MIXER_VOLUME +/* The maximum volume setting for the mixer. + This is a signed 8.8 fixed point number. */ +/* Default max volume is 0db */ +#define MAX_MIXER_VOLUME (0x0000) +#endif + +#ifndef VOLUME_RES_MIXER +/* The resolution of the volume control in db as a 8.8 fixed point number */ +/* Default volume resolution 1db */ +#define VOLUME_RES_MIXER (0x100) +#endif + +/* Handle out volume control in the mixer */ +#if defined(OUT_VOLUME_IN_MIXER) && (OUT_VOLUME_IN_MIXER==0) +#undef OUT_VOLUME_IN_MIXER +#else +#if defined(MIXER) +// Enabled by default +#define OUT_VOLUME_IN_MIXER +#endif +#endif + +/* Apply out volume controls after the mix */ +#if defined(OUT_VOLUME_AFTER_MIX) && (OUT_VOLUME_AFTER_MIX==0) +#undef OUT_VOLUME_AFTER_MIX +#else +#if defined(MIXER) && defined(OUT_VOLUME_IN_MIXER) +// Enabled by default +#define OUT_VOLUME_AFTER_MIX +#endif +#endif + +/* Define for reporting as self or bus-powered to the host */ +#if defined(SELF_POWERED) && (SELF_POWERED==0) +#undef SELF_POWERED +#endif + +/* Handle in volume control in the mixer */ +#if defined(IN_VOLUME_IN_MIXER) && (IN_VOLUME_IN_MIXER==0) +#undef IN_VOLUME_IN_MIXER +#else +#if defined(MIXER) +/* Enabled by default */ +#define IN_VOLUME_IN_MIXER +#endif +#endif + +/* Apply in volume controls after the mix */ +#if defined(IN_VOLUME_AFTER_MIX) && (IN_VOLUME_AFTER_MIX==0) +#undef IN_VOLUME_AFTER_MIX +#else +#if defined(MIXER) && defined(IN_VOLUME_IN_MIXER) +// Enabled by default +#define IN_VOLUME_AFTER_MIX +#endif +#endif + +#if defined(AUDIO_CLASS_FALLBACK) && (AUDIO_CLASS_FALLBACK==0) +#undef AUDIO_CLASS_FALLBACK +#endif + +/* Defines for DFU */ +#ifndef PID_DFU +#define PID_DFU PID_AUDIO_2 +#endif + +#define DFU_VENDOR_ID VENDOR_ID +#define DFU_BCD_DEVICE BCD_DEVICE +#define DFU_SERIAL_STR_INDEX SERIAL_STR_INDEX +#define DFU_MANUFACTURER_INDEX MANUFACTURER_STR_INDEX +#define DFU_PRODUCT_INDEX PRODUCT_STR_INDEX +#endif diff --git a/module_usb_aud_shared/endpoint0/audiorequests.xc b/module_usb_aud_shared/endpoint0/audiorequests.xc new file mode 100644 index 00000000..f14ecc3d --- /dev/null +++ b/module_usb_aud_shared/endpoint0/audiorequests.xc @@ -0,0 +1,1291 @@ +/** + * @file AudioRequests.xc + * @brief Implements relevant requests from the USB Audio 2.0 Specification + * @author Ross Owen, XMOS Semiconductor + * @version 1.4 + */ + +#include +//#include + +#include "xud.h" +#include "usb.h" +#include "usbaudio20.h" +#include "dbcalc.h" +#include "devicedefines.h" +#include "common.h" +#include "clockcmds.h" +#ifdef MIXER +#include "mixer.h" +#endif +#include + +#define CS_XU_MIXSEL (0x06) + +extern unsigned int multOut[NUM_USB_CHAN_OUT + 1]; +extern unsigned int multIn[NUM_USB_CHAN_IN + 1]; + +extern int interfaceAlt[]; + +/* Global volume and mute tables */ +extern int volsOut[]; +extern unsigned int mutesOut[]; + +extern int volsIn[]; +extern unsigned int mutesIn[]; + +/* Mixer settings */ +extern unsigned char mixer1Crossbar[]; +extern short mixer1Weights[]; + +/* Device channel mapping */ +#if NUM_USB_CHAN_OUT > 0 +extern unsigned char channelMapAud[NUM_USB_CHAN_OUT]; +#endif +#if NUM_USB_CHAN_IN > 0 +extern unsigned char channelMapUsb[NUM_USB_CHAN_IN]; +#endif + +/* Mixer input mapping */ +extern unsigned char mixSel[MIX_INPUTS]; + +/* Global var for current frequency */ +extern unsigned int g_curSamFreq; +extern unsigned int g_curSamFreq48000Family; +extern unsigned int g_curSamFreqMultiplier; + +/* Store an int into a char array: Note this allows non-word aligned access unlike reinerpret cast */ +void storeInt(unsigned char buffer[], int index, int val) +{ + buffer[index+3] = val>>24; + buffer[index+2] = val>>16; + buffer[index+1] = val>>8; + buffer[index] = val; +} + +/* Store an short into a char array: Note this allows non-word aligned access unlike reinerpret cast */ +void storeShort(unsigned char buffer[], int index, short val) +{ + buffer[index+1] = val>>8; + buffer[index] = val; +} + +void storeFreq(unsigned char buffer[], int &i, int freq) +{ + storeInt(buffer, i, freq); + i+=4; + storeInt(buffer, i, freq); + i+=4; + storeInt(buffer, i, 0); + i+=4; + return; +} + + +unsigned longMul(unsigned a, unsigned b, int prec) +{ + unsigned x,y; + unsigned ret; + + // {x, y} = lmul(a, b, 0, 0); + asm("lmul %0, %1, %2, %3, %4, %5":"=r"(x),"=r"(y):"r"(a),"r"(b),"r"(0),"r"(0)); + + + ret = (x << (32-prec) | (y >> prec)); + return ret; +} + +void setG_curSamFreqMultiplier(int x) { + asm(" stw %0, dp[g_curSamFreqMultiplier]" :: "r"(x)); +} + +/* Update master volume i.e. i.e update weights for all channels */ +void updateMasterVol( int unitID, chanend ?c_mix_ctl) +{ + int x; + switch( unitID) + { + case FU_USBOUT: + for (int i = 1; i < (NUM_USB_CHAN_OUT + 1); i++) + { + /* Calc multipliers with 29 fractional bits from a db value with 8 fractional bits */ + /* 0x8000 is a special value representing -inf (i.e. mute) */ + unsigned master_vol = volsOut[0] == 0x8000 ? 0 : db_to_mult(volsOut[0], 8, 29); + unsigned vol = volsOut[i] == 0x8000 ? 0 : db_to_mult(volsOut[i], 8, 29); + + x = longMul(master_vol, vol, 29) * !mutesOut[0] * !mutesOut[i]; + +#ifdef OUT_VOLUME_IN_MIXER + if (!isnull(c_mix_ctl)) + { + outuint(c_mix_ctl, SET_MIX_OUT_VOL); + outuint(c_mix_ctl, i-1); + outuint(c_mix_ctl, x); + outct(c_mix_ctl, XS1_CT_END); + } +#else + asm("stw %0, %1[%2]"::"r"(x),"r"(multOut),"r"(i-1)); +#endif + + } + break; + + case FU_USBIN: + for (int i = 1; i < (NUM_USB_CHAN_IN + 1); i++) + { + /* Calc multipliers with 29 fractional bits from a db value with 8 fractional bits */ + /* 0x8000 is a special value representing -inf (i.e. mute) */ + unsigned master_vol = volsIn[0] == 0x8000 ? 0 : db_to_mult(volsIn[0], 8, 29); + unsigned vol = volsIn[i] == 0x8000 ? 0 : db_to_mult(volsIn[i], 8, 29); + + x = longMul(master_vol, vol, 29) * !mutesIn[0] * !mutesIn[i]; + +#ifdef IN_VOLUME_IN_MIXER + if (!isnull(c_mix_ctl)) + { + //master + //{ + //c_mix_ctl <: SET_MIX_IN_VOL; + //c_mix_ctl <: i-1; + //c_mix_ctl <: x; + //} + outuint(c_mix_ctl, SET_MIX_IN_VOL); + outuint(c_mix_ctl, i-1); + outuint(c_mix_ctl, x); + outct(c_mix_ctl, XS1_CT_END); + + + + } +#else + asm("stw %0, %1[%2]"::"r"(x),"r"(multIn),"r"(i-1)); +#endif + } + break; + + default: + XUD_Error_hex("MVol: No such unit: ", unitID); + break; + } +} + +void updateVol(int unitID, int channel, chanend ?c_mix_ctl) +{ + int x; + + /* Check for master volume update */ + if (channel == 0) + { + updateMasterVol( unitID , c_mix_ctl); + } + else + { + switch( unitID ) + { + case FU_USBOUT: { + /* Calc multipliers with 29 fractional bits from a db value with 8 fractional bits */ + /* 0x8000 is a special value representing -inf (i.e. mute) */ + unsigned master_vol = volsOut[0] == 0x8000 ? 0 : db_to_mult(volsOut[0], 8, 29); + unsigned vol = volsOut[channel] == 0x8000 ? 0 : db_to_mult(volsOut[channel], 8, 29); + + x = longMul(master_vol, vol, 29) * !mutesOut[0] * !mutesOut[channel]; + +#ifdef OUT_VOLUME_IN_MIXER + if (!isnull(c_mix_ctl)) + { + //master { + // c_mix_ctl <: SET_MIX_OUT_VOL; + // c_mix_ctl <: channel-1; + // /c_mix_ctl <: x; + //} + outuint(c_mix_ctl, SET_MIX_OUT_VOL); + outuint(c_mix_ctl, channel-1); + outuint(c_mix_ctl, x); + outct(c_mix_ctl, XS1_CT_END); + + } + + + +#else + asm("stw %0, %1[%2]"::"r"(x),"r"(multOut),"r"(channel-1)); +#endif + + + break; + } + case FU_USBIN: { + /* Calc multipliers with 29 fractional bits from a db value with 8 fractional bits */ + /* 0x8000 is a special value representing -inf (i.e. mute) */ + unsigned master_vol = volsIn[0] == 0x8000 ? 0 : db_to_mult(volsIn[0], 8, 29); + unsigned vol = volsIn[channel] == 0x8000 ? 0 : db_to_mult(volsIn[channel], 8, 29); + + x = longMul(master_vol, vol, 29) * !mutesIn[0] * !mutesIn[channel]; + +#ifdef IN_VOLUME_IN_MIXER + if (!isnull(c_mix_ctl)) { + //master { + // c_mix_ctl <: SET_MIX_IN_VOL; + // c_mix_ctl <: channel-1; + // c_mix_ctl <: x; + //} + outuint(c_mix_ctl, SET_MIX_IN_VOL); + outuint(c_mix_ctl, channel-1); + outuint(c_mix_ctl, x); + outct(c_mix_ctl, XS1_CT_END); + + + } +#else + asm("stw %0, %1[%2]"::"r"(x),"r"(multIn),"r"(channel-1)); +#endif + break; + } + default: + /* Don't hit */ + XUD_Error_hex("Vol: No such unit: ", unitID); + break; + } + } +} + +#ifdef EP0_THREAD_COMBINED_WITH_SPI +void spi(chanend c_spi, chanend c_spi_ss); +#endif + +/* Handles the audio class specific requests + * returns: 0 if request delt with successfully without error, + * <0 for device reset suspend + * else 1 + */ +int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, SetupPacket &sp, chanend c_audioControl, chanend ?c_mix_ctl, chanend ?c_clk_ctl +#ifdef EP0_THREAD_COMBINED_WITH_SPI + , chanend c_spi, chanend c_spi_ss +#endif +) +{ + unsigned char buffer[1024]; + int i_tmp; + int unitID; + int loop = 1; + int datalength; + + /* Inspect request, NOTE: these are class specific requests */ + switch( sp.bRequest ) + { + + /* CUR Request*/ + case CUR: + { + /* Extract unitID from wIndex */ + unitID = sp.wIndex >> 8; + + switch( unitID ) + { + /* Clock Unit(s) */ + case ID_CLKSRC_INT: + case ID_CLKSRC_EXT: + case ID_CLKSRC_ADAT: + { + /* Check Control selector (CS) */ + switch( sp.wValue >> 8 ) + { + /* Sample Frequency control */ + case CS_SAM_FREQ_CONTROL: + { + /* Direction: Host-to-device */ + if( sp.bmRequestType.Direction == 0 ) + { + /* Get OUT data with Sample Rate into buffer*/ + datalength = XUD_GetBuffer(ep0_out, buffer); + + /* Check for reset/suspend */ + if(datalength < 0) + { + return datalength; + } + + if(datalength == 4) + { + + /* Re-construct Sample Freq */ + i_tmp = buffer[0] | (buffer[1] << 8) | buffer[2] << 16 | buffer[3] << 24; + + /* Instruct audio thread to change sample freq */ + g_curSamFreq = i_tmp; + g_curSamFreq48000Family = g_curSamFreq % 48000 == 0; + + if(g_curSamFreq48000Family) + { + i_tmp = MCLK_48; + } + else + { + i_tmp = MCLK_441; + } + + setG_curSamFreqMultiplier(g_curSamFreq/(i_tmp/512)); + + outuint(c_audioControl, SET_SAMPLE_FREQ); + outuint(c_audioControl, g_curSamFreq); + +#ifdef EP0_THREAD_COMBINED_WITH_SPI + spi(c_spi, c_spi_ss); /* CodecConfig */ +#endif + + /* Wait for handshake back - i.e. pll locked and clocks okay */ + chkct(c_audioControl, XS1_CT_END); + + /* Allow time for our feedback to stabalise*/ + { + timer t; + unsigned time; + t :> time; + t when timerafter(time+5000000):> void; + } + } + + + /* Send 0 Length as status stage */ + return XUD_SetBuffer_ResetPid(ep0_in, buffer, 0, PIDn_DATA1); + + } + /* Direction: Device-to-host: Send Current Sample Freq */ + else + { + switch(unitID) + { + case ID_CLKSRC_EXT: + case ID_CLKSRC_ADAT: + +#ifdef REPORT_SPDIF_FREQ + /* Interogate clockgen thread for SPDIF freq */ + if (!isnull(c_clk_ctl)) + { + outuint(c_clk_ctl, GET_FREQ); + outuint(c_clk_ctl, CLOCK_SPDIF_INDEX); + outct(c_clk_ctl, XS1_CT_END); + + (buffer, unsigned[])[0] = inuint(c_clk_ctl); + chkct(c_clk_ctl, XS1_CT_END); + } + else + { + + (buffer, unsigned[])[0] = g_curSamFreq; + } + + break; +#endif + case ID_CLKSRC_INT: + + /* Always report our current operating frequency */ + (buffer, unsigned[])[0] = g_curSamFreq; + break; + + default: + XUD_Error_hex("Unknown Unit ID in Sample Frequency Control Request", unitID); break; + } + + return XUD_DoGetRequest(ep0_out, ep0_in, buffer, sp.wLength, sp.wLength ); + } + break; + } + + /* Clock Valid Control */ + case CS_CLOCK_VALID_CONTROL: + { + switch(unitID) + { + case ID_CLKSRC_INT: + + /* Internal clock always valid */ + buffer[0] = 1; + break; + + case ID_CLKSRC_EXT: + + /* Interogate clockgen thread for validity */ + if (!isnull(c_clk_ctl)) + { + outuint(c_clk_ctl, GET_VALID); + outuint(c_clk_ctl, CLOCK_SPDIF_INDEX); + outct(c_clk_ctl, XS1_CT_END); + buffer[0] = inuint(c_clk_ctl); + chkct(c_clk_ctl, XS1_CT_END); + } + + break; + + case ID_CLKSRC_ADAT: + + if (!isnull(c_clk_ctl)) + { + outuint(c_clk_ctl, GET_VALID); + outuint(c_clk_ctl, CLOCK_ADAT_INDEX); + outct(c_clk_ctl, XS1_CT_END); + buffer[0] = inuint(c_clk_ctl); + chkct(c_clk_ctl, XS1_CT_END); + } + + + break; + + default: + XUD_Error_hex("Unknown Unit ID in Clock Valid Control Request: ", unitID); + break; + } + + return XUD_DoGetRequest( ep0_out, ep0_in, buffer, sp.wLength, sp.wLength ); + + break; + } + + default: + XUD_Error_hex("Unknown Control Selector for Clock Unit: ", sp.wValue >> 8 ); + break; + + } + break; /* Clock Unit IDs */ + } + + /* Clock Selector Unit(s) */ + case ID_CLKSEL: + { + if ((sp.wValue >> 8) == CX_CLOCK_SELECTOR_CONTROL) + { + if( sp.bmRequestType.Direction == 0 ) + { + /* Direction: Host-to-device */ + datalength = XUD_GetBuffer(ep0_out, buffer); + + if(datalength < 0) + return datalength; + + /* Check for correct datalength for clock sel */ + if(datalength == 1) + { + + if (!isnull(c_clk_ctl)) + { + outuint(c_clk_ctl, SET_SEL); + outuint(c_clk_ctl, buffer[0]); + outct(c_clk_ctl, XS1_CT_END); + } + } + + /* Send 0 Length as status stage */ + return XUD_DoSetRequestStatus(ep0_in, 0); + } + else + { + buffer[0] = 1; + + /* Direction: Device-to-host: Send Current Selection */ + + if (!isnull(c_clk_ctl)) + { + outuint(c_clk_ctl, GET_SEL); + outct(c_clk_ctl, XS1_CT_END); + buffer[0] = inuint(c_clk_ctl); + chkct(c_clk_ctl, XS1_CT_END); + } + + return XUD_DoGetRequest( ep0_out, ep0_in, buffer, 1, sp.wLength ); + } + } + else + { + XUD_Error_hex("Unknown control on clock selector", sp.wValue); + } + break; + } + + /* Feature Units */ + case FU_USBOUT: + case FU_USBIN: + + /* Inspect Control Selector (CS) */ + switch(sp.wValue >> 8) + { + case FU_VOLUME_CONTROL: + + if(sp.bmRequestType.Direction == BM_REQTYPE_DIRECTION_OUT) /* Direction: Host-to-device */ + { + /* Expect OUT here (with v2yyolume) */ + loop = XUD_GetBuffer(ep0_out, buffer); + + /* Check for rst/suspend */ + if(loop < 0) + return loop; + + if(unitID == FU_USBOUT) + { + if ((sp.wValue & 0xff) <= NUM_USB_CHAN_OUT) { + volsOut[ sp.wValue&0xff ] = buffer[0] | (((int) (signed char) buffer[1]) << 8); + updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl ); + } + } + else + { + if ((sp.wValue & 0xff) <= NUM_USB_CHAN_IN) { + volsIn[ sp.wValue&0xff ] = buffer[0] | (((int) (signed char) buffer[1]) << 8); + updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl ); + } + } + + /* Send 0 Length as status stage */ + return XUD_DoSetRequestStatus(ep0_in, 0); + } + else /* Direction: Device-to-host */ + { + if(unitID == FU_USBOUT) + { + if ((sp.wValue & 0xff) <= NUM_USB_CHAN_OUT) { + buffer[0] = volsOut[ sp.wValue&0xff ]; + buffer[1] = volsOut[ sp.wValue&0xff ] >> 8; + } + else { + buffer[0] = buffer[1] = 0; + } + } + else + { + if ((sp.wValue & 0xff) <= NUM_USB_CHAN_IN) { + buffer[0] = volsIn[ sp.wValue&0xff ]; + buffer[1] = volsIn[ sp.wValue&0xff ] >> 8; + } + else { + buffer[0] = buffer[1] = 0; + } + } + return XUD_DoGetRequest(ep0_out, ep0_in, buffer, sp.wLength, sp.wLength); + } + break; /* FU_VOLUME_CONTROL */ + + case FU_MUTE_CONTROL: + + if(sp.bmRequestType.Direction == BM_REQTYPE_DIRECTION_OUT) // Direction: Host-to-device + { + /* Expect OUT here with mute */ + loop = XUD_GetBuffer(ep0_out, buffer); + + if(loop < 0) + return loop; + + if (unitID == FU_USBOUT) + { + if ((sp.wValue & 0xff) <= NUM_USB_CHAN_OUT) { + mutesOut[sp.wValue & 0xff] = buffer[0]; + updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl); + } + } + else + { + mutesIn[ sp.wValue&0xff ] = buffer[0]; + updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl); + } + + /* Send 0 Length as status stage */ + return XUD_DoSetRequestStatus(ep0_in, 0); + } + else // Direction: Device-to-host + { + if(unitID == FU_USBOUT) + { + if ((sp.wValue & 0xff) <= NUM_USB_CHAN_OUT) { + buffer[0] = mutesOut[sp.wValue&0xff]; + } + else { + buffer[0] = 0; + } + } + else + { + buffer[0] = mutesIn[ sp.wValue&0xff ]; + } + return XUD_DoGetRequest(ep0_out, ep0_in, buffer, sp.wLength, sp.wLength); + } + break; + + //default: + // XUD_Error("Unknown Control Selector for FU"); + //break; + } + + break; /* FU_USBIN */ + +#ifdef MIXER + case ID_XU_OUT: + { + if(sp.bmRequestType.Direction == BM_REQTYPE_DIRECTION_OUT) /* Direction: Host-to-device */ + { + unsigned volume = 0; + int c = sp.wValue & 0xff; + + loop = XUD_GetBuffer(ep0_out, buffer); + + if(loop < 0) + return loop; + + channelMapAud[c] = buffer[0] | buffer[1] << 8; + + if (!isnull(c_mix_ctl)) + { + if (c < NUM_USB_CHAN_OUT) + { + //master { + // c_mix_ctl <: SET_SAMPLES_TO_DEVICE_MAP; + // c_mix_ctl <: c; + // c_mix_ctl <: (int) channelMapAud[c]; + //} + outuint(c_mix_ctl, SET_SAMPLES_TO_DEVICE_MAP); + outuint(c_mix_ctl, c); + outuint(c_mix_ctl, channelMapAud[c]); + outct(c_mix_ctl, XS1_CT_END); + + } + } + + /* Send 0 Length as status stage */ + return XUD_DoSetRequestStatus(ep0_in, 0); + } + else + { + buffer[0] = channelMapAud[sp.wValue & 0xff]; + buffer[1] = 0; + + return XUD_DoGetRequest(ep0_out, ep0_in, buffer, sp.wLength, sp.wLength); + } + + } + break; + + case ID_XU_IN: + { + if(sp.bmRequestType.Direction == BM_REQTYPE_DIRECTION_OUT) /* Direction: Host-to-device */ + { + unsigned volume = 0; + int c = sp.wValue & 0xff; + + loop = XUD_GetBuffer(ep0_out, buffer); + + if(loop < 0) + return loop; + + channelMapUsb[c] = buffer[0] | buffer[1] << 8; + + if (!isnull(c_mix_ctl)) + { + if (c < NUM_USB_CHAN_IN) + { + //master { + // c_mix_ctl <: SET_SAMPLES_TO_HOST_MAP; + // c_mix_ctl <: c; + // c_mix_ctl <: (int) channelMapUsb[c]; + //} + outuint(c_mix_ctl, SET_SAMPLES_TO_HOST_MAP); + outuint(c_mix_ctl, c); + outuint(c_mix_ctl, channelMapUsb[c]); + outct(c_mix_ctl, XS1_CT_END); + + } + } + + /* Send 0 Length as status stage */ + return XUD_DoSetRequestStatus(ep0_in, 0); + } + else + { + buffer[0] = channelMapUsb[sp.wValue & 0xff]; + buffer[1] = 0; + + return XUD_DoGetRequest(ep0_out, ep0_in, buffer, sp.wLength, sp.wLength); + } + + } + break; + + + case ID_XU_MIXSEL: + { + int cs = sp.wValue >> 8; /* Control Selector */ + int cn = sp.wValue & 0xff; /* Channel number */ + + /* Check for Get or Set */ + if(sp.bmRequestType.Direction == BM_REQTYPE_DIRECTION_OUT) + { + /* Direction: Host-to-device */ /* Host-to-device */ + datalength = XUD_GetBuffer(ep0_out, buffer); + + /* Check for reset/suspend */ + if(datalength < 0) + { + return datalength; + } + + if(datalength > 0) + { + /* cn bounds check for safety..*/ + if(cn < MIX_INPUTS) + { + if(cs == CS_XU_MIXSEL) + { + /* Check for "off" - update local state */ + if(buffer[0] == 0xFF) + mixSel[cn] = (NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT); + else + mixSel[cn] = buffer[0]; + + /* Update all mix maps */ + for (int i = 0; i < MAX_MIX_COUNT; i++) + { + outuint(c_mix_ctl, SET_MIX_MAP); + outuint(c_mix_ctl, i); /* Mix bus */ + outuint(c_mix_ctl, cn); /* Mixer input */ + outuint(c_mix_ctl, (int) mixSel[cn]); /* Source */ + outct(c_mix_ctl, XS1_CT_END); + } + } + } + + } + + /* Send 0 Length as status stage */ + return XUD_DoSetRequestStatus(ep0_in, 0); + + + } + else + { + /* Direction: Device-to-Host (GET) */ + buffer[0] = 0; + + /* Channel Number bounds check for safety */ + if(cn < MIX_INPUTS) + { + /* Inspect control selector */ + if(cs == CS_XU_MIXSEL) + { + buffer[0] = mixSel[cn]; + } + } + + return XUD_DoGetRequest(ep0_out, ep0_in, buffer, 1, 1 ); + + + } + break; + } + + case ID_MIXER_1: + + if(sp.bmRequestType.Direction == BM_REQTYPE_DIRECTION_OUT) /* Direction: Host-to-device */ + { + unsigned volume = 0; + /* Expect OUT here with mute */ + loop = XUD_GetBuffer(ep0_out, buffer); + + if(loop < 0) + return loop; + + mixer1Weights[sp.wValue & 0xff] = buffer[0] | buffer[1] << 8; + + if (mixer1Weights[sp.wValue & 0xff] == 0x8000) { + volume = 0; + } + else { + volume = db_to_mult(mixer1Weights[sp.wValue & 0xff], 8, 25); + } + if (!isnull(c_mix_ctl)) + { + //master { + // c_mix_ctl <: SET_MIX_MULT; + //c_mix_ctl <: (sp.wValue & 0xff) % 8; + //c_mix_ctl <: (sp.wValue & 0xff) / 8; + ///c_mix_ctl <: volume; + //}/ + outuint(c_mix_ctl, SET_MIX_MULT); + outuint(c_mix_ctl, (sp.wValue & 0xff) % 8); + outuint(c_mix_ctl, (sp.wValue & 0xff) / 8); + outuint(c_mix_ctl, volume); + outct(c_mix_ctl, XS1_CT_END); + + } + + /* Send 0 Length as status stage */ + return XUD_DoSetRequestStatus(ep0_in, 0); + } + else + { + short weight = mixer1Weights[sp.wValue & 0xff]; + buffer[0] = weight & 0xff; + buffer[1] = (weight >> 8) & 0xff; + + return XUD_DoGetRequest(ep0_out, ep0_in, buffer, sp.wLength, sp.wLength); + } + break; + +#endif + //default: + + ///* We dont have a unit with this ID! */ + //XUD_Error_hex("ERR: Unknown control unit: ", sp.wIndex); + //break; + + } /* switch(sp.wIndex >> 8) i.e Unit ID */ + break; + } + + case RANGE: + { + unitID = sp.wIndex >> 8; + + switch( unitID ) + { + /* Clock Source Units */ + case ID_CLKSRC_EXT: + case ID_CLKSRC_ADAT: + case ID_CLKSRC_INT: + + /* Control Selector (CS) */ + switch( sp.wValue >> 8 ) + { + case CS_SAM_FREQ_CONTROL: + + /* Currently always return all freqs for all clocks */ + //switch(unitID) + //{ + // case ID_CLKSRC_INT: + // case ID_CLKSRC_EXT: + + { + int num_freqs = 0; + int i = 2; + + if(interfaceAlt[1] != 0) + printint(interfaceAlt[1]); + + #if MAX_FREQ >= 44100 + storeFreq(buffer, i, 44100); + num_freqs++; + #endif + #if MAX_FREQ >= 48000 + storeFreq(buffer, i, 48000); + num_freqs++; + #endif + #if MAX_FREQ >= 88200 + storeFreq(buffer, i, 88200); + num_freqs++; + #endif + #if MAX_FREQ >= 96000 + storeFreq(buffer, i, 96000); + num_freqs++; + #endif + #if MAX_FREQ >= 176400 + storeFreq(buffer, i, 176400); + num_freqs++; + #endif + #if MAX_FREQ >= 192000 + storeFreq(buffer, i, 192000); + num_freqs++; + #endif + storeShort(buffer, 0, num_freqs); + // break; + //} + + return XUD_DoGetRequest(ep0_out, ep0_in, buffer, i, sp.wLength); + } + break; + + //default: + // XUD_Error_hex("Unknown Control Selector in Clock Source Range Request: ", sp.wValue); + // break; + } + + break; + + /* Feature Units */ + case FU_USBIN: /* USB Data into Device */ + case FU_USBOUT: /* USB Data from Device */ + + /* Control Selector (CS) */ + switch( sp.wValue >> 8 ) + { + /* Volume control, send back same range for all channels (i.e. ignore CN) */ + case FU_VOLUME_CONTROL: + + storeShort(buffer, 0, 1); + storeShort(buffer, 2, MIN_VOLUME); + storeShort(buffer, 4, MAX_VOLUME); + storeShort(buffer, 6, VOLUME_RES); + + return XUD_DoGetRequest(ep0_out, ep0_in, buffer, sp.wLength, sp.wLength); + + break; + + //default: + //XUD_Error_hex("Unknown control selector for FU: ", sp.wValue); + // break; + + } + break; + +#ifdef MIXER + /* Mixer Unit */ + case ID_MIXER_1: + + storeShort(buffer, 0, 1); + storeShort(buffer, 2, MIN_MIXER_VOLUME); + storeShort(buffer, 4, MAX_MIXER_VOLUME); + storeShort(buffer, 6, VOLUME_RES_MIXER); + + return XUD_DoGetRequest(ep0_out, ep0_in, buffer, sp.wLength, sp.wLength); + + break; +#endif + + + //default: + //XUD_Error_hex("Unknown Unit ID in Range Request selector for FU: ", sp.wIndex >> 8); + //break; + + } + + break; + } + +#ifdef MIXER + case MEM: /* Memory Requests (5.2.7.1) */ + + unitID = sp.wIndex >> 8; + + switch( unitID ) + { + case ID_MIXER_1: + + if(sp.bmRequestType.Direction == BM_REQTYPE_DIRECTION_IN) + { + int length = 0; + + /* Device-to-Host (GET) */ + switch(sp.wValue) /* offset */ + { + case 0: /* Input levels */ + length = (NUM_USB_CHAN_IN + NUM_USB_CHAN_OUT) * 2; /* 2 bytes per chan */ + + for(int i = 0; i < (NUM_USB_CHAN_IN + NUM_USB_CHAN_OUT); i++) + { + /* Get the level and truncate to 16-bit */ + if(i < NUM_USB_CHAN_IN) + { + if (!isnull(c_mix_ctl)) + { + outuint(c_mix_ctl, GET_INPUT_LEVELS); + outuint(c_mix_ctl, (i - NUM_USB_CHAN_IN)); + outct(c_mix_ctl, XS1_CT_END); + storeShort(buffer, i*2, (inuint(c_mix_ctl)>>15)); + chkct(c_mix_ctl, XS1_CT_END); + } + else + { + storeShort(buffer, i*2, 0); + } + } + else + { + if (!isnull(c_mix_ctl)) + { + outuint(c_mix_ctl, GET_STREAM_LEVELS); + outuint(c_mix_ctl, (i - NUM_USB_CHAN_IN)); + outct(c_mix_ctl, XS1_CT_END); + storeShort(buffer, i*2, (inuint(c_mix_ctl) >> 15)); + chkct(c_mix_ctl, XS1_CT_END); + } + else + { + storeShort(buffer, i*2, 0); + } + } + } + + break; + + case 1: /* Mixer Output levels */ + length = MAX_MIX_COUNT * 2; /* 2 bytes per chan */ + + for(int i = 0; i < MAX_MIX_COUNT; i++) + { + if (!isnull(c_mix_ctl)) { + outuint(c_mix_ctl, GET_OUTPUT_LEVELS); + outuint(c_mix_ctl, i); + outct(c_mix_ctl, XS1_CT_END); + storeShort(buffer, i*2, (inuint(c_mix_ctl) >> 15)); + chkct(c_mix_ctl, XS1_CT_END); + } + else + { + storeShort(buffer, i*2, 0); + } + } + + break; + } + return XUD_DoGetRequest(ep0_out, ep0_in, buffer, length, sp.wLength); + } + else + { + /* Host-to-device (SET) */ + /* Currently no action for set mem request for any offset */ + datalength = XUD_GetBuffer(ep0_out, buffer); + + /* Check for reset/suspend */ + if(datalength < 0) + { + return datalength; + } + + /* Send 0 Length as status stage */ + return XUD_DoSetRequestStatus(ep0_in, 0); + + } + break; + } + + break; + + + +#endif + + } + + /* Didn't deal with request, return 1 */ + return 1; + +} + +#if defined (AUDIO_CLASS_FALLBACK) || (AUDIO_CLASS==1) +/* Handles the Audio Class 1.0 specific requests */ +int AudioClassRequests_1(XUD_ep c_ep0_out, XUD_ep c_ep0_in, SetupPacket &sp, chanend c_audioControl, chanend ?c_mix_ctl, chanend ?c_clk_ctl +#ifdef EP0_THREAD_COMBINED_WITH_SPI + , chanend c_spi, chanend c_spi_ss +#endif +) +{ + unsigned char buffer[1024]; + int unitID; + int loop = 1; + int i_tmp; + + /* Inspect request, NOTE: these are class specific requests */ + switch( sp.bRequest ) + { + case SET_INTERFACE: + { + return XUD_SetBuffer_ResetPid(c_ep0_in, buffer, 0, PIDn_DATA1); + + break; + } + + case B_REQ_SET_CUR: + { + loop = XUD_GetBuffer(c_ep0_out, buffer); + + /* Inspect for rst/suspend */ + if(loop < 0) + return loop; + + unitID = sp.wIndex >> 8; + + if (unitID == FU_USBOUT) + { + switch ((sp.wValue>>8) & 0xff) + { + case FU_VOLUME_CONTROL: + { + volsOut[ sp.wValue & 0xff ] = buffer[0] | (((int) (signed char) buffer[1]) << 8); + updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl ); + break; + } + case FU_MUTE_CONTROL: + { + mutesOut[ sp.wValue & 0xff ] = buffer[0]; + updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl ); + break; + } + } + } + else if (unitID == FU_USBIN) + { + switch ((sp.wValue>>8) & 0xff) + { + case FU_VOLUME_CONTROL: + { + volsIn[ sp.wValue & 0xff ] = buffer[0] | (((int) (signed char) buffer[1]) << 8); + updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl ); + break; + } + case FU_MUTE_CONTROL: + { + mutesIn[ sp.wValue & 0xff ] = buffer[0]; + updateVol( unitID, ( sp.wValue & 0xff ), c_mix_ctl ); + break; + } + } + } + else if (unitID == 0) // sample freq + { + i_tmp = buffer[0] | (buffer [1] << 8) | (buffer[2] << 16); + + + if(i_tmp != g_curSamFreq) + { + int curSamFreq44100Family; + + /* Windows Audio Class driver has a nice habbit of sending invalid SF's (e.g. 48001Hz) + * when under stress. Lets double check it here and ignore if not valid. */ + g_curSamFreq48000Family = i_tmp % 48000 == 0; + curSamFreq44100Family = i_tmp % 44100 == 0; + + if(g_curSamFreq48000Family || curSamFreq44100Family) + { + g_curSamFreq = i_tmp; + + if(g_curSamFreq48000Family) + { + i_tmp = MCLK_48; + } + else + { + i_tmp = MCLK_441; + } + + setG_curSamFreqMultiplier(g_curSamFreq/(i_tmp/512)); + + /* Instruct audio thread to change sample freq */ + outuint(c_audioControl, SET_SAMPLE_FREQ); + outuint(c_audioControl, g_curSamFreq); + +#ifdef EP0_THREAD_COMBINED_WITH_SPI + spi(c_spi, c_spi_ss); /* CodecConfig */ +#endif + + /* Wait for handshake back - i.e. pll locked and clocks okay */ + chkct(c_audioControl, XS1_CT_END); + + + /* Allow time for the change - feedback to stabalise */ + { + timer t; + unsigned time; + t :> time; + t when timerafter(time+50000000):> void; + } + } + } + } + + return XUD_SetBuffer_ResetPid(c_ep0_in, buffer, 0, PIDn_DATA1); + + break; + } + case B_REQ_GET_CUR: + { + unitID = sp.wIndex >> 8; + if (unitID == FU_USBOUT) + { + switch ((sp.wValue>>8) & 0xff) + { + case FU_VOLUME_CONTROL: + { + buffer[0] = volsOut[ sp.wValue&0xff ]; + buffer[1] = volsOut[ sp.wValue&0xff ] >> 8; + break; + } + case FU_MUTE_CONTROL: + { + buffer[0] = mutesOut[ sp.wValue & 0xff ]; + break; + } + } + } + else if (unitID == FU_USBIN) + { + switch ((sp.wValue>>8) & 0xff) + { + case FU_VOLUME_CONTROL: + { + buffer[0] = volsIn[ sp.wValue&0xff ]; + buffer[1] = volsIn[ sp.wValue&0xff ] >> 8; + break; + } + case FU_MUTE_CONTROL: + { + buffer[0] = mutesIn[ sp.wValue & 0xff ]; + break; + } + } + } + else if(unitID == 0) + { + printintln(unitID); + } + + loop = XUD_SetBuffer_ResetPid(c_ep0_in, buffer, sp.wLength, PIDn_DATA1); + + if(loop < 0) + return loop; + + /* Status stage (0 length OUT) */ + return XUD_GetBuffer(c_ep0_out,buffer); + break; + } + case B_REQ_GET_MIN: + { + buffer[0] = (MIN_MIXER_VOLUME & 0xff); + buffer[1] = (MIN_MIXER_VOLUME >> 8); + + loop = XUD_SetBuffer_ResetPid(c_ep0_in, buffer, sp.wLength, PIDn_DATA1); + + if(loop < 0) + return loop; + + // Status stage (0 length OUT) + return XUD_GetBuffer(c_ep0_out, buffer); + break; + } + case B_REQ_GET_MAX: + { + buffer[0] = (MAX_MIXER_VOLUME & 0xff); + buffer[1] = (MAX_MIXER_VOLUME >> 8); + + loop = XUD_SetBuffer_ResetPid(c_ep0_in, buffer, sp.wLength, PIDn_DATA1); + + if(loop < 0) + return 0; + + // Status stage (0 length OUT) + return XUD_GetBuffer(c_ep0_out, buffer); + break; + } + case B_REQ_GET_RES: + { + buffer[0] = (VOLUME_RES_MIXER & 0xff); + buffer[1] = (VOLUME_RES_MIXER >> 8); + loop = XUD_SetBuffer_ResetPid(c_ep0_in, buffer, sp.wLength, PIDn_DATA1); + + if(loop < 0) + return loop; + + // Status stage (0 length OUT) + return XUD_GetBuffer(c_ep0_out, buffer); + break; + } + } + + return 1; + +} +#endif + diff --git a/module_usb_aud_shared/endpoint0/cscope.out b/module_usb_aud_shared/endpoint0/cscope.out new file mode 100644 index 00000000..897a1106 --- /dev/null +++ b/module_usb_aud_shared/endpoint0/cscope.out @@ -0,0 +1,1829 @@ +cscope 15 $HOME/apps/apps_usb_aud_shared/module_usb_aud_shared/endpoint0 0000011173 + @dbcalc.h + +1 #iâdeà +__dbÿlc_h__ + + +2  + #__dbÿlc_h__ + + + ) + +18  +db_to_muÉ +( +db +,  +db_äac_b™s +,  +»suÉ_äac_b™s +); + + @descriptors_2.h + +8 #iâdeà +_DEVICE_DESCRIPTORS_2_ + + +9  + #_DEVICE_DESCRIPTORS_2_ + + + ) + +11  + ~"usb.h +" + +12  + ~"usbaudio20.h +" + +13  + ~"deviûdefšes.h +" + +17 #ià +defšed +( +AUDIO_CLASS_FALLBACK +è|| ( +AUDIO_CLASS +==1) + +19  + gdevDesc_Audio1 +[] = + +22 +DEVICE +, + +29 ( +VENDOR_ID + & 0xFF), + +30 ( +VENDOR_ID + >> 8), + +31 ( +PID_AUDIO_1 + & 0xFF), + +32 ( +PID_AUDIO_1 + >> 8), + +33 ( +BCD_DEVICE + & 0xFF), + +34 ( +BCD_DEVICE + >> 8), + +43  + gdevDesc_Audio2 +[] = + +46 +DEVICE +, + +53 ( +VENDOR_ID + & 0xFF), + +54 ( +VENDOR_ID + >> 8), + +55 ( +PID_AUDIO_2 + & 0xFF), + +56 ( +PID_AUDIO_2 + >> 8), + +57 ( +BCD_DEVICE + & 0xFF), + +58 ( +BCD_DEVICE + >> 8), + +67  + gdevDesc_NuÎ +[] = + +70 +DEVICE +, + +77 ( +VENDOR_ID + & 0xFF), + +78 ( +VENDOR_ID + >> 8), + +79 ( +PID_AUDIO_2 + & 0xFF), + +80 ( +PID_AUDIO_2 + >> 8), + +81 ( +BCD_DEVICE + & 0xFF), + +82 ( +BCD_DEVICE + >> 8), + +93  + gdevQu®Desc_Audio2 +[] = + +96 +DEVICE_QUALIFIER +, + +107 #ià +defšed +( +AUDIO_CLASS_FALLBACK +è|| ( +AUDIO_CLASS +==1) + +109  + gdevQu®Desc_Audio1 +[] = + +112 +DEVICE_QUALIFIER +, + +125  + gdevQu®Desc_NuÎ +[] = + +128 +DEVICE_QUALIFIER +, + +143  + #LEN_OUTPUT_TERMINAL + (0x0C) + + ) + +144  + #LEN_INPUT_TERMINAL + (0x11) + + ) + +147  + #LEN_TERMS_OUT + (( +LEN_OUTPUT_TERMINAL + + +LEN_INPUT_TERMINAL +è* +OUTPUT_INTERFACES +) + + ) + +148  + #LEN_TERMS_IN + (( +LEN_OUTPUT_TERMINAL + + +LEN_INPUT_TERMINAL +è* +INPUT_INTERFACES +) + + ) + +151 #ifdeà +OUTPUT + + +152  + #LEN_FU_OUT + (6 + ( +NUM_USB_CHAN_OUT + + 1è* 4) + + ) + +154  + #LEN_FU_OUT + 0 + + ) + +157 #ifdeà +INPUT + + +158  + #LEN_FU_IN + (6 + ( +NUM_USB_CHAN_IN + + 1è* 4) + + ) + +160  + #LEN_FU_IN + 0 + + ) + +164 #ifdeà +MIDI + + +165  + #MIDI_LENGTH + (92) + + ) + +167  + #MIDI_LENGTH + (0) + + ) + +170 #ifdeà +MIXER + + +171  + #MIXER_LENGTH + (13+18+18) + + ) + +173  + #MIXER_LENGTH + (0) + + ) + +178  + #LEN_CLK + (8) + + ) + +179  + #LEN_CLK_SEL + (7 + +NUM_CLOCKS +) + + ) + +180  + #LEN_CLOCKING + ( +LEN_CLK_SEL + + ( +NUM_CLOCKS + * +LEN_CLK +)) + + ) + +183  + #LEN_AC + (9) + + ) + +184  + #TLEN_AC + ( +LEN_AC + + +LEN_FU_OUT + + +LEN_FU_IN + + +LEN_CLOCKING + + +LEN_TERMS_OUT + + +LEN_TERMS_IN +) + + ) + +187 #ifdeà +ADAT_RX + + +188  + #INPUT_ALT_LENGTH + (46) + + ) + +190  + #INPUT_ALT_LENGTH + 0 + + ) + +193 #ifdeà +ADAT_TX + + +194  + #OUTPUT_ALT_LENGTH + 46 + + ) + +196  + #OUTPUT_ALT_LENGTH + 0 + + ) + +200  + #CFG_TOTAL_LENGTH_A2 + (7 + 26 + ( +INPUT_INTERFACES + * 55è+ ( +OUTPUT_INTERFACES + * 62è+ ( +MIDI_LENGTH +è+ ( +DFU_INTERFACES + * 16è+ +TLEN_AC + + ( +MIXER_LENGTH +è+ +INPUT_ALT_LENGTH + + +OUTPUT_ALT_LENGTH +) + + ) + +203  + #AUDIO_INTERFACES + ( +INPUT_INTERFACES + + +OUTPUT_INTERFACES + + 1) + + ) + +206  + gcfgDesc_Audio2 +[] = + +209 +CONFIGURATION +, + +210 ( +CFG_TOTAL_LENGTH_A2 + & 0xFF), + +211 ( +CFG_TOTAL_LENGTH_A2 + >> 8), + +212 +NUM_INTERFACES +, + +215 #ifdeà +SELF_POWERED + + +226 +AUDIO_INTERFACES +, + +227 +AUDIO_FUNCTION +, + +228 +FUNCTION_SUBCLASS_UNDEFINED +, + +229 +AF_VERSION_02_00 +, + +234 +INTERFACE +, + +238 +AUDIO +, + +239 +AUDIOCONTROL +, + +240 +IP_VERSION_02_00 +, + +244 +LEN_AC +, + +245 +CS_INTERFACE +, + +246 +HEADER +, + +248 +IO_BOX +, + +249 ( +TLEN_AC + & 0xFF), + +250 ( +TLEN_AC + >> 8), + +254 +LEN_CLK +, + +255 +CS_INTERFACE +, + +256 +CLOCK_SOURCE +, + +257 +ID_CLKSRC_INT +, + +273 #ifdeà +SPDIF_RX + + +275 +LEN_CLK +, + +276 +CS_INTERFACE +, + +277 +CLOCK_SOURCE +, + +278 +ID_CLKSRC_EXT +, + +294 #ifdeà +ADAT_RX + + +296 +LEN_CLK +, + +297 +CS_INTERFACE +, + +298 +CLOCK_SOURCE +, + +299 +ID_CLKSRC_ADAT +, + +316 +LEN_CLK_SEL +, + +317 +CS_INTERFACE +, + +318 +CLOCK_SELECTOR +, + +319 +ID_CLKSEL +, + +320 +NUM_CLOCKS +, + +321 +ID_CLKSRC_INT +, + +322 #ifdeà +SPDIF_RX + + +323 +ID_CLKSRC_EXT +, + +325 #ifdeà +ADAT_RX + + +326 +ID_CLKSRC_ADAT +, + +334 #ifdeà +OUTPUT + + +338 +CS_INTERFACE +, + +339 +INPUT_TERMINAL +, + +340 +ID_IT_USB +, + +341 ( +USB_STREAMING +&0xff),USB_STREAMING>>8, + +343 +ID_CLKSEL +, + +344 +NUM_USB_CHAN_OUT +, + +351 +LEN_FU_OUT +, + +354 +FU_USBOUT +, + +355 +ID_IT_USB +, + +356 #ià( +NUM_USB_CHAN_OUT + > 0) + +360 #ià( +NUM_USB_CHAN_OUT + > 1) + +363 #ià( +NUM_USB_CHAN_OUT + > 2) + +366 #ià( +NUM_USB_CHAN_OUT + > 3) + +369 #ià( +NUM_USB_CHAN_OUT + > 4) + +372 #ià( +NUM_USB_CHAN_OUT + > 5) + +375 #ià( +NUM_USB_CHAN_OUT + > 6) + +378 #ià( +NUM_USB_CHAN_OUT + > 7) + +381 #ià( +NUM_USB_CHAN_OUT + > 8) + +384 #ià( +NUM_USB_CHAN_OUT + > 9) + +387 #ià( +NUM_USB_CHAN_OUT + > 10) + +390 #ià( +NUM_USB_CHAN_OUT + > 11) + +393 #ià( +NUM_USB_CHAN_OUT + > 12) + +396 #ià( +NUM_USB_CHAN_OUT + > 13) + +399 #ià( +NUM_USB_CHAN_OUT + > 14) + +402 #ià( +NUM_USB_CHAN_OUT + > 15) + +405 #ià( +NUM_USB_CHAN_OUT + > 16) + +408 #ià( +NUM_USB_CHAN_OUT + > 17) + +411 #ià( +NUM_USB_CHAN_OUT + > 18) + +412 #”rÜ +NUM_USB_CHAN_OUT + > 18 + +418 +CS_INTERFACE +, + +419 +OUTPUT_TERMINAL +, + +420 +ID_OT_AUD +, + +421 ( +SPEAKER +&0xff),SPEAKER>>8, + +423 +FU_USBOUT +, + +424 +ID_CLKSEL +, + +429 #ifdeà +INPUT + + +433 +CS_INTERFACE +, + +434 +INPUT_TERMINAL +, + +435 +ID_IT_AUD +, + +436 ( +MICROPHONE_ +&0xff),MICROPHONE_>>8, + +438 +ID_CLKSEL +, + +439 +NUM_USB_CHAN_IN +, + +446 +LEN_FU_IN +, + +447 +CS_INTERFACE +, + +448 +FEATURE_UNIT +, + +449 +FU_USBIN +, + +450 +ID_IT_AUD +, + +451 #ià( +NUM_USB_CHAN_IN + > 0) + +455 #ià( +NUM_USB_CHAN_IN + > 1) + +458 #ià( +NUM_USB_CHAN_IN + > 2) + +461 #ià( +NUM_USB_CHAN_IN + > 3) + +464 #ià( +NUM_USB_CHAN_IN + > 4) + +467 #ià( +NUM_USB_CHAN_IN + > 5) + +470 #ià( +NUM_USB_CHAN_IN + > 6) + +473 #ià( +NUM_USB_CHAN_IN + > 7) + +476 #ià( +NUM_USB_CHAN_IN + > 8) + +479 #ià( +NUM_USB_CHAN_IN + > 9) + +482 #ià( +NUM_USB_CHAN_IN + > 10) + +485 #ià( +NUM_USB_CHAN_IN + > 11) + +488 #ià( +NUM_USB_CHAN_IN + > 12) + +491 #ià( +NUM_USB_CHAN_IN + > 13) + +494 #ià( +NUM_USB_CHAN_IN + > 14) + +497 #ià( +NUM_USB_CHAN_IN + > 15) + +500 #ià( +NUM_USB_CHAN_IN + > 16) + +503 #ià( +NUM_USB_CHAN_IN + > 17) + +506 #ià( +NUM_USB_CHAN_IN + > 18) + +507 #”rÜ +NUM_USB_CHAN + > 18 + +513 +CS_INTERFACE +, + +514 +OUTPUT_TERMINAL +, + +515 +ID_OT_USB +, + +516 ( +USB_STREAMING +&0xff),USB_STREAMING>>8, + +518 +FU_USBIN +, + +519 +ID_CLKSEL +, + +523 #ifdeà +MIXER + + +530 +CS_INTERFACE +, + +532 +ID_MIXER_1 +, + +534 +ID_IT_USB +, + +535 +ID_IT_USB +, + +536 +ID_IT_USB +, + +537 +ID_IT_USB +, + +538 +ID_IT_USB +, + +539 +ID_IT_USB +, + +540 +ID_IT_USB +, + +541 +ID_IT_USB +, + +542 +ID_IT_USB +, + +543 +ID_IT_USB +, + +544 +ID_IT_AUD +, + +545 +ID_IT_AUD +, + +546 +ID_IT_AUD +, + +547 +ID_IT_AUD +, + +548 +ID_IT_AUD +, + +549 +ID_IT_AUD +, + +550 +ID_IT_AUD +, + +551 +ID_IT_AUD +, + +574 #ifdeà +OUTPUT + + +577 +INTERFACE +, + +581 +AUDIO +, + +582 +AUDIOSTREAMING +, + +583 +IP_VERSION_02_00 +, + +588 +INTERFACE +, + +592 +AUDIO +, + +593 +AUDIOSTREAMING +, + +594 +IP_VERSION_02_00 +, + +599 +CS_INTERFACE +, + +600 +AS_GENERAL +, + +601 +ID_IT_USB +, + +604 +PCM +, 0x00, 0x00, 0x00, + +605 #ifdeà +ADAT_TX + + +606 +NUM_USB_CHAN_OUT +-8, + +608 +NUM_USB_CHAN_OUT +, + +615 +CS_INTERFACE +, + +616 +FORMAT_TYPE +, + +617 +FORMAT_TYPE_I +, + +623 +ENDPOINT +, + +631 +CS_ENDPOINT +, + +640 +ENDPOINT +, + +646 #ifdeà +ADAT_TX + + +649 +INTERFACE +, + +653 +AUDIO +, + +654 +AUDIOSTREAMING +, + +655 +IP_VERSION_02_00 +, + +660 +CS_INTERFACE +, + +661 +AS_GENERAL +, + +662 +ID_IT_USB +, + +665 +PCM +, 0x00, 0x00, 0x00, + +666 +NUM_USB_CHAN_OUT +, + +672 +CS_INTERFACE +, + +673 +FORMAT_TYPE +, + +674 +FORMAT_TYPE_I +, + +680 +ENDPOINT +, + +688 +CS_ENDPOINT +, + +699 #ifdeà +INPUT + + +702 +INTERFACE +, + +703 ( +OUTPUT_INTERFACES + + 1), + +706 +AUDIO +, + +707 +AUDIOSTREAMING +, + +713 +INTERFACE +, + +714 ( +OUTPUT_INTERFACES + + 1), + +717 +AUDIO +, + +718 +AUDIOSTREAMING +, + +719 +IP_VERSION_02_00 +, + +724 +CS_INTERFACE +, + +725 +AS_GENERAL +, + +726 +ID_OT_USB +, + +729 +PCM +, 0x00, 0x00, 0x00, + +730 #ifdeà +ADAT_RX + + +731 +NUM_USB_CHAN_IN +, + +733 +NUM_USB_CHAN_IN +, + +740 +CS_INTERFACE +, + +741 +FORMAT_TYPE +, + +742 +FORMAT_TYPE_I +, + +748 +ENDPOINT +, + +756 +CS_ENDPOINT +, + +763 #ifdeà +ADAT_RX + + +766 +INTERFACE +, + +767 ( +OUTPUT_INTERFACES + + 1), + +770 +AUDIO +, + +771 +AUDIOSTREAMING +, + +772 +IP_VERSION_02_00 +, + +777 +CS_INTERFACE +, + +778 +AS_GENERAL +, + +779 +ID_OT_USB +, + +782 +PCM +, 0x00, 0x00, 0x00, + +783 +NUM_USB_CHAN_IN + - 4, + +789 +CS_INTERFACE +, + +790 +FORMAT_TYPE +, + +791 +FORMAT_TYPE_I +, + +797 +ENDPOINT +, + +805 +CS_ENDPOINT +, + +815 +INTERFACE +, + +816 ( +OUTPUT_INTERFACES + + 1), + +819 +AUDIO +, + +820 +AUDIOSTREAMING +, + +821 +IP_VERSION_02_00 +, + +826 +CS_INTERFACE +, + +827 +AS_GENERAL +, + +828 +ID_OT_USB +, + +831 +PCM +, 0x00, 0x00, 0x00, + +832 +NUM_USB_CHAN_IN +-6, + +838 +CS_INTERFACE +, + +839 +FORMAT_TYPE +, + +840 +FORMAT_TYPE_I +, + +846 +ENDPOINT +, + +854 +CS_ENDPOINT +, + +866 #ifdeà +MIDI + + +871 ( +INPUT_INTERFACES + + +OUTPUT_INTERFACES + + 1), + +893 ( +INPUT_INTERFACES ++ +OUTPUT_INTERFACES ++2), + +985 #ifdeà +DFU + + +989 ( +INPUT_INTERFACES ++ +OUTPUT_INTERFACES ++ +MIDI_INTERFACES ++1), + +1009  + #APPEND_VENDOR_STR +( +x +è +VENDOR_STR +#x + + ) + +1013  + g¡rDescs_Audio2 +[][40] = + +1016 +APPEND_VENDOR_STR +( ), + +1017 +APPEND_VENDOR_STR +( +USB + +Audio + 2.0), + +1018 +SERIAL_STR +, + +1019 +APPEND_VENDOR_STR +( +USB + 2.0 +Audio + +Out +), + +1020 +APPEND_VENDOR_STR +( +USB + 2.0 +Audio + +In +), + +1022 +APPEND_VENDOR_STR +( +Audio + 2.0 +Ouut +), + +1023 +APPEND_VENDOR_STR +( +Audio + 2.0 +IÅut +), + +1025 +APPEND_VENDOR_STR +( +Clock + +S–eùÜ +), + +1026 +APPEND_VENDOR_STR +( +IÁ”Çl + +Clock +), + +1027 +APPEND_VENDOR_STR +( +S +/ +PDIF + +Clock +), + +1028 +APPEND_VENDOR_STR +( +ADAT + +Clock +), + +1029 +APPEND_VENDOR_STR +( +DFU +), + +1081  + gcfgDesc_NuÎ +[] = + +1084 +CONFIGURATION +, + +1090 #ifdeà +SELF_POWERED + + +1111  + goS³edCfgDesc +[] = + +1114 +OTHER_SPEED_CONFIGURATION +, + +1137  + #AC_LENGTH + (8 + +INPUT_INTERFACES + + +OUTPUT_INTERFACES +) + + ) + +1138  + #AC_TOTAL_LENGTH + ( +AC_LENGTH + + ( +INPUT_INTERFACES + * 31è+ ( +OUTPUT_INTERFACES + * 31)) + + ) + +1139  + #STREAMING_INTERFACES + ( +INPUT_INTERFACES + + +OUTPUT_INTERFACES +) + + ) + +1142  + #CFG_TOTAL_LENGTH_A1 + (18 + +AC_TOTAL_LENGTH + + ( +INPUT_INTERFACES + * 61è+ ( +OUTPUT_INTERFACES + * 70è+ ( +DFU_INTERFACES + * 16)) + + ) + +1143 #ifdeà +AUDIO_CLASS_FALLBACK + + +1144  + gcfgDesc_Audio1 +[] = + +1147 +CONFIGURATION +, + +1148 ( +CFG_TOTAL_LENGTH_A1 + & 0xFF), + +1149 ( +CFG_TOTAL_LENGTH_A1 + >> 8), + +1150 #ifdeà +MIDI + + +1151 +NUM_INTERFACES +-2, + +1153 +NUM_INTERFACES +, + +1162 +INTERFACE +, + +1166 +AUDIO +, + +1167 +AUDIOCONTROL +, + +1172 +AC_LENGTH +, + +1173 +CS_INTERFACE +, + +1176 ( +AC_TOTAL_LENGTH + & 0xFF), + +1177 ( +AC_TOTAL_LENGTH + >> 8), + +1178 +STREAMING_INTERFACES +, + +1179 #ifdeà +OUTPUT + + +1182 #ifdeà +INPUT + + +1183 ( +OUTPUT_INTERFACES + + 1), + +1186 #ifdeà +OUTPUT + + +1189 +CS_INTERFACE +, + +1201 +CS_INTERFACE +, + +1202 +FEATURE_UNIT +, + +1213 +CS_INTERFACE +, + +1223 #ifdeà +INPUT + + +1226 +CS_INTERFACE +, + +1238 +CS_INTERFACE +, + +1248 +CS_INTERFACE +, + +1249 +FEATURE_UNIT +, + +1260 #ifdeà +OUTPUT + + +1285 +CS_INTERFACE +, + +1293 +CS_INTERFACE +, + +1302 #ià +defšed +( +OUTPUT +è&& defšed( +INPUT +) + +1315 #ià +defšed +( +OUTPUT +è&& defšed( +INPUT +) + +1343 #ifdeà +INPUT + + +1347 ( +OUTPUT_INTERFACES + + 1), + +1358 ( +OUTPUT_INTERFACES + + 1), + +1368 +CS_INTERFACE +, + +1376 +CS_INTERFACE +, + +1385 #ià +defšed +( +OUTPUT +è&& defšed( +INPUT +) + +1398 #ià +defšed +( +OUTPUT +è&& defšed( +INPUT +) + +1416 #ifdeà +DFU + + +1420 ( +INPUT_INTERFACES ++ +OUTPUT_INTERFACES ++1), + +1440  + #APPEND_VENDOR_STR +( +x +è +VENDOR_STR +#x + + ) + +1442  + g¡rDescs_Audio1 +[][40] = + +1445 +APPEND_VENDOR_STR +(), + +1446 +APPEND_VENDOR_STR +( +USB + +Audio + 1.0), + +1447 +SERIAL_STR +, + +1449 +APPEND_VENDOR_STR +( +USB + 1.0 +Audio + +Out +), + +1450 +APPEND_VENDOR_STR +( +USB + 1.0 +Audio + +In +), + +1452 +APPEND_VENDOR_STR +( +Audio + 1.0 +Ouut +), + +1453 +APPEND_VENDOR_STR +( +Audio + 1.0 +IÅut +), + +1455 +APPEND_VENDOR_STR +( +DFU +) + + @endpoint0.h + +2 #iâdeà +_ENDPOINT0_H_ + + +3  + #_ENDPOINT0_H_ + + + ) + +5  +Endpošt0 +Ð +chª’d + +c_•0_out +, chª’d +c_•0_š +, chª’d +c_audioCŒl +, chª’d ? +c_mix_ùl +,chª’d ? +c_þk_ùl + + +6 #ifdeà +EP0_THREAD_COMBINED_WITH_SPI + + +7 , +chª’d + +c_¥i +, chª’d +c_¥i_ss + + + @ +1 +. +1 +/usr/include +3 +37 +dbcalc.h +descriptors_2.h +endpoint0.h diff --git a/module_usb_aud_shared/endpoint0/dbcalc.h b/module_usb_aud_shared/endpoint0/dbcalc.h new file mode 100644 index 00000000..2207d005 --- /dev/null +++ b/module_usb_aud_shared/endpoint0/dbcalc.h @@ -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__ diff --git a/module_usb_aud_shared/endpoint0/dbcalc.xc b/module_usb_aud_shared/endpoint0/dbcalc.xc new file mode 100644 index 00000000..e1a70f3b --- /dev/null +++ b/module_usb_aud_shared/endpoint0/dbcalc.xc @@ -0,0 +1,127 @@ +#include +#include +/* 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; + + if (intpart) { + val0 = 1 << DB_CALC_PREC; + for (int i=0;i> DB_CALC_PREC; + if (intpart) { + val0 = 1 << DB_CALC_PREC; + for (int i=0;i> 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 + +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 diff --git a/module_usb_aud_shared/endpoint0/descriptors_2.h b/module_usb_aud_shared/endpoint0/descriptors_2.h new file mode 100644 index 00000000..c1b86fee --- /dev/null +++ b/module_usb_aud_shared/endpoint0/descriptors_2.h @@ -0,0 +1,1619 @@ +/** + * @file DeviceDescriptors_2.h + * @brief Device Descriptors and params for Audio 2.0 + * @author Ross Owen, XMOS Limited + * @version 1.4 +*/ + +#ifndef _DEVICE_DESCRIPTORS_2_ +#define _DEVICE_DESCRIPTORS_2_ + +#include "usb.h" +#include "usbaudio20.h" /* Defines from the USB Audio 2.0 Specifications */ +#include "devicedefines.h" /* Define specific define */ + +/***** Device Descriptors *****/ + +#if defined(AUDIO_CLASS_FALLBACK) || (AUDIO_CLASS==1) +/* Device Descriptor for Audio Class 1.0 (Assumes Full-Speed) */ +unsigned char devDesc_Audio1[] = +{ + 18, /* 0 bLength : Size of descriptor in Bytes (18 Bytes) */ + DEVICE, /* 1 bdescriptorType */ + 0x0, /* 2 bcd USB */ + 0x1, /* 3 bcdUSB */ + 0, /* 4 bDeviceClass */ + 0, /* 5 bDeviceSubClass */ + 0, /* 6 bDeviceProtocol */ + 64, /* 7 bMaxPacketSize */ + (VENDOR_ID & 0xFF), /* 8 idVendor */ + (VENDOR_ID >> 8), /* 9 idVendor */ + (PID_AUDIO_1 & 0xFF), /* 10 idProduct */ + (PID_AUDIO_1 >> 8), /* 11 idProduct */ + (BCD_DEVICE & 0xFF), /* 12 bcdDevice : Device release number */ + (BCD_DEVICE >> 8), /* 13 bcdDevice : Device release number */ + MANUFACTURER_STR_INDEX, /* 14 iManufacturer : Index of manufacturer string */ + PRODUCT_STR_INDEX, /* 15 iProduct : Index of product string descriptor */ + SERIAL_STR_INDEX, /* 16 iSerialNumber : Index of serial number decriptor */ + 0x01 /* 17 bNumConfigurations : Number of possible configs. */ +}; +#endif + +/* Device Descriptor for Audio Class 2.0 (Assumes High-Speed ) */ +unsigned char devDesc_Audio2[] = +{ + 18, /* 0 bLength : Size of descriptor in Bytes (18 Bytes) */ + DEVICE, /* 1 bdescriptorType */ + 0, /* 2 bcdUSB */ + 2, /* 3 bcdUSB */ + 0xEF, /* 4 bDeviceClass (See Audio Class Spec page 45) */ + 0x02, /* 5 bDeviceSubClass */ + 0x01, /* 6 bDeviceProtocol */ + 64, /* 7 bMaxPacketSize */ + (VENDOR_ID & 0xFF), /* 8 idVendor */ + (VENDOR_ID >> 8), /* 9 idVendor */ + (PID_AUDIO_2 & 0xFF), /* 10 idProduct */ + (PID_AUDIO_2 >> 8), /* 11 idProduct */ + (BCD_DEVICE & 0xFF), /* 12 bcdDevice : Device release number */ + (BCD_DEVICE >> 8), /* 13 bcdDevice : Device release number */ + MANUFACTURER_STR_INDEX, /* 14 iManufacturer : Index of manufacturer string */ + PRODUCT_STR_INDEX, /* 15 iProduct : Index of product string descriptor */ + SERIAL_STR_INDEX, /* 16 iSerialNumber : Index of serial number decriptor */ + 0x02 /* 17 bNumConfigurations : Number of possible configs. Set to 2 so that Windows + does not load Composite driver. */ +}; + +/* Device Descriptor for Null Device */ +unsigned char devDesc_Null[] = +{ + 18, /* 0 bLength : Size of descriptor in Bytes (18 Bytes) */ + DEVICE, /* 1 bdescriptorType */ + 0, /* 2 bcdUSB */ + 2, /* 3 bcdUSB */ + 0x0, /* 4 bDeviceClass */ + 0x0 , /* 5 bDeviceSubClass */ + 0x00, /* 6 bDeviceProtocol */ + 64, /* 7 bMaxPacketSize */ + (VENDOR_ID & 0xFF), /* 8 idVendor */ + (VENDOR_ID >> 8), /* 9 idVendor */ + (PID_AUDIO_2 & 0xFF), /* 10 idProduct */ + (PID_AUDIO_2 >> 8), /* 11 idProduct */ + (BCD_DEVICE & 0xFF), /* 12 bcdDevice : Device release number */ + (BCD_DEVICE >> 8), /* 13 bcdDevice : Device release number */ + MANUFACTURER_STR_INDEX, /* 14 iManufacturer : Index of manufacturer string */ + PRODUCT_STR_INDEX, /* 15 iProduct : Index of product string descriptor */ + SERIAL_STR_INDEX, /* 16 iSerialNumber : Index of serial number decriptor */ + 0x01 /* 17 bNumConfigurations : Number of possible configs */ +}; + + +/****** Device Qualifier Descriptors *****/ + +/* Device Qualifier Descriptor for Audio 2.0 device (Use when running at full-speed. Matches audio 2.0 device descriptor) */ +unsigned char devQualDesc_Audio2[] = +{ + 10, /* 0 bLength (10 Bytes) */ + DEVICE_QUALIFIER, /* 1 bDescriptorType */ + 0x00, /* 2 bcdUSB (Binary Coded Decimal of usb version) */ + 0x02, /* 3 bcdUSB */ + 0xEF, /* 4 bDeviceClass */ + 0x02, /* 5 bDeviceSubClass */ + 0x01, /* 6 bDeviceProtocol */ + 64, /* 7 bMaxPacketSize */ + 0x01, /* 8 bNumConfigurations : Number of possible configs */ + 0x00 /* 9 bReserved (must be zero) */ +}; + +#if defined(AUDIO_CLASS_FALLBACK) || (AUDIO_CLASS==1) +/* Device Qualifier Descriptor for running at high-speed (matches audio 1.0 device descriptor) */ +unsigned char devQualDesc_Audio1[] = +{ + 10, /* 0 bLength (10 Bytes) */ + DEVICE_QUALIFIER, /* 1 bDescriptorType */ + 0x00, /* 2 bcdUSB (Binary Coded Decimal of usb version) */ + 0x02, /* 3 bcdUSB */ + 0x00, /* 4 bDeviceClass */ + 0x00, /* 5 bDeviceSubClass */ + 0x00, /* 6 bDeviceProtocol */ + 64, /* 7 bMaxPacketSize */ + 0x01, /* 8 bNumConfigurations : Number of possible configs */ + 0x00 /* 9 bReserved (must be zero) */ +}; +#endif + +/* Device Qualifier Descriptor for Null Device (Use when running at high-speed) */ +unsigned char devQualDesc_Null[] = +{ + 10, /* 0 bLength (10 Bytes) */ + DEVICE_QUALIFIER, /* 1 bDescriptorType */ + 0x00, /* 2 bcdUSB (Binary Coded Decimal of usb version) */ + 0x02, /* 3 bcdUSB */ + 0x00, /* 4 bDeviceClass */ + 0x00, /* 5 bDeviceSubClass */ + 0x00, /* 6 bDeviceProtocol */ + 64, /* 7 bMaxPacketSize */ + 0x01, /* 8 bNumConfigurations : Number of possible configs */ + 0x00 /* 9 bReserved (must be zero) */ +}; + + +#if defined(MIXER) && !defined(AUDIO_PATH_XUS) +#warning Extention units on the audio path are required for mixer. Enabling them now. +#define AUDIO_PATH_XUS +#endif + +/* Lenths of input/output term descriptors - from spec */ +#define LEN_OUTPUT_TERMINAL (0x0C) +#define LEN_INPUT_TERMINAL (0x11) + +/* Lengh of out terminal descriptors in total */ +#define LEN_TERMS_OUT ((LEN_OUTPUT_TERMINAL + LEN_INPUT_TERMINAL) * OUTPUT_INTERFACES) +#define LEN_TERMS_IN ((LEN_OUTPUT_TERMINAL + LEN_INPUT_TERMINAL) * INPUT_INTERFACES) + +/* Calc total length of configuration desc based on defines */ +#ifdef OUTPUT +#define LEN_FU_OUT (6 + (NUM_USB_CHAN_OUT + 1) * 4) +#else +#define LEN_FU_OUT 0 +#endif + +#ifdef INPUT +#define LEN_FU_IN (6 + (NUM_USB_CHAN_IN + 1) * 4) +#else +#define LEN_FU_IN 0 +#endif + + +#ifdef MIDI +#define MIDI_LENGTH (92) +#else +#define MIDI_LENGTH (0) +#endif + +#ifdef AUDIO_PATH_XUS +#define LEN_XU_OUT (16 * OUTPUT_INTERFACES) +#define LEN_XU_IN (16 * INPUT_INTERFACES) +#else +#define LEN_XU_OUT (0) +#define LEN_XU_IN (0) +#endif + +#ifdef MIXER + #define LEN_XU_MIX (17) + #define MIX_BMCONTROLS_LEN_TMP ((MAX_MIX_COUNT * MIX_INPUTS) / 8) + + #if ((MAX_MIX_COUNT * MIX_INPUTS)%8)==0 + #define MIX_BMCONTROLS_LEN (MIX_BMCONTROLS_LEN_TMP) + #else + #define MIX_BMCONTROLS_LEN (MIX_BMCONTROLS_LEN_TMP+1) + #endif + #define MIXER_LENGTH (13+1+MIX_BMCONTROLS_LEN) +#else + #define LEN_XU_MIX (0) + #define MIXER_LENGTH (0) +#endif + +#define LEN_CLK (8) +#define LEN_CLK_SEL (7 + NUM_CLOCKS) +#define LEN_CLOCKING (LEN_CLK_SEL + (NUM_CLOCKS * LEN_CLK)) + +/* Total length of the Class-Specific AC Interface Descriptor - Clock Entities, Units and Terminals */ +#define LEN_AC (9) +#define TLEN_AC (LEN_AC + LEN_FU_OUT + LEN_FU_IN + LEN_CLOCKING + LEN_TERMS_OUT + LEN_TERMS_IN + LEN_XU_OUT + LEN_XU_IN + LEN_XU_MIX) + + +#ifdef ADAT_RX +#define INPUT_ALT_LENGTH (46) +#else +#define INPUT_ALT_LENGTH 0 +#endif + +#ifdef ADAT_TX +#define OUTPUT_ALT_LENGTH 46 +#else +#define OUTPUT_ALT_LENGTH 0 +#endif + + +/* Total length of config descriptor */ +#define CFG_TOTAL_LENGTH_A2 (7 + 26 + (INPUT_INTERFACES * 55) + (OUTPUT_INTERFACES * 62) + (MIDI_LENGTH) + (DFU_INTERFACES * 18) + TLEN_AC + (MIXER_LENGTH) + INPUT_ALT_LENGTH + OUTPUT_ALT_LENGTH) + +/* Define for number of audio interfaces (+1 for mandatory control interface) */ +#define AUDIO_INTERFACES (INPUT_INTERFACES + OUTPUT_INTERFACES + 1) + +/* Configuration Descriptor for Audio 2.0 (HS) operation */ +unsigned char cfgDesc_Audio2[] = +{ + 0x09, /* 0 bLength */ + CONFIGURATION, /* 1 bDescriptorType */ + (CFG_TOTAL_LENGTH_A2 & 0xFF), /* 2 wTotalLength */ + (CFG_TOTAL_LENGTH_A2 >> 8), /* 3 wTotalLength */ + NUM_INTERFACES, /* 4 bNumInterface: Number of interfaces*/ + 0x01, /* 5 bConfigurationValue */ + 0x00, /* 6 iConfiguration */ +#ifdef SELF_POWERED + 192, /* 7 bmAttributes */ + 5, /* 8 bMaxPower */ +#else + 128, /* 7 bmAttributes */ + 250, /* 8 bMaxPower */ +#endif + + /* Interface Association Descriptor */ + 0x08, /* 0 bLength */ + 0x0b, /* 1 bDescriptorType */ + 0x00, /* 2 bFirstInterface */ + AUDIO_INTERFACES, /* 3 bInterfaceCount */ + AUDIO_FUNCTION, /* 4 bFunctionClass: AUDIO_FUNCTION */ + FUNCTION_SUBCLASS_UNDEFINED, /* 5 bFunctionSubClass: FUNCTION_SUBCLASS_UNDEFINED */ + AF_VERSION_02_00, /* 6 bFunctionProtocol: AF_VERSION_02_00 */ + 0x00, /* 7 iFunction (String Index) *(re-use iProduct) */ + + /* Standard Audio Control Interface Descriptor (Note: Must be first with lowest interface number)r */ + 0x09, /* 0 bLength: 9 */ + INTERFACE, /* 1 bDescriptorType: INTERFACE */ + 0x00, /* 2 bInterfaceNumber */ + 0x00, /* 3 bAlternateSetting: Must be 0 */ + 0x01, /* 4 bNumEndpoints (0 or 1 if optional interrupt endpoint is present */ + AUDIO, /* 5 bInterfaceClass: AUDIO */ + AUDIOCONTROL, /* 6 bInterfaceSubClass: AUDIOCONTROL*/ + IP_VERSION_02_00, /* 7 bInterfaceProtocol: IP_VERSION_02_00 */ + PRODUCT_STR_INDEX, /* 8 iInterface (re-use iProduct) */ + + /* Class Specific Audio Control Interface Header Descriptor: */ + LEN_AC, /* 0 bLength */ + CS_INTERFACE, /* 1 bDescriptorType: 0x24 */ + HEADER, /* 2 bDescriptorSubtype: HEADER */ + 0x00, 0x02, /* 3:4 bcdUSB */ + IO_BOX, /* 5 bCatagory (Primary use of audio function) */ + (TLEN_AC & 0xFF), /* 6 wTotalLength */ + (TLEN_AC >> 8), /* 7 wTotalLength */ + 0x00, /* 8 bmControls (0:1 Latency Control, 2:7 must be 0 */ + + /* Clock Source Descriptor (4.7.2.1) */ + LEN_CLK, /* 0 bLength: 8 */ + CS_INTERFACE, /* 1 bDescriptorType */ + CLOCK_SOURCE, /* 2 bDescriptorSubtype */ + ID_CLKSRC_INT, /* 3 bClockID */ + 0x03, /* 4 bmAttributes: + D[1:0] : + 00: External Clock + 01: Internal Fixed Clock + 10: Internal Variable Clock + 11: Internal Progamable Clock + D[2] : Clock synced to SOF + D[7:3] : Reserved (0) */ + 0x07, /* 5 bmControls + D[1:0] : Clock Freq Control + D[3:2] : Clock Validity Control + D[7:4] : Reserved (0) */ + 0x00, /* 6 bAssocTerminal */ + 0x09, /* 7 iClockSource (String Index) */ + +#ifdef SPDIF_RX + /* Clock Source Descriptor (4.7.2.1) */ + LEN_CLK, /* 0 bLength: 8 */ + CS_INTERFACE, /* 1 bDescriptorType */ + CLOCK_SOURCE, /* 2 bDescriptorSubtype */ + ID_CLKSRC_EXT, /* 3 bClockID */ + 0x00, /* 4 bmAttributes: + D[1:0] : + 00: External Clock + 01: Internal Fixed Clock + 10: Internal Variable Clock + 11: Internal Progamable Clock + D[2] : Clock synced to SOF + D[7:3] : Reserved (0) */ + 0x07, /* 5 bmControls + D[1:0] : Clock Freq Control + D[3:2] : Clock Validity Control + D[7:4] : Reserved (0) */ + 0x00, /* 6 bAssocTerminal */ + 10, /* 7 iClockSource (String Index) */ +#endif +#ifdef ADAT_RX + /* Clock Source Descriptor (4.7.2.1) */ + LEN_CLK, /* 0 bLength: 8 */ + CS_INTERFACE, /* 1 bDescriptorType */ + CLOCK_SOURCE, /* 2 bDescriptorSubtype */ + ID_CLKSRC_ADAT, /* 3 bClockID */ + 0x00, /* 4 bmAttributes: + D[1:0] : + 00: External Clock + 01: Internal Fixed Clock + 10: Internal Variable Clock + 11: Internal Progamable Clock + D[2] : Clock synced to SOF + D[7:3] : Reserved (0) */ + 0x07, /* 5 bmControls + D[1:0] : Clock Freq Control + D[3:2] : Clock Validity Control + D[7:4] : Reserved (0) */ + 0x00, /* 6 bAssocTerminal */ + 11, /* 7 iClockSource (String Index) */ +#endif + /* Clock Selector Descriptor (4.7.2.2) */ + LEN_CLK_SEL, /* 0 bLength */ + CS_INTERFACE, /* 1 bDescriptorType */ + CLOCK_SELECTOR, /* 2 bDescriptorSubtype */ + ID_CLKSEL, /* 3 bClockID */ + NUM_CLOCKS, /* 4 Number of input pins*/ + ID_CLKSRC_INT, +#ifdef SPDIF_RX + ID_CLKSRC_EXT, +#endif +#ifdef ADAT_RX + ID_CLKSRC_ADAT, +#endif + 0x03, /* 5 bmControls + D[1:0] : Clock Selector Control + D[7:4] : Reserved (0) */ + 8, /* 7 iClockSel (String Index) */ + +#ifdef OUTPUT + /* OUTPUT PATH FROM HOST TO DEVICE */ + /* Input Terminal Descriptor (USB Input Terminal) */ + 0x11, /* 0 bLength in bytes: 17 */ + CS_INTERFACE, /* 1 bDescriptorType: 0x24 */ + INPUT_TERMINAL, /* 2 bDescriptorSubType: INPUT_TERMINAL */ + ID_IT_USB, /* 3 bTerminalID */ + (USB_STREAMING&0xff),USB_STREAMING>>8, /* 4 wTerminalType: USB Streaming */ + 0x00, /* 6 bAssocTerminal */ + ID_CLKSEL, /* 7 bCSourceID: ID of Clock Entity */ + NUM_USB_CHAN_OUT, /* 8 bNrChannels */ + 0,0,0,0, /* 9 bmChannelConfig */ + 15, /* 13 iChannelNames */ + 0x00, 0x00, /* 14 bmControls */ + 6, /* 16 iTerminal */ + +#ifdef AUDIO_PATH_XUS + /* Extension Unit Descriptor (4.7.2.12) */ + LEN_XU_OUT, /* 0 bLength (15 + p, when p is number of sources) */ + CS_INTERFACE, /* 1 bDescriptorType */ + EXTENSION_UNIT, /* 2 bDescriptorSubtype */ + ID_XU_OUT, /* 3 bUnitID */ + 0, /* 4 wExtensionCode */ + 0, /* 5 wExtensionCode */ + 1, /* 6 bNrPins */ + ID_IT_USB, /* 7 baSourceId(1) */ + NUM_USB_CHAN_OUT, /* 8+p bNrChannels */ + 0, /* 9+p bmChannelConfig */ + 0, /* 10+p bmChannelConfig */ + 0, /* 11+p bmChannelConfig */ + 0, /* 12+p bmChannelConfig */ + 0, /* 13+p iChannelNames */ + 3, /* 14+p bmControls */ + 0, /* 15+p iExtension */ +#endif + + /* Feature Unit Descriptor */ + LEN_FU_OUT, /* 0 bLength: 6+(ch + 1)*4 */ + 0x24, /* 1 bDescriptorType: CS_INTERFACE */ + 0x06, /* 2 bDescriptorSubType: FEATURE_UNIT */ + FU_USBOUT, /* 3 bUnitID */ +#ifdef AUDIO_PATH_XUS + ID_XU_OUT, /* 4 bSourceID */ +#else + ID_IT_USB, /* 4 bSourceID */ +#endif +#if (NUM_USB_CHAN_OUT > 0) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(0) : Mute and Volume host read and writable */ + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(1) */ +#endif +#if (NUM_USB_CHAN_OUT > 1) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(2) */ +#endif +#if (NUM_USB_CHAN_OUT > 2) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(3) */ +#endif +#if (NUM_USB_CHAN_OUT > 3) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(4) */ +#endif +#if (NUM_USB_CHAN_OUT > 4) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(5) */ +#endif +#if (NUM_USB_CHAN_OUT > 5) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(6) */ +#endif +#if (NUM_USB_CHAN_OUT > 6) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(7) */ +#endif +#if (NUM_USB_CHAN_OUT > 7) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(8) */ +#endif +#if (NUM_USB_CHAN_OUT > 8) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(9) */ +#endif +#if (NUM_USB_CHAN_OUT > 9) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(10) */ +#endif +#if (NUM_USB_CHAN_OUT > 10) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(11) */ +#endif +#if (NUM_USB_CHAN_OUT > 11) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(12) */ +#endif +#if (NUM_USB_CHAN_OUT > 12) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(13) */ +#endif +#if (NUM_USB_CHAN_OUT > 13) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(14) */ +#endif +#if (NUM_USB_CHAN_OUT > 14) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(15) */ +#endif +#if (NUM_USB_CHAN_OUT > 15) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(16) */ +#endif +#if (NUM_USB_CHAN_OUT > 16) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(17) */ +#endif +#if (NUM_USB_CHAN_OUT > 17) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(18) */ +#endif +#if (NUM_USB_CHAN_OUT > 18) +#error NUM_USB_CHAN_OUT > 18 +#endif + 0, /* 60 iFeature */ + + /* Output Terminal Descriptor (Audio) */ + 0x0C, /* 0 bLength */ + CS_INTERFACE, /* 1 bDescriptorType: 0x24 */ + OUTPUT_TERMINAL, /* 2 bDescriptorSubType: OUTPUT_TERMINAL */ + ID_OT_AUD, /* 3 bTerminalID */ + (SPEAKER&0xff),SPEAKER>>8, /* 4 wTerminalType */ + 0x00, /* 6 bAssocTerminal */ + FU_USBOUT, /* 7 bSourceID Connect to analog input feature unit*/ + ID_CLKSEL, /* 8 bCSourceUD */ + 0x00, 0x00, /* 9 bmControls */ + 0, /* 11 iTerminal */ +#endif + +#ifdef INPUT + /* INPUT FROM DEVICE TO HOST PATH */ + /* Input Terminal Descriptor (Analogue Input Terminal) */ + 0x11, /* 0 bLength in bytes: 17 */ + CS_INTERFACE, /* 1 bDescriptorType: 0x24 */ + INPUT_TERMINAL, /* 2 bDescriptorSubType: INPUT_TERMINAL */ + ID_IT_AUD, /* 3 bTerminalID */ + (MICROPHONE_&0xff),MICROPHONE_>>8, /* 4 wTerminalType: USB Streaming */ + 0x00, /* 6 bAssocTerminal */ + ID_CLKSEL, /* 7 bCSourceID: ID of Clock Entity */ + NUM_USB_CHAN_IN, /* 8 bNrChannels */ + 0,0,0,0, /* 9 bmChannelConfig */ + 33, /* 13 iChannelNames */ + 0x00, 0x00, /* 14 bmControls */ + 0, /* 16 iTerminal */ + +#ifdef AUDIO_PATH_XUS + /* Extension Unit Descriptor (4.7.2.12) */ + LEN_XU_IN, /* 0 bLength (15 + p, when p is number of sources) */ + CS_INTERFACE, /* 1 bDescriptorType */ + EXTENSION_UNIT, /* 2 bDescriptorSubtype */ + ID_XU_IN, /* 3 bUnitID */ + 0, /* 4 wExtensionCode */ + 0, /* 5 wExtensionCode */ + 1, /* 6 bNrPins */ + ID_IT_AUD, /* 7 baSourceId(1) */ + NUM_USB_CHAN_OUT, /* 8+p bNrChannels */ + 0, /* 9+p bmChannelConfig */ + 0, /* 10+p bmChannelConfig */ + 0, /* 11+p bmChannelConfig */ + 0, /* 12+p bmChannelConfig */ + 0, /* 13+p iChannelNames */ + 3, /* 14+p bmControls */ + 0, /* 15+p iExtension */ +#endif + + /* Feature Unit Descriptor */ + LEN_FU_IN, /* 0 bLength: 6+(ch+1)*4 */ + CS_INTERFACE, /* 1 bDescriptorType: CS_INTERFACE */ + FEATURE_UNIT, /* 2 bDescriptorSubType: FEATURE_UNIT */ + FU_USBIN, /* 3 bUnitID */ +#ifdef AUDIO_PATH_XUS + ID_XU_IN, /* 4 bSourceID */ +#else + ID_IT_AUD, /* 4 bSourceID */ +#endif +#if (NUM_USB_CHAN_IN > 0) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(0) : Mute and Volume host read and writable */ + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(1) */ +#endif +#if (NUM_USB_CHAN_IN > 1) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(2) */ +#endif +#if (NUM_USB_CHAN_IN > 2) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(3) */ +#endif +#if (NUM_USB_CHAN_IN > 3) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(4) */ +#endif +#if (NUM_USB_CHAN_IN > 4) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(5) */ +#endif +#if (NUM_USB_CHAN_IN > 5) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(6) */ +#endif +#if (NUM_USB_CHAN_IN > 6) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(7) */ +#endif +#if (NUM_USB_CHAN_IN > 7) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(8) */ +#endif +#if (NUM_USB_CHAN_IN > 8) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(9) */ +#endif +#if (NUM_USB_CHAN_IN > 9) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(10) */ +#endif +#if (NUM_USB_CHAN_IN > 10) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(11) */ +#endif +#if (NUM_USB_CHAN_IN > 11) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(12) */ +#endif +#if (NUM_USB_CHAN_IN > 12) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(13) */ +#endif +#if (NUM_USB_CHAN_IN > 13) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(14) */ +#endif +#if (NUM_USB_CHAN_IN > 14) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(15) */ +#endif +#if (NUM_USB_CHAN_IN > 15) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(16) */ +#endif +#if (NUM_USB_CHAN_IN > 16) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(17) */ +#endif +#if (NUM_USB_CHAN_IN > 17) + 0x0F, 0x00, 0x00, 0x00, /* bmaControls(18) */ +#endif +#if (NUM_USB_CHAN_IN > 18) +#error NUM_USB_CHAN > 18 +#endif + 0, /* 60 iFeature */ + + /* Output Terminal Descriptor (USB Streaming) */ + 0x0C, /* 0 bLength */ + CS_INTERFACE, /* 1 bDescriptorType: 0x24 */ + OUTPUT_TERMINAL, /* 2 bDescriptorSubType: OUTPUT_TERMINAL */ + ID_OT_USB, /* 3 bTerminalID */ + (USB_STREAMING&0xff),USB_STREAMING>>8, /* 4 wTerminalType */ + 0x00, /* 6 bAssocTerminal */ + FU_USBIN, /* 7 bSourceID Connect to analog input feature unit*/ + ID_CLKSEL, /* 8 bCSourceUD */ + 0x00, 0x00, /* 9 bmControls */ + 7, /* 11 iTerminal */ +#endif + + + +#ifdef MIXER + /* Extension Unit Descriptor (4.7.2.12) */ + LEN_XU_MIX, /* 0 bLength (15 + p, when p is number of sources) */ + CS_INTERFACE, /* 1 bDescriptorType */ + EXTENSION_UNIT, /* 2 bDescriptorSubtype */ + ID_XU_MIXSEL, /* 3 bUnitID */ + 0, /* 4 wExtensionCode */ + 0, /* 5 wExtensionCode */ + 2, /* 6 bNrPins */ + ID_IT_USB, /* 7 baSourceId(1) */ + ID_IT_AUD, /* 7 baSourceId(2) */ + MIX_INPUTS, /* 8+p bNrChannels */ + 0, /* 9+p bmChannelConfig */ + 0, /* 10+p bmChannelConfig */ + 0, /* 11+p bmChannelConfig */ + 0, /* 12+p bmChannelConfig */ + 0, /* 13+p iChannelNames */ + 3, /* 14+p bmControls */ + 0, /* 15+p iExtension */ + + + + +/* Mixer Unit Descriptors */ + + +/* N = 144 (18 * 8) */ +/* Mixer Unit Bitmap - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff */ + MIXER_LENGTH, /* 0 bLength : 13 + num inputs + bit map (inputs * outputs) */ + CS_INTERFACE, /* 1 bDescriptorType: 0x24 */ + 0x04, /* bDescriptorSubtype: MIXER_UNIT */ + ID_MIXER_1, /* Mixer unit id */ + 0x01, /* Number of input pins */ + ID_XU_MIXSEL, /* Connected terminal or unit id for input pin */ + MAX_MIX_COUNT, /* Number of mixer output channels */ + 0x00, 0x00, 0x00, 0x00, /* Spacial location ???? */ + 49, /* Channel name index */ +#if MIX_BMCONTROLS_LEN > 0 /* Mixer programmable control bitmap */ + 0xff, +#endif +#if MIX_BMCONTROLS_LEN > 1 + 0xff, +#endif +#if MIX_BMCONTROLS_LEN > 2 + 0xff, +#endif +#if MIX_BMCONTROLS_LEN > 3 + 0xff, +#endif +#if MIX_BMCONTROLS_LEN > 4 + 0xff, +#endif +#if MIX_BMCONTROLS_LEN > 5 + 0xff, +#endif +#if MIX_BMCONTROLS_LEN > 6 + 0xff, +#endif +#if MIX_BMCONTROLS_LEN > 7 + 0xff, +#endif +#if MIX_BMCONTROLS_LEN > 8 + 0xff, +#endif +#if MIX_BMCONTROLS_LEN > 9 + 0xff, +#endif +#if MIX_BMCONTROLS_LEN > 10 + 0xff, +#endif +#if MIX_BMCONTROLS_LEN > 11 + 0xff, +#endif +#if MIX_BMCONTROLS_LEN > 12 + 0xff, +#endif +#if MIX_BMCONTROLS_LEN > 13 + 0xff, +#endif +#if MIX_BMCONTROLS_LEN > 14 + 0xff, +#endif +#if MIX_BMCONTROLS_LEN > 15 + 0xff, +#endif +#if MIX_BMCONTROLS_LEN > 16 + 0xff, +#endif +#if MIX_BMCONTROLS_LEN > 17 + 0xff, +#endif +#if MIX_BMCONTROLS_LEN > 18 +#error unxpected BMCONTROLS_LEN +#endif + 0x00, /* bmControls */ + 0, /* Mixer unit string descriptor index */ +#endif + +#if 1 + /* Standard AS Interrupt Endpoint Descriptor (4.8.2.1): */ + 0x07, /* 0 bLength: 7 */ + 0x05, /* 1 bDescriptorType: ENDPOINT */ + 0x84, /* 2 bEndpointAddress (D7: 0:out, 1:in) */ + 3, /* 3 bmAttributes (bitmap) */ + 6,0, /* 4 wMaxPacketSize */ + 8, /* 6 bInterval */ +#endif + +#ifdef OUTPUT + /* Standard AS Interface Descriptor (4.9.1) */ + 0x09, /* 0 bLength: (in bytes, 9) */ + INTERFACE, /* 1 bDescriptorType: INTERFACE */ + 1, /* 2 bInterfaceNumber: Number of interface */ + 0, /* 3 bAlternateSetting */ + 0, /* 4 bNumEndpoints */ + AUDIO, /* 5 bInterfaceClass: AUDIO */ + AUDIOSTREAMING, /* 6 bInterfaceSubClass: AUDIO_STREAMING */ + IP_VERSION_02_00, /* 7 bInterfaceProtocol: IP_VERSION_02_00 */ + 4, /* 8 iInterface: (Sting index) */ + + /* Standard AS Interface Descriptor (4.9.1) (Alt) */ + 0x09, /* 0 bLength: (in bytes, 9) */ + INTERFACE, /* 1 bDescriptorType: INTERFACE */ + 1, /* 2 bInterfaceNumber: Number of interface */ + 1, /* 3 bAlternateSetting */ + 2, /* 4 bNumEndpoints */ + AUDIO, /* 5 bInterfaceClass: AUDIO */ + AUDIOSTREAMING, /* 6 bInterfaceSubClass: AUDIO_STREAMING */ + IP_VERSION_02_00, /* 7 bInterfaceProtocol: IP_VERSION_02_00 */ + 4, /* 8 iInterface: (Sting index) */ + + /* Class Specific AS Interface Descriptor */ + 0x10, /* 0 bLength: 16 */ + CS_INTERFACE, /* 1 bDescriptorType: 0x24 */ + AS_GENERAL, /* 2 bDescriptorSubType */ + ID_IT_USB, /* 3 bTerminalLink (Linked to USB input terminal) */ + 0x00, /* 4 bmControls */ + 0x01, /* 5 bFormatType */ + PCM, 0x00, 0x00, 0x00, /* 6:10 bmFormats (note this is a bitmap) */ + NUM_USB_CHAN_OUT, /* 11 bNrChannels */ + 0,0,0,0, /* 12:14: bmChannelConfig */ + 15, /* 15 iChannelNames */ + + /* Type 1 Format Type Descriptor */ + 0x06, /* 0 bLength (in bytes): 6 */ + CS_INTERFACE, /* 1 bDescriptorType: 0x24 */ + FORMAT_TYPE, /* 2 bDescriptorSubtype: FORMAT_TYPE */ + FORMAT_TYPE_I, /* 3 bFormatType: FORMAT_TYPE_1 */ + 0x04, /* 4 bSubslotSize (Number of bytes per subslot) */ + 24, /* 5 bBitResolution (Number of bits used per subslot) */ + + /* Standard AS Isochronous Audio Data Endpoint Descriptor (4.10.1.1) */ + 0x07, /* 0 bLength: 7 */ + ENDPOINT, /* 1 bDescriptorType: ENDPOINT */ + 0x01, /* 2 bEndpointAddress (D7: 0:out, 1:in) */ + 0x05, /* 3 bmAttributes (bitmap) */ + 0,4, /* 4 wMaxPacketSize */ + 1, /* 6 bInterval */ + + /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor (4.10.1.2) */ + 0x08, /* 0 bLength */ + CS_ENDPOINT, /* 1 bDescriptorType */ + 0x01, /* 2 bDescriptorSubtype */ + 0x00, /* 3 bmAttributes */ + 0x00, /* 4 bmControls (Bitmap: Pitch control, over/underun etc) */ + 0x02, /* 5 bLockDelayUnits: Decoded PCM samples */ + 8,0, /* 6:7 bLockDelay */ + + /* Feedback EP */ + 0x07, /* 0 bLength: 7 */ + ENDPOINT, /* 1 bDescriptorType: ENDPOINT */ + 0x81, /* 2 bEndpointAddress (D7: 0:out, 1:in) */ + 17, /* 3 bmAttributes (bitmap) */ + 4,0, /* 4 wMaxPacketSize */ + 4, /* 6 bInterval. Only values <= 1 frame (8) supported by MS */ + +#ifdef ADAT_TX + /* Standard AS Interface Descriptor (4.9.1) (Alt) */ + 0x09, /* 0 bLength: (in bytes, 9) */ + INTERFACE, /* 1 bDescriptorType: INTERFACE */ + 1, /* 2 bInterfaceNumber: Number of interface */ + 2, /* 3 bAlternateSetting */ + 2, /* 4 bNumEndpoints */ + AUDIO, /* 5 bInterfaceClass: AUDIO */ + AUDIOSTREAMING, /* 6 bInterfaceSubClass: AUDIO_STREAMING */ + IP_VERSION_02_00, /* 7 bInterfaceProtocol: IP_VERSION_02_00 */ + 4, /* 8 iInterface: (Sting index) */ + + /* Class Specific AS Interface Descriptor */ + 0x10, /* 0 bLength: 16 */ + CS_INTERFACE, /* 1 bDescriptorType: 0x24 */ + AS_GENERAL, /* 2 bDescriptorSubType */ + ID_IT_USB, /* 3 bTerminalLink (Linked to USB input terminal) */ + 0x00, /* 4 bmControls */ + 0x01, /* 5 bFormatType */ + PCM, 0x00, 0x00, 0x00, /* 6:10 bmFormats (note this is a bitmap) */ + NUM_USB_CHAN_OUT, /* 11 bNrChannels */ + 0,0,0,0, /* 12:14: bmChannelConfig */ + 15, /* 15 iChannelNames */ + + /* Type 1 Format Type Descriptor */ + 0x06, /* 0 bLength (in bytes): 6 */ + CS_INTERFACE, /* 1 bDescriptorType: 0x24 */ + FORMAT_TYPE, /* 2 bDescriptorSubtype: FORMAT_TYPE */ + FORMAT_TYPE_I, /* 3 bFormatType: FORMAT_TYPE_1 */ + 0x04, /* 4 bSubslotSize (Number of bytes per subslot) */ + 24, /* 5 bBitResolution (Number of bits used per subslot) */ + + /* Standard AS Isochronous Audio Data Endpoint Descriptor (4.10.1.1) */ + 0x07, /* 0 bLength: 7 */ + ENDPOINT, /* 1 bDescriptorType: ENDPOINT */ + 0x01, /* 2 bEndpointAddress (D7: 0:out, 1:in) */ + 0x05, /* 3 bmAttributes (bitmap) */ + 0,4, /* 4 wMaxPacketSize */ + 1, /* 6 bInterval */ + + /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor (4.10.1.2) */ + 0x08, /* 0 bLength */ + CS_ENDPOINT, /* 1 bDescriptorType */ + 0x01, /* 2 bDescriptorSubtype */ + 0x00, /* 3 bmAttributes */ + 0x00, /* 4 bmControls (Bitmap: Pitch control, over/underun etc) */ + 0x02, /* 5 bLockDelayUnits: Decoded PCM samples */ + 8,0, /* 6:7 bLockDelay */ + + /* Feedback EP */ + 0x07, /* 0 bLength: 7 */ + ENDPOINT, /* 1 bDescriptorType: ENDPOINT */ + 0x81, /* 2 bEndpointAddress (D7: 0:out, 1:in) */ + 17, /* 3 bmAttributes (bitmap) */ + 4,0, /* 4 wMaxPacketSize */ + 8, /* 6 bInterval */ + +#endif /* ADAT_TX */ +#endif /* OUTPUT */ + +#ifdef INPUT + /* Standard AS Interface Descriptor (4.9.1) */ + 0x09, /* 0 bLength: (in bytes, 9) */ + INTERFACE, /* 1 bDescriptorType: INTERFACE */ + (OUTPUT_INTERFACES + 1), /* 2 bInterfaceNumber: Number of interface */ + 0, /* 3 bAlternateSetting */ + 0, /* 4 bNumEndpoints */ + AUDIO, /* 5 bInterfaceClass: AUDIO */ + AUDIOSTREAMING, /* 6 bInterfaceSubClass: AUDIO_STREAMING */ + 0x20, /* 7 bInterfaceProtocol: IP_VERSION_02_00 */ + 5, /* 8 iInterface: (Sting index) */ + + /* Standard AS Interface Descriptor (4.9.1) (Alt) */ + 0x09, /* 0 bLength: (in bytes, 9) */ + INTERFACE, /* 1 bDescriptorType: INTERFACE */ + (OUTPUT_INTERFACES + 1), /* 2 bInterfaceNumber: Number of interface */ + 1, /* 3 bAlternateSetting */ + 1, /* 4 bNumEndpoints */ + AUDIO, /* 5 bInterfaceClass: AUDIO */ + AUDIOSTREAMING, /* 6 bInterfaceSubClass: AUDIO_STREAMING */ + IP_VERSION_02_00, /* 7 bInterfaceProtocol: IP_VERSION_02_00 */ + 5, /* 8 iInterface: (Sting index) */ + + /* Class Specific AS Interface Descriptor */ + 0x10, /* 0 bLength: 16 */ + CS_INTERFACE, /* 1 bDescriptorType: 0x24 */ + AS_GENERAL, /* 2 bDescriptorSubType */ + ID_OT_USB, /* 3 bTerminalLink */ + 0x00, /* 4 bmControls */ + 0x01, /* 5 bFormatType */ + PCM, 0x00, 0x00, 0x00, /* 6:10 bmFormats (note this is a bitmap) */ + NUM_USB_CHAN_IN, /* 11 bNrChannels */ + 0,0,0,0, /* 12:14: bmChannelConfig */ + 33, /* 15 iChannelNames */ + + /* Type 1 Format Type Descriptor */ + 0x06, /* 0 bLength (in bytes): 6 */ + CS_INTERFACE, /* 1 bDescriptorType: 0x24 */ + FORMAT_TYPE, /* 2 bDescriptorSubtype: FORMAT_TYPE */ + FORMAT_TYPE_I, /* 3 bFormatType: FORMAT_TYPE_1 */ + 0x04, /* 4 bSubslotSize (Number of bytes per subslot) */ + 24, /* 5 bBitResolution (Number of bits used per subslot) */ + + /* Standard AS Isochronous Audio Data Endpoint Descriptor (4.10.1.1) */ + 0x07, /* 0 bLength: 7 */ + ENDPOINT, /* 1 bDescriptorType: ENDPOINT */ + 0x82, /* 2 bEndpointAddress (D7: 0:out, 1:in) */ + 5, /* 3 bmAttributes (bitmap) */ + 0,4, /* 4 wMaxPacketSize */ + 1, /* 6 bInterval */ + + /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor (4.10.1.2) */ + 0x08, /* 0 bLength */ + CS_ENDPOINT, /* 1 bDescriptorType */ + 0x01, /* 2 bDescriptorSubtype */ + 0x00, /* 3 bmAttributes */ + 0x00, /* 4 bmControls (Bitmap: Pitch control, over/underun etc) */ + 0x02, /* 5 bLockDelayUnits: Decoded PCM samples */ + 8,0, /* 6:7 bLockDelay */ + +#ifdef ADAT_RX + /* Standard AS Interface Descriptor (4.9.1) (Alt) */ + 0x09, /* 0 bLength: (in bytes, 9) */ + INTERFACE, /* 1 bDescriptorType: INTERFACE */ + (OUTPUT_INTERFACES + 1), /* 2 bInterfaceNumber: Number of interface */ + 2, /* 3 bAlternateSetting */ + 1, /* 4 bNumEndpoints */ + AUDIO, /* 5 bInterfaceClass: AUDIO */ + AUDIOSTREAMING, /* 6 bInterfaceSubClass: AUDIO_STREAMING */ + IP_VERSION_02_00, /* 7 bInterfaceProtocol: IP_VERSION_02_00 */ + 5, /* 8 iInterface: (Sting index) */ + + /* Class Specific AS Interface Descriptor */ + 0x10, /* 0 bLength: 16 */ + CS_INTERFACE, /* 1 bDescriptorType: 0x24 */ + AS_GENERAL, /* 2 bDescriptorSubType */ + ID_OT_USB, /* 3 bTerminalLink */ + 0x00, /* 4 bmControls */ + 0x01, /* 5 bFormatType */ + PCM, 0x00, 0x00, 0x00, /* 6:10 bmFormats (note this is a bitmap) */ + NUM_USB_CHAN_IN - 4, /* 11 bNrChannels */ + 0,0,0,0, /* 12:14: bmChannelConfig */ + 33, /* 15 iChannelNames */ + + /* Type 1 Format Type Descriptor */ + 0x06, /* 0 bLength (in bytes): 6 */ + CS_INTERFACE, /* 1 bDescriptorType: 0x24 */ + FORMAT_TYPE, /* 2 bDescriptorSubtype: FORMAT_TYPE */ + FORMAT_TYPE_I, /* 3 bFormatType: FORMAT_TYPE_1 */ + 0x04, /* 4 bSubslotSize (Number of bytes per subslot) */ + 24, /* 5 bBitResolution (Number of bits used per subslot) */ + + /* Standard AS Isochronous Audio Data Endpoint Descriptor (4.10.1.1) */ + 0x07, /* 0 bLength: 7 */ + ENDPOINT, /* 1 bDescriptorType: ENDPOINT */ + 0x82, /* 2 bEndpointAddress (D7: 0:out, 1:in) */ + 5, /* 3 bmAttributes (bitmap) */ + 0,4, /* 4 wMaxPacketSize */ + 1, /* 6 bInterval */ + + /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor (4.10.1.2) */ + 0x08, /* 0 bLength */ + CS_ENDPOINT, /* 1 bDescriptorType */ + 0x01, /* 2 bDescriptorSubtype */ + 0x00, /* 3 bmAttributes */ + 0x00, /* 4 bmControls (Bitmap: Pitch control, over/underun etc) */ + 0x02, /* 5 bLockDelayUnits: Decoded PCM samples */ + 8,0, /* 6:7 bLockDelay */ + +#if 0 + /* Standard AS Interface Descriptor (4.9.1) (Alt) */ + 0x09, /* 0 bLength: (in bytes, 9) */ + INTERFACE, /* 1 bDescriptorType: INTERFACE */ + (OUTPUT_INTERFACES + 1), /* 2 bInterfaceNumber: Number of interface */ + 3, /* 3 bAlternateSetting */ + 1, /* 4 bNumEndpoints */ + AUDIO, /* 5 bInterfaceClass: AUDIO */ + AUDIOSTREAMING, /* 6 bInterfaceSubClass: AUDIO_STREAMING */ + IP_VERSION_02_00, /* 7 bInterfaceProtocol: IP_VERSION_02_00 */ + 11, /* 8 iInterface: (Sting index) */ + + /* Class Specific AS Interface Descriptor */ + 0x10, /* 0 bLength: 16 */ + CS_INTERFACE, /* 1 bDescriptorType: 0x24 */ + AS_GENERAL, /* 2 bDescriptorSubType */ + ID_OT_USB, /* 3 bTerminalLink */ + 0x00, /* 4 bmControls */ + 0x01, /* 5 bFormatType */ + PCM, 0x00, 0x00, 0x00, /* 6:10 bmFormats (note this is a bitmap) */ + NUM_USB_CHAN_IN-6, /* 11 bNrChannels */ + 0,0,0,0, /* 12:14: bmChannelConfig */ + 29, /* 15 iChannelNames */ + + /* Type 1 Format Type Descriptor */ + 0x06, /* 0 bLength (in bytes): 6 */ + CS_INTERFACE, /* 1 bDescriptorType: 0x24 */ + FORMAT_TYPE, /* 2 bDescriptorSubtype: FORMAT_TYPE */ + FORMAT_TYPE_I, /* 3 bFormatType: FORMAT_TYPE_1 */ + 0x04, /* 4 bSubslotSize (Number of bytes per subslot) */ + 24, /* 5 bBitResolution (Number of bits used per subslot) */ + + /* Standard AS Isochronous Audio Data Endpoint Descriptor (4.10.1.1) */ + 0x07, /* 0 bLength: 7 */ + ENDPOINT, /* 1 bDescriptorType: ENDPOINT */ + 0x82, /* 2 bEndpointAddress (D7: 0:out, 1:in) */ + 5, /* 3 bmAttributes (bitmap) */ + 0,4, /* 4 wMaxPacketSize */ + 1, /* 6 bInterval */ + + /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor (4.10.1.2) */ + 0x08, /* 0 bLength */ + CS_ENDPOINT, /* 1 bDescriptorType */ + 0x01, /* 2 bDescriptorSubtype */ + 0x00, /* 3 bmAttributes */ + 0x00, /* 4 bmControls (Bitmap: Pitch control, over/underun etc) */ + 0x02, /* 5 bLockDelayUnits: Decoded PCM samples */ + 8,0, /* 6:7 bLockDelay */ +#endif +#endif + + +#endif + +#ifdef MIDI +/* 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) */ + (INPUT_INTERFACES + OUTPUT_INTERFACES + 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) */ + +/* 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 */ + +/* 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) */ + (INPUT_INTERFACES+OUTPUT_INTERFACES+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) */ + +/* 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 */ + +/* 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) */ + +/* 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) */ + 14, /* 5 iJack : Unused. (field size 1 bytes) */ + +/* 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) */ + +/* 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. */ + 13, /* 8 iJack : Unused. (field size 1 bytes) */ + +/* 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) */ + 0x02, /* 2 bEndpointAddress : OUT Endpoint 3. (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) - has to be 0x200 for compliance*/ + 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) */ + +/* 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) */ + +/* 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) */ + 0x83, /* 2 bEndpointAddress : IN Endpoint 3. (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) - has to be 0x200 for compliance*/ + 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) */ + +/* 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) */ +#endif + +#ifdef DFU + /* 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) */ + (INPUT_INTERFACES+OUTPUT_INTERFACES+MIDI_INTERFACES+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) */ + 0xFE, /* 5 bInterfaceClass : DFU. (field size 1 bytes) */ + 0x01, /* 6 bInterfaceSubclass : (field size 1 bytes) */ + 0x01, /* 7 bInterfaceProtocol : Unused. (field size 1 bytes) */ + 12, /* 8 iInterface : Unused. (field size 1 bytes) */ + +#if 0 + /* DFU 1.0 Run-Time DFU Functional Descriptor */ + 0x07, + 0x21, + 0x07, + 0xFA, + 0x00, + 0x40, + 0x00 +#else + /* 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, /* 7 bcdDFUVersion */ + +#endif +#endif + +}; + +#define APPEND_VENDOR_STR(x) VENDOR_STR#x + +/* String table */ + +static unsigned char strDescs_Audio2[][40] = +{ + "Langids", /* String 0 (LangIDs) place holder */ + APPEND_VENDOR_STR( ), // 1 iManufacturer (at MANUFACTURER_STRING_INDEX) + APPEND_VENDOR_STR(USB Audio 2.0), // 2 iProduct and iInterface for control interface (at PRODUCT_STR_INDEX) + SERIAL_STR, // 3 iSerialNumber (at SERIAL_STR_INDEX) + APPEND_VENDOR_STR(USB 2.0 Audio Out), // 4 iInterface for Streaming interaces + APPEND_VENDOR_STR(USB 2.0 Audio In), // 5 + + APPEND_VENDOR_STR(Audio 2.0 Output), // 6 "USB Input Terminal" (User sees as output from host) + APPEND_VENDOR_STR(Audio 2.0 Input), // 7 "USB Output Terminal" (User sees as input to host) + + APPEND_VENDOR_STR(Clock Selector), // 8 iClockSel + APPEND_VENDOR_STR(Internal Clock), // 9 iClockSource + APPEND_VENDOR_STR(S/PDIF Clock), // 10 iClockSource + APPEND_VENDOR_STR(ADAT Clock), // 11 iClockSource + APPEND_VENDOR_STR(DFU), // 12 iInterface for DFU interface + + APPEND_VENDOR_STR(MIDI Out), // 13 + APPEND_VENDOR_STR(MIDI In ), // 14 + + "Analogue 1", // 15 Output channel name place holders - Get customised at runtime based on device config + "Analogue 2", // 16 + "Analogue 3", // 17 + "Analogue 4", // 18 + "Analogue 5", // 19 + "Analogue 6", // 20 + "Analogue 7", // 21 + "Analogue 8", // 22 + "Analogue 9", // 23 + "Analogue 10", // 24 + "Analogue 11", // 25 + "Analogue 12", // 26 + "Analogue 13", // 27 + "Analogue 14", // 28 + "Analogue 15", // 29 + "Analogue 16", // 30 + "Analogue 17", // 31 + "Analogue 18", // 32 + + "Analogue 1", // 33 Input channel name place holders - Get customised at runtime based on device config */ + "Analogue 2", // 34 Input channel name place holders - Get customised at runtime based on device config */ + "Analogue 3", // 35 Input channel name place holders - Get customised at runtime based on device config */ + "Analogue 4", // 36 Input channel name place holders - Get customised at runtime based on device config */ + "Analogue 5", // 37 Input channel name place holders - Get customised at runtime based on device config */ + "Analogue 6", // 38 Input channel name place holders - Get customised at runtime based on device config */ + "Analogue 7", // 39 Input channel name place holders - Get customised at runtime based on device config */ + "Analogue 8", // 40 Input channel name place holders - Get customised at runtime based on device config */ + "Analogue 9", // 41 Input channel name place holders - Get customised at runtime based on device config */ + "Analogue 10", // 42 Input channel name place holders - Get customised at runtime based on device config */ + "Analogue 11", // 43 Input channel name place holders - Get customised at runtime based on device config */ + "Analogue 12", // 44 Input channel name place holders - Get customised at runtime based on device config */ + "Analogue 13", // 45 Input channel name place holders - Get customised at runtime based on device config */ + "Analogue 14", // 46 Input channel name place holders - Get customised at runtime based on device config */ + "Analogue 15", // 47 Input channel name place holders - Get customised at runtime based on device config */ + "Analogue 16", // 48 Input channel name place holders - Get customised at runtime based on device config */ + "Analogue 17", // 49 Input channel name place holders - Get customised at runtime based on device config */ + "Analogue 18", // 50 Input channel name place holders - Get customised at runtime based on device config */ + + "Mixer Out 1", // 51/* Mixer output names */ + "Mixer Out 2", // 52 /* Mixer output names */ + "Mixer Out 3", // 53 /* Mixer output names */ + "Mixer Out 4", // 54 /* Mixer output names */ + "Mixer Out 5", // 55 /* Mixer output names */ + "Mixer Out 6", // 56 /* Mixer output names */ + "Mixer Out 7", // 57 /* Mixer output names */ + "Mixer Out 8", // 58 /* Mixer output names */ +}; + +/* Configuration Descriptor for Null device */ +unsigned char cfgDesc_Null[] = +{ + 0x09, /* 0 bLength */ + CONFIGURATION, /* 1 bDescriptorType */ + 0x12, /* 2 wTotalLength */ + 0x00, /* 3 wTotalLength */ + 0x01, /* 4 bNumInterface: Number of interfaces*/ + 0x01, /* 5 bConfigurationValue */ + 0x00, /* 6 iConfiguration */ +#ifdef SELF_POWERED + 192, /* 7 bmAttributes */ +#else + 128, +#endif + 250, /* 8 bMaxPower */ + + 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) */ + 0x00, /* 5 bInterfaceClass : */ + 0x00, /* 6 bInterfaceSubclass */ + 0x00, /* 7 bInterfaceProtocol : Unused. (field size 1 bytes) */ + 0x00, /* 8 iInterface : Unused. (field size 1 bytes) */ + 0x09, /* 0 bLength */ +}; + +#if 0 +/* OtherSpeed Configuration Descriptor */ +unsigned char oSpeedCfgDesc[] = +{ + 0x09, /* 0 bLength */ + OTHER_SPEED_CONFIGURATION, /* 1 bDescriptorType */ + 0x12, /* 2 wTotalLength */ + 0x00, /* 3 wTotalLength */ + 0x01, /* 4 bNumInterface: Number of interfaces*/ + 0x00, /* 5 bConfigurationValue */ + 0x00, /* 6 iConfiguration */ + 128, /* 7 bmAttributes */ + 250, /* 8 bMaxPower */ + + 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) */ + 0x00, /* 5 bInterfaceClass : */ + 0x00, /* 6 bInterfaceSubclass */ + 0x00, /* 7 bInterfaceProtocol : Unused. (field size 1 bytes) */ + 0x00, /* 8 iInterface : Unused. (field size 1 bytes) */ + +}; +#endif + +/* Configuration descriptor for Audio v1.0 */ +#define AC_LENGTH (8 + INPUT_INTERFACES + OUTPUT_INTERFACES) +#define AC_TOTAL_LENGTH (AC_LENGTH + (INPUT_INTERFACES * 31) + (OUTPUT_INTERFACES * 31)) +#define STREAMING_INTERFACES (INPUT_INTERFACES + OUTPUT_INTERFACES) + + +#define CFG_TOTAL_LENGTH_A1 (18 + AC_TOTAL_LENGTH + (INPUT_INTERFACES * 61) + (OUTPUT_INTERFACES * 70) + (DFU_INTERFACES * 18)) +#ifdef AUDIO_CLASS_FALLBACK +unsigned char cfgDesc_Audio1[] = +{ + /* Configuration descriptor */ 0x09, + CONFIGURATION, + (CFG_TOTAL_LENGTH_A1 & 0xFF), /* wTotalLength */ + (CFG_TOTAL_LENGTH_A1 >> 8), /* wTotalLength */ +#ifdef MIDI + NUM_INTERFACES-2, /* numInterfaces - we dont support MIDI in audio 1.0 mode*/ +#else + NUM_INTERFACES, +#endif + 0x01, /* ID of this configuration */ + 0x00, /* Unused */ +#ifdef SELF_POWERED + 192, /* 7 bmAttributes */ + 5, /* 8 bMaxPower */ +#else + 128, /* 7 bmAttributes */ + 250, /* 8 bMaxPower */ +#endif + + /* Standard AC interface descriptor */ + 0x09, + INTERFACE, + 0x00, /* Interface No */ + 0x00, /* Alternate setting*/ + 0x00, /* Num endpoints */ + AUDIO, + AUDIOCONTROL, + 0x00, /* Unused */ + PRODUCT_STR_INDEX, /* iInterface - re-use iProduct */ + + /* CS (Class Specific) AudioControl interface header descriptor (4.3.2) */ + AC_LENGTH, + CS_INTERFACE, + 0x01, /* HEADER */ + 0x00, 0x01, /* Class spec revision - 1.0 */ + (AC_TOTAL_LENGTH & 0xFF), /* wTotallength (Combined length of this descriptor and all Unit and Terminal Descriptors) */ + (AC_TOTAL_LENGTH >> 8), /* wTotalLength */ + STREAMING_INTERFACES, /* Num streaming interfaces */ +#ifdef OUTPUT + 0x01, /* AudioStreaming interface 1 belongs to AC interface */ +#endif +#ifdef INPUT + (OUTPUT_INTERFACES + 1), /* AudioStreaming interface 2 belongs to AC interface */ +#endif + +#ifdef OUTPUT + /* CS_Interface Input Terminal 1 Descriptor - USB streaming Host to Device */ + 0x0C, + CS_INTERFACE, /* CS_INTERFACE */ + 0x02, /* INPUT_TERMINAL */ + 0x01, /* Terminal ID */ + 0x01, 0x01, /* Type - streaming */ + 0x00, /* Associated terminal - unused */ + 2, /* bNrChannels */ + 0x03, 0x00, /* wChannelConfig */ + 0x00, /* iChannelNames - Unused */ + 0x06, /* iTerminal */ + + /* CS_Interface class specific AC interface feature unit descriptor - mute & volume for dac */ + 0x0A, + CS_INTERFACE, + FEATURE_UNIT, + 0x0A, /* unitID */ + 0x01, /* sourceID - ID of the unit/terminal to which this feature unit is connected */ + 0x01, /* controlSize - 1 */ + 0x03, /* bmaControls(0) */ + 0x03, /* bmaControls(1) */ + 0x03, /* bmaControls(2) */ + 0x00, /* String table index */ + + /* CS_Interface Output Terminal Descriptor - Analogue out to speaker */ + 0x09, + CS_INTERFACE, + 0x03, /* OUTPUT_TERMINAL */ + 0x06, /* Terminal ID */ + 0x01, 0x03, /* Type - streaming out, speaker */ + 0x00, /* Associated terminal - unused */ + 0x0A, /* sourceID */ + 0x00, /* Unused */ + +#endif + +#ifdef INPUT + /* CS_Interface Input Terminal 2 Descriptor - Analog in from line in */ + 0x0C, + CS_INTERFACE, + 0x02, /* INPUT_TERMINAL */ + 0x02, /* Terminal ID */ + 0x01, 0x02, /* Type - streaming in, mic */ + 0x00, /* Associated terminal - unused */ + 2, /* bNrChannels */ + 0x03, 0x00, /* wChannelConfigs */ + 0x00, /* iChannelNames */ + 0x07, /* iTerminal */ + + /* CS_Interface Output Terminal Descriptor - USB Streaming Device to Host*/ + 0x09, + CS_INTERFACE, + 0x03, /* OUTPUT_TERMINAL */ + 0x07, /* Terminal ID */ + 0x01, 0x01, /* Type - streaming */ + 0x01, /* Associated terminal - unused */ + 0x0B, /* sourceID - from selector unit ?? */ + 0x00, /* Unused */ + + /* CS_Interface class specific AC interface feature unit descriptor - mute & volume for adc */ + 0x0A, + CS_INTERFACE, + FEATURE_UNIT, + 0x0B, /* unitID */ + 0x02, /* sourceID - ID of the unit/terminal to which this feature unit is connected */ + 0x01, /* controlSize - 1 */ + 0x03, /* bmaControls(0) */ + 0x03, /* bmaControls(1) */ + 0x03, /* bmaControls(2) */ + 0x00, /* String table index */ +#endif + + +#ifdef OUTPUT + /* Standard AS Interface Descriptor (4.5.1) */ + 0x09, /* bLength */ + 0x04, /* INTERFACE */ + 0x01, /* bInterfaceNumber */ + 0x00, /* bAlternateSetting */ + 0x00, /* bnumEndpoints */ + 0x01, /* bInterfaceClass - AUDIO */ + 0x02, /* bInterfaceSubclass - AUDIO_STREAMING */ + 0x00, /* bInterfaceProtocol - Not used */ + 0x04, /* iInterface */ + + /* Standard As Interface Descriptor (4.5.1) */ + 0x09, + 0x04, /* INTERFACE */ + 0x01, /* Interface no */ + 0x01, /* AlternateSetting */ + 0x02, /* num endpoints 2: audio EP and feedback EP */ + 0x01, /* Interface class - AUDIO */ + 0x02, /* subclass - AUDIO_STREAMING */ + 0x00, /* Unused */ + 0x04, /* String table index */ + + /* Class-Specific AS Interface Descriptor (4.5.2) */ + 0x07, + CS_INTERFACE, /* bDescriptorType */ + 0x01, /* bDescriptorSubtype - GENERAL */ + 0x01, /* iTerminalLink - linked to Streaming IN terminal */ + 0x01, /* bDelay */ + 0x01, 0x00, /* wFormatTag - PCM */ + + /* CS_Interface Format Type Descriptor */ + 0x14, + CS_INTERFACE, + 0x02, /* Subtype - FORMAT_TYPE */ + 0x01, /* Format type - FORMAT_TYPE_1 */ + 2, /* nrChannels */ + 0x03, /* subFrameSize - 4 bytes per slot */ + 24, /* bitResolution - 24bit */ + 0x04, /* SamFreqType - 4 sample freq */ + 0x44, 0xAC, 0x00, /* sampleFreq - 44.1Khz */ + 0x80, 0xBB, 0x00, /* sampleFreq - 48KHz */ +#if defined(OUTPUT) && defined(INPUT) + 0x80, 0xBB, 0x00, /* sampleFreq - 48KHz */ + 0x80, 0xBB, 0x00, /* sampleFreq - 48KHz */ +#else + 0x88, 0x58, 0x01, /* sampleFreq - 88.2KHz */ + 0x00, 0x77, 0x01, /* sampleFreq - 96KHz */ +#endif + + /* Standard AS Isochronous Audio Data Endpoint Descriptor 4.6.1.1 */ + 0x09, + 0x05, /* ENDPOINT */ + 0x01, /* endpointAddress - D7, direction (0 OUT, 1 IN). D6..4 reserved (0). D3..0 endpoint no. */ + 0x05, /* attributes - isochronous async */ +#if defined(OUTPUT) && defined(INPUT) + 0x26, 0x01, /* maxPacketSize 294 */ +#else + 0x46, 0x02, /* maxPacketSize 582 */ +#endif + 0x01, /* bInterval */ + 0x00, /* bRefresh */ + 0x81, /* bSynchAdddress - address of EP used to communicate sync info */ + + /* CS_Endpoint Descriptor ?? */ + 0x07, + 0x25, /* CS_ENDPOINT */ + 0x01, /* subtype - GENERAL */ + 0x01, /* attributes. D[0]: sample freq ctrl. */ + 0x02, /* bLockDelayUnits */ + 0x00, 0x00, /* bLockDelay */ + + /* Feedback EP */ + 0x09, + 0x05, /* bDescriptorType: ENDPOINT */ + 0x81, /* bEndpointAddress (D3:0 - EP no. D6:4 - reserved 0. D7 - 0:out, 1:in) */ + 0x01, /* bmAttributes (bitmap) */ + 0x03,0x0, /* wMaxPacketSize */ + 0x01, /* bInterval - Must be 1 for compliance */ + 0x04, /* bRefresh 2^x */ + 0x0, /* bSynchAddress */ +#endif + +#ifdef INPUT + /* Standard Interface Descriptor - Audio streaming IN */ + 0x09, + 0x04, /* INTERFACE */ + (OUTPUT_INTERFACES + 1), /* bInterfaceNumber*/ + 0x00, /* AlternateSetting */ + 0x00, /* num endpoints */ + 0x01, /* Interface class - AUDIO */ + 0x02, /* subclass - AUDIO_STREAMING */ + 0x00, /* Unused */ + 0x05, /* String table index */ + + /* Standard Interface Descriptor - Audio streaming IN */ + 0x09, + 0x04, /* INTERFACE */ + (OUTPUT_INTERFACES + 1), /* bInterfaceNumber */ + 0x01, /* AlternateSetting */ + 0x01, /* num endpoints */ + 0x01, /* Interface class - AUDIO */ + 0x02, /* Subclass - AUDIO_STREAMING */ + 0x00, /* Unused */ + 0x05, /* String table index */ + + /* CS_Interface AC interface header descriptor */ + 0x07, + CS_INTERFACE, + 0x01, /* subtype - GENERAL */ + 0x07, /* TerminalLink - linked to Streaming OUT terminal */ + 0x01, /* Interface delay */ + 0x01,0x00, /* Format - PCM */ + + /* CS_Interface Terminal Descriptor */ + 0x14, + CS_INTERFACE, + 0x02, /* Subtype - FORMAT_TYPE */ + 0x01, /* Format type - FORMAT_TYPE_1 */ + 2, /* bNrChannels - 2 */ + 0x03, /* subFrameSize - 4 bytes per slot */ + 24, /* bitResolution - 24bit */ + 0x04, /* SamFreqType - 4 sample freq */ + 0x44, 0xAC, 0x00, /* sampleFreq - 44.1Khz */ + 0x80, 0xBB, 0x00, /* sampleFreq - 48KHz */ +#if defined(OUTPUT) && defined(INPUT) + 0x80, 0xBB, 0x00, /* sampleFreq - 48KHz */ + 0x80, 0xBB, 0x00, /* sampleFreq - 48KHz */ +#else + 0x88, 0x58, 0x01, /* sampleFreq - 88.2KHz */ + 0x00, 0x77, 0x01, /* sampleFreq - 96KHz */ +#endif + + /* Standard Endpoint Descriptor */ + 0x09, + 0x05, /* ENDPOINT */ + 0x82, /* EndpointAddress */ + 0x05, /* Attributes - isochronous async */ +#if defined(OUTPUT) && defined(INPUT) + 0x26, 0x01, /* maxPacketSize 294 */ +#else + 0x46, 0x02, /* maxPacketSize 582 */ +#endif + 0x01, /* bInterval */ + 0x00, /* bRefresh */ + 0x00, /* bSynchAddress */ + + /* CS_Endpoint Descriptor */ + 0x07, + 0x25, /* CS_ENDPOINT */ + 0x01, /* Subtype - GENERAL */ + 0x01, /* Attributes. D[0]: sample freq ctrl. */ + 0x00, /* Unused */ + 0x00, 0x00, /* Unused */ + +#endif + /* 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) */ + (INPUT_INTERFACES+OUTPUT_INTERFACES+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) */ + 0xFE, /* 5 bInterfaceClass : DFU. (field size 1 bytes) */ + 0x01, /* 6 bInterfaceSubclass : (field size 1 bytes) */ + 0x01, /* 7 bInterfaceProtocol : Unused. (field size 1 bytes) */ + 8, /* 8 iInterface : Unused. (field size 1 bytes) */ + +#if 0 + /* DFU 1.0 Run-Time DFU Functional Descriptor */ + 0x07, + 0x21, + 0x07, + 0xFA, + 0x00, + 0x40, + 0x00 +#else + /* 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, /* 7 bcdDFUVersion */ + +#endif +}; + +#endif + +#define APPEND_VENDOR_STR(x) VENDOR_STR#x + +static unsigned char strDescs_Audio1[][40] = +{ + "Langids", /* String 0 (LangIDs) place holder */ + APPEND_VENDOR_STR(), // 1 iManufacturer + APPEND_VENDOR_STR(USB Audio 1.0), // 2 iProduct and iInterface for control interface + SERIAL_STR, // 3 iSerialNumber + + APPEND_VENDOR_STR(USB 1.0 Audio Out), // 4 iInterface for Streaming interaces + APPEND_VENDOR_STR(USB 1.0 Audio In), // 5 + + APPEND_VENDOR_STR(Audio 1.0 Output), // 6 "USB Input Terminal" (User sees as output from host) + APPEND_VENDOR_STR(Audio 1.0 Input), // 7 "USB Output Terminal" (User sees as input to host) + + APPEND_VENDOR_STR(DFU) // 8 iInterface for DFU interface +}; + +#endif diff --git a/module_usb_aud_shared/endpoint0/descriptors_2.rst b/module_usb_aud_shared/endpoint0/descriptors_2.rst new file mode 100644 index 00000000..db49e9a0 --- /dev/null +++ b/module_usb_aud_shared/endpoint0/descriptors_2.rst @@ -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 diff --git a/module_usb_aud_shared/endpoint0/endpoint0.h b/module_usb_aud_shared/endpoint0/endpoint0.h new file mode 100644 index 00000000..3052e609 --- /dev/null +++ b/module_usb_aud_shared/endpoint0/endpoint0.h @@ -0,0 +1,23 @@ + +#ifndef _ENDPOINT0_H_ +#define _ENDPOINT0_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. + */ +void Endpoint0( chanend c_ep0_out, chanend c_ep0_in, chanend c_audioCtrl, chanend ?c_mix_ctl,chanend ?c_clk_ctl +#ifdef EP0_THREAD_COMBINED_WITH_SPI + , chanend c_spi, chanend c_spi_ss +#endif +); + +#endif diff --git a/module_usb_aud_shared/endpoint0/endpoint0.xc b/module_usb_aud_shared/endpoint0/endpoint0.xc new file mode 100755 index 00000000..51d3c687 --- /dev/null +++ b/module_usb_aud_shared/endpoint0/endpoint0.xc @@ -0,0 +1,840 @@ +/** + * @file endpoint0.xc + * @brief Implements endpoint zero for an USB Audio 1.0/2.0 device + * @author Ross Owen, XMOS Semiconductor + */ + +#include +#include +#include + +#include "xud.h" /* XUD user defines and functions */ +#include "usb.h" /* Defines from USB 2.0 Spec */ +#include "usbaudio20.h" /* Defines from USB Audio 2.0 spec */ + +#include "devicedefines.h" +#include "DescriptorRequests.h" /* This device's descriptors */ +#include "descriptors_2.h" /* Descriptors */ +#include "clockcmds.h" +#include "audiostream.h" +#include "vendorrequests.h" +#include "dfu_types.h" + +/* 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.Udi + * Of course, OSX is unaffected. + */ +#if ((AUDIO_CLASS==1) || defined(AUDIO_CLASS_FALLBACK)) && defined(DFU) +#warning DFU will require a seperate driver (but will be enabled) in AUDIO 1.0 mode +#endif + +/* MIDI not supported in Audio 1.0 mode */ +#if ((AUDIO_CLASS==1) || defined(AUDIO_CLASS_FALLBACK)) && defined(MIDI) +#warning MIDI is not supported and will not be enabled in AUDIO 1.0 mode +#endif + + +#ifndef PID_DFU +#warning PID_DFU 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 + +unsigned int DFU_mode_active = 0; // 0 - App active, 1 - DFU active +extern void device_reboot(void); +#endif + +/* Handles Audio Class requests */ +int AudioClassRequests_2(XUD_ep ep0_out, XUD_ep ep0_in, SetupPacket &sp, chanend c_audioControl, chanend ?c_mix_ctl, chanend ?c_clk_ctl); +int AudioClassRequests_1(XUD_ep ep0_out, XUD_ep ep0_in, SetupPacket &sp, chanend c_audioControl, chanend ?c_mix_ctl, chanend ?c_clk_ctl); + +/* Global var for current frequency, set to default freq */ +unsigned int g_curSamFreq = DEFAULT_FREQ; +unsigned int g_curSamFreq48000Family = DEFAULT_FREQ % 48000 == 0; +unsigned int g_curSamFreqMultiplier = DEFAULT_FREQ / 48000; + +/* Global volume and mute tables */ +int volsOut[NUM_USB_CHAN_OUT + 1]; +unsigned int mutesOut[NUM_USB_CHAN_OUT + 1]; +//unsigned int multOut[NUM_USB_CHAN_OUT + 1]; + +int volsIn[NUM_USB_CHAN_IN + 1]; +unsigned int mutesIn[NUM_USB_CHAN_IN + 1]; +//unsigned int multIn[NUM_USB_CHAN_IN + 1]; + +unsigned char mixer1Crossbar[18]; +short mixer1Weights[18*8]; +//#define MAX_MIX_COUNT 8 + +unsigned char channelMap[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT]; +#if NUM_USB_CHAN_OUT > 0 +unsigned char channelMapAud[NUM_USB_CHAN_OUT]; +#endif +#if NUM_USB_CHAN_IN > 0 +unsigned char channelMapUsb[NUM_USB_CHAN_IN]; +#endif +unsigned char mixSel[MIX_INPUTS]; + +int min(int x, int y); + +/* Records alt setting for each interface */ +int interfaceAlt[NUM_INTERFACES] = {0, 0, 0, 0}; + +/* Global current device config var*/ +unsigned g_config = 0; + +/* Global endpoint status arrays */ +unsigned g_epStatusOut[NUM_EP_OUT]; +unsigned g_epStatusIn[NUM_EP_IN]; + +/* Global variable for current USB bus speed (i.e. FS/HS) */ +unsigned g_curUsbSpeed = 0; + +#ifdef HOST_ACTIVE_CALL +void VendorHostActive(int active); +#endif + +/* Used when setting/clearing EP halt */ +void SetEndpointStatus(unsigned epNum, unsigned status) +{ + /* Inspect for IN bit */ + if( epNum & 0x80 ) + { + epNum &= 0x7f; + + /* Range check */ + if(epNum < NUM_EP_IN) + { + g_epStatusIn[ epNum & 0x7F ] = status; + } + } + else + { + if(epNum < NUM_EP_OUT) + { + g_epStatusOut[ epNum ] = status; + } + } +} + +#define STR_USENG 0x0409 + +#define DESC_STR_LANGIDS \ +{ \ + STR_USENG & 0xff, /* 2 wLangID[0] */ \ + STR_USENG>>8, /* 3 wLangID[0] */ \ + '\0' \ +} + +/* String descriptors */ +static unsigned char strDesc_langIDs[] = DESC_STR_LANGIDS; + +void VendorAudioRequestsInit(chanend c_audioControl, chanend ?c_mix_ctl, chanend ?c_clk_ctl); + +/* 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 +) +{ + unsigned char buffer[512]; + SetupPacket sp; + XUD_ep ep0_out = XUD_Init_Ep(c_ep0_out); + XUD_ep ep0_in = XUD_Init_Ep(c_ep0_in); + + /* Init endpoint status tables */ + for (int i = 0; i++; i < NUM_EP_OUT) + g_epStatusOut[i] = 0; + + for (int i = 0; i++; i < NUM_EP_IN) + g_epStatusIn[i] = 0; + + /* 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; + } + + /* 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 i = 0; i < MIX_INPUTS; i++) + { + mixSel[i] = i; + } + + /* Copy langIDs string desc into string[0] */ + /* TODO: Macro? */ +#if defined(AUDIO_CLASS_FALLBACK) || (AUDIO_CLASS == 1) + safememcpy(strDescs_Audio1[0], strDesc_langIDs, sizeof(strDesc_langIDs)); +#endif + safememcpy(strDescs_Audio2[0], strDesc_langIDs, sizeof(strDesc_langIDs)); + + /* Build up channel string table - By default all channels are marked as analogue + * TODO We really want to do this an build time... */ +#if defined(SPDIF_RX) && (SPDIF_RX_INDEX != 0) + safestrcpy(strDescs_Audio2[SPDIF_RX_INDEX + 33], "S/PDIF 1"); + safestrcpy(strDescs_Audio2[SPDIF_RX_INDEX + 34], "S/PDIF 2"); +#endif +#if defined(ADAT_RX) && (ADAT_RX_INDEX != 0) + safestrcpy(strDescs_Audio2[ADAT_RX_INDEX + 33], "ADAT 1"); + safestrcpy(strDescs_Audio2[ADAT_RX_INDEX + 34], "ADAT 2"); + safestrcpy(strDescs_Audio2[ADAT_RX_INDEX + 35], "ADAT 3"); + safestrcpy(strDescs_Audio2[ADAT_RX_INDEX + 36], "ADAT 4"); + safestrcpy(strDescs_Audio2[ADAT_RX_INDEX + 37], "ADAT 5"); + safestrcpy(strDescs_Audio2[ADAT_RX_INDEX + 38], "ADAT 6"); + safestrcpy(strDescs_Audio2[ADAT_RX_INDEX + 39], "ADAT 7"); + safestrcpy(strDescs_Audio2[ADAT_RX_INDEX + 40], "ADAT 8"); +#endif + +#if defined(SPDIF) && (SPDIF_TX_INDEX != 0) /* "Analogue naming gets priority */ + safestrcpy(strDescs_Audio2[SPDIF_TX_INDEX + 15], "S/PDIF 1"); + safestrcpy(strDescs_Audio2[SPDIF_TX_INDEX + 16], "S/PDIF 2"); +#endif +#if defined(ADAT_TX) && (ADAT_TX_INDEX != 0) + safestrcpy(strDescs_Audio2[ADAT_TX_INDEX + 15], "ADAT 1"); + safestrcpy(strDescs_Audio2[ADAT_TX_INDEX + 16], "ADAT 2"); + safestrcpy(strDescs_Audio2[ADAT_TX_INDEX + 17], "ADAT 3"); + safestrcpy(strDescs_Audio2[ADAT_TX_INDEX + 18], "ADAT 4"); + safestrcpy(strDescs_Audio2[ADAT_TX_INDEX + 19], "ADAT 5"); + safestrcpy(strDescs_Audio2[ADAT_TX_INDEX + 20], "ADAT 6"); + safestrcpy(strDescs_Audio2[ADAT_TX_INDEX + 21], "ADAT 7"); + safestrcpy(strDescs_Audio2[ADAT_TX_INDEX + 22], "ADAT 8"); +#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 + //chkct(c_audioControl, XS1_CT_END); + DFU_mode_active = 1; + } +#endif + + while(1) + { + int retVal = 1; + + /* Do standard enumeration requests */ +#ifndef DFU + if(g_curUsbSpeed == XUD_SPEED_HS) + { + +#ifdef AUDIO_CLASS_FALLBACK + /* Return Audio 2.0 Descriptors with Audio 1.0 as fallback */ + cfgDesc_Audio2[1] = CONFIGURATION; + cfgDesc_Audio1[1] = OTHER_SPEED_CONFIGURATION; + + retVal = DescriptorRequests(ep0_out, ep0_in, + devDesc_Audio2, sizeof(devDesc_Audio2), + cfgDesc_Audio2, sizeof(cfgDesc_Audio2), + devQualDesc_Audio1, sizeof(devQualDesc_Audio1), + cfgDesc_Audio1, sizeof(cfgDesc_Audio1), + strDescs_Audio2, sp); +#else + /* Return Audio 2.0 Descriptors */ + cfgDesc_Audio2[1] = CONFIGURATION; + cfgDesc_Null[1] = OTHER_SPEED_CONFIGURATION; + + retVal = DescriptorRequests(ep0_out, ep0_in, + devDesc_Audio2, sizeof(devDesc_Audio2), + cfgDesc_Audio2, sizeof(cfgDesc_Audio2), + devQualDesc_Null, sizeof(devQualDesc_Null), + cfgDesc_Null, sizeof(cfgDesc_Null), + strDescs_Audio2, sp); +#endif + } + else + { + /* Return descriptors for full-speed - Audio 1.0? */ +#ifdef AUDIO_CLASS_FALLBACK + cfgDesc_Audio1[1] = CONFIGURATION; + cfgDesc_Audio2[1] = OTHER_SPEED_CONFIGURATION; + + retVal = DescriptorRequests(ep0_out, ep0_in, + devDesc_Audio1, sizeof(devDesc_Audio1), + cfgDesc_Audio1, sizeof(cfgDesc_Audio1), + devQualDesc_Audio2, sizeof(devQualDesc_Audio2), + cfgDesc_Audio2, sizeof(cfgDesc_Audio2), + strDescs_Audio1, sp); + +#else + cfgDesc_Null[1] = CONFIGURATION; + cfgDesc_Audio2[1] = OTHER_SPEED_CONFIGURATION; + + retVal = DescriptorRequests(ep0_out, ep0_in, + devDesc_Null, sizeof(devDesc_Null), + cfgDesc_Null, sizeof(cfgDesc_Null), + devQualDesc_Audio2, sizeof(devQualDesc_Audio2), + cfgDesc_Audio2, sizeof(cfgDesc_Audio2), + strDescs_Audio2, sp); +#endif + + } +#else /* ifndef DFU */ + if (!DFU_mode_active) + { + if(g_curUsbSpeed == XUD_SPEED_HS) + { + +#ifdef AUDIO_CLASS_FALLBACK + /* Return Audio 2.0 Descriptors with Audio 1.0 as fallback */ + cfgDesc_Audio2[1] = CONFIGURATION; + cfgDesc_Audio1[1] = OTHER_SPEED_CONFIGURATION; + + retVal = DescriptorRequests(ep0_out, ep0_in, + devDesc_Audio2, sizeof(devDesc_Audio2), + cfgDesc_Audio2, sizeof(cfgDesc_Audio2), + devQualDesc_Audio1, sizeof(devQualDesc_Audio1), + cfgDesc_Audio1, sizeof(cfgDesc_Audio1), + strDescs_Audio2, sp); +#else + /* Return Audio 2.0 Descriptors with Null device as fallback */ + cfgDesc_Audio2[1] = CONFIGURATION; + cfgDesc_Null[1] = OTHER_SPEED_CONFIGURATION; + + retVal = DescriptorRequests(ep0_out, ep0_in, + devDesc_Audio2, sizeof(devDesc_Audio2), + cfgDesc_Audio2, sizeof(cfgDesc_Audio2), + devQualDesc_Null, sizeof(devQualDesc_Null), + cfgDesc_Null, sizeof(cfgDesc_Null), + strDescs_Audio2, sp); +#endif + + } + else + { + /* Return descriptors for full-speed - Audio 1.0? */ +#ifdef AUDIO_CLASS_FALLBACK + cfgDesc_Audio1[1] = CONFIGURATION; + cfgDesc_Audio2[1] = OTHER_SPEED_CONFIGURATION; + + retVal = DescriptorRequests(ep0_out, ep0_in, + devDesc_Audio1, sizeof(devDesc_Audio1), + cfgDesc_Audio1, sizeof(cfgDesc_Audio1), + devQualDesc_Audio2, sizeof(devQualDesc_Audio2), + cfgDesc_Audio2, sizeof(cfgDesc_Audio2), + strDescs_Audio1, sp); +#else + cfgDesc_Null[1] = CONFIGURATION; + cfgDesc_Audio2[1] = OTHER_SPEED_CONFIGURATION; + + retVal = DescriptorRequests(ep0_out, ep0_in, + devDesc_Null, sizeof(devDesc_Null), + cfgDesc_Null, sizeof(cfgDesc_Null), + devQualDesc_Audio2, sizeof(devQualDesc_Audio2), + cfgDesc_Audio2, sizeof(cfgDesc_Audio2), + strDescs_Audio2, sp); +#endif + + } + } + else + { + /* Running in DFU mode - always return same descs for DFU whether HS or FS */ + retVal = DescriptorRequests(ep0_out, ep0_in, + DFUdevDesc, sizeof(DFUdevDesc), + DFUcfgDesc, sizeof(DFUcfgDesc), + DFUdevQualDesc, sizeof(DFUdevQualDesc), + DFUoSpeedCfgDesc, sizeof(DFUoSpeedCfgDesc), + strDescs_Audio2, sp); + } +#endif + + if (retVal == 1) + { + /* Request not covered by XUD_DoEnumReqs() so decode ourselves */ + /* Inspect Request type and Receipient */ + switch( (sp.bmRequestType.Recipient ) | (sp.bmRequestType.Type << 5) ) + { + case STANDARD_INTERFACE_REQUEST: + + switch(sp.bRequest) + { + /* Set Interface */ + case SET_INTERFACE: + + +#if defined(OUTPUT) && defined(INPUT) + /* Check for stream start stop on output and input audio interfaces */ + if(sp.wValue && !interfaceAlt[1] && !interfaceAlt[2]) + { + /* If start and input AND output not currently running */ + AudioStreamStart(); + } + else if(((sp.wIndex == 1)&& (!sp.wValue)) && interfaceAlt[1] && (!interfaceAlt[2])) + { + /* if output stop and output running and input not running */ + AudioStreamStop(); + } + else if(((sp.wIndex == 2) && (!sp.wValue)) && interfaceAlt[2] && (!interfaceAlt[1])) + { + /* if input stop and input running and output not running */ + AudioStreamStop(); + } +#elif defined(OUTPUT) || defined(INPUT) + if(sp.wValue && (!interfaceAlt[1])) + { + /* if start and not currently running */ + AudioStreamStart(); + } + else if (!sp.wValue && interfaceAlt[1]) + { + /* if stop and currently running */ + AudioStreamStop(); + } + +#endif + /* Record interface change */ + if( sp.wIndex < NUM_INTERFACES ) + interfaceAlt[sp.wIndex] = sp.wValue; +#if 1 + /* Check for audio stream from host start/stop */ + if(sp.wIndex == 2) // Input interface + { + switch(sp.wValue) + { + case 0: + + break; + + case 1: + /* Stream active + 0 chans */ + outuint(c_audioControl, SET_CHAN_COUNT_IN); + outuint(c_audioControl, NUM_USB_CHAN_IN); + +#ifdef ADAT_RX + outuint(c_clk_ctl, SET_SMUX); + outuint(c_clk_ctl, 0); + outct(c_clk_ctl, XS1_CT_END); +#endif + + break; + +#ifdef ADAT_RX + case 2: + + /* Stream active + 8 chans */ + outuint(c_audioControl, SET_CHAN_COUNT_IN); + outuint(c_audioControl, NUM_USB_CHAN_IN-4); + + outuint(c_clk_ctl, SET_SMUX); + outuint(c_clk_ctl, 1); + outct(c_clk_ctl, XS1_CT_END); + break; + + case 3: + outuint(c_audioControl, SET_CHAN_COUNT_IN); + outuint(c_audioControl, NUM_USB_CHAN_IN-6); + + + outuint(c_clk_ctl, SET_SMUX); + outuint(c_clk_ctl, 1); + outct(c_clk_ctl, XS1_CT_END); + /* Stream active + 8 chans */ + //outuint(c_audioControl, 8); + // Handshake + //chkct(c_audioControl, XS1_CT_END); + + break; + +#endif + + } + } +#endif + /* No data stage for this request, just do data stage */ + retVal = XUD_DoSetRequestStatus(ep0_in, 0); + break; + + /* A device must support the GetInterface request if it has alternate setting for that interface */ + case GET_INTERFACE: + + buffer[0] = 0; + + /* Bounds check */ + if( sp.wIndex < NUM_INTERFACES ) + buffer[0] = interfaceAlt[sp.wIndex]; + + retVal = XUD_DoGetRequest(ep0_out, ep0_in, buffer, 1, sp.wLength); + break; + + + default: + printstr("Unknown Standard Interface Request: "); + printhexln(sp.bRequest); + printhexln(sp.bmRequestType.Type); + printhexln(sp.bmRequestType.Recipient); + printhexln(sp.bmRequestType.Recipient | (sp.bmRequestType.Type << 5)); + break; + } + break; + + /* Recipient: Device */ + case STANDARD_DEVICE_REQUEST: + + /* Standard Device requests (8) */ + switch( sp.bRequest ) + { + + /* Set Device Address: This is a unique set request. */ + case SET_ADDRESS: + + /* Status stage: Send a zero length packet */ + retVal = XUD_SetBuffer_ResetPid(ep0_in, buffer, 0, PIDn_DATA1); + + /* TODO We should wait until ACK is received for status stage before changing address */ + //XUD_Sup_Delay(50000); + { + timer t; + unsigned time; + t :> time; + t when timerafter(time+50000) :> void; + } + + /* Set device address in XUD */ + XUD_SetDevAddr(sp.wValue); + + break; + + + /* TODO Check direction */ + /* Standard request: SetConfiguration */ + case SET_CONFIGURATION: + + g_config = sp.wValue; + +#ifdef HOST_ACTIVE_CALL + if(g_config == 1) + { + /* Consider host active with valid driver at this point */ + VendorHostActive(1); + } +#endif + + /* No data stage for this request, just do status stage */ + retVal = XUD_DoSetRequestStatus(ep0_in, 0); + break; + + case GET_CONFIGURATION: + buffer[0] = g_config; + retVal = XUD_DoGetRequest(ep0_out, ep0_in, buffer, 1, sp.wLength); + break; + + /* Get Status request */ + case GET_STATUS: + + buffer[0] = 0; // bus powered + buffer[1] = 0; // remote wakeup not supported + + retVal = XUD_DoGetRequest(ep0_out, ep0_in, buffer, 2, sp.wLength); + break; + + + default: + XUD_Error("Unknown device request"); + break; + + } + break; + + /* Receipient: Endpoint */ + case STANDARD_ENDPOINT_REQUEST: + + /* Standard endpoint requests */ + switch ( sp.bRequest ) + { + + /* ClearFeature */ + case CLEAR_FEATURE: + + switch ( sp.wValue ) + { + case ENDPOINT_HALT: + + /* Mark the endpoint status */ + + SetEndpointStatus(sp.wIndex, 0); + + /* No data stage for this request, just do status stage */ + retVal = XUD_DoSetRequestStatus(ep0_in, 0); + + break; + + + default: + XUD_Error( "Unknown request in Endpoint ClearFeature" ); + break; + } + break; /* B_REQ_CLRFEAR */ + + /* SetFeature */ + case SET_FEATURE: + + switch( sp.wValue ) + { + case ENDPOINT_HALT: + + /* Check request is in range */ + SetEndpointStatus(sp.wIndex, 1); + + break; + + default: + XUD_Error("Unknown feature in SetFeature Request"); + break; + } + + + retVal = XUD_DoSetRequestStatus(ep0_in, 0); + + break; + + + + /* Endpoint GetStatus Request */ + case GET_STATUS: + + buffer[0] = 0; + buffer[1] = 0; + + if( sp.wIndex & 0x80 ) + { + /* IN Endpoint */ + if((sp.wIndex&0x7f) < NUM_EP_IN) + { + buffer[0] = ( g_epStatusIn[ sp.wIndex & 0x7F ] & 0xff ); + buffer[1] = ( g_epStatusIn[ sp.wIndex & 0x7F ] >> 8 ); + } + } + else + { + /* OUT Endpoint */ + if(sp.wIndex < NUM_EP_OUT) + { + buffer[0] = ( g_epStatusOut[ sp.wIndex ] & 0xff ); + buffer[1] = ( g_epStatusOut[ sp.wIndex ] >> 8 ); + } + } + + retVal = XUD_DoGetRequest(ep0_out, ep0_in, buffer, 2, sp.wLength); + + break; + + default: + //printstrln("Unknown Standard Endpoint Request"); + break; + + } + break; + + case CLASS_INTERFACE_REQUEST: + case CLASS_ENDPOINT_REQUEST: + { + unsigned interfaceNum = sp.wIndex & 0xff; + unsigned request = (sp.bmRequestType.Recipient ) | (sp.bmRequestType.Type << 5); + + /* TODO Check interface number */ + /* TODO Check on return value retval = */ +#ifdef DFU + unsigned DFU_IF = DFU_IF_NUM; + + /* DFU interface number changes based on which mode we are currently running in */ + if (DFU_mode_active) + { + DFU_IF = 0; + } + + if (interfaceNum == DFU_IF) + { + + /* If running in application mode stop audio */ + /* Don't interupt audio for save and restore cmds */ + if ((DFU_IF == DFU_IF_NUM) && (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 */ + if (DFUDeviceRequests(ep0_out, ep0_in, sp, null, interfaceAlt[sp.wIndex], 1)) + { + timer tmr; + unsigned s; + /* Send reboot command */ + //outuint(c_audioControl, SET_SAMPLE_FREQ); + //outuint(c_audioControl, AUDIO_REBOOT_FROM_DFU); + tmr :> s; + tmr when timerafter(s + 50000000) :> s; + device_reboot(); + } + + /* TODO we should not make the assumption that all DFU requests are handled */ + retVal = 0; + } + /* Check for: Audio interface request - always 0, note we check for DFU first + * Audio endpoint request */ + else if(((request == CLASS_INTERFACE_REQUEST) && (interfaceNum == 0)) + || (request == CLASS_ENDPOINT_REQUEST && ((interfaceNum == 0x82) || (interfaceNum == 0x01)))) + { +#endif + +#if (AUDIO_CLASS == 2) && defined(AUDIO_CLASS_FALLBACK) + if(g_curUsbSpeed == XUD_SPEED_HS) + { + retVal = AudioClassRequests_2(ep0_out, ep0_in, sp, c_audioControl, c_mix_ctl, c_clk_ctl); + } + else + { + retVal = AudioClassRequests_1(ep0_out, ep0_in, sp, c_audioControl, c_mix_ctl, c_clk_ctl); + } +#elif (AUDIO_CLASS==2) + retVal = AudioClassRequests_2(ep0_out, ep0_in, sp, c_audioControl, c_mix_ctl, c_clk_ctl); +#else + retVal = AudioClassRequests_1(ep0_out, ep0_in, sp, c_audioControl, c_mix_ctl, c_clk_ctl); +#endif + +#ifdef VENDOR_AUDIO_REQS + /* If retVal is 1 at this point, then request to audio interface not handled - handle vendor audio reqs */ + if(retVal == 1) + { + retVal = 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 +#ifdef DFU + } +#endif + } + break; + + default: + //printstr("unrecognised request\n"); + //printhexln(sp.bRequest); + //printhexln(sp.bmRequestType.Type); + //printhexln(sp.bmRequestType.Recipient); + //printhexln(sp.bmRequestType.Recipient | (sp.bmRequestType.Type << 5)); + break; + + + } + + } /* if(retVal == 0) */ + + if(retVal == 1) + { + /* Did not handle request - Protocol Stall Secion 8.4.5 of USB 2.0 spec + * Detailed in Section 8.5.3. Protocol stall is unique to control pipes. + Protocol stall differs from functional stall in meaning and duration. + A protocol STALL is returned during the Data or Status stage of a control + transfer, and the STALL condition terminates at the beginning of the + next control transfer (Setup). The remainder of this section refers to + the general case of a functional stall */ + XUD_SetStall_Out(0); + XUD_SetStall_In(0); + } + + if (retVal < 0) + { + g_curUsbSpeed = XUD_ResetEndpoint(ep0_in, ep0_out); + + g_config = 0; + +#ifdef DFU + if (DFUReportResetState(null)) + { + if (!DFU_mode_active) + { + timer tmr; + unsigned s; + DFU_mode_active = 1; + //tmr :> s; + //tmr when timerafter(s + 500000) :> s; + } + } + else + { + if (DFU_mode_active) + { + timer tmr; + unsigned s; + // Restart audio + //outuint(c_audioControl, AUDIO_START_FROM_DFU); + DFU_mode_active = 0; + // Send reboot command + //outuint(c_audioControl, SET_SAMPLE_FREQ); + //outuint(c_audioControl, AUDIO_REBOOT_FROM_DFU); + // No handshake on reboot + tmr :> s; + tmr when timerafter(s + 5000000) :> s; + device_reboot(); + } + } +#endif + } + + } +} diff --git a/module_usb_aud_shared/endpoint0/vendorrequests.h b/module_usb_aud_shared/endpoint0/vendorrequests.h new file mode 100644 index 00000000..9e6c43ea --- /dev/null +++ b/module_usb_aud_shared/endpoint0/vendorrequests.h @@ -0,0 +1,16 @@ +#ifndef _VENDORREQUESTS_H_ +#define _VENDORREQUESTS_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) + * + * */ + +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, chanend ?c_mix_ctl, chanend ?c_clk_ctL); + +#endif + diff --git a/module_usb_aud_shared/i2c/i2c.h b/module_usb_aud_shared/i2c/i2c.h new file mode 100644 index 00000000..6248e914 --- /dev/null +++ b/module_usb_aud_shared/i2c/i2c.h @@ -0,0 +1,4 @@ + +void I2cRegWrite(int deviceAdrs, int Adrs, int WrData, port AUD_SCLK, port AUD_SDIN); + +int I2cRegRead(int deviceAdrs, int Adrs, port AUD_SCLK, port AUD_SDIN); diff --git a/module_usb_aud_shared/i2c/i2c.xc b/module_usb_aud_shared/i2c/i2c.xc new file mode 100755 index 00000000..fd252260 --- /dev/null +++ b/module_usb_aud_shared/i2c/i2c.xc @@ -0,0 +1,321 @@ +#include +#include + +int I2cRegRead(int device, int addr, port scl, port sda) +{ + //int Result; + timer gt; + unsigned time; + int Temp, CtlAdrsData, i; + // three device ACK + int ack[3]; + int rdData; + + // initial values. + scl <: 1; + sda <: 1; + sync(sda); + gt :> time; + time += 1000 + 1000; + gt when timerafter(time) :> int _; + // start bit on SDI + scl <: 1; + sda <: 0; + gt :> time; + time += 1000; + gt when timerafter(time) :> int _; + scl <: 0; + // shift 7bits of address and 1bit R/W (fixed to write). + // WARNING: Assume MSB first. + for (i = 0; i < 8; i += 1) + { + Temp = (device >> (7 - i)) & 0x1; + sda <: Temp; + gt :> time; + time += 1000; + gt when timerafter(time) :> int _; + scl <: 1; + gt :> time; + time += 1000; + gt when timerafter(time) :> int _; + scl <: 0; + } + // turn the data to input + sda :> Temp; + gt :> time; + time += 1000; + gt when timerafter(time) :> int _; + scl <: 1; + // sample first ACK. + sda :> ack[0]; + gt :> time; + time += 1000; + gt when timerafter(time) :> int _; + scl <: 0; + + CtlAdrsData = (addr & 0xFF); + // shift first 8 bits. + for (i = 0; i < 8; i += 1) + { + Temp = (CtlAdrsData >> (7 - i)) & 0x1; + sda <: Temp; + gt :> time; + time += 1000; + gt when timerafter(time) :> int _; + scl <: 1; + gt :> time; + time += 1000; + gt when timerafter(time) :> int _; + scl <: 0; + } + // turn the data to input + sda :> Temp; + gt :> time; + time += 1000; + gt when timerafter(time) :> int _; + scl <: 1; + // sample second ACK. + sda :> ack[1]; + gt :> time; + time += 1000; + gt when timerafter(time) :> int _; + scl <: 0; + + + // stop bit + gt :> time; + time += 1000 + 1000; + gt when timerafter(time) :> int _; + // start bit on SDI + scl <: 1; + sda <: 1; + time += 1000 + 1000; + gt when timerafter(time) :> int _; + scl <: 0; + time += 1000 + 1000; + gt when timerafter(time) :> int _; + + + // send address and read + scl <: 1; + sda <: 1; + sync(sda); + gt :> time; + time += 1000 + 1000; + gt when timerafter(time) :> int _; + // start bit on SDI + scl <: 1; + sda <: 0; + gt :> time; + time += 1000; + gt when timerafter(time) :> int _; + scl <: 0; + // shift 7bits of address and 1bit R/W (fixed to write). + // WARNING: Assume MSB first. + for (i = 0; i < 8; i += 1) + { + int deviceAddr = device | 1; + Temp = (deviceAddr >> (7 - i)) & 0x1; + sda <: Temp; + gt :> time; + time += 1000; + gt when timerafter(time) :> int _; + scl <: 1; + gt :> time; + time += 1000; + gt when timerafter(time) :> int _; + scl <: 0; + } + // turn the data to input + sda :> Temp; + gt :> time; + time += 1000; + gt when timerafter(time) :> int _; + scl <: 1; + // sample first ACK. + sda :> ack[0]; + gt :> time; + time += 1000; + gt when timerafter(time) :> int _; + scl <: 0; + + + rdData = 0; + // shift second 8 bits. + for (i = 0; i < 8; i += 1) + { + + gt :> time; + time += 1000; + gt when timerafter(time) :> int _; + scl <: 1; + + sda :> Temp; + rdData = (rdData << 1) | (Temp & 1); + + gt :> time; + time += 1000; + gt when timerafter(time) :> int _; + scl <: 0; + } + + // turn the data to input + sda :> Temp; + gt :> time; + time += 1000; + gt when timerafter(time) :> int _; + scl <: 1; + // sample second ACK. + sda :> ack[2]; + gt :> time; + time += 1000; + gt when timerafter(time) :> int _; + scl <: 0; + gt :> time; + time += 1000; + gt when timerafter(time) :> int _; + scl <: 1; + // put the data to a good value for next round. + sda <: 1; + // validate all items are ACK properly. + //Result = 0; + //for (i = 0; i < 3; i += 1) + //{ + //if ((ack[i]&1) != 0) + //{ + //Result = Result | (1 << i); + //} + //} + + return rdData; +} + +void I2cRegWrite(int device, int addr, int data, port scl, port sda) +{ + //int Result; + timer gt; + unsigned time; + int Temp, CtlAdrsData, i; + // three device ACK + int ack[3]; + + // initial values. + scl <: 1; + sda <: 1; + sync(sda); + + gt :> time; + time += 1000 + 1000; + gt when timerafter(time) :> void; + + // start bit on SDI + scl <: 1; + sda <: 0; + gt :> time; + time += 1000; + gt when timerafter(time) :> void; + scl <: 0; + + // shift 7bits of address and 1bit R/W (fixed to write). + // WARNING: Assume MSB first. + for (i = 0; i < 8; i += 1) + { + Temp = (device >> (7 - i)) & 0x1; + sda <: Temp; + gt :> time; + time += 1000; + gt when timerafter(time) :> void; + scl <: 1; + gt :> time; + time += 1000; + gt when timerafter(time) :> void; + scl <: 0; + } + + // turn the data to input + sda :> Temp; + gt :> time; + time += 1000; + gt when timerafter(time) :> void; + scl <: 1; + + // sample first ACK. + sda :> ack[0]; + gt :> time; + time += 1000; + gt when timerafter(time) :> void; + scl <: 0; + + CtlAdrsData = (addr & 0xFF); + + // shift first 8 bits. + for (i = 0; i < 8; i += 1) + { + Temp = (CtlAdrsData >> (7 - i)) & 0x1; + sda <: Temp; + gt :> time; + time += 1000; + gt when timerafter(time) :> void; + scl <: 1; + gt :> time; + time += 1000; + gt when timerafter(time) :> void; + scl <: 0; + } + // turn the data to input + sda :> Temp; + gt :> time; + time += 1000; + gt when timerafter(time) :> void; + scl <: 1; + // sample second ACK. + sda :> ack[1]; + gt :> time; + time += 1000; + gt when timerafter(time) :> void; + scl <: 0; + + CtlAdrsData = (data & 0xFF); + // shift second 8 bits. + for (i = 0; i < 8; i += 1) + { + Temp = (CtlAdrsData >> (7 - i)) & 0x1; + sda <: Temp; + gt :> time; + time += 1000; + gt when timerafter(time) :> void; + scl <: 1; + gt :> time; + time += 1000; + gt when timerafter(time) :> void; + scl <: 0; + } + // turn the data to input + sda :> Temp; + gt :> time; + time += 1000; + gt when timerafter(time) :> void; + scl <: 1; + // sample second ACK. + sda :> ack[2]; + gt :> time; + time += 1000; + gt when timerafter(time) :> void; + scl <: 0; + gt :> time; + time += 1000; + gt when timerafter(time) :> void; + scl <: 1; + // put the data to a good value for next round. + sda <: 1; + // validate all items are ACK properly. + //Result = 0; + //for (i = 0; i < 3; i += 1) + //{ + //if ((ack[i]&1) != 0) + //{ + //Result = Result | (1 << i); + //} + //} + //return(Result); +} diff --git a/module_usb_aud_shared/mixer/fastmix.S b/module_usb_aud_shared/mixer/fastmix.S new file mode 100644 index 00000000..16e3c703 --- /dev/null +++ b/module_usb_aud_shared/mixer/fastmix.S @@ -0,0 +1,167 @@ +//#include "devicedefines.h" + +#define MAX_MIX_COUNT 8 +#define MIX_INPUTS 18 + + + +#define DOMIX_TOP(i) \ +.cc_top doMix##i.function,doMix##i; \ + .align 4 ;\ +.globl doMix##i ;\ +.globl doMix##i##.nstackwords ;\ +.globl doMix##i##.maxthreads ; \ +.globl doMix##i##.maxtimers ; \ +.globl doMix##i##.maxchanends ; \ +.globl doMix##i##.maxsync ;\ +.linkset doMix##i##.locnoside, 1; \ +.linkset doMix##i##.locnochandec, 1;\ +.linkset doMix##i##.nstackwords, 0 ;\ +.linkset doMix##i##.maxchanends, 0 ;\ +.linkset doMix##i##.maxtimers, 0 ;\ +.linkset doMix##i##.maxthreads, 1; \ +doMix##i##: ;\ + set cp, r0; \ + set dp, r1; \ + lsub r0, r1, r0, r0, r0;\ + .label_##i##: + +#define DOMIX_BOT(i) \ + ldap r11, _dp; \ + set dp, r11;\ + ldap r11, _cp;\ + set cp, r11;\ +\ + mov r0, r1;\ + ldc r2, 0x19;\ + sext r0, r2;\ + eq r0, r0, r1;\ + bf r0, .L20; \ +\ + shl r0, r1, 0x7;\ + retsp 0x0;\ +\ +\ +.cc_bottom doMix##i##.function; + +#define N MIX_INPUTS +#define BODY(i) \ + ldw r2,cp[i]; \ + ldw r11, dp[i]; \ + maccs r1, r0, r2, r11; + + +.text + +.L20:\ + lss r0, r1, r3;\ + bt r0, .L16; \ + ldw r0, cp[.LC0];\ + retsp 0x0; \ +.L16:\ + ldw r0, cp[.LC1];\ + retsp 0x0; \ + + + + +#if(MAX_MIX_COUNT > 0) +DOMIX_TOP(0) +#include "repeat.h" +DOMIX_BOT(0) +#endif + + +#if(MAX_MIX_COUNT > 1) +DOMIX_TOP(1) +#include "repeat.h" +DOMIX_BOT(1) +#endif + +#if(MAX_MIX_COUNT > 2) +DOMIX_TOP(2) +#include "repeat.h" +DOMIX_BOT(2) +#endif + +#if(MAX_MIX_COUNT > 3) +DOMIX_TOP(3) +#include "repeat.h" +DOMIX_BOT(3) +#endif + +#if(MAX_MIX_COUNT > 4) +DOMIX_TOP(4) +#include "repeat.h" +DOMIX_BOT(4) +#endif + +#if(MAX_MIX_COUNT > 5) +DOMIX_TOP(5) +#include "repeat.h" +DOMIX_BOT(5) +#endif + +#if(MAX_MIX_COUNT > 6) +DOMIX_TOP(6) +#include "repeat.h" +DOMIX_BOT(6) +#endif + +#if(MAX_MIX_COUNT > 7) +DOMIX_TOP(7) +#include "repeat.h" +DOMIX_BOT(7) +#endif + +#if(MAX_MIX_COUNT>8) +#error MAX_MIX_COUNT>7 +#endif + +/* We need MIX_OUTPUT x setPtr functions */ + +#undef N +#undef BODY +#define N MAX_MIX_COUNT +.cc_top setPtr.function,setPtr; + .align 4 ; +.globl setPtr.nstackwords; +.globl setPtr; +.globl setPtr.maxthreads; +.globl setPtr.maxtimers; +.globl setPtr.maxchanends; +.globl setPtr.maxsync; +.linkset setPtr.locnoside, 1; +.linkset setPtr.locnochandec, 1; +.linkset setPtr.nstackwords, 0; +.linkset setPtr.maxchanends, 0; +.linkset setPtr.maxtimers, 0; +.linkset setPtr.maxthreads, 1; +setPtr: + shl r2, r2, 1 +.xtabranch .label_0 + bru r2 +#define BODY(i) \ + ldap r11, .label_##i; \ + bu setPtr_go +#include "repeat.h" +setPtr_go: + shl r0, r0, 3; + ldc r2, 0x80; + add r1, r1, r2; + st8 r1, r11[r0]; + retsp 0; +.cc_bottom setPtr.function + + .section .cp.const4, "acM", @progbits, 4 +.LC0: + .align 4 + .int 0x7fffff00 +.LC1: + .int 0x80000000 + +#undef N +#undef BODY + + + diff --git a/module_usb_aud_shared/mixer/mixer.h b/module_usb_aud_shared/mixer/mixer.h new file mode 100644 index 00000000..56d144e1 --- /dev/null +++ b/module_usb_aud_shared/mixer/mixer.h @@ -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 diff --git a/module_usb_aud_shared/mixer/mixer.xc b/module_usb_aud_shared/mixer/mixer.xc new file mode 100644 index 00000000..2040b9ff --- /dev/null +++ b/module_usb_aud_shared/mixer/mixer.xc @@ -0,0 +1,669 @@ + + +#include +#include +#include "mixer.h" +#include "devicedefines.h" + +#ifdef MIXER + +#define FAST_MIXER 1 +#warning USING FAST MIXER + +#ifdef OUT_VOLUME_IN_MIXER +static unsigned int multOut[NUM_USB_CHAN_OUT + 1]; +#endif +#ifdef IN_VOLUME_IN_MIXER +unsigned int multIn[NUM_USB_CHAN_IN + 1]; +#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 + +int samples[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT + 1]; /* One larger for an "off" channel for mixer sources" */ +int savedsamples2[NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + MAX_MIX_COUNT]; + +int samples_to_host_map[NUM_USB_CHAN_IN]; + +int samples_to_device_map[NUM_USB_CHAN_OUT]; + +#if MAX_MIX_COUNT > 0 +int mix_mult[MAX_MIX_COUNT][MIX_INPUTS]; +#ifndef FAST_MIXER +int mix_map[MAX_MIX_COUNT][MIX_INPUTS]; +#endif +#endif + +/* Arrays for level data */ +int samples_to_host_inputs[NUM_USB_CHAN_IN]; /* Audio transmitted to host i.e. dev inputs */ +#ifdef LEVEL_METER_LEDS +int samples_to_host_inputs_buff[NUM_USB_CHAN_IN]; /* Audio transmitted to host i.e. dev inputs */ +#endif +static int samples_to_host_streams[NUM_USB_CHAN_OUT]; /* Audio stream to host from host */ +static int samples_to_host_outputs[NUM_USB_CHAN_OUT]; /* Device outputs */ + +#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; + unsigned ptr; + + + x = abs(sample); + + /* y = samples_to_host_outputs[i] */ + asm("ldaw %0, dp[samples_to_host_outputs]":"=r"(ptr):); /* Might want to hoist this */ + asm("ldw %0, %1[%2]":"=r"(y):"r"(ptr),"r"(i)); + + if(x > y) + { + /* samples_to_host_outputs[i] = x; */ + asm("stw %0, %1[%2]"::"r"(y),"r"(ptr),"r"(i)); + } +} +#endif +#ifdef FAST_MIXER +void setPtr(int src, int dst, int mix); +int doMix0(const int samples[], int mult[]); +int doMix1(const int samples[], int mult[]); +int doMix2(const int samples[], int mult[]); +int doMix3(const int samples[], int mult[]); +int doMix4(const int samples[], int mult[]); +int doMix5(const int samples[], int mult[]); +int doMix6(const int samples[], int mult[]); +int doMix7(const int samples[], int mult[]); +int doMix8(const int samples[], int 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 +int doMix(const int samples[], const int ptr[], int 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>32) + h = (0x80000000>>7); + else + h = (0x7fffff00>>7); + } +#endif + return h<<7; +} +#endif + +#pragma unsafe arrays +void giveSamplesToHost(chanend c, const int samples[], const int ptr[], const unsigned 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 samples_to_host_streams[i]) + samples_to_host_streams[i] = x; +#endif + +#if defined(OUT_VOLUME_IN_MIXER) && !defined(OUT_VOLUME_AFTER_MIX) +#warning OUT Vols in mixer, BEFORE mix & map + asm("ldw %0, %1[%2]":"=r"(mult):"r"(multOut),"r"(i)); + {h, l} = macs(mult, sample, 0, 0); + h<<=3; + asm("stw %0, %1[%2]"::"r"(h),"r"(samples),"r"(base+i)); +#else + asm("stw %0, %1[%2]"::"r"(sample),"r"(samples),"r"(base+i)); +#endif + } +} + +#pragma unsafe arrays +void giveSamplesToDevice(chanend c, const int samples[],const int ptr[], const unsigned multOut[]) +{ +#pragma loop unroll + for (int i=0;i samples_to_host_inputs[i]) + samples_to_host_inputs[i] = x; +#endif + +#if defined(IN_VOLUME_IN_MIXER) && !defined(IN_VOLUME_AFTER_MIX) + asm("ldw %0, %1[%2]":"=r"(mult):"r"(multIn),"r"(i)); + {h, l} = macs(mult, sample, 0, 0); + h <<=3; + asm("stw %0, %1[%2]"::"r"(h),"r"(samples),"r"(base+i)); +#else + asm("stw %0, %1[%2]"::"r"(sample),"r"(samples),"r"(base+i)); +#endif + } +} + +int mixer1_mix2_flag = (DEFAULT_FREQ > 96000); + +#pragma unsafe arrays +void mixer1(chanend c_host, chanend c_mix_ctl, chanend c_mixer2) +{ + int mixed; + unsigned cmd; + + while (1) + { + +#pragma xta endpoint "mixer1_req" + inuint(c_mixer2); + + /* Request data from decouple thread */ + outuint(c_host, 0); + + /* Between request to decouple and respose ~ 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); + + asm("stw %0, %1[%2]"::"r"(val),"r"(samples_to_host_map),"r"(index)); + break; + + case SET_SAMPLES_TO_DEVICE_MAP: + index = inuint(c_mix_ctl); + val = inuint(c_mix_ctl); + inct(c_mix_ctl); + asm("stw %0, %1[%2]"::"r"(val),"r"(samples_to_device_map),"r"(index)); + break; + + case SET_MIX_MULT: + mix = inuint(c_mix_ctl); + index = inuint(c_mix_ctl); + val = inuint(c_mix_ctl); + inct(c_mix_ctl); + + asm("stw %0, %1[%2]"::"r"(val),"r"(mix_mult[mix]),"r"(index)); + 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 + asm("stw %0, %1[%2]"::"r"(val),"r"(mix_map[mix]),"r"(index)); +#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); + + asm("stw %0, %1[%2]"::"r"(val),"r"(multIn),"r"(index)); + 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); + + asm("stw %0, %1[%2]"::"r"(val),"r"(multOut),"r"(index)); + break; +#endif + + case GET_STREAM_LEVELS: + index = inuint(c_mix_ctl); + chkct(c_mix_ctl, XS1_CT_END); + outuint(c_mix_ctl, samples_to_host_streams[index]); + outct(c_mix_ctl, XS1_CT_END); + samples_to_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 */ + asm("ldw %0, %1[%2]":"=r"(val):"r"(samples_to_host_inputs_buff),"r"(index)); + asm("stw %0, %1[%2]"::"r"(0),"r"(samples_to_host_inputs_buff),"r"(index)); +#else + /* We dont have a level LEDs process, so reset ourselves */ + asm("ldw %0, %1[%2]":"=r"(val):"r"(samples_to_host_inputs),"r"(index)); + asm("stw %0, %1[%2]"::"r"(0),"r"(samples_to_host_inputs),"r"(index)); +#endif + outuint(c_mix_ctl, val); + outct(c_mix_ctl, XS1_CT_END); + break; + +#if MAX_MIX_COUNT > 0 + case GET_OUTPUT_LEVELS: + index = inuint(c_mix_ctl); + chkct(c_mix_ctl, XS1_CT_END); + asm("ldw %0, %1[%2]":"=r"(val):"r"(samples_to_host_outputs),"r"(index)); + asm("stw %0, %1[%2]"::"r"(mix),"r"(samples_to_host_outputs),"r"(index)); + 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" + inct(c_host); + sampFreq = inuint(c_host); + + mixer1_mix2_flag = sampFreq > 96000; + +#pragma loop unroll + for (int i=0;i 0 + outuint(c_mixer2, 0); + giveSamplesToHost(c_host, samples, samples_to_host_map, multIn); + + outuint(c_mixer2, 0); + inuint(c_mixer2); + getSamplesFromHost(c_host, samples, 0); + outuint(c_mixer2, 0); + inuint(c_mixer2); +#ifdef FAST_MIXER + mixed = doMix0(samples, mix_mult[0]); +#else + mixed = doMix(samples,mix_map[0],mix_mult[0]); +#endif + asm("stw %0, %1[%2]":: + "r"(mixed),"r"(samples), + "r"(NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 0)); + +#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[2]); +#else + mixed = doMix(samples,mix_map[2],mix_mult[2]); +#endif + asm("stw %0, %1[%2]":: + "r"(mixed),"r"(samples), + "r"(NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 2)); + +#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[4]); +#else + mixed = doMix(samples,mix_map[4],mix_mult[4]); +#endif + asm("stw %0, %1[%2]":: + "r"(mixed),"r"(samples), + "r"(NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 4)); + +#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[6]); +#else + mixed = doMix(samples,mix_map[6],mix_mult[6]); +#endif + asm("stw %0, %1[%2]":: + "r"(mixed),"r"(samples), + "r"(NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 6)); + +#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 */ + giveSamplesToDevice(c_mixer2, samples, samples_to_device_map, multOut); + getSamplesFromDevice(c_mixer2, samples, NUM_USB_CHAN_OUT); + giveSamplesToHost(c_host, samples, samples_to_host_map, multIn); + getSamplesFromHost(c_host, samples, 0); +#endif + } + } +} + +int mixer2_mix2_flag = (DEFAULT_FREQ > 96000); + +#pragma unsafe arrays +void mixer2(chanend c_mixer1, chanend c_audio) +{ + int mixed; + + while (1) { + outuint(c_mixer1, 0); +#pragma xta endpoint "mixer2_req" + inuint(c_audio); + if(testct(c_mixer1)) + { + int sampFreq; +#pragma xta endpoint "mixer2_rate_change" + inct(c_mixer1); + sampFreq = inuint(c_mixer1); + + + mixer2_mix2_flag = sampFreq > 96000; + + for (int i=0;i 1 +#ifdef FAST_MIXER + mixed = doMix1(samples, mix_mult[1]); +#else + mixed = doMix(samples,mix_map[1],mix_mult[1]); +#endif + + asm("stw %0, %1[%2]":: + "r"(mixed),"r"(samples), + "r"(NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 1)); + +#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[3]); +#else + mixed = doMix(samples,mix_map[3],mix_mult[3]); +#endif + + asm("stw %0, %1[%2]":: + "r"(mixed),"r"(samples), + "r"(NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 3)); +#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[5]); +#else + mixed = doMix(samples,mix_map[5],mix_mult[5]); +#endif + asm("stw %0, %1[%2]":: + "r"(mixed),"r"(samples), + "r"(NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 5)); +#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[7]); +#else + mixed = doMix(samples,mix_map[7],mix_mult[7]); +#endif + asm("stw %0, %1[%2]":: + "r"(mixed),"r"(samples), + "r"(NUM_USB_CHAN_OUT + NUM_USB_CHAN_IN + 7)); +#if defined (LEVEL_METER_HOST) || defined(LEVEL_METER_LEDS) + ComputeMixerLevel(mixed, 7); +#endif +#endif + } + + } + } +} + +void mixer(chanend c_mix_in, chanend c_mix_out, chanend c_mix_ctl) +{ + chan c; + for (int i=0;i 96000 ? 2 : MAX_MIX_COUNT; + for (int i=0;i 0 + for (int i=0;i> 3 : 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 diff --git a/module_usb_aud_shared/mixer/repeat.h b/module_usb_aud_shared/mixer/repeat.h new file mode 100644 index 00000000..aed640f2 --- /dev/null +++ b/module_usb_aud_shared/mixer/repeat.h @@ -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 diff --git a/module_usb_aud_shared/module_build_info b/module_usb_aud_shared/module_build_info new file mode 100644 index 00000000..4705b9d2 --- /dev/null +++ b/module_usb_aud_shared/module_build_info @@ -0,0 +1,32 @@ +# 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 diff --git a/module_usb_aud_shared/module_description b/module_usb_aud_shared/module_description new file mode 100644 index 00000000..c89f75e0 --- /dev/null +++ b/module_usb_aud_shared/module_description @@ -0,0 +1 @@ +Common USB Audio source files/headers. diff --git a/module_usb_aud_shared/pll/pll.h b/module_usb_aud_shared/pll/pll.h new file mode 100644 index 00000000..d79ca14f --- /dev/null +++ b/module_usb_aud_shared/pll/pll.h @@ -0,0 +1,4 @@ +void PllInit(void); + + +void PllMult(unsigned mult); diff --git a/module_usb_aud_shared/ports/audioports.h b/module_usb_aud_shared/ports/audioports.h new file mode 100644 index 00000000..2e19b039 --- /dev/null +++ b/module_usb_aud_shared/ports/audioports.h @@ -0,0 +1,6 @@ +#ifndef _AUDIOPORTS_H_ +#define _AUDIOPORTS_H_ + +void ConfigAudioPorts(unsigned int divide); + +#endif diff --git a/module_usb_aud_shared/ports/audioports.xc b/module_usb_aud_shared/ports/audioports.xc new file mode 100644 index 00000000..0bc1a7e1 --- /dev/null +++ b/module_usb_aud_shared/ports/audioports.xc @@ -0,0 +1,138 @@ +#include +#include "devicedefines.h" +#include "audioports.h" + +/* Audio IOs */ + +#if (I2S_CHANS_DAC != 0) +extern buffered out port:32 p_i2s_dac[I2S_WIRES_DAC]; +#endif + +#if (I2S_CHANS_ADC != 0) +extern buffered in port:32 p_i2s_adc[I2S_WIRES_ADC]; +#endif + +#if (I2S_CHANS_DAC != 0) || (I2S_CHANS_ADC != 0) +#ifdef CODEC_SLAVE +extern buffered out port:32 p_lrclk; +extern buffered out port:32 p_bclk; +#else +extern in port p_lrclk; +extern in port p_bclk; +#endif +#endif + +extern port p_mclk; + +extern clock clk_audio_mclk; +extern clock clk_audio_bclk; + +void ConfigAudioPorts(unsigned int divide) +{ + +#ifdef CODEC_SLAVE + /* Output 0 on BCLK to ensure clock is low + * Required as stop_clock will only complete when the clock is low + */ + configure_out_port_no_ready(p_bclk, clk_audio_mclk, 0); + p_bclk <: 0; + + /* Stop bit and master clock blocks and clear port buffers */ + stop_clock(clk_audio_bclk); + stop_clock(clk_audio_mclk); + + clearbuf(p_lrclk); + clearbuf(p_bclk); + +#if (I2S_CHANS_ADC != 0) + for(int i = 0; i < I2S_WIRES_ADC; i++) + { + clearbuf(p_i2s_adc[i]); + } +#endif + +#if (I2S_CHANS_DAC != 0) + for(int i = 0; i < I2S_WIRES_DAC; i++) + { + clearbuf(p_i2s_dac[i]); + } +#endif + + /* Clock master clock-block from master-clock port */ + configure_clock_src(clk_audio_mclk, p_mclk); + + /* 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); + } + 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); + + +#if (I2S_CHANS_DAC != 0) + /* Clock I2S output data ports from 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 + + /* Clock LR clock from bit clock-block */ + configure_out_port_no_ready(p_lrclk, clk_audio_bclk, 0); + + /* Start clock blocks ticking */ + start_clock(clk_audio_mclk); + start_clock(clk_audio_bclk); + + /* bclk initial state needs to be high */ + p_bclk <: 0xFFFFFFFF; + + /* Pause until output completes */ + sync(p_bclk); + +#else + /* Stop bit and master clock blocks and clear port buffers */ + stop_clock(clk_audio_bclk); + stop_clock(clk_audio_mclk); + + /* Clock bclk clock-block from bclk pin */ + configure_clock_src(clk_audio_bclk, p_bclk); + + /* 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); + } + + /* 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); + } + + configure_in_port_no_ready(p_lrclk, clk_audio_bclk); + + start_clock(clk_audio_bclk); + +#endif +} diff --git a/module_usb_aud_shared/reboot.c b/module_usb_aud_shared/reboot.c new file mode 100644 index 00000000..707ac4a3 --- /dev/null +++ b/module_usb_aud_shared/reboot.c @@ -0,0 +1,15 @@ +#include +#include + + +int write_sswitch_reg_blind(unsigned coreid, unsigned reg, unsigned data); + +/* Reboots XMOS device by writing to the PLL config register */ +void device_reboot(void) +{ + unsigned int pllVal; + unsigned int core_id = get_core_id(); + read_sswitch_reg(core_id, 6, &pllVal); + write_sswitch_reg_blind(core_id^0x8000, 6, pllVal); + write_sswitch_reg_blind(core_id, 6, pllVal); +} diff --git a/module_usb_aud_shared/usb_buffer/decouple.h b/module_usb_aud_shared/usb_buffer/decouple.h new file mode 100644 index 00000000..95a39322 --- /dev/null +++ b/module_usb_aud_shared/usb_buffer/decouple.h @@ -0,0 +1,18 @@ +#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 + * \param c_led Optional chanend connected to an led driver thread for + * debugging purposes + * \param c_midi Optional chanend connect to usb_midi() thread if present + * \param c_clk_int Optional chanend connected to the clockGen() thread if present + */ +void decouple(chanend c_audio_out, + chanend ?c_led, + chanend ?c_midi, chanend ?c_clk_int); + +#endif // __decouple_h__ diff --git a/module_usb_aud_shared/usb_buffer/decouple.xc b/module_usb_aud_shared/usb_buffer/decouple.xc new file mode 100644 index 00000000..861f2b0f --- /dev/null +++ b/module_usb_aud_shared/usb_buffer/decouple.xc @@ -0,0 +1,1142 @@ +/** + * Module: module_usb_aud_shared + * Version: 2v3 + * Build: 920238b18f6b0967226369682640e1b063865f02 + * File: decouple.xc + * + * The copyrights, all other intellectual and industrial + * property rights are retained by XMOS and/or its licensors. + * Terms and conditions covering the use of this code can + * be found in the Xmos End User License Agreement. + * + * Copyright XMOS Ltd 2010 + * + * In the case where this code is a modification of existing code + * under a separate license, the separate license terms are shown + * below. The modifications to the code are still covered by the + * copyright notice above. + * + **/ +#include +#include +#include "xc_ptr.h" +#define NO_INLINE_MIDI_SELECT_HANDLER 1 +#include "usb_midi.h" +#include "devicedefines.h" +#include "testct_byref.h" +#include "interrupt.h" +#include "clockcmds.h" +#include "xud.h" +#include "usb.h" + +/* This function changes the buffer staged for an IN transaction. + * **It can only be used if you know that the IN transaction will not occur** + * Otherwise a race condition can occur. + * + */ +inline void XUD_Change_ReadyIn_Buffer(XUD_ep e, unsigned bufferPtr, int len) +{ + int chan_array_ptr; + int xud_chan; + int my_chan; + int tail; + + asm ("ldw %0, %1[0]":"=r"(chan_array_ptr):"r"(e)); + asm ("ldw %0, %1[2]":"=r"(my_chan):"r"(e)); + + tail = len & 0x3; + bufferPtr += (len-tail); + tail <<= 5; + + asm ("ldw %0, %1[1]":"=r"(xud_chan):"r"(e)); + + len >>= 2; + len = -len; + + /* Store buffer pointer */ + asm ("stw %0, %1[5]"::"r"(bufferPtr),"r"(e)); + + /* Store length */ + asm ("stw %0, %1[3]"::"r"(len),"r"(e)); + + /* Mark EP ready with pointer */ + asm ("stw %0, %1[0]"::"r"(xud_chan),"r"(chan_array_ptr)); +} + + +#define MAX(x,y) ((x)>(y) ? (x) : (y)) +#define MAX_CLASS_ONE_FREQ 96000 +#define MAX_CLASS_ONE_CHAN 2 + +#define CLASS_TWO_PACKET_SIZE ((((MAX_FREQ+7999)/8000))+3) +#define CLASS_ONE_PACKET_SIZE ((((MAX_CLASS_ONE_FREQ+999)/1000))+3) + +#define BUFF_SIZE_OUT MAX(4 * CLASS_TWO_PACKET_SIZE * NUM_USB_CHAN_OUT, 4 * CLASS_ONE_PACKET_SIZE * MAX_CLASS_ONE_CHAN) +#define BUFF_SIZE_IN MAX(4 * CLASS_TWO_PACKET_SIZE * NUM_USB_CHAN_IN, 4 * CLASS_ONE_PACKET_SIZE * MAX_CLASS_ONE_CHAN) +#define MAX_USB_AUD_PACKET_SIZE 1028 +//#define OUT_BUFFER_PREFILL (2*4*BUFF_SIZE_OUT/3) +//#define OUT_BUFFER_PREFILL MAX(CLASS_ONE_PACKET_SIZE*3+4,CLASS_TWO_PACKET_SIZE*4+4)*2 +//#define IN_BUFFER_PREFILL MAX(CLASS_ONE_PACKET_SIZE*3+4,CLASS_TWO_PACKET_SIZE*4+4)*2 +#define OUT_BUFFER_PREFILL (MAX(MAX_CLASS_ONE_CHAN*CLASS_ONE_PACKET_SIZE*3+4,NUM_USB_CHAN_OUT*CLASS_TWO_PACKET_SIZE*4+4)*1) +#define IN_BUFFER_PREFILL (MAX(CLASS_ONE_PACKET_SIZE*3+4,CLASS_TWO_PACKET_SIZE*4+4)*2) +//#pragma xta command "add exclusion out_underflow" +//#pragma xta command "add exclusion freq_change" +//#pragma xta command "add exclusion print_err"is_as +//#pragma xta command "add exclusion out_soverflow" +//#pragma xta command "analyse path mixer_request mixer_request" +//#pragma xta command "set required - 5200 ns" /* 192kHz */ + +/* Volume and mute tables */ +#ifndef OUT_VOLUME_IN_MIXER +unsigned int multOut[NUM_USB_CHAN_OUT + 1]; +#endif +#ifndef IN_VOLUME_IN_MIXER +unsigned int multIn[NUM_USB_CHAN_IN + 1]; +#endif + +/* Number of channels to/from the USB bus */ +unsigned g_numUsbChanOut = NUM_USB_CHAN_OUT; +unsigned g_numUsbChanIn = NUM_USB_CHAN_IN; + +#define MAX_DEVICE_AUD_PACKET_SIZE_CLASS_TWO ((MAX_FREQ/8000+1)*NUM_USB_CHAN_IN*4) +#define MAX_DEVICE_AUD_PACKET_SIZE_CLASS_ONE (((MAX_CLASS_ONE_FREQ/1000+1)*MAX_CLASS_ONE_CHAN*3)+4) + +#define MAX_DEVICE_AUD_PACKET_SIZE (MAX(MAX_DEVICE_AUD_PACKET_SIZE_CLASS_ONE, MAX_DEVICE_AUD_PACKET_SIZE_CLASS_TWO)) + +/* Circular audio buffers */ +unsigned outAudioBuff[BUFF_SIZE_OUT + (MAX_USB_AUD_PACKET_SIZE>>2) + 4]; +unsigned audioBuffIn[BUFF_SIZE_IN + (MAX_DEVICE_AUD_PACKET_SIZE>>2) + 4]; + +unsigned inZeroBuff[(MAX_DEVICE_AUD_PACKET_SIZE>>2)+4]; + +unsigned ledVal = 1; +unsigned dir = 0; + +void led(chanend ?c_led) +{ + if(dir == 0) + ledVal <<= 1; + else + ledVal >>= 1; + + if(ledVal == 0b10000000 || ledVal == 1) + dir = !dir; + + if (!isnull(c_led)) { + c_led <: ledVal; + } +} + +/* 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 + * + * Note: This function uses the multiple return value feature of XC + */ + +void GetADCCounts(unsigned samFreq, int &min, int &mid, int &max); + +static inline void swap(xc_ptr &a, xc_ptr &b) +{ + xc_ptr tmp; + tmp = a; + a = b; + b = tmp; + return; +} + +// shared global midi buffering variables +unsigned g_midi_from_host_flag = 0; +unsigned g_midi_to_host_flag = 0; +int midi_to_host_usb_ep = 0; +int midi_from_host_usb_ep = 0; +int aud_from_host_usb_ep = 0; +int aud_to_host_usb_ep = 0; +int int_usb_ep = 0; + +unsigned int g_midi_to_host_buffer_A[MAX_USB_MIDI_PACKET_SIZE/4+4]; +unsigned int g_midi_to_host_buffer_B[MAX_USB_MIDI_PACKET_SIZE/4+4]; +int g_midi_from_host_buffer[MAX_USB_MIDI_PACKET_SIZE+4]; + +// shared global aud buffering variables + +unsigned g_aud_from_host_buffer; +unsigned g_aud_to_host_buffer; +unsigned g_aud_to_host_flag = 0; +int buffer_aud_ctl_chan = 0; +unsigned g_aud_from_host_flag = 0; +unsigned g_aud_from_host_info; +unsigned g_freqChange_flag = 0; +unsigned g_freqChange_sampFreq; +int speedRem = 0; + + +xc_ptr aud_from_host_fifo_start; +xc_ptr aud_from_host_fifo_end; +xc_ptr g_aud_from_host_wrptr; +xc_ptr g_aud_from_host_rdptr; + + +xc_ptr aud_to_host_fifo_start; +xc_ptr aud_to_host_fifo_end; +xc_ptr g_aud_to_host_wrptr; +xc_ptr g_aud_to_host_dptr; +xc_ptr g_aud_to_host_rdptr; +xc_ptr g_aud_to_host_zeros; +int sampsToWrite = 0; +int totalSampsToWrite = 0; + + +int aud_data_remaining_to_device = 0; + + +/* Over/under flow flags */ +unsigned outUnderflow = 1; +unsigned outOverflow = 0; +unsigned inUnderflow = 1; +unsigned inOverflow = 0; + + +int aud_req_in_count = 0; +int aud_req_out_count = 0; + +unsigned unpackState = 0; +unsigned unpackData = 0; + +unsigned packState = 0; +unsigned packData = 0; + +#if (AUDIO_CLASS==2) +int slotSize = 4; /* 4 bytes per ssample for Audio Class 2.0 */ +#else +int slotSize = 3; /* 3 bytes per sample for Audio Class 1.0 */ +#endif + +#pragma select handler +#pragma unsafe arrays +void handle_audio_request(chanend c_mix_out, chanend ?c_led) +{ + int outSamps; + int space_left; + int usb_speed; + + /* Input word that triggered interrupt and handshake back */ + (void) inuint(c_mix_out); + outuint(c_mix_out, 0); + + asm("ldw %0, dp[g_curUsbSpeed]" : "=r" (usb_speed) :); + + /* slotSize different for Audio Class 1.0/2.0. */ +#if defined(AUDIO_CLASS_FALLBACK) + if (usb_speed == XUD_SPEED_HS) + { + slotSize = 4; /* 4 bytes per sample */ + } + else + { + slotSize = 3; /* 3 bytes per sample */ + } +#endif + + /* If in overflow condition then receive samples and throw away */ + if(inOverflow || sampsToWrite == 0) + { +#pragma loop unroll + for(int i = 0; i < NUM_USB_CHAN_IN; i++) + { + (void) inuint(c_mix_out); + } + + /* Calculate how much space left in buffer */ + space_left = g_aud_to_host_rdptr - g_aud_to_host_wrptr; + + if (space_left <= 0) + { + space_left += BUFF_SIZE_IN*4; + } + + /* Check if we can come out of overflow */ + if (space_left > (BUFF_SIZE_IN*4/2)) + { + inOverflow = 0; + } + } + else + { + /* Not in overflow, store samples from mixer into sample buffer */ + if (usb_speed == XUD_SPEED_HS) + { + unsigned ptr = g_aud_to_host_dptr; + + for(int i = 0; i < g_numUsbChanIn; i++) + { + /* Receive sample */ + int sample = inuint(c_mix_out); +#if !defined(IN_VOLUME_IN_MIXER) + /* Apply volume */ + int mult; + int h; + unsigned l; + asm("ldw %0, %1[%2]":"=r"(mult):"r"(multIn),"r"(i)); + {h, l} = macs(mult, sample, 0, 0); + sample = h << 3; +#elif defined(IN_VOLUME_IN_MIXER) && defined(IN_VOLUME_AFTER_MIX) + sample = sample << 3; +#endif + /* Write into fifo */ + write_via_xc_ptr(ptr, sample); + ptr+=4; + } + + /* Update global pointer */ + g_aud_to_host_dptr = ptr; + } + else + { + for(int i = 0; i < g_numUsbChanIn; i++) + { + /* Receive sample */ + int sample = inuint(c_mix_out); +#ifndef IN_VOLUME_IN_MIXER + /* Apply volume */ + int mult; + int h; + unsigned l; + asm("ldw %0, %1[%2]":"=r"(mult):"r"(multIn),"r"(i)); + {h, l} = macs(mult, sample, 0, 0); + sample = h << 3; +#endif + /* Pack 3 byte samples */ + switch (packState&0x3) + { + case 0: + packData = sample; + break; + case 1: + packData = packData >> 8 | ((sample & 0xff00)<<16); + write_via_xc_ptr(g_aud_to_host_dptr, packData); + g_aud_to_host_dptr+=4; + write_via_xc_ptr(g_aud_to_host_dptr, sample>>16); + packData = sample; + break; + case 2: + packData = (packData>>16) | ((sample & 0xffff00) << 8); + write_via_xc_ptr(g_aud_to_host_dptr, packData); + g_aud_to_host_dptr+=4; + packData = sample; + break; + case 3: + packData = (packData >> 24) | (sample & 0xffffff00); + write_via_xc_ptr(g_aud_to_host_dptr, packData); + g_aud_to_host_dptr+=4; + break; + } + packState++; + } + } + + /* Input any remaining channels - past this thread we always operate on max channel count */ + for(int i = 0; i < NUM_USB_CHAN_IN - g_numUsbChanIn; i++) + { + inuint(c_mix_out); + } + + sampsToWrite--; + } + + if(outUnderflow) + { +#pragma xta endpoint "out_underflow" + /* We're still pre-buffering, send out 0 samps */ + for(int i = 0; i < NUM_USB_CHAN_OUT; i++) + { + outuint(c_mix_out, 0); + } + + /* Calc how many samples left in buffer */ + outSamps = g_aud_from_host_wrptr - g_aud_from_host_rdptr; + if (outSamps < 0) + { + outSamps += BUFF_SIZE_OUT*4; + } + + /* If we have a decent number of samples, come out of underflow cond */ + if (outSamps >= (OUT_BUFFER_PREFILL)) + { + outUnderflow = 0; + } + } + else + { + if (usb_speed == XUD_SPEED_HS) + { + /* Buffering not underflow condition send out some samples...*/ + for(int i = 0; i < g_numUsbChanOut; i++) + { +#pragma xta endpoint "mixer_request" + int sample; + int mult; + int h; + unsigned l; + + read_via_xc_ptr(sample, g_aud_from_host_rdptr); + g_aud_from_host_rdptr+=4; + +#ifndef OUT_VOLUME_IN_MIXER + asm("ldw %0, %1[%2]":"=r"(mult):"r"(multOut),"r"(i)); + {h, l} = macs(mult, sample, 0, 0); + h <<= 3; + outuint(c_mix_out, h); +#else + outuint(c_mix_out, sample); + +#endif + } + } + else + { + + /* Buffering not underflow condition send out some samples...*/ + for(int i = 0; i < g_numUsbChanOut; i++) + { +#pragma xta endpoint "mixer_request" + int sample; + int mult; + int h; + unsigned l; + + /* Unpack 3 byte samples */ + switch (unpackState&0x3) + { + case 0: + read_via_xc_ptr(unpackData, g_aud_from_host_rdptr); + g_aud_from_host_rdptr+=4; + sample = unpackData << 8; + break; + case 1: + sample = (unpackData >> 16); + read_via_xc_ptr(unpackData, g_aud_from_host_rdptr); + g_aud_from_host_rdptr+=4; + sample = sample | (unpackData << 16); + break; + case 2: + sample = (unpackData >> 8); + read_via_xc_ptr(unpackData, g_aud_from_host_rdptr); + g_aud_from_host_rdptr+=4; + sample = sample | (unpackData<< 24); + break; + case 3: + sample = unpackData & 0xffffff00; + break; + } + unpackState++; + +#ifndef OUT_VOLUME_IN_MIXER + asm("ldw %0, %1[%2]":"=r"(mult):"r"(multOut),"r"(i)); + {h, l} = macs(mult, sample, 0, 0); + h <<= 3; + outuint(c_mix_out, h); +#else + outuint(c_mix_out, sample); + +#endif + } + } + + /* Output remaining channels. Past this point we always operate on MAX chan count */ + for(int i = 0; i < NUM_USB_CHAN_OUT - g_numUsbChanOut; i++) + { + outuint(c_mix_out, 0); + } + + /* 3/4 bytes per sample */ + aud_data_remaining_to_device -= (g_numUsbChanOut*slotSize); + } + + if (!inOverflow) + { + if (sampsToWrite == 0) + { + int speed; + + if (totalSampsToWrite) + { + if (usb_speed == XUD_SPEED_HS) + { + g_aud_to_host_wrptr += 4+totalSampsToWrite*4*g_numUsbChanIn; + } + else + { + unsigned int datasize = totalSampsToWrite*3*g_numUsbChanIn; + datasize = (datasize+3) & (~0x3); // round up to nearest word + g_aud_to_host_wrptr += 4+datasize; + } + if (g_aud_to_host_wrptr >= aud_to_host_fifo_end) + { + g_aud_to_host_wrptr = aud_to_host_fifo_start; + } + } + + /* Get feedback val - ideally this would be syncronised */ + asm("ldw %0, dp[g_speed]" : "=r" (speed) :); + + /* Calc packet size to send back based on our fb */ + speedRem += speed; + totalSampsToWrite = speedRem >> 16; + speedRem &= 0xffff; + + if (usb_speed == XUD_SPEED_HS) + { + if (totalSampsToWrite < 0 || totalSampsToWrite*4*g_numUsbChanIn > (MAX_DEVICE_AUD_PACKET_SIZE_CLASS_TWO)) + { + totalSampsToWrite = 0; + } + } + else + { + if (totalSampsToWrite < 0 || totalSampsToWrite*3*g_numUsbChanIn > (MAX_DEVICE_AUD_PACKET_SIZE_CLASS_ONE)) + { + totalSampsToWrite = 0; + } + } + + /* Calc slots left in fifo */ + space_left = g_aud_to_host_rdptr - g_aud_to_host_wrptr; + + /* Mod and special case */ + if (space_left <= 0 && g_aud_to_host_rdptr == aud_to_host_fifo_start) + { + space_left = aud_to_host_fifo_end - g_aud_to_host_wrptr; + } + + if ((space_left <= 0) || (space_left > totalSampsToWrite*g_numUsbChanIn*4+4)) + { + /* Packet okay, write to fifo */ + if (totalSampsToWrite) + { + write_via_xc_ptr(g_aud_to_host_wrptr, totalSampsToWrite*slotSize*g_numUsbChanIn); + packState = 0; + g_aud_to_host_dptr = g_aud_to_host_wrptr + 4; + } + } + else + { + inOverflow = 1; + totalSampsToWrite = 0; + } + sampsToWrite = totalSampsToWrite; + } + } + + if (!outUnderflow && (aud_data_remaining_to_device<(slotSize*g_numUsbChanOut))) + { + /* Handle any tail - incase a bad driver sent us a datalength not a multiple of chan count */ + if (aud_data_remaining_to_device) + { + /* Round up to nearest word */ + aud_data_remaining_to_device +=3; + aud_data_remaining_to_device &= (~3); + + /* Skip the rest of this malformed packet */ + g_aud_from_host_rdptr += aud_data_remaining_to_device; + + aud_data_remaining_to_device = 0; + } + + /* Wrap read pointer */ + if (g_aud_from_host_rdptr >= aud_from_host_fifo_end) + { + g_aud_from_host_rdptr = aud_from_host_fifo_start; + } + + outUnderflow = (g_aud_from_host_rdptr == g_aud_from_host_wrptr); + + if (!outUnderflow) + { + read_via_xc_ptr(aud_data_remaining_to_device, g_aud_from_host_rdptr); + + unpackState = 0; + + g_aud_from_host_rdptr+=4; + } +#ifdef DEBUG_LEDS + else + { + led(c_led); + } +#endif + } + +} + + +unsigned g_intFlag = 0; + +extern unsigned char g_intData[8]; + +void check_for_interrupt(chanend ?c_clk_int) { + unsigned tmp; + + select + { + /* Clocking thread wants to produce an interrupt... */ + case inuint_byref(c_clk_int, tmp): + chkct(c_clk_int, XS1_CT_END); + + /* Check if we have interrupt pending */ + /* TODO This means we can loose interrupts */ + if(!g_intFlag) + { + int x; + + g_intFlag = 1; + + g_intData[5] = tmp; + + /* Make request to send to XUD endpoint - response handled in usb_buffer */ + //XUD_SetReady(int_usb_ep, 0); + + asm("ldaw %0, dp[g_intData]":"=r"(x)); + XUD_SetReady_In(int_usb_ep, 0,x,6); + } + + break; + default: + break; + } +} + + +#pragma unsafe arrays +void decouple(chanend c_mix_out, + chanend ?c_midi, chanend ?c_clk_int) +{ + unsigned sampFreq = DEFAULT_FREQ; + int aud_from_host_flag=0; + int aud_to_host_flag=0; + xc_ptr released_buffer; + +#ifdef MIDI + xc_ptr midi_from_host_rdptr; + xc_ptr midi_from_host_buffer; + 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; + int midi_to_host_flag = 0; + int midi_from_host_flag = 0; + + +#endif + int t = array_to_xc_ptr(outAudioBuff); + int aud_in_ready = 0; + + + aud_from_host_fifo_start = t; + aud_from_host_fifo_end = aud_from_host_fifo_start + BUFF_SIZE_OUT*4; + g_aud_from_host_wrptr = aud_from_host_fifo_start; + g_aud_from_host_rdptr = aud_from_host_fifo_start; + + t = array_to_xc_ptr(audioBuffIn); + + aud_to_host_fifo_start = t; + aud_to_host_fifo_end = aud_to_host_fifo_start + BUFF_SIZE_IN*4; + g_aud_to_host_wrptr = aud_to_host_fifo_start; + g_aud_to_host_rdptr = aud_to_host_fifo_start; + + t = array_to_xc_ptr(inZeroBuff); + g_aud_to_host_zeros = t; + + /* Init interrupt report */ + g_intData[0] = 0; // Class-specific, caused by interface + g_intData[1] = 1; // attribute: CUR + g_intData[2] = 0; // CN/ MCN + g_intData[3] = 0; // CS + g_intData[4] = 0; // interface + g_intData[5] = 0; // ID of entity causing interrupt - this will get modified + + /* Init vol mult tables */ +#ifndef OUT_VOLUME_IN_MIXER + for (int i = 0; i < NUM_USB_CHAN_OUT + 1; i++) + { + asm("stw %0, %1[%2]"::"r"(MAX_VOL),"r"(multOut),"r"(i)); + } +#endif + +#ifndef IN_VOLUME_IN_MIXER + for (int i = 0; i < NUM_USB_CHAN_IN + 1; i++) + { + asm("stw %0, %1[%2]"::"r"(MAX_VOL),"r"(multIn),"r"(i)); + } +#endif + + + { int c=0; + while(!c) { + asm("ldw %0, dp[buffer_aud_ctl_chan]":"=r"(c)); + } + } + + + set_interrupt_handler(handle_audio_request, 200, 1, c_mix_out, 0); + +#ifdef MIDI + //asm("ldaw %0, dp[g_midi_to_host_buffer]":"=r"(midi_to_host_buffer)); + asm("ldaw %0, dp[g_midi_from_host_buffer]":"=r"(midi_from_host_buffer)); + + // wait for usb_buffer to set up + while(!midi_from_host_flag) { + GET_SHARED_GLOBAL(midi_from_host_flag, g_midi_from_host_flag); + } + + midi_from_host_flag = 0; + SET_SHARED_GLOBAL(g_midi_from_host_flag, midi_from_host_flag); + + // send the current host -> device buffer out of the fifo + XUD_SetReady(midi_from_host_usb_ep, 1); +#endif + +#ifdef OUTPUT + // wait for usb_buffer to set up + while(!aud_from_host_flag) { + GET_SHARED_GLOBAL(aud_from_host_flag, g_aud_from_host_flag); + } + + aud_from_host_flag = 0; + SET_SHARED_GLOBAL(g_aud_from_host_flag, aud_from_host_flag); + + // send the current host -> device buffer out of the fifo + SET_SHARED_GLOBAL(g_aud_from_host_buffer, g_aud_from_host_wrptr); + XUD_SetReady(aud_from_host_usb_ep, 1); +#endif + +#ifdef INPUT + // wait for usb_buffer to set up + while(!aud_to_host_flag) { + GET_SHARED_GLOBAL(aud_to_host_flag, g_aud_to_host_flag); + } + + aud_to_host_flag = 0; + SET_SHARED_GLOBAL(g_aud_to_host_flag, aud_to_host_flag); + + // send the current host -> device buffer out of the fifo + SET_SHARED_GLOBAL(g_aud_to_host_buffer, g_aud_to_host_zeros); + { + xc_ptr p; + int len; + + GET_SHARED_GLOBAL(p, g_aud_to_host_buffer); + read_via_xc_ptr(len, p); + + XUD_SetReady_In(aud_to_host_usb_ep, PIDn_DATA0, g_aud_to_host_buffer, len); + } +#endif + + while(1) + { + if (!isnull(c_clk_int)) + { + check_for_interrupt(c_clk_int); + } + + asm("#decouple-default"); + + /* Check for freq change or other update */ + { + int tmp; + GET_SHARED_GLOBAL(tmp, g_freqChange_flag); + if (tmp == SET_SAMPLE_FREQ) + { + SET_SHARED_GLOBAL(g_freqChange_flag, 0); + GET_SHARED_GLOBAL(sampFreq, g_freqChange_sampFreq); + + /* Pass on to mixer */ + DISABLE_INTERRUPTS(); + inuint(c_mix_out); + outct(c_mix_out, 9); + outuint(c_mix_out, sampFreq); + + inOverflow = 0; + inUnderflow = 1; + SET_SHARED_GLOBAL(g_aud_to_host_rdptr, + aud_to_host_fifo_start); + SET_SHARED_GLOBAL(g_aud_to_host_wrptr, + aud_to_host_fifo_start); + SET_SHARED_GLOBAL(sampsToWrite, 0); + SET_SHARED_GLOBAL(totalSampsToWrite, 0); + SET_SHARED_GLOBAL(g_aud_to_host_buffer, + g_aud_to_host_zeros); + + /* Check if we have an IN packet ready to go */ + if (aud_in_ready) + { + xc_ptr p; + int len; + + GET_SHARED_GLOBAL(p, g_aud_to_host_buffer); + read_via_xc_ptr(len, p); + + /* Update the audio in buffer to send the correct + * length back to the host for the new sample rate */ + XUD_Change_ReadyIn_Buffer(aud_to_host_usb_ep, p+4, len); + } + + /* Reset OUT buffer state */ + outUnderflow = 1; + SET_SHARED_GLOBAL(g_aud_from_host_rdptr, aud_from_host_fifo_start); + SET_SHARED_GLOBAL(g_aud_from_host_wrptr, aud_from_host_fifo_start); + SET_SHARED_GLOBAL(aud_data_remaining_to_device, 0); + + if(outOverflow) + { + XUD_SetReady(aud_from_host_usb_ep, 1); + outOverflow = 0; + } + + + /* Wait for handshake back and pass back up */ + chkct(c_mix_out, XS1_CT_END); + + SET_SHARED_GLOBAL(g_freqChange, 0); + asm("outct res[%0],%1"::"r"(buffer_aud_ctl_chan),"r"(XS1_CT_END)); + + ENABLE_INTERRUPTS(); + + speedRem = 0; + continue; + } + else if(tmp == SET_CHAN_COUNT_IN) + { + /* Change in IN channel count */ + DISABLE_INTERRUPTS(); + SET_SHARED_GLOBAL(g_freqChange_flag, 0); + GET_SHARED_GLOBAL(g_numUsbChanIn, g_freqChange_sampFreq); /* Misuse of g_freqChange_sampFreq */ + + /* Reset IN buffer state */ + inOverflow = 0; + inUnderflow = 1; + SET_SHARED_GLOBAL(g_aud_to_host_rdptr, aud_to_host_fifo_start); + SET_SHARED_GLOBAL(g_aud_to_host_wrptr,aud_to_host_fifo_start); + SET_SHARED_GLOBAL(sampsToWrite, 0); + SET_SHARED_GLOBAL(totalSampsToWrite, 0); + SET_SHARED_GLOBAL(g_aud_to_host_buffer, g_aud_to_host_zeros); + + SET_SHARED_GLOBAL(g_freqChange, 0); + ENABLE_INTERRUPTS(); + } + } + +#ifdef OUTPUT + /* Check for OUT data flag from host - set by buffer() */ + GET_SHARED_GLOBAL(aud_from_host_flag, g_aud_from_host_flag); + if (aud_from_host_flag) + { + /* The buffer thread has filled up a buffer */ + int datalength; + int space_left; + int aud_from_host_wrptr; + int aud_from_host_rdptr; + GET_SHARED_GLOBAL(aud_from_host_wrptr, g_aud_from_host_wrptr); + GET_SHARED_GLOBAL(aud_from_host_rdptr, g_aud_from_host_rdptr); + + SET_SHARED_GLOBAL(g_aud_from_host_flag, 0); + GET_SHARED_GLOBAL(released_buffer, g_aud_from_host_buffer); + + /* Read datalength from buffer */ + read_via_xc_ptr(datalength, released_buffer); + + /* Ignore bad small packets */ + if ((datalength >= (g_numUsbChanOut * slotSize)) && (released_buffer == g_aud_from_host_wrptr)) + { + /* Move the write pointer of the fifo on - round up to nearest word */ + aud_from_host_wrptr = aud_from_host_wrptr + ((datalength+3)&~0x3) + 4; + + /* Wrap pointer */ + if (aud_from_host_wrptr >= aud_from_host_fifo_end) + { + aud_from_host_wrptr = aud_from_host_fifo_start; + } + SET_SHARED_GLOBAL(g_aud_from_host_wrptr, aud_from_host_wrptr); + } + + /* if we have enough space left then send a new buffer pointer + * back to the buffer thread */ + space_left = aud_from_host_rdptr - aud_from_host_wrptr; + + /* Mod and special case */ + if(space_left <= 0 && g_aud_from_host_rdptr == aud_from_host_fifo_start) + { + space_left = aud_from_host_fifo_end - g_aud_from_host_wrptr; + } + + + if (space_left <= 0 || space_left >= MAX_USB_AUD_PACKET_SIZE) + { + SET_SHARED_GLOBAL(g_aud_from_host_buffer, aud_from_host_wrptr); + XUD_SetReady(aud_from_host_usb_ep, 1); + } + else + { + /* Enter OUT over flow state */ + outOverflow = 1; +#ifdef DEBUG_LEDS + led(c_led); +#endif + } + continue; + } + else if (outOverflow) + { + int space_left; + int aud_from_host_wrptr; + int aud_from_host_rdptr; + GET_SHARED_GLOBAL(aud_from_host_wrptr, g_aud_from_host_wrptr); + GET_SHARED_GLOBAL(aud_from_host_rdptr, g_aud_from_host_rdptr); + space_left = aud_from_host_rdptr - aud_from_host_wrptr; + if (space_left <= 0) + space_left += BUFF_SIZE_OUT*4; + if (space_left >= (BUFF_SIZE_OUT*4/2)) + { + /* Come out of OUT overflow state */ + outOverflow = 0; + SET_SHARED_GLOBAL(g_aud_from_host_buffer, aud_from_host_wrptr); + XUD_SetReady(aud_from_host_usb_ep, 1); +#ifdef DEBUG_LEDS + led(c_led); +#endif + } + } +#endif + + +#ifdef INPUT + { + /* Check if buffer() has sent a packet to host - uses shared mem flag to save chanends */ + int tmp; + GET_SHARED_GLOBAL(tmp, g_aud_to_host_flag); + //case inuint_byref(c_buf_in, tmp): + if (tmp) + { + /* Signals that the IN endpoint has sent data from the passed buffer */ + /* Reset flag */ + SET_SHARED_GLOBAL(g_aud_to_host_flag, 0); + aud_in_ready = 0; + + if (inUnderflow) + { + int aud_to_host_wrptr; + int aud_to_host_rdptr; + int fill_level; + GET_SHARED_GLOBAL(aud_to_host_wrptr, g_aud_to_host_wrptr); + GET_SHARED_GLOBAL(aud_to_host_rdptr, g_aud_to_host_rdptr); + + /* Check if we have come out of underflow */ + fill_level = aud_to_host_wrptr - aud_to_host_rdptr; + + if (fill_level < 0) + fill_level += BUFF_SIZE_IN*4; + + if (fill_level >= IN_BUFFER_PREFILL) + { + inUnderflow = 0; + SET_SHARED_GLOBAL(g_aud_to_host_buffer, aud_to_host_rdptr); + } + else + { + SET_SHARED_GLOBAL(g_aud_to_host_buffer, g_aud_to_host_zeros); + } + + } + else + { + /* Not in IN underflow state */ + int datalength; + int aud_to_host_wrptr; + int aud_to_host_rdptr; + GET_SHARED_GLOBAL(aud_to_host_wrptr, g_aud_to_host_wrptr); + GET_SHARED_GLOBAL(aud_to_host_rdptr, g_aud_to_host_rdptr); + + /* Read datalength and round to nearest word */ + read_via_xc_ptr(datalength, aud_to_host_rdptr); + aud_to_host_rdptr = aud_to_host_rdptr + ((datalength+3)&~0x3) + 4; + if (aud_to_host_rdptr >= aud_to_host_fifo_end) + { + aud_to_host_rdptr = aud_to_host_fifo_start; + } + SET_SHARED_GLOBAL(g_aud_to_host_rdptr, aud_to_host_rdptr); + + /* Check for read pointer hitting write pointer - underflow */ + if (aud_to_host_rdptr != aud_to_host_wrptr) + { + SET_SHARED_GLOBAL(g_aud_to_host_buffer, aud_to_host_rdptr); + } + else + { + inUnderflow = 1; + SET_SHARED_GLOBAL(g_aud_to_host_buffer, g_aud_to_host_zeros); + + } + } + + /* Request to send packet */ + { + int p, len; + GET_SHARED_GLOBAL(p, g_aud_to_host_buffer); + asm("ldw %0, %1[0]":"=r"(len):"r"(p)); + XUD_SetReady_In(aud_to_host_usb_ep, PIDn_DATA0, p+4, len); + aud_in_ready = 1; + } + continue; + } + } +#endif // INPUT + +#ifdef MIDI + /* Check if buffer() has send MIDI packet to host */ + GET_SHARED_GLOBAL(midi_to_host_flag, g_midi_to_host_flag); + if (midi_to_host_flag) + { + /* The buffer has been sent to the host, so we can ack the midi thread */ + SET_SHARED_GLOBAL(g_midi_to_host_flag, 0); + if (midi_data_collected_from_device != 0) + { + /* We have some more data to send set the amount of data to send */ + write_via_xc_ptr(midi_to_host_buffer_being_collected, midi_data_collected_from_device); + + /* Swap the collecting and sending buffer */ + swap(midi_to_host_buffer_being_collected, midi_to_host_buffer_being_sent); + + { + /* Request to send packet */ + int len; + asm("ldw %0, %1[0]":"=r"(len):"r"(midi_to_host_buffer_being_sent)); + XUD_SetReady_In(midi_to_host_usb_ep, 0, midi_to_host_buffer_being_sent+4, len); + } + + /* 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; + } + continue; + } + else + { + /* Check if host has sent us MIDI OUT data */ + GET_SHARED_GLOBAL(midi_from_host_flag, g_midi_from_host_flag); + if (midi_from_host_flag) + { + /* The buffer() thread has filled up a buffer */ + int datalength; + int space_left; + + /* Reset flag */ + SET_SHARED_GLOBAL(g_midi_from_host_flag, 0); + + /* Read length from buffer[0] */ + read_via_xc_ptr(midi_data_remaining_to_device, midi_from_host_buffer); + + /* Increment read pointer - buffer[0] is length */ + midi_from_host_rdptr = midi_from_host_buffer + 4; + + 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; + } + } + } + + select + { + /* 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 */ + XUD_SetReady(midi_from_host_usb_ep, 1); + } + 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 + 4) + 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) + { + write_via_xc_ptr(midi_to_host_buffer_being_collected, midi_data_collected_from_device); + + midi_data_collected_from_device = 0; + swap(midi_to_host_buffer_being_collected, midi_to_host_buffer_being_sent); + + // Signal other side to swap + { + int len; + asm("ldw %0, %1[0]":"=r"(len):"r"(midi_to_host_buffer_being_sent)); + XUD_SetReady_In(midi_to_host_usb_ep, 0, midi_to_host_buffer_being_sent+4, len); + } + midi_waiting_on_send_to_host = 1; + } + } + break; + default: + break; + } +#endif // MIDI + } +} + diff --git a/module_usb_aud_shared/usb_buffer/decouple.xc.orig b/module_usb_aud_shared/usb_buffer/decouple.xc.orig new file mode 100644 index 00000000..98ad583c --- /dev/null +++ b/module_usb_aud_shared/usb_buffer/decouple.xc.orig @@ -0,0 +1,1169 @@ +#include +#include +#include +#define ecallf(e) asm("ecallf %0" :: "r"(e)); +#include "xc_ptr.h" +#define NO_INLINE_MIDI_SELECT_HANDLER 1 +#include "usb_midi.h" +#include "devicedefines.h" +#include "testct_byref.h" +#include "interrupt.h" +#include "clockcmds.h" +#include "xud.h" +#include "usb.h" + + +#define MAX(x,y) ((x)>(y) ? (x) : (y)) +#define MAX_CLASS_ONE_FREQ 96000 +#define MAX_CLASS_ONE_CHAN 2 + +#define CLASS_TWO_PACKET_SIZE ((((MAX_FREQ+7999)/8000))+3) +#define CLASS_ONE_PACKET_SIZE ((((MAX_CLASS_ONE_FREQ+999)/1000))+3) + +#define BUFF_SIZE_OUT MAX(4 * CLASS_TWO_PACKET_SIZE * NUM_USB_CHAN_OUT, 4 * CLASS_ONE_PACKET_SIZE * MAX_CLASS_ONE_CHAN) +#define BUFF_SIZE_IN MAX(4 * CLASS_TWO_PACKET_SIZE * NUM_USB_CHAN_IN, 4 * CLASS_ONE_PACKET_SIZE * MAX_CLASS_ONE_CHAN) +#define MAX_USB_AUD_PACKET_SIZE 1028 +//#define OUT_BUFFER_PREFILL (2*4*BUFF_SIZE_OUT/3) +#define OUT_BUFFER_PREFILL MAX(CLASS_ONE_PACKET_SIZE*3+4,CLASS_TWO_PACKET_SIZE*4+4)*2 +#define IN_BUFFER_PREFILL MAX(CLASS_ONE_PACKET_SIZE*3+4,CLASS_TWO_PACKET_SIZE*4+4)*2 + + +//#pragma xta command "config threads stdcore[1] 6" +//#pragma xta command "add exclusion out_underflow" +//#pragma xta command "add exclusion freq_change" +//#pragma xta command "add exclusion print_err" +//#pragma xta command "add exclusion out_soverflow" +//#pragma xta command "analyse path mixer_request mixer_request" +//#pragma xta command "set required - 5200 ns" /* 192kHz */ + +/* Volume and mute tables */ +#ifndef OUT_VOLUME_IN_MIXER +unsigned int multOut[NUM_USB_CHAN_OUT + 1]; +#endif +#ifndef IN_VOLUME_IN_MIXER +unsigned int multIn[NUM_USB_CHAN_IN + 1]; +#endif + +/* Number of channels to/from the USB bus */ +unsigned g_numUsbChanOut = NUM_USB_CHAN_OUT; +unsigned g_numUsbChanIn = NUM_USB_CHAN_IN; + +#define MAX_DEVICE_AUD_PACKET_SIZE_CLASS_TWO ((MAX_FREQ/8000+1)*NUM_USB_CHAN_IN*4) +#define MAX_DEVICE_AUD_PACKET_SIZE_CLASS_ONE (((MAX_CLASS_ONE_FREQ/1000+1)*MAX_CLASS_ONE_CHAN*3)+4) + +#define MAX_DEVICE_AUD_PACKET_SIZE (MAX(MAX_DEVICE_AUD_PACKET_SIZE_CLASS_ONE, MAX_DEVICE_AUD_PACKET_SIZE_CLASS_TWO)) + +/* Circular audio buffers */ +unsigned outAudioBuff[BUFF_SIZE_OUT + (MAX_USB_AUD_PACKET_SIZE>>2) + 4]; +unsigned audioBuffIn[BUFF_SIZE_IN + (MAX_DEVICE_AUD_PACKET_SIZE>>2) + 4]; + +unsigned inZeroBuff[(MAX_DEVICE_AUD_PACKET_SIZE>>2)+4]; + +unsigned ledVal = 1; +unsigned dir = 0; + +void led(chanend ?c_led) +{ + if(dir == 0) + ledVal <<= 1; + else + ledVal >>= 1; + + if(ledVal == 0b10000000 || ledVal == 1) + dir = !dir; + + if (!isnull(c_led)) { + c_led <: ledVal; + } +} + +/* 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 + * + * Note: This function uses the multiple return value feature of XC + */ + +void GetADCCounts(unsigned samFreq, int &min, int &mid, int &max); + +static inline void swap(xc_ptr &a, xc_ptr &b) +{ + xc_ptr tmp; + tmp = a; + a = b; + b = tmp; + return; +} + +// shared global midi buffering variables +unsigned g_midi_from_host_flag = 0; +unsigned g_midi_to_host_flag = 0; +int midi_to_host_usb_ep = 0; +int midi_from_host_usb_ep = 0; +int aud_from_host_usb_ep = 0; +int aud_to_host_usb_ep = 0; +int int_usb_ep = 0; + +int g_midi_to_host_buffer[8]; +int g_midi_from_host_buffer[MAX_USB_PACKET_SIZE+4]; + +// shared global aud buffering variables + +unsigned g_aud_from_host_buffer; +unsigned g_aud_to_host_buffer; +unsigned g_aud_to_host_flag = 0; +int buffer_aud_ctl_chan = 0; +unsigned g_aud_from_host_flag = 0; +unsigned g_aud_from_host_info; +unsigned g_freqChange_flag = 0; +unsigned g_freqChange_sampFreq; +int speedRem = 0; + + +xc_ptr aud_from_host_fifo_start; +xc_ptr aud_from_host_fifo_end; +xc_ptr g_aud_from_host_wrptr; +xc_ptr g_aud_from_host_rdptr; + + +xc_ptr aud_to_host_fifo_start; +xc_ptr aud_to_host_fifo_end; +xc_ptr g_aud_to_host_wrptr; +xc_ptr g_aud_to_host_dptr; +xc_ptr g_aud_to_host_rdptr; +xc_ptr g_aud_to_host_zeros; +int sampsToWrite = 0; +int totalSampsToWrite = 0; + + +int aud_data_remaining_to_device = 0; + + +/* Over/under flow flags */ +unsigned outUnderflow = 1; +unsigned outOverflow = 0; +unsigned inUnderflow = 1; +unsigned inOverflow = 0; + + +int aud_req_in_count = 0; +int aud_req_out_count = 0; + +unsigned unpackState = 0; +unsigned unpackData = 0; + +unsigned packState = 0; +unsigned packData = 0; + + +#pragma select handler +#pragma unsafe arrays +void handle_audio_request(chanend c_mix_out, chanend ?c_led) +{ + int outSamps; + int space_left; + int usb_speed; + + asm("ldw %0, dp[g_curUsbSpeed]" : "=r" (usb_speed) :); + + (void) inuint(c_mix_out); + outuint(c_mix_out, 0); + + /* If in overflow condition, throw samples away */ + if(inOverflow || sampsToWrite == 0) + { +#pragma loop unroll + for(int i = 0; i < NUM_USB_CHAN_IN; i++) + { + (void) inuint(c_mix_out); + } + + /* Calculate how much space left in buffer */ + space_left = g_aud_to_host_rdptr - g_aud_to_host_wrptr; + if (space_left <= 0) + space_left += BUFF_SIZE_IN*4; + + if (space_left > (BUFF_SIZE_IN*4/2)) + { + inOverflow = 0; + } + } + else + { + /* Not in overflow, store samples from mixer into sample buffer */ + if (usb_speed == XUD_SPEED_HS) + { + unsigned ptr = g_aud_to_host_dptr; + + for(int i = 0; i < g_numUsbChanIn; i++) + { + int sample = inuint(c_mix_out); +#if !defined(IN_VOLUME_IN_MIXER) + int mult; + int h; + unsigned l; + asm("ldw %0, %1[%2]":"=r"(mult):"r"(multIn),"r"(i)); + {h, l} = macs(mult, sample, 0, 0); + sample = h << 3; +#elif defined(IN_VOLUME_IN_MIXER) && defined(IN_VOLUME_AFTER_MIX) + sample = sample << 3; +#endif + write_via_xc_ptr(ptr, sample); + ptr+=4; + } + + g_aud_to_host_dptr = ptr; + } + else + { + for(int i = 0; i < g_numUsbChanIn; i++) + { + int sample = inuint(c_mix_out); +#ifndef IN_VOLUME_IN_MIXER + int mult; + int h; + unsigned l; + asm("ldw %0, %1[%2]":"=r"(mult):"r"(multIn),"r"(i)); + {h, l} = macs(mult, sample, 0, 0); + sample = h << 3; +#endif + switch (packState&0x3) + { + case 0: + packData = sample; + break; + case 1: + packData = packData >> 8 | ((sample & 0xff00)<<16); + write_via_xc_ptr(g_aud_to_host_dptr, packData); + g_aud_to_host_dptr+=4; + write_via_xc_ptr(g_aud_to_host_dptr, sample>>16); + packData = sample; + break; + case 2: + packData = (packData>>16) | ((sample & 0xffff00) << 8); + write_via_xc_ptr(g_aud_to_host_dptr, packData); + g_aud_to_host_dptr+=4; + packData = sample; + break; + case 3: + packData = (packData >> 24) | (sample & 0xffffff00); + write_via_xc_ptr(g_aud_to_host_dptr, packData); + g_aud_to_host_dptr+=4; + break; + } + packState++; + } + + } + + /* Input any remaining channels */ + for(int i = 0; i < NUM_USB_CHAN_IN - g_numUsbChanIn; i++) + { + inuint(c_mix_out); + } + + sampsToWrite--; + } + + if(outUnderflow) + { +#pragma xta endpoint "out_underflow" + /* We're still pre-buffering, send out 0 samps */ + for(int i = 0; i < NUM_USB_CHAN_OUT; i++) + { + outuint(c_mix_out, 0); + } + + /* Calc how many samples left in buffer */ + outSamps = g_aud_from_host_wrptr - g_aud_from_host_rdptr; + if (outSamps < 0) + { + outSamps += BUFF_SIZE_OUT*4; + } + + /* If we have a decent number of samples, come out of underflow cond */ + if (outSamps >= (OUT_BUFFER_PREFILL)) + { + outUnderflow = 0; + } + } + else + { + + if (usb_speed == XUD_SPEED_HS) + { + /* Buffering not underflow condition send out some samples...*/ + for(int i = 0; i < g_numUsbChanOut; i++) + { +#pragma xta endpoint "mixer_request" + int sample; + int mult; + int h; + unsigned l; + + read_via_xc_ptr(sample, g_aud_from_host_rdptr); + g_aud_from_host_rdptr+=4; + +#ifndef OUT_VOLUME_IN_MIXER + asm("ldw %0, %1[%2]":"=r"(mult):"r"(multOut),"r"(i)); + {h, l} = macs(mult, sample, 0, 0); + h <<= 3; + outuint(c_mix_out, h); +#else + outuint(c_mix_out, sample); + +#endif + } + } + else + { + + /* Buffering not underflow condition send out some samples...*/ + for(int i = 0; i < g_numUsbChanOut; i++) + { +#pragma xta endpoint "mixer_request" + int sample; + int mult; + int h; + unsigned l; + + switch (unpackState&0x3) + { + case 0: + read_via_xc_ptr(unpackData, g_aud_from_host_rdptr); + g_aud_from_host_rdptr+=4; + sample = unpackData << 8; + break; + case 1: + sample = (unpackData >> 16); + read_via_xc_ptr(unpackData, g_aud_from_host_rdptr); + g_aud_from_host_rdptr+=4; + sample = sample | (unpackData << 16); + break; + case 2: + sample = (unpackData >> 8); + read_via_xc_ptr(unpackData, g_aud_from_host_rdptr); + g_aud_from_host_rdptr+=4; + sample = sample | (unpackData<< 24); + break; + case 3: + sample = unpackData & 0xffffff00; + break; + } + unpackState++; + +#ifndef OUT_VOLUME_IN_MIXER + asm("ldw %0, %1[%2]":"=r"(mult):"r"(multOut),"r"(i)); + {h, l} = macs(mult, sample, 0, 0); + h <<= 3; + outuint(c_mix_out, h); +#else + outuint(c_mix_out, sample); + +#endif + } + } + + /* Output remaining channels. Past this point we always operate on MAX chan count */ + for(int i = 0; i < NUM_USB_CHAN_OUT - g_numUsbChanOut; i++) + { + outuint(c_mix_out, 0); + } + + + + if(usb_speed == XUD_SPEED_HS) + { + /* 4 bytes per sample */ + aud_data_remaining_to_device -= (g_numUsbChanOut*4); + } + else { + /* 3 bytes per sample */ + aud_data_remaining_to_device -= (g_numUsbChanOut*3); + } + + + + } + + if (!inOverflow) + { + if (sampsToWrite == 0) + { + int speed; + + if (totalSampsToWrite) + { + if (usb_speed == XUD_SPEED_HS) + { + g_aud_to_host_wrptr += 4+totalSampsToWrite*4*g_numUsbChanIn; + } + else + { + unsigned int datasize = totalSampsToWrite*3*g_numUsbChanIn; + datasize = (datasize+3) & (~0x3); // round up to nearest word + g_aud_to_host_wrptr += 4+datasize; + } + if (g_aud_to_host_wrptr >= aud_to_host_fifo_end) + { + g_aud_to_host_wrptr = aud_to_host_fifo_start; + } + } + + /* Get feedback val */ + /* TODO, this should be syncronised ideally */ + asm("ldw %0, dp[g_speed]" : "=r" (speed) :); + + /* Calc packet size to send back based on our fb */ + speedRem += speed; + totalSampsToWrite = speedRem >> 16; + speedRem &= 0xffff; + + if (usb_speed == XUD_SPEED_HS) + { + if (totalSampsToWrite < 0 || totalSampsToWrite*4*g_numUsbChanIn > (MAX_DEVICE_AUD_PACKET_SIZE_CLASS_TWO)) + { + totalSampsToWrite = 0; + } + } + else + { + if (totalSampsToWrite < 0 || totalSampsToWrite*3*g_numUsbChanIn > (MAX_DEVICE_AUD_PACKET_SIZE_CLASS_ONE)) + { + totalSampsToWrite = 0; + } + } + + /* Calc slots left in fifo */ + space_left = g_aud_to_host_rdptr - g_aud_to_host_wrptr; + + /* mod and special case */ + if (space_left <= 0 && g_aud_to_host_rdptr == aud_to_host_fifo_start) + { + space_left = aud_to_host_fifo_end - g_aud_to_host_wrptr; + } + + if ((space_left <= 0) || (space_left > totalSampsToWrite*g_numUsbChanIn*4+4)) + { + // packet ok + if (totalSampsToWrite) + { + if (usb_speed == XUD_SPEED_HS) + { + write_via_xc_ptr(g_aud_to_host_wrptr, totalSampsToWrite*4*g_numUsbChanIn); + } + else + { + write_via_xc_ptr(g_aud_to_host_wrptr, totalSampsToWrite*3*g_numUsbChanIn); + packState = 0; + } + g_aud_to_host_dptr = g_aud_to_host_wrptr + 4; + } + } + else + { + inOverflow = 1; + totalSampsToWrite = 0; + } + sampsToWrite = totalSampsToWrite; + } + } + + if (!outUnderflow && (aud_data_remaining_to_device<3)) + { + /* Wrap read pointer */ + if (g_aud_from_host_rdptr >= aud_from_host_fifo_end) + g_aud_from_host_rdptr = aud_from_host_fifo_start; + + outUnderflow = (g_aud_from_host_rdptr == g_aud_from_host_wrptr); + + if (!outUnderflow) + { + read_via_xc_ptr(aud_data_remaining_to_device, g_aud_from_host_rdptr); + //ecallf(aud_data_remaining_to_device <= 432); + + unpackState = 0; + //aud_data_remaining_to_device >>= 2; + /* if (aud_data_remaining_to_device % g_numUsbChanOut != 0) { + asm("ecallf %0"::"r"(0)); + }*/ + g_aud_from_host_rdptr+=4; + } +#ifdef DEBUG_LEDS + else + { + led(c_led); + } +#endif + } + +} + + +unsigned g_intFlag = 0; + +extern unsigned char g_intData[8]; + +void check_for_interrupt(chanend ?c_clk_int) { + unsigned tmp; + + select + { + /* Clocking thread wants to produce an interrupt... */ + case inuint_byref(c_clk_int, tmp): + chkct(c_clk_int, XS1_CT_END); + + /* Check if we have interrupt pending */ + /* TODO This means we can loose interrupts */ + if(!g_intFlag) + { + int x; + + g_intFlag = 1; + + g_intData[5] = tmp; + + /* Make request to send to XUD endpoint - response handled in usb_buffer */ + //XUD_SetReady(int_usb_ep, 0); + + asm("ldaw %0, dp[g_intData]":"=r"(x)); + XUD_SetReady_In(int_usb_ep, 0,x,6); + } + + break; + default: + break; + } +} + + +#pragma unsafe arrays +void decouple(chanend c_mix_out, + chanend ?c_midi, chanend ?c_clk_int) +{ + unsigned sampFreq = DEFAULT_FREQ; + int aud_from_host_flag=0; + int aud_to_host_flag=0; + xc_ptr released_buffer; + +#ifdef MIDI + xc_ptr midi_from_host_rdptr = midi_from_host_fifo_start; + + xc_ptr midi_from_host_buffer = array_to_xc_ptr(g_midi_from_host_buffer); + xc_ptr midi_to_host_buffer = array_to_xc_ptr(g_midi_to_host_buffer); + int is_ack; + unsigned int datum; + int midi_data_remaining_to_device = 0; + int midi_to_host_flag = 0; + int midi_from_host_flag = 0; +#endif + int t = array_to_xc_ptr(outAudioBuff); + + + aud_from_host_fifo_start = t; + aud_from_host_fifo_end = aud_from_host_fifo_start + BUFF_SIZE_OUT*4; + g_aud_from_host_wrptr = aud_from_host_fifo_start; + g_aud_from_host_rdptr = aud_from_host_fifo_start; + + t = array_to_xc_ptr(audioBuffIn); + + aud_to_host_fifo_start = t; + aud_to_host_fifo_end = aud_to_host_fifo_start + BUFF_SIZE_IN*4; + g_aud_to_host_wrptr = aud_to_host_fifo_start; + g_aud_to_host_rdptr = aud_to_host_fifo_start; + + t = array_to_xc_ptr(inZeroBuff); + g_aud_to_host_zeros = t; + + /* Init interrupt report */ + g_intData[0] = 0; // Class-specific, caused by interface + g_intData[1] = 1; // attribute: CUR + g_intData[2] = 0; // CN/ MCN + g_intData[3] = 0; // CS + g_intData[4] = 0; // interface + g_intData[5] = 0; // ID of entity causing interrupt - this will get modified + + /* Init vol mult tables */ +#ifndef OUT_VOLUME_IN_MIXER + for (int i = 0; i < NUM_USB_CHAN_OUT + 1; i++) + { + asm("stw %0, %1[%2]"::"r"(MAX_VOL),"r"(multOut),"r"(i)); + } +#endif + +#ifndef IN_VOLUME_IN_MIXER + for (int i = 0; i < NUM_USB_CHAN_IN + 1; i++) + { + asm("stw %0, %1[%2]"::"r"(MAX_VOL),"r"(mulIn),"r"(i)); + } +#endif + + + { int c=0; + while(!c) { + asm("ldw %0, dp[buffer_aud_ctl_chan]":"=r"(c)); + } + } + + + set_interrupt_handler(handle_audio_request, 200, 1, c_mix_out, 0); + +#ifdef MIDI + asm("ldaw %0, dp[g_midi_to_host_buffer]":"=r"(midi_to_host_buffer)); + asm("ldaw %0, dp[g_midi_from_host_buffer]":"=r"(midi_from_host_buffer)); + + // wait for usb_buffer to set up + while(!midi_from_host_flag) { + GET_SHARED_GLOBAL(midi_from_host_flag, g_midi_from_host_flag); + } + + midi_from_host_flag = 0; + SET_SHARED_GLOBAL(g_midi_from_host_flag, midi_from_host_flag); + + // send the current host -> device buffer out of the fifo + XUD_SetReady(midi_from_host_usb_ep, 1); +#endif + +#ifdef OUTPUT + // wait for usb_buffer to set up + while(!aud_from_host_flag) { + GET_SHARED_GLOBAL(aud_from_host_flag, g_aud_from_host_flag); + } + + aud_from_host_flag = 0; + SET_SHARED_GLOBAL(g_aud_from_host_flag, aud_from_host_flag); + + // send the current host -> device buffer out of the fifo + SET_SHARED_GLOBAL(g_aud_from_host_buffer, g_aud_from_host_wrptr); + XUD_SetReady(aud_from_host_usb_ep, 1); +#endif + +#ifdef INPUT + // wait for usb_buffer to set up + while(!aud_to_host_flag) { + GET_SHARED_GLOBAL(aud_to_host_flag, g_aud_to_host_flag); + } + + aud_to_host_flag = 0; + SET_SHARED_GLOBAL(g_aud_to_host_flag, aud_to_host_flag); + + // send the current host -> device buffer out of the fifo + SET_SHARED_GLOBAL(g_aud_to_host_buffer, g_aud_to_host_zeros); + { + xc_ptr p; + int len; + + GET_SHARED_GLOBAL(p, g_aud_to_host_buffer); + read_via_xc_ptr(len, p); + + XUD_SetReady_In(aud_to_host_usb_ep, PIDn_DATA0, g_aud_to_host_buffer, len); + + } +#endif + + while(1) + { + if (!isnull(c_clk_int)) + { + check_for_interrupt(c_clk_int); + } + + asm("#decouple-default"); + + /* Check for freq change or other update */ + { + int tmp; + GET_SHARED_GLOBAL(tmp, g_freqChange_flag); + if (tmp == SET_SAMPLE_FREQ) + { + SET_SHARED_GLOBAL(g_freqChange_flag, 0); + GET_SHARED_GLOBAL(sampFreq, g_freqChange_sampFreq); + + /* Pass on to mixer */ + DISABLE_INTERRUPTS(); + inuint(c_mix_out); + outct(c_mix_out, 9); + outuint(c_mix_out, sampFreq); + + inOverflow = 0; + inUnderflow = 1; + SET_SHARED_GLOBAL(g_aud_to_host_rdptr, + aud_to_host_fifo_start); + SET_SHARED_GLOBAL(g_aud_to_host_wrptr, + aud_to_host_fifo_start); + SET_SHARED_GLOBAL(sampsToWrite, 0); + SET_SHARED_GLOBAL(totalSampsToWrite, 0); + SET_SHARED_GLOBAL(g_aud_to_host_buffer, + g_aud_to_host_zeros); + + /* Wait for handshake back and pass back up */ + + chkct(c_mix_out, XS1_CT_END); + + + SET_SHARED_GLOBAL(g_freqChange, 0); + asm("outct res[%0],%1"::"r"(buffer_aud_ctl_chan),"r"(XS1_CT_END)); + + ENABLE_INTERRUPTS(); + + speedRem = 0; + continue; + } + else if(tmp == SET_CHAN_COUNT_IN) + { + + DISABLE_INTERRUPTS(); + SET_SHARED_GLOBAL(g_freqChange_flag, 0); + GET_SHARED_GLOBAL(g_numUsbChanIn, g_freqChange_sampFreq); /* Misuse of g_freqChange_sampFreq */ + + inOverflow = 0; + inUnderflow = 1; + SET_SHARED_GLOBAL(g_aud_to_host_rdptr, + aud_to_host_fifo_start); + SET_SHARED_GLOBAL(g_aud_to_host_wrptr, + aud_to_host_fifo_start); + SET_SHARED_GLOBAL(sampsToWrite, 0); + SET_SHARED_GLOBAL(totalSampsToWrite, 0); + SET_SHARED_GLOBAL(g_aud_to_host_buffer, + g_aud_to_host_zeros); + + + // g_buffSizeIn = (BUFF_MULT * g_numUsbChanIn); + // inWr = 0; // Reseting wr/rd prob not required since we will underflow and reset + // inRd = (g_buffSizeIn + (int) inWr - (g_numUsbChanIn * 25)) % g_buffSizeIn; + // sampsToWrite = 0; + + SET_SHARED_GLOBAL(g_freqChange, 0); + ENABLE_INTERRUPTS(); + } + } + +#ifdef OUTPUT + GET_SHARED_GLOBAL(aud_from_host_flag, g_aud_from_host_flag); + if (aud_from_host_flag) + { + // the buffer thread has filled up a buffer + int datalength; + int space_left; + int aud_from_host_wrptr; + int aud_from_host_rdptr; + GET_SHARED_GLOBAL(aud_from_host_wrptr, g_aud_from_host_wrptr); + GET_SHARED_GLOBAL(aud_from_host_rdptr, g_aud_from_host_rdptr); + + SET_SHARED_GLOBAL(g_aud_from_host_flag, 0); + GET_SHARED_GLOBAL(released_buffer, g_aud_from_host_buffer); + + read_via_xc_ptr(datalength, released_buffer); + + if (datalength) { + // move the write pointer of the fifo on + + aud_from_host_wrptr = + aud_from_host_wrptr + ((datalength+3)&~0x3) + 4; + if (aud_from_host_wrptr >= aud_from_host_fifo_end) + aud_from_host_wrptr = aud_from_host_fifo_start; + + SET_SHARED_GLOBAL(g_aud_from_host_wrptr, aud_from_host_wrptr); + } + + // if we have enough space left then send a new buffer pointer + // back to the buffer thread + space_left = aud_from_host_rdptr - aud_from_host_wrptr; + + + if (space_left <= 0 || space_left >= MAX_USB_AUD_PACKET_SIZE) { + SET_SHARED_GLOBAL(g_aud_from_host_buffer, aud_from_host_wrptr); + XUD_SetReady(aud_from_host_usb_ep, 1); + } + else { + outOverflow = 1; +#ifdef DEBUG_LEDS + led(c_led); +#endif + // overflow + } + continue; + } + else if (outOverflow) { + int space_left; + int aud_from_host_wrptr; + int aud_from_host_rdptr; + GET_SHARED_GLOBAL(aud_from_host_wrptr, g_aud_from_host_wrptr); + GET_SHARED_GLOBAL(aud_from_host_rdptr, g_aud_from_host_rdptr); + space_left = aud_from_host_rdptr - aud_from_host_wrptr; + if (space_left <= 0) + space_left += BUFF_SIZE_OUT*4; + if (space_left >= (BUFF_SIZE_OUT*4/2)) { + outOverflow = 0; + SET_SHARED_GLOBAL(g_aud_from_host_buffer, + aud_from_host_wrptr); + XUD_SetReady(aud_from_host_usb_ep, 1); +#ifdef DEBUG_LEDS + led(c_led); +#endif + } + } +#endif + + +#ifdef INPUT + { int tmp; + GET_SHARED_GLOBAL(tmp, g_aud_to_host_flag); + //case inuint_byref(c_buf_in, tmp): + if (tmp) { + /* Signals that the IN endpoint has sent data + from the passed buffer */ + SET_SHARED_GLOBAL(g_aud_to_host_flag, 0); + + if (inUnderflow) { + int aud_to_host_wrptr; + int aud_to_host_rdptr; + int fill_level; + GET_SHARED_GLOBAL(aud_to_host_wrptr, g_aud_to_host_wrptr); + GET_SHARED_GLOBAL(aud_to_host_rdptr, g_aud_to_host_rdptr); + + // check if we have come out of underflow. + + fill_level = aud_to_host_wrptr - aud_to_host_rdptr; + + if (fill_level < 0) + fill_level += BUFF_SIZE_IN*4; + + if (fill_level >= IN_BUFFER_PREFILL) { + inUnderflow = 0; + SET_SHARED_GLOBAL(g_aud_to_host_buffer, aud_to_host_rdptr); + } + else { + SET_SHARED_GLOBAL(g_aud_to_host_buffer, + g_aud_to_host_zeros); + } + + } else { + int datalength; + int aud_to_host_wrptr; + int aud_to_host_rdptr; + GET_SHARED_GLOBAL(aud_to_host_wrptr, g_aud_to_host_wrptr); + GET_SHARED_GLOBAL(aud_to_host_rdptr, g_aud_to_host_rdptr); + + read_via_xc_ptr(datalength, aud_to_host_rdptr); + aud_to_host_rdptr = + aud_to_host_rdptr + ((datalength+3)&~0x3) + 4; + if (aud_to_host_rdptr >= aud_to_host_fifo_end) + aud_to_host_rdptr = aud_to_host_fifo_start; + + SET_SHARED_GLOBAL(g_aud_to_host_rdptr, aud_to_host_rdptr); + + if (aud_to_host_rdptr != aud_to_host_wrptr) { + SET_SHARED_GLOBAL(g_aud_to_host_buffer, + aud_to_host_rdptr); + } + else { + inUnderflow = 1; + SET_SHARED_GLOBAL(g_aud_to_host_buffer, + g_aud_to_host_zeros); + + } + } + + { + int p, len; + + GET_SHARED_GLOBAL(p, g_aud_to_host_buffer); + + asm("ldw %0, %1[0]":"=r"(len):"r"(p)); + + XUD_SetReady_In(aud_to_host_usb_ep, PIDn_DATA0, p+4, len); + } + continue; + } + } +#endif // INPUT + +#ifdef MIDI +<<<<<<< HEAD + GET_SHARED_GLOBAL(midi_to_host_flag, g_midi_to_host_flag); + if (midi_to_host_flag) + { + // An ack from the buffer thread means that the buffer has been + // sent to the host, so we can ack the midi thread + SET_SHARED_GLOBAL(g_midi_to_host_flag, 0); + midi_send_ack(c_midi); + continue; + } + else + { + GET_SHARED_GLOBAL(midi_from_host_flag, g_midi_from_host_flag); + if (midi_from_host_flag) + { + // the buffer thread has filled up a buffer + int datalength; + int space_left; + + SET_SHARED_GLOBAL(g_midi_from_host_flag, 0); + + read_via_xc_ptr(midi_data_remaining_to_device, + midi_from_host_buffer); + midi_from_host_rdptr = midi_from_host_buffer + 4; + + if (midi_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; + } + } + } + + + select + { + 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 + if (midi_data_remaining_to_device == 0) { + // we have read an entire packet + XUD_SetReady(midi_from_host_usb_ep, 1); + } + else { + read_via_xc_ptr(datum, midi_from_host_rdptr); + outuint(c_midi, datum); + midi_data_remaining_to_device -= 4; + } + } + else { + // set up a single event packet + write_via_xc_ptr(midi_to_host_buffer, datum); + XUD_SetReady_In(midi_to_host_usb_ep, 0, + midi_to_host_buffer, 4); + + } + break; + default: + break; + } + } + +======= + GET_SHARED_GLOBAL(midi_to_host_flag, g_midi_to_host_flag); + if (midi_to_host_flag) + { + // An ack from the buffer thread means that the buffer has been + // sent to the host + SET_SHARED_GLOBAL(g_midi_to_host_flag, 0); + + if (midi_data_collected_from_device != 0) + { + // printstr("agg"); + // we have some more data to send + // printstr("decouple->buffer: "); + // printintln(midi_data_collected_from_device); + + // set the amount of data to send + write_via_xc_ptr(midi_to_host_buffer_being_collected, midi_data_collected_from_device); + + // swap the collecting and sending buffer + swap(midi_to_host_buffer_being_collected, midi_to_host_buffer_being_sent); + + +#if 1 + { + int len; + + asm("ldw %0, %1[0]":"=r"(len):"r"(midi_to_host_buffer_being_sent)); + + XUD_SetReady_In(midi_to_host_usb_ep, 0, midi_to_host_buffer_being_sent+4, len); + } +#else + // signal other side to swap + XUD_SetReady(midi_to_host_usb_ep, 0); +#endif + + // midi_send_ack(c_midi_buf); + 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; + } + continue; + } + else + { + GET_SHARED_GLOBAL(midi_from_host_flag, g_midi_from_host_flag); + if (midi_from_host_flag) + { + // the buffer thread has filled up a buffer + int datalength; + int space_left; + + SET_SHARED_GLOBAL(g_midi_from_host_flag, 0); + GET_SHARED_GLOBAL(released_buffer, g_midi_from_host_buffer); + + read_via_xc_ptr(datalength, released_buffer); + + if (datalength) { + // move the write pointer of the fifo on + midi_from_host_wrptr = midi_from_host_wrptr + datalength + 4; + if (midi_from_host_wrptr >= midi_from_host_fifo_end) + midi_from_host_wrptr = midi_from_host_fifo_start; + } + + // if we have enough space left then send a new buffer pointer + // back to the buffer thread + space_left = midi_from_host_rdptr - midi_from_host_wrptr; + + if (space_left < 0 || space_left >= MAX_USB_MIDI_PACKET_SIZE) { + SET_SHARED_GLOBAL(g_midi_from_host_buffer, midi_from_host_wrptr); + XUD_SetReady(midi_from_host_usb_ep, 1); + } + else { + // overflow + + midi_from_device_overflow = 1; + // SET_SHARED_GLOBAL(g_midi_from_host_buffer, + // null_midi_from_host_buffer); + // asm("out res[%1], %0"::"r"(1),"r"(midi_from_host_usb_chan)); + //printstrln("decouple h->d overflow"); + } + + if (datalength) { + // if weare not currently sending data to the midi-uart thread + // intiate sending it some + if (midi_data_remaining_to_device == 0) { + read_via_xc_ptr(midi_data_remaining_to_device, + midi_from_host_rdptr); + midi_from_host_rdptr += 4; + read_via_xc_ptr(datum, midi_from_host_rdptr); + outuint(c_midi, datum); + } + } + + continue; + } + } + + {int cont = 0; + select + { + 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 + + // update the read pointer of the fifo + midi_from_host_rdptr += 4; + midi_data_remaining_to_device -= 4; + + if (midi_data_remaining_to_device == 0) + { + // We have read an entire packet + if (midi_from_host_rdptr >= midi_from_host_fifo_end) + midi_from_host_rdptr = midi_from_host_fifo_start; + + if (midi_from_host_rdptr != midi_from_host_wrptr) + { + // There is another packet to send + read_via_xc_ptr(midi_data_remaining_to_device, midi_from_host_rdptr); + midi_from_host_rdptr += 4; + } + } + + if (midi_from_device_overflow) + { + int space_left = midi_from_host_rdptr - midi_from_host_wrptr; + + if (space_left < 0 || space_left >= MAX_USB_MIDI_PACKET_SIZE) + { + midi_from_device_overflow = 0; + SET_SHARED_GLOBAL(g_midi_from_host_buffer, midi_from_host_wrptr); + XUD_SetReady(midi_from_host_usb_ep, 1); + } + } + + if (midi_data_remaining_to_device) + { + /* There is more data to send to the midi/uart thread */ + read_via_xc_ptr(datum, midi_from_host_rdptr); + outuint(c_midi, datum); + } + } + else /* if(is_ack) */ + { + // the midi/uart thread has sent us some data + 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 + 4) + + midi_data_collected_from_device; + + // add data to the buffer + write_via_xc_ptr(p, datum); + midi_data_collected_from_device += 4; + } + else + { + //printstrln("decouple d->h overflow"); + // too many events from device - drop it + } + + // if we are not sending data to the host then initiate it + if (!midi_waiting_on_send_to_host) + { + // printstr("decouple->buffer: "); + // printintln(midi_data_collected_from_device); + write_via_xc_ptr(midi_to_host_buffer_being_collected, + midi_data_collected_from_device); + midi_data_collected_from_device = 0; + swap(midi_to_host_buffer_being_collected, + midi_to_host_buffer_being_sent); + // signal other side to swap + { + int len; + asm("ldw %0, %1[0]":"=r"(len):"r"(midi_to_host_buffer_being_sent)); + XUD_SetReady_In(midi_to_host_usb_ep, 0, midi_to_host_buffer_being_sent+4, len); + } + // midi_send_ack(c_midi_buf); + midi_waiting_on_send_to_host = 1; + } + } + cont = 1; + break; + default: + break; + } + if (cont) + continue; + } +>>>>>>> master +#endif // MIDI + + + } +} + diff --git a/module_usb_aud_shared/usb_buffer/get_adc_counts.c b/module_usb_aud_shared/usb_buffer/get_adc_counts.c new file mode 100644 index 00000000..629398f2 --- /dev/null +++ b/module_usb_aud_shared/usb_buffer/get_adc_counts.c @@ -0,0 +1,24 @@ +extern unsigned int g_curUsbSpeed; +#define XUD_SPEED_HS 2 +void GetADCCounts(unsigned samFreq, int *min, int *mid, int *max) +{ + unsigned frameTime; + int 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; + } + +} diff --git a/module_usb_aud_shared/usb_buffer/interrupt.h b/module_usb_aud_shared/usb_buffer/interrupt.h new file mode 100644 index 00000000..e05e29cf --- /dev/null +++ b/module_usb_aud_shared/usb_buffer/interrupt.h @@ -0,0 +1,198 @@ +#ifndef __interrupt_h__ +#define __interrupt_h__ + +#define store_args0(c) \ + asm("kentsp 19; stw %0, sp[1]; krestsp 19"::"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 21; stw %0, sp[1];" \ + "stw %1, sp[2];" \ + "stw %2, sp[3];" \ + " krestsp 21"::"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 store_args4(c,x0,x1,x2,x3) \ + asm("kentsp 23; stw %4, sp[1];" \ + "stw %0, sp[2];" \ + "stw %1, sp[3];" \ + "stw %2, sp[4];" \ + "stw %3, sp[5];" \ + " krestsp 23"::"r"(c),"r"(x0),"r"(x1),"r"(x2),"r"(x3)); + +#define store_args5(c,x0,x1,x2,x3,x4) \ + asm("kentsp 24;" \ + "stw %4, sp[1];" \ + "stw %5, sp[2];" \ + "stw %0, sp[3];" \ + "stw %1, sp[4];" \ + "stw %2, sp[5];" \ + "stw %3, sp[6];" \ + " krestsp 24"::"r"(c),"r"(x0),"r"(x1),"r"(x2),"r"(x3),"r"(x4)); + +#define store_args6(c,x0,x1,x2,x3,x4,x5) \ + asm("kentsp 25;" \ + "stw %4, sp[1];" \ + "stw %5, sp[2];" \ + "stw %6, sp[3];" \ + "stw %0, sp[4];" \ + "stw %1, sp[5];" \ + "stw %2, sp[6];" \ + "stw %3, sp[7];" \ + " krestsp 25"::"r"(c),"r"(x0),"r"(x1),"r"(x2),"r"(x3),"r"(x4),"r"(x5)); + +#define store_args7(c,x0,x1,x2,x3,x4,x5,x6) \ + asm("kentsp 26;" \ + "stw %4, sp[1];" \ + "stw %5, sp[2];" \ + "stw %6, sp[3];" \ + "stw %7, sp[4];" \ + "stw %0, sp[5];" \ + "stw %1, sp[6];" \ + "stw %2, sp[7];" \ + "stw %3, sp[8];" \ + " krestsp 26"::"r"(c),"r"(x0),"r"(x1),"r"(x2),"r"(x3),"r"(x4),"r"(x5),"r"(x6)); + +#define store_args8(c,x0,x1,x2,x3,x4,x5,x6,x7) \ + asm("kentsp 27;" \ + "stw %4, sp[1];" \ + "stw %5, sp[2];" \ + "stw %6, sp[3];" \ + "stw %7, sp[4];" \ + "stw %8, sp[5];" \ + "stw %0, sp[6];" \ + "stw %1, sp[7];" \ + "stw %2, sp[8];" \ + "stw %3, sp[9];" \ + " krestsp 27"::"r"(c),"r"(x0),"r"(x1),"r"(x2),"r"(x3),"r"(x4),"r"(x5),"r"(x6),"r"(x7)); + + + + +#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 load_argsn(f, args) \ + ".linkset __"#f"_handler_arg0, "#args"-2\n"\ + "ldw r0, sp[" "__"#f"_handler_arg0" "]\n" \ + ".linkset __"#f"_handler_arg1, "#args"-1\n"\ + "ldw r1, sp[" "__"#f"_handler_arg1" "]\n" \ + ".linkset __"#f"_handler_arg2, "#args"-0\n"\ + "ldw r2, sp[" "__"#f"_handler_arg2" "]\n" \ + ".linkset __"#f"_handler_arg3, "#args"+1\n"\ + "ldw r3, sp[" "__"#f"_handler_arg3" "]\n" + +#define load_args4(f) load_argsn(f,4) +#define load_args5(f) load_argsn(f,5) +#define load_args6(f) load_argsn(f,6) +#define load_args7(f) load_argsn(f,7) +#define load_args8(f) load_argsn(f,8) + +#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_r4_save, "#args"+4\n" \ + "stw r4, sp[" "__"#f"_handler_r4_save" "]\n" \ + ".linkset __"#f"_handler_r5_save, "#args"+5\n" \ + "stw r5, sp[" "__"#f"_handler_r5_save" "]\n" \ + ".linkset __"#f"_handler_r6_save, "#args"+6\n" \ + "stw r6, sp[" "__"#f"_handler_r6_save" "]\n" \ + ".linkset __"#f"_handler_r7_save, "#args"+7\n" \ + "stw r7, sp[" "__"#f"_handler_r7_save" "]\n" \ + ".linkset __"#f"_handler_r8_save, "#args"+8\n" \ + "stw r8, sp[" "__"#f"_handler_r8_save" "]\n" \ + ".linkset __"#f"_handler_r9_save, "#args"+9\n" \ + "stw r9, sp[" "__"#f"_handler_r9_save" "]\n" \ + ".linkset __"#f"_handler_r10_save, "#args"+10\n" \ + "stw r10, sp[" "__"#f"_handler_r10_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 r4, sp[" "__"#f"_handler_r4_save" "]\n" \ + "ldw r5, sp[" "__"#f"_handler_r5_save" "]\n" \ + "ldw r6, sp[" "__"#f"_handler_r6_save" "]\n" \ + "ldw r7, sp[" "__"#f"_handler_r7_save" "]\n" \ + "ldw r8, sp[" "__"#f"_handler_r8_save" "]\n" \ + "ldw r9, sp[" "__"#f"_handler_r9_save" "]\n" \ + "ldw r10, sp[" "__"#f"_handler_r10_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; + +#define do_interrupt_handler(f,args) \ + asm("bu .L__" #f "_handler_skip;\n" \ + "__" #f "_handler:\n" \ + "kentsp " #args " + 19\n" \ + "__kent:" \ + save_state(f,args) \ + load_args ## args (f) \ + "bl " #f "\n" \ + restore_state(f,args) \ + "krestsp " #args " + 19 \n" \ + "__kret:\n" \ + "kret\n" \ + ".L__" #f "_handler_skip:\n"); + +#define set_interrupt_handler(f, nstackwords, args, c, ...) \ + asm (" .section .dp.data, \"adw\", @progbits\n" \ + " .align 4\n" \ + "__" #f "_kernel_stack:\n" \ + " .space " #nstackwords ", 0\n" \ + " .text\n"); \ + asm("mov r10, %0; ldaw r11, dp[__" #f "_kernel_stack];add r11, r11, r10;ldaw r10, sp[0]; "\ + "set sp,r11;stw r10, sp[0]; krestsp 0"::"r"(nstackwords-8):"r10","r11"); \ + store_args ## args(c, __VA_ARGS__) \ + do_interrupt_handler(f, 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 + diff --git a/module_usb_aud_shared/usb_buffer/testct_byref.h b/module_usb_aud_shared/usb_buffer/testct_byref.h new file mode 100644 index 00000000..6952301d --- /dev/null +++ b/module_usb_aud_shared/usb_buffer/testct_byref.h @@ -0,0 +1,2 @@ +#pragma select handler +void testct_byref(chanend c, unsigned &isCt) ; diff --git a/module_usb_aud_shared/usb_buffer/testct_byref.xc b/module_usb_aud_shared/usb_buffer/testct_byref.xc new file mode 100644 index 00000000..779bc7b9 --- /dev/null +++ b/module_usb_aud_shared/usb_buffer/testct_byref.xc @@ -0,0 +1,15 @@ +#include + +/* 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_byref(chanend c, unsigned &isCt) +{ + if (testct(c)) + { + isCt = 1; + } + else + { + isCt = 0; + } +} diff --git a/module_usb_aud_shared/usb_buffer/ub b/module_usb_aud_shared/usb_buffer/ub new file mode 100644 index 00000000..6377b046 --- /dev/null +++ b/module_usb_aud_shared/usb_buffer/ub @@ -0,0 +1,14 @@ +#if 0 + { + tmr :> sof_time; + if (last_sof_time && ((sof_time - last_sof_time) > 20000)) { + missed_sofcount = sofCount; + missed_count--; + if (!missed_count) { + asm("ecallf %0"::"r"(0)); + } + + } + last_sof_time = sof_time; + } +#endif diff --git a/module_usb_aud_shared/usb_buffer/usb_buffer.h b/module_usb_aud_shared/usb_buffer/usb_buffer.h new file mode 100644 index 00000000..746320c5 --- /dev/null +++ b/module_usb_aud_shared/usb_buffer/usb_buffer.h @@ -0,0 +1,28 @@ +#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_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) + */ +void buffer(chanend c_aud_out, chanend c_aud_in, chanend c_aud_fb, + chanend c_midi_from_host, + chanend c_midi_to_host, + chanend c_int, + chanend c_sof, + chanend c_aud_ctl, + in port p_off_mclk); + + +#endif diff --git a/module_usb_aud_shared/usb_buffer/usb_buffer.xc b/module_usb_aud_shared/usb_buffer/usb_buffer.xc new file mode 100644 index 00000000..16f8d177 --- /dev/null +++ b/module_usb_aud_shared/usb_buffer/usb_buffer.xc @@ -0,0 +1,508 @@ + +#include +#include + +//In this file xud.h is not included since we are interpreting the +//assembly functions GetData/SetData as taking xc_ptrs +//#include "xud.h" + +#define XUD_SPEED_HS 2 + +#include "usb.h" +#include "devicedefines.h" +#include "usb_midi.h" +#include "xc_ptr.h" +#include "clockcmds.h" +#include "xud.h" + + +//typedef unsigned int XUD_ep; + +//int XUD_GetData_NoReq(chanend c, xc_ptr buffer); +//int XUD_SetData_NoReq(chanend c, xc_ptr buffer, unsigned datalength, unsigned startIndex); +XUD_ep XUD_Init_Ep(chanend c_ep); + +inline void XUD_SetNotReady(XUD_ep e) +{ + int chan_array_ptr; + asm ("ldw %0, %1[0]":"=r"(chan_array_ptr):"r"(e)); + asm ("stw %0, %1[0]"::"r"(0),"r"(chan_array_ptr)); +} + +void GetADCCounts(unsigned samFreq, int &min, int &mid, int &max); +#define BUFFER_SIZE_OUT (1028 >> 2) +#define BUFFER_SIZE_IN (1028 >> 2) + +/* Packet buffers for audio data */ + +extern unsigned int g_curSamFreqMultiplier; + + +/* Global var for speed. Related to feedback. Used by input stream to determine IN packet size */ +unsigned g_speed; +unsigned g_freqChange = 0; + +/* Interrupt EP data */ +unsigned char g_intData[8]; + +#ifdef MIDI +static inline void swap(xc_ptr &a, xc_ptr &b) +{ + xc_ptr tmp; + tmp = a; + a = b; + b = tmp; + return; +} +#endif + +unsigned char fb_clocks[16]; + +//#define FB_TOLERANCE_TEST +#define FB_TOLERANCE 0x100 + +extern unsigned inZeroBuff[]; +extern unsigned g_numUsbChanIn; +/** + * 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, chanend c_aud_fb, + chanend c_midi_from_host, + chanend c_midi_to_host, + chanend c_int, chanend c_sof, + chanend c_aud_ctl, + in port p_off_mclk + ) +{ + XUD_ep ep_aud_out = XUD_Init_Ep(c_aud_out); + XUD_ep ep_aud_in = XUD_Init_Ep(c_aud_in); + XUD_ep ep_aud_fb = XUD_Init_Ep(c_aud_fb); +#ifdef MIDI + XUD_ep ep_midi_from_host = XUD_Init_Ep(c_midi_from_host); + XUD_ep ep_midi_to_host = XUD_Init_Ep(c_midi_to_host); +#endif + XUD_ep ep_int = XUD_Init_Ep(c_int); + + unsigned datalength; + unsigned tmp; + unsigned sampleFreq = 0; + unsigned lastClock; + + unsigned clocks = 0; + + + unsigned bufferIn = 1; + unsigned remnant = 0, cycles; + unsigned sofCount = 0; + unsigned freqChange = 0; + //unsigned expected = (DEFAULT_FREQ/8000)<<16; + +#ifdef FB_TOLERANCE_TEST + unsigned expected_fb = 0; +#endif + + xc_ptr aud_from_host_buffer = 0; + +#ifdef MIDI + xc_ptr midi_from_host_buffer = 0; + xc_ptr midi_to_host_buffer = 0; + xc_ptr midi_to_host_waiting_buffer = 0; +#endif + + + set_thread_fast_mode_on(); + + asm("stw %0, dp[int_usb_ep]"::"r"(ep_int)); + 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)); + + /* Wait for USB connect then setup our first packet */ + { + int min, mid, max; + int usb_speed = 0; + int frameTime; + + while(usb_speed == 0) + asm("ldw %0, dp[g_curUsbSpeed]" : "=r" (usb_speed) :); + + GetADCCounts(DEFAULT_FREQ, min, mid, max); + asm("stw %0, dp[g_speed]"::"r"(mid << 16)); + if (usb_speed == XUD_SPEED_HS) + mid*=NUM_USB_CHAN_IN*4; + else + mid*=NUM_USB_CHAN_IN*3; + asm("stw %0, %1[0]"::"r"(mid),"r"(inZeroBuff)); + +#ifdef FB_TOLERANCE_TEST + expected_fb = ((DEFAULT_FREQ * 0x2000) / 1000); +#endif + + } + +#ifdef MIDI + // get the two buffers to use for midi device->host + asm("ldaw %0, dp[g_midi_to_host_buffer_A]":"=r"(midi_to_host_buffer)); + asm("ldaw %0, dp[g_midi_to_host_buffer_B]":"=r"(midi_to_host_waiting_buffer)); + asm("ldaw %0, dp[g_midi_from_host_buffer]":"=r"(midi_from_host_buffer)); + + + // pass the midi->XUD chanends to decouple so that thread can + // initialize comm with XUD + asm("stw %0, dp[midi_to_host_usb_ep]"::"r"(ep_midi_to_host)); + asm("stw %0, dp[midi_from_host_usb_ep]"::"r"(ep_midi_from_host)); + swap(midi_to_host_buffer, midi_to_host_waiting_buffer); + SET_SHARED_GLOBAL(g_midi_from_host_flag, 1); +#endif + +#ifdef OUTPUT + SET_SHARED_GLOBAL(g_aud_from_host_flag, 1); +#endif + +#ifdef INPUT + SET_SHARED_GLOBAL(g_aud_to_host_flag, 1); +#endif + + (fb_clocks, unsigned[])[0] = 0; + + + { + int usb_speed; + int x; + + asm("ldaw %0, dp[fb_clocks]":"=r"(x)); + asm("ldw %0, dp[g_curUsbSpeed]" : "=r" (usb_speed) :); + + if (usb_speed == XUD_SPEED_HS) + { + + XUD_SetReady_In(ep_aud_fb, PIDn_DATA0, x, 4); + + } + else + { + + XUD_SetReady_In(ep_aud_fb, PIDn_DATA0, x, 3); + } + } + + while(1) + { + /* Wait for response from XUD and service relevant EP */ + select + { + /* Interrupt EP, send back interrupt data. Note, request made from decouple */ + case inuint_byref(c_int, tmp): + { + int sent_ok = 0; + /* Start XUD_SetData */ + + XUD_SetData_Inline(ep_int, c_int); + +#if 0 + while (!sent_ok) + { + outct(c_int, 64); + asm("ldw %0, dp[g_intData]":"=r"(tmp)); + outuint(c_int, tmp); + asm("ldw %0, dp[g_intData+4]":"=r"(tmp)); + outct(c_int, 64); + outuint(c_int, tmp); + sent_ok = inuint(c_int); + /* End XUD_SetData */ + } +#endif + + asm("stw %0, dp[g_intFlag]" :: "r" (0) ); + XUD_SetNotReady(ep_int); + break; + } + + /* Sample Freq our chan count update from ep 0 */ + case inuint_byref(c_aud_ctl, tmp): + { + int min, mid, max; + int usb_speed; + int frameTime; + asm("ldw %0, dp[g_curUsbSpeed]" : "=r" (usb_speed) :); + + if(tmp == SET_SAMPLE_FREQ) + { + sampleFreq = inuint(c_aud_ctl); + + /* Tidy up double buffer, note we can do better than this for 44.1 etc but better + * than sending two packets at old speed! */ + if (usb_speed == XUD_SPEED_HS) + frameTime = 8000; + else + frameTime = 1000; + + min = sampleFreq / frameTime; + + max = min + 1; + + mid = min; + + /* Check for INT(SampFreq/8000) == SampFreq/8000 */ + if((sampleFreq % frameTime) == 0) + { + min -= 1; + } +#ifdef FB_TOLERANCE_TEST + expected_fb = ((sampleFreq * 0x2000) / frametime); +#endif + + asm("stw %0, dp[g_speed]"::"r"(mid << 16)); + + if (usb_speed == XUD_SPEED_HS) + mid *= NUM_USB_CHAN_IN*4; + else + mid *= NUM_USB_CHAN_IN*3; + + asm("stw %0, %1[0]"::"r"(mid),"r"(inZeroBuff)); + + /* Reset FB */ + /* Note, Endpoint 0 will hold off host for a sufficient period to allow out feedback + * to stabilise (i.e. sofCount == 128 to fire) */ + sofCount = 0; + clocks = 0; + remnant = 0; + + /* Ideally we want to wait for handshake (and pass back up) here. But we cannot keep this + * thread locked, it must stay responsive to packets/SOFs. So, set a flag and check for + * handshake elsewhere */ + /* Pass on sample freq change to decouple */ + SET_SHARED_GLOBAL(g_freqChange, SET_SAMPLE_FREQ); + SET_SHARED_GLOBAL(g_freqChange_sampFreq, sampleFreq); + SET_SHARED_GLOBAL(g_freqChange_flag, SET_SAMPLE_FREQ); + } + else + { + sampleFreq = inuint(c_aud_ctl); + SET_SHARED_GLOBAL(g_freqChange, tmp); /* Set command */ + SET_SHARED_GLOBAL(g_freqChange_sampFreq, sampleFreq); /* Set flag */ + SET_SHARED_GLOBAL(g_freqChange_flag, tmp); + } + + + + break; + } + + #define MASK_16_13 (7) // Bits that should not be transmitted as part of feedback. + #define MASK_16_10 (127) //(63) /* For Audio 1.0 we use a mask 1 bit longer than expected to avoid Windows LSB isses */ + + case inuint_byref(c_sof, tmp): + + /* NOTE our feedback will be wrong for a couple of SOF's after a SF change due to + * lastClock being incorrect */ + asm("#sof"); + + /* Get MCLK count */ + asm (" getts %0, res[%1]" : "=r" (tmp) : "r" (p_off_mclk)); + + GET_SHARED_GLOBAL(freqChange, g_freqChange); + if(freqChange == SET_SAMPLE_FREQ) + { + /* Keep getting MCLK counts */ + lastClock = tmp; + } + else + { + unsigned mask = MASK_16_13, usb_speed; + + asm("ldw %0, dp[g_curUsbSpeed]" : "=r" (usb_speed) :); + + if(usb_speed != XUD_SPEED_HS) + mask = MASK_16_10; + + /* Number of MCLKS this SOF, approx 125 * 24 (3000), sample by sample rate */ + asm("ldw %0, dp[g_curSamFreqMultiplier]":"=r"(cycles)); + cycles = ((int)((short)(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 = tmp; + + /* Reset counts based on SOF counting. Expect 16ms (128 HS SOFs/16 FS SOFS) per feedback poll + * We always could 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("stw %0, dp[g_speed]"::"r"(clocks)); // g_speed = clocks + //fb_clocks = clocks; + + asm("ldw %0, dp[g_curUsbSpeed]" : "=r" (usb_speed) :); + + if (usb_speed == XUD_SPEED_HS) + { + (fb_clocks, unsigned[])[0] = clocks; + } + else + { + (fb_clocks, unsigned[])[0] = clocks>>2; + } + } +#ifdef FB_TOLERANCE_TEST + else { + } +#endif + clocks = 0; + } + + sofCount++; + } + break; + +#ifdef OUTPUT + /* Audio HOST -> DEVICE */ + case inuint_byref(c_aud_out, tmp): + + asm("#h->d aud data"); + GET_SHARED_GLOBAL(aud_from_host_buffer, g_aud_from_host_buffer); + + // XUD_GetData + { + xc_ptr p = aud_from_host_buffer+4; + xc_ptr p0 = p; + int tail; + while (!testct(c_aud_out)) + { + unsigned int datum = inuint(c_aud_out); + write_via_xc_ptr(p, datum); + p += 4; + } + tail = inct(c_aud_out); + datalength = p - p0 - 4; + switch (tail) + { + case 10: + // the tail is 0 which means + datalength -= 2; + break; + default: + // the tail is 2 which means the input was word aligned + break; + } + } + + XUD_SetNotReady(ep_aud_out); + write_via_xc_ptr(aud_from_host_buffer, datalength); + /* Sync with audio thread */ + SET_SHARED_GLOBAL(g_aud_from_host_flag, 1); + + break; +#endif + +#ifdef INPUT + /* DEVICE -> HOST */ + case inuint_byref(c_aud_in, tmp): + { + XUD_SetData_Inline(ep_aud_in, c_aud_in); + + XUD_SetNotReady(ep_aud_in); + + /* Inform stream that buffer sent */ + SET_SHARED_GLOBAL(g_aud_to_host_flag, bufferIn+1); + } + break; + +#endif + +#ifdef OUTPUT + /* Feedback Pipe */ + case inuint_byref(c_aud_fb, tmp): + { + + int usb_speed; + int x; + + asm("#aud fb"); + + XUD_SetData_Inline(ep_aud_fb, c_aud_fb); + + asm("ldaw %0, dp[fb_clocks]":"=r"(x)); + asm("ldw %0, dp[g_curUsbSpeed]" : "=r" (usb_speed) :); + + if (usb_speed == XUD_SPEED_HS) + { + XUD_SetReady_In(ep_aud_fb, PIDn_DATA0, x, 4); + } + else + { + XUD_SetReady_In(ep_aud_fb, PIDn_DATA0, x, 3); + } + } + break; +#endif + + +#ifdef MIDI + case inuint_byref(c_midi_from_host, tmp): + asm("#midi h->d"); + + /* Get buffer data from host - MIDI OUT from host always into a single buffer */ + { + xc_ptr p = midi_from_host_buffer + 4; + xc_ptr p0 = p; + xc_ptr p1 = p + MAX_USB_MIDI_PACKET_SIZE; + while (!testct(c_midi_from_host)) + { + unsigned int datum = inuint(c_midi_from_host); + write_via_xc_ptr(p, datum); + p += 4; + } + (void) inct(c_midi_from_host); + datalength = p - p0 - 4; + } + + XUD_SetNotReady(ep_midi_from_host); + + write_via_xc_ptr(midi_from_host_buffer, datalength); + + /* release the buffer */ + SET_SHARED_GLOBAL(g_midi_from_host_flag, 1); + + break; + + /* MIDI IN to host */ + case inuint_byref(c_midi_to_host, tmp): + asm("#midi d->h"); + + // fill in the data + XUD_SetData_Inline(ep_midi_to_host, c_midi_to_host); + + XUD_SetNotReady(ep_midi_to_host); + + // ack the decouple thread to say it has been sent to host + SET_SHARED_GLOBAL(g_midi_to_host_flag, 1); + + swap(midi_to_host_buffer, midi_to_host_waiting_buffer); + + break; +#endif + } + + } + + set_thread_fast_mode_off(); + +} diff --git a/module_usb_aud_shared/usb_buffer/xc_ptr.h b/module_usb_aud_shared/usb_buffer/xc_ptr.h new file mode 100644 index 00000000..e2750668 --- /dev/null +++ b/module_usb_aud_shared/usb_buffer/xc_ptr.h @@ -0,0 +1,23 @@ +#ifndef __xc_ptr__ +#define __xc_ptr__ + +typedef unsigned int xc_ptr; + +inline xc_ptr array_to_xc_ptr(unsigned a[]) { + xc_ptr x; + asm("mov %0, %1":"=r"(x):"r"(a)); + return x; +} + +#define write_via_xc_ptr(p,x) asm("stw %0, %1[0]"::"r"(x),"r"(p)) + +#define write_via_xc_ptr_indexed(p,i,x) asm("stw %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)); + +#define read_via_xc_ptr_indexed(x,p,i) asm("ldw %0, %1[%2]":"=r"(x):"r"(p),"r"(i)); + +#define GET_SHARED_GLOBAL(x, g) asm("ldw %0, dp[" #g "]":"=r"(x)) +#define SET_SHARED_GLOBAL(g, v) asm("stw %0, dp[" #g "]"::"r"(v)) + +#endif diff --git a/module_usb_aud_shared/write_sswitch_reg_blind.S b/module_usb_aud_shared/write_sswitch_reg_blind.S new file mode 100644 index 00000000..35861075 --- /dev/null +++ b/module_usb_aud_shared/write_sswitch_reg_blind.S @@ -0,0 +1,59 @@ + +#include "xs1_kernel.h" +#include "xs1_user.h" + + + +.global write_sswitch_reg_blind, "f{si}(ui,ui,ui)" +.global write_sswitch_reg_blind.nstackwords +.linkset write_sswitch_reg_blind.nstackwords, 0 +//.type read_sswitch_reg, @function +// r0 - coreid +// r1 - reg +// r2 - data +.cc_top write_sswitch_reg_blind.function, write_sswitch_reg_blind +.align 2 +write_sswitch_reg_blind: + // Check range of coreid + shr r3, r0, XS1_CHAN_ID_PROCESSOR_SIZE + XS1_CHAN_ID_NODE_SIZE + bt r3, write_switch_reg_fail + + // Check range of reg + shr r3, r1, 16 + bt r3, write_switch_reg_fail + + // Allocate channel end + getr r3, XS1_RES_TYPE_CHANEND + + // Set destination + ldc r11, XS1_RES_TYPE_CONFIG | (XS1_CT_SSCTRL << XS1_CHAN_ID_CHANNUM_SHIFT) + + // r0 - l + // r1 - reg + // r2 - data + // r3 - chanend + // r11 - low half of dest + write_switch_reg: + shl r0, r0, XS1_CHAN_ID_PROCESSOR_SHIFT + or r0, r0, r11 + setd res[r3], r0 + + // Send packet + ldc r11, XS1_CT_WRITEC // Too big for outct immediate + outct res[r3], r11 + mkmsk r0, 32 + shl r0, r0, 8 + shr r11, r1, 8 + or r0, r0, r11 + out res[r3], r0 // (0xffffff00) | (reg >> 8) + outt res[r3], r1 // reg & 0xff + out res[r3], r2 + outct res[r3], XS1_CT_END + + // Receive response + freer res[r3] + retsp 0 + write_switch_reg_fail: + ldc r0, 0 + retsp 0 + .cc_bottom write_sswitch_reg_blind.function diff --git a/module_usb_aud_shared/xuduser/xuduser.xc b/module_usb_aud_shared/xuduser/xuduser.xc new file mode 100644 index 00000000..8a654439 --- /dev/null +++ b/module_usb_aud_shared/xuduser/xuduser.xc @@ -0,0 +1,23 @@ + +#include "devicedefines.h" + +#ifdef HOST_ACTIVE_CALL +void VendorHostActive(int valid); + +void XUD_UserSuspend(void) +{ + VendorHostActive(0); +} + +void XUD_UserResume(void) +{ + unsigned config; + + asm("ldw %0, dp[g_config]" : "=r" (config):); + + if(config == 1) + { + VendorHostActive(1); + } +} +#endif diff --git a/module_usb_midi/README b/module_usb_midi/README new file mode 100644 index 00000000..483bfb35 --- /dev/null +++ b/module_usb_midi/README @@ -0,0 +1 @@ +module README. diff --git a/module_usb_midi/module_build_info b/module_usb_midi/module_build_info new file mode 100644 index 00000000..cb931043 --- /dev/null +++ b/module_usb_midi/module_build_info @@ -0,0 +1,11 @@ +# 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.. + diff --git a/module_usb_midi/module_description b/module_usb_midi/module_description new file mode 100644 index 00000000..00a6de45 --- /dev/null +++ b/module_usb_midi/module_description @@ -0,0 +1 @@ +One line module description. diff --git a/module_usb_midi/src/midi_descriptor.h b/module_usb_midi/src/midi_descriptor.h new file mode 100644 index 00000000..bcfa83ea --- /dev/null +++ b/module_usb_midi/src/midi_descriptor.h @@ -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 diff --git a/module_usb_midi/src/midiinparse.h b/module_usb_midi/src/midiinparse.h new file mode 100644 index 00000000..811ad8ee --- /dev/null +++ b/module_usb_midi/src/midiinparse.h @@ -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 diff --git a/module_usb_midi/src/midiinparse.xc b/module_usb_midi/src/midiinparse.xc new file mode 100644 index 00000000..ed651e2a --- /dev/null +++ b/module_usb_midi/src/midiinparse.xc @@ -0,0 +1,218 @@ +/** + * @file midiinparse.xc + * @brief Generates USB MIDI events from MIDI events + * @author Russell Gallop, XMOS Semiconductor + * @version 0.1 + */ + +#include +//#include +#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 + * + */ +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}; +} diff --git a/module_usb_midi/src/midioutparse.h b/module_usb_midi/src/midioutparse.h new file mode 100644 index 00000000..908b2e4b --- /dev/null +++ b/module_usb_midi/src/midioutparse.h @@ -0,0 +1,6 @@ +#ifndef MIDIOUTPARSE_XH +#define MIDIOUTPARSE_XH + +{unsigned, unsigned, unsigned, unsigned} midi_out_parse(unsigned event); + +#endif diff --git a/module_usb_midi/src/midioutparse.xc b/module_usb_midi/src/midioutparse.xc new file mode 100644 index 00000000..57c99b0c --- /dev/null +++ b/module_usb_midi/src/midioutparse.xc @@ -0,0 +1,68 @@ +/** + * @file midioutparse.xc + * @brief Parses USB-MIDI events into set of MIDI bytes + * @author Russell Gallop, XMOS Semiconductor + * @version 0.1 + */ + +#include "midioutparse.h" + +/** + * @brief Breaks a USB-MIDI event into it's constituant fields + * + * @param[in] ev USB-MIDI event + */ +{unsigned, unsigned, unsigned, unsigned, unsigned} 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}; +} + +/** + * @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}; +} diff --git a/module_usb_midi/src/usb_midi.h b/module_usb_midi/src/usb_midi.h new file mode 100644 index 00000000..3a141eb4 --- /dev/null +++ b/module_usb_midi/src/usb_midi.h @@ -0,0 +1,63 @@ +#ifndef __usb_midi_h__ +#define __usb_midi_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(in port ?p_midi_in, out port ?p_midi_out, + clock ?clk_midi, + chanend c_midi, + unsigned cable_number); + +#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); + (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); +} + + +#endif // __usb_midi_h__ diff --git a/module_usb_midi/src/usb_midi.xc b/module_usb_midi/src/usb_midi.xc new file mode 100644 index 00000000..1c6292d5 --- /dev/null +++ b/module_usb_midi/src/usb_midi.xc @@ -0,0 +1,322 @@ +#include +#include +#include "usb_midi.h" +#include "midiinparse.h" +#include "midioutparse.h" +#include + +//#define MIDI_LOOPBACK 1 + +static unsigned makeSymbol(unsigned data) { + // Start and stop bits to the data packet + return (data << 1) | 0x200; +} + +#define RATE 31250 + +static unsigned bit_time = XS1_TIMER_MHZ * 1000000 / (unsigned) RATE; +static unsigned bit_time_2 = (XS1_TIMER_MHZ * 1000000 / (unsigned) RATE) / 2; + +int mr_count = 0; +int th_count = 0; + +#ifdef MIDI_LOOPBACK +static inline void handle_byte_from_uart(chanend c_midi, struct midi_in_parse_state &mips, int cable_number, + int &got_next_event, int &next_event, int &waiting_for_ack, int byte) +{ + int valid; + unsigned event; + {valid, event} = midi_in_parse(mips, cable_number, byte); + if (valid && !got_next_event) { + // 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); + next_event = event; + got_next_event = 1; + } + } + else if (valid) { + // printstr("g\n"); + } +} +#endif + +int uout_count = 0; +int uin_count = 0; + +void usb_midi(in port ?p_midi_in, out port ?p_midi_out, + clock ?clk_midi, + chanend c_midi, + unsigned cable_number) +{ + int is_ack; + unsigned int datum; + unsigned symbol = 0x0; + unsigned outputting = 0; + unsigned time; + //unsigned inputPortState, newInputPortState; + int waiting_for_ack = 0; + // Receiver + unsigned rxByte; + int rxI; + int rxT; + int isRX = 0; + timer t; + timer t2; + + // these two vars make a one place buffer for data going out to host + int got_next_event = 0; + int next_event; + unsigned outputting_symbol, outputted_symbol; + + struct midi_in_parse_state mips; + + // the symbol fifo (to go out of uart) + unsigned symbol_fifo[USB_MIDI_DEVICE_OUT_FIFO_SIZE]; + int rdptr = 0; + int wrptr = 0; + unsigned rxPT, txPT; + int midi_from_host_overflow = 0; + int space_left; + + + //configure_clock_rate(clk_midi, 100, 1); + + configure_out_port_no_ready(p_midi_out, clk_midi, 1); + configure_in_port(p_midi_in, clk_midi); + + start_clock(clk_midi); + start_port(p_midi_out); + start_port(p_midi_in); + + reset_midi_state(mips); + + t :> time; + t2 :> rxT; + +#ifndef MIDI_LOOPBACK + p_midi_out <: 1; // Start with high bit. + // printstr("mout0"); +#endif + + + + + while (1) { + select + { + // Input to read the start bit +#ifndef MIDI_LOOPBACK +#ifdef MIDI_IN_4BIT_PORT + case !isRX => p_midi_in when pinseq(0xE) :> void @ rxPT: +#else + case !isRX => p_midi_in when pinseq(0) :> void @ rxPT: +#endif + 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 isRX => t2 when timerafter(rxT) :> int _ : + if (rxI++ < 8) + { + unsigned bit; + p_midi_in :> bit; + rxByte = (bit << 31) | (rxByte >> 1); + rxT += bit_time; + rxPT += bit_time; + asm("setpt res[%0],%1"::"r"(p_midi_in),"r"(rxPT)); + } + else + { + unsigned bit; + // rcv and check stop bit + p_midi_in :> bit; + if ((bit & 0x1) == 1) + { + unsigned valid = 0; + unsigned event = 0; + uin_count++; + rxByte >>= 24; + // if (rxByte != outputted_symbol) { + // printhexln(rxByte); + // printhexln(outputted_symbol); + // } + + {valid, event} = midi_in_parse(mips, cable_number, rxByte); + if (valid && !got_next_event) { + 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 { + next_event = event; + got_next_event = 1; + } + } + else if (valid) { + // printstr("g"); + } + + } + isRX = 0; + } + break; + + // Output + // If outputting 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 outputting => t when timerafter(time) :> int _: + if (symbol == 0) + { + uout_count++; + outputted_symbol = outputting_symbol; + // have we got another symbol to send to uart? + if (rdptr != wrptr) { + outputting_symbol = symbol_fifo[rdptr]; + symbol = makeSymbol(symbol_fifo[rdptr]); + rdptr++; + if (rdptr > USB_MIDI_DEVICE_OUT_FIFO_SIZE - 1) + rdptr = 0; + + space_left = rdptr - wrptr; + if (space_left < 0) + space_left += USB_MIDI_DEVICE_OUT_FIFO_SIZE; + + if (space_left > 3 && midi_from_host_overflow) { + midi_from_host_overflow = 0; + midi_send_ack(c_midi); + } + + p_midi_out <: 1 @ txPT; + // printstr("mout1\n"); + t :> time; + time += bit_time; + txPT += bit_time; + } + else + outputting = 0; + } + else + { + time += bit_time; + txPT += bit_time; + p_midi_out @ txPT <: (symbol & 1); + // printstr("mout2\n"); + symbol >>= 1; + } + break; +#endif + + case midi_get_ack_or_data(c_midi, is_ack, datum): + if (is_ack) { + // have we got more data to send + //printstr("ack\n"); + if (got_next_event) { + //printstr("uart->decouple\n"); + outuint(c_midi, next_event); + th_count++; + got_next_event = 0; + } + else { + waiting_for_ack = 0; + } + } + else { + int event; + unsigned midi[3]; + unsigned size; + int valid; + // received data from host + event = byterev(datum); + mr_count++; +#ifdef MIDI_LOOPBACK + if (!got_next_event) { + // 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); + next_event = event; + got_next_event = 1; + } + } +#else + {midi[0], midi[1], midi[2], size} = midi_out_parse(event); + for (int i = 0; i != size; i++) { + // add symbol to fifo + unsigned sym = midi[i]; + int new_wrptr = wrptr + 1; + + if (new_wrptr > USB_MIDI_DEVICE_OUT_FIFO_SIZE - 1) { + new_wrptr = 0; + } + + symbol_fifo[wrptr] = sym; + wrptr = new_wrptr; + } + + + space_left = rdptr - wrptr; + if (space_left < 0) + space_left += USB_MIDI_DEVICE_OUT_FIFO_SIZE; + + if (space_left > 3) { + midi_send_ack(c_midi); + } + else { + midi_from_host_overflow = 1; + } + + if (wrptr != rdptr && !outputting) { + outputting_symbol = symbol_fifo[rdptr]; + symbol = makeSymbol(symbol_fifo[rdptr]); + rdptr++; + if (rdptr > USB_MIDI_DEVICE_OUT_FIFO_SIZE - 1) + rdptr = 0; + + if (space_left > 2 && midi_from_host_overflow) { + midi_from_host_overflow = 0; + midi_send_ack(c_midi); + } + +#ifdef MIDI_LOOPBACK + handle_byte_from_uart(c_midi, mips, cable_number, got_next_event, next_event, waiting_for_ack, symbol); +#else + p_midi_out <: 1 @ txPT; + t :> time; + time += bit_time; + txPT += bit_time; + outputting = 1; +#endif + + } +#endif + } + break; + } + } +} + diff --git a/module_usb_midi/src/usb_midi_support.xc b/module_usb_midi/src/usb_midi_support.xc new file mode 100644 index 00000000..5fad315c --- /dev/null +++ b/module_usb_midi/src/usb_midi_support.xc @@ -0,0 +1,3 @@ +#include +#define __MIDI_IMPL +#include "usb_midi.h" diff --git a/xpd.xml b/xpd.xml new file mode 100644 index 00000000..0bc106f1 --- /dev/null +++ b/xpd.xml @@ -0,0 +1,9 @@ + + + USB Audio Shared Components. For use in the XMOS USB Audio Refererence Designs. + ssh://git@github.com:22/xcore/sc_usb_audio.git + USB Audio Shared Components + XMOS + 1.0 + False + \ No newline at end of file