diff --git a/apps/auracast_usb/include/tusb_config.h b/apps/auracast_usb/include/tusb_config.h new file mode 100644 index 0000000000..16cae2db43 --- /dev/null +++ b/apps/auracast_usb/include/tusb_config.h @@ -0,0 +1,445 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#include "syscfg/syscfg.h" + +#ifdef __cplusplus +extern "C" { +#endif +/** + * COMMON CONFIGURATION + */ + +#include + +/* defined by compiler flags for flexibility */ +#ifndef CFG_TUSB_MCU +#error CFG_TUSB_MCU must be defined +#endif + +#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE + +#define CFG_TUSB_OS OPT_OS_MYNEWT +#define CFG_TUSB_DEBUG 1 + +#define CFG_TUD_EP_MAX 9 + +#ifndef CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN +#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) +#endif + +/** + * DEVICE CONFIGURATION + */ +#define CFG_TUD_ENDPOINT0_SIZE MYNEWT_VAL(USBD_EP0_SIZE) + +/* ------------- CLASS ------------- */ +#define CFG_TUD_CDC MYNEWT_VAL(USBD_CDC) +#define CFG_TUD_HID MYNEWT_VAL(USBD_HID) +#define CFG_TUD_MSC 0 +#define CFG_TUD_MIDI 0 +#define CFG_TUD_VENDOR 0 +#define CFG_TUD_USBTMC 0 +#define CFG_TUD_DFU_RT 0 +#define CFG_TUD_ECM_RNDIS 0 +#define CFG_TUD_BTH MYNEWT_VAL(USBD_BTH) +#define CFG_TUD_AUDIO_IN MYNEWT_VAL(USBD_AUDIO_IN) +#define CFG_TUD_AUDIO_OUT MYNEWT_VAL(USBD_AUDIO_OUT) +#define CFG_TUD_AUDIO_IN_OUT MYNEWT_VAL(USBD_AUDIO_IN_OUT) +#define CFG_TUD_AUDIO (MYNEWT_VAL(USBD_AUDIO_IN) || MYNEWT_VAL(USBD_AUDIO_OUT) || \ + MYNEWT_VAL(USBD_AUDIO_IN_OUT)) + +/* Audio format type */ +#define CFG_TUD_AUDIO_FORMAT_TYPE_TX AUDIO_FORMAT_TYPE_I +#define CFG_TUD_AUDIO_FORMAT_TYPE_RX AUDIO_FORMAT_TYPE_I + +/* Audio format type I specifications */ +#define CFG_TUD_AUDIO_FORMAT_TYPE_I_TX AUDIO_DATA_FORMAT_TYPE_I_PCM +#define CFG_TUD_AUDIO_FORMAT_TYPE_I_RX AUDIO_DATA_FORMAT_TYPE_I_PCM +#define CFG_TUD_AUDIO_N_CHANNELS_TX 2 +#define CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX 2 +#define CFG_TUD_AUDIO_N_CHANNELS_RX MYNEWT_VAL(USB_AUDIO_OUT_CHANNELS) +#define CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX 2 +#define CFG_TUD_AUDIO_RX_ITEMSIZE 2 +#define CFG_TUD_AUDIO_SAMPLE_RATE MYNEWT_VAL(USB_AUDIO_OUT_SAMPLE_RATE) +#define SAMPLES_PER_PACKET ((((CFG_TUD_AUDIO_SAMPLE_RATE) -1) / 1000) + 1) + +/* EP and buffer size - for isochronous EP´s, the buffer and EP size are equal (different sizes would not make sense) */ +#define CFG_TUD_AUDIO_EPSIZE_IN (CFG_TUD_AUDIO_IN * SAMPLES_PER_PACKET * \ + (CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX) *(CFG_TUD_AUDIO_N_CHANNELS_TX)) /* 48 Samples (48 kHz) x 2 Bytes/Sample x n Channels */ +#define CFG_TUD_AUDIO_TX_FIFO_COUNT (CFG_TUD_AUDIO_IN * 1) +#define CFG_TUD_AUDIO_TX_FIFO_SIZE (CFG_TUD_AUDIO_IN ? ((CFG_TUD_AUDIO_EPSIZE_IN)) : 0) + +/* EP and buffer size - for isochronous EP´s, the buffer and EP size are equal (different sizes would not make sense) */ +#define CFG_TUD_AUDIO_ENABLE_EP_OUT 1 +#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP 0 +#define CFG_TUD_AUDIO_EPSIZE_OUT (CFG_TUD_AUDIO_OUT * \ + ((SAMPLES_PER_PACKET + 1) * \ + (CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX) *(CFG_TUD_AUDIO_N_CHANNELS_RX))) /* N Samples (N kHz) x 2 Bytes/Sample x n Channels */ +#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX CFG_TUD_AUDIO_EPSIZE_OUT +#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ (CFG_TUD_AUDIO_EPSIZE_OUT * 40) +#define CFG_TUD_AUDIO_RX_FIFO_COUNT (CFG_TUD_AUDIO_OUT * 1) +#define CFG_TUD_AUDIO_RX_FIFO_SIZE (CFG_TUD_AUDIO_OUT ? (3 * \ + (CFG_TUD_AUDIO_EPSIZE_OUT / \ + CFG_TUD_AUDIO_RX_FIFO_COUNT)) : 0) + +/* Number of Standard AS Interface Descriptors (4.9.1) defined per audio function - this is required to be able to remember the current alternate settings of these interfaces - We restrict us here to have a constant number for all audio functions (which means this has to be the maximum number of AS interfaces an audio function has and a second audio function with less AS interfaces just wastes a few bytes) */ +#define CFG_TUD_AUDIO_N_AS_INT 1 + +/* Size of control request buffer */ +#define CFG_TUD_AUDIO_CTRL_BUF_SIZE 64 + +/* Minimal number for alternative interfaces that is recognized by Windows as Bluetooth radio controller */ +#define CFG_TUD_BTH_ISO_ALT_COUNT 2 + +/* CDC FIFO size of TX and RX */ +#define CFG_TUD_CDC_RX_BUFSIZE 64 +#define CFG_TUD_CDC_TX_BUFSIZE 64 + +/* HID buffer size Should be sufficient to hold ID (if any) + Data */ +#define CFG_TUD_HID_BUFSIZE 16 + +#define TUD_AUDIO_SPEAKER_MONO_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN \ + + TUD_AUDIO_DESC_STD_AC_LEN \ + + TUD_AUDIO_DESC_CS_AC_LEN \ + + TUD_AUDIO_DESC_CLK_SRC_LEN \ + + TUD_AUDIO_DESC_INPUT_TERM_LEN \ + + TUD_AUDIO_DESC_OUTPUT_TERM_LEN \ + + TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN \ + + TUD_AUDIO_DESC_STD_AS_INT_LEN \ + + TUD_AUDIO_DESC_STD_AS_INT_LEN \ + + TUD_AUDIO_DESC_CS_AS_INT_LEN \ + + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN \ + + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN \ + + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN) + +#define TUD_AUDIO_SPEAKER_MONO_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epout, _epsize) \ + /* Standard Interface Association Descriptor (IAD) */ \ + TUD_AUDIO_DESC_IAD(/*_firstitfs*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00), \ + /* Standard AC Interface Descriptor(4.7.1) */ \ + TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx), \ + /* Class-Specific AC Interface Header Descriptor(4.7.2) */ \ + TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_DESKTOP_SPEAKER, \ + /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+ \ + TUD_AUDIO_DESC_OUTPUT_TERM_LEN+TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN, \ + /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS), \ + /* Clock Source Descriptor(4.7.2.1) */ \ + TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ 0x04, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, \ + /*_ctrl*/ (AUDIO_CTRL_R << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ 0x00, \ + /*_stridx*/ 0x00), \ + /* Input Terminal Descriptor(4.7.2.4) */ \ + TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ 0x01, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, \ + /*_clkid*/ 0x04, /*_nchannelslogical*/ 0x01, \ + /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, \ + /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00), \ + /* Output Terminal Descriptor(4.7.2.5) */ \ + TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ 0x03, /*_termtype*/ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER, \ + /*_assocTerm*/ 0x00, /*_srcid*/ 0x02, /*_clkid*/ 0x04, /*_ctrl*/ 0x0000, \ + /*_stridx*/ 0x00), \ + /* Feature Unit Descriptor(4.7.2.8) */ \ + TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL(/*_unitid*/ 0x02, /*_srcid*/ 0x01, \ + /*_ctrlch0master*/ 0 * (AUDIO_CTRL_RW << \ + AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | \ + AUDIO_CTRL_RW << \ + AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS), \ + /*_ctrlch1*/ 0 * (AUDIO_CTRL_RW << \ + AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << \ + AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS), \ + /*_stridx*/ 0x00), \ + /* Standard AS Interface Descriptor(4.9.1) */ \ + /* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */ \ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, \ + /*_stridx*/ 0x00), \ + /* Standard AS Interface Descriptor(4.9.1) */ \ + /* Interface 1, Alternate 1 - alternate interface for data streaming */ \ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, \ + /*_stridx*/ 0x00), \ + /* Class-Specific AS Interface Descriptor(4.9.2) */ \ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ 0x01, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, \ + /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x01, \ + /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), \ + /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ \ + TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \ + /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \ + TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, \ + /*_attr*/ (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_SYNCHRONOUS | \ + TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, \ + /*_interval*/ (CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED) ? 0x04 : 0x01), \ + /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */ \ + TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, \ + /*_ctrl*/ AUDIO_CTRL_NONE, \ + /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, \ + /*_lockdelay*/ 0x0000) \ + +/* XXXXX + * 3 iso endpoint alternative for 16,32,48 kHz does not work on windows, works on Linux, on MAC reported freq is 48kHz and mac sends data for 16kHz + * MAC does not change clock selection like Linux does + */ + +#define TUD_AUDIO_SPEAKER3_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epout, _epsize1, \ + _epsize2, _epsize3) \ + /* Standard Interface Association Descriptor (IAD) */ \ + TUD_AUDIO_DESC_IAD(/*_firstitfs*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00), \ + /* Standard AC Interface Descriptor(4.7.1) */ \ + TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx), \ + /* Class-Specific AC Interface Header Descriptor(4.7.2) */ \ + TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_DESKTOP_SPEAKER, \ + /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+ \ + TUD_AUDIO_DESC_OUTPUT_TERM_LEN+0*TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN, \ + /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS), \ + /* Clock Source Descriptor(4.7.2.1) */ \ + TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ 0x04, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, \ + /*_ctrl*/ (AUDIO_CTRL_RW << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ 0x00, \ + /*_stridx*/ 0x00), \ + /* Input Terminal Descriptor(4.7.2.4) */ \ + TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ 0x01, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, \ + /*_clkid*/ 0x04, /*_nchannelslogical*/ 0x02, \ + /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_FRONT_LEFT | AUDIO_CHANNEL_CONFIG_FRONT_RIGHT, \ + /*_idxchannelnames*/ 0x00, \ + /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00), \ + /* Output Terminal Descriptor(4.7.2.5) */ \ + TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ 0x03, /*_termtype*/ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER, \ + /*_assocTerm*/ 0x00, /*_srcid*/ 0x01, /*_clkid*/ 0x04, /*_ctrl*/ 0x0000, \ + /*_stridx*/ 0x00), \ + /* Standard AS Interface Descriptor(4.9.1) */ \ + /* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */ \ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, \ + /*_stridx*/ 0x00), \ + /* Standard AS Interface Descriptor(4.9.1) */ \ + \ + /* Interface 1, Alternate 1 - alternate interface for data streaming */ \ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, \ + /*_stridx*/ 0x00), \ + /* Class-Specific AS Interface Descriptor(4.9.2) */ \ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ 0x01, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, \ + /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x02, \ + /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_FRONT_LEFT | AUDIO_CHANNEL_CONFIG_FRONT_RIGHT, \ + /*_stridx*/ 0x00), \ + /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ \ + TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \ + /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \ + TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, \ + /*_attr*/ (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_SYNCHRONOUS | \ + TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize1, \ + /*_interval*/ (CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED) ? 0x04 : 0x01), \ + /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */ \ + TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, \ + /*_ctrl*/ AUDIO_CTRL_NONE, \ + /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, \ + /*_lockdelay*/ 0x0000), \ + \ + /* Interface 1, Alternate 2 - alternate interface for data streaming */ \ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x02, /*_nEPs*/ 0x01, \ + /*_stridx*/ 0x00), \ + /* Class-Specific AS Interface Descriptor(4.9.2) */ \ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ 0x01, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, \ + /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x02, \ + /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_FRONT_LEFT | AUDIO_CHANNEL_CONFIG_FRONT_RIGHT, \ + /*_stridx*/ 0x00), \ + /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ \ + TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \ + /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \ + TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, \ + /*_attr*/ (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_SYNCHRONOUS | \ + TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize2, \ + /*_interval*/ (CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED) ? 0x04 : 0x01), \ + /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */ \ + TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, \ + /*_ctrl*/ AUDIO_CTRL_NONE, \ + /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, \ + /*_lockdelay*/ 0x0000), \ + \ + /* Interface 1, Alternate 1 - alternate interface for data streaming */ \ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x03, /*_nEPs*/ 0x01, \ + /*_stridx*/ 0x00), \ + /* Class-Specific AS Interface Descriptor(4.9.2) */ \ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ 0x01, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, \ + /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x02, \ + /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_FRONT_LEFT | AUDIO_CHANNEL_CONFIG_FRONT_RIGHT, \ + /*_stridx*/ 0x00), \ + /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ \ + TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \ + /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \ + TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, \ + /*_attr*/ (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_SYNCHRONOUS | \ + TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize3, \ + /*_interval*/ (CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED) ? 0x04 : 0x01), \ + /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */ \ + TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, \ + /*_ctrl*/ AUDIO_CTRL_NONE, \ + /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, \ + /*_lockdelay*/ 0x0000) \ + +#define TUD_AUDIO_SPK3_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN \ + + TUD_AUDIO_DESC_STD_AC_LEN \ + + TUD_AUDIO_DESC_CS_AC_LEN \ + + TUD_AUDIO_DESC_CLK_SRC_LEN \ + + TUD_AUDIO_DESC_INPUT_TERM_LEN \ + + TUD_AUDIO_DESC_OUTPUT_TERM_LEN \ + + 0 * TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN \ + + TUD_AUDIO_DESC_STD_AS_INT_LEN \ + + (TUD_AUDIO_DESC_STD_AS_INT_LEN \ + + TUD_AUDIO_DESC_CS_AS_INT_LEN \ + + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN \ + + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN \ + + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN) * 3) + + +#define TUD_AUDIO_SPEAKER_DESCRIPTOR_FLOAT(_itfnum, _stridx, _epout, _epsize) \ + /* Standard Interface Association Descriptor (IAD) */ \ + TUD_AUDIO_DESC_IAD(/*_firstitfs*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00), \ + /* Standard AC Interface Descriptor(4.7.1) */ \ + TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx), \ + /* Class-Specific AC Interface Header Descriptor(4.7.2) */ \ + TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_DESKTOP_SPEAKER, \ + /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+ \ + TUD_AUDIO_DESC_OUTPUT_TERM_LEN+0*TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN, \ + /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS), \ + /* Clock Source Descriptor(4.7.2.1) */ \ + TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ 0x04, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, \ + /*_ctrl*/ (AUDIO_CTRL_RW << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ 0x00, \ + /*_stridx*/ 0x00), \ + /* Input Terminal Descriptor(4.7.2.4) */ \ + TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ 0x01, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, \ + /*_clkid*/ 0x04, /*_nchannelslogical*/ 0x02, \ + /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_FRONT_LEFT | AUDIO_CHANNEL_CONFIG_FRONT_RIGHT, \ + /*_idxchannelnames*/ 0x00, \ + /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00), \ + /* Output Terminal Descriptor(4.7.2.5) */ \ + TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ 0x03, /*_termtype*/ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER, \ + /*_assocTerm*/ 0x00, /*_srcid*/ 0x01, /*_clkid*/ 0x04, /*_ctrl*/ 0x0000, \ + /*_stridx*/ 0x00), \ + /* Standard AS Interface Descriptor(4.9.1) */ \ + /* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */ \ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, \ + /*_stridx*/ 0x00), \ + /* Standard AS Interface Descriptor(4.9.1) */ \ + /* Interface 1, Alternate 1 - alternate interface for data streaming */ \ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, \ + /*_stridx*/ 0x00), \ + /* Class-Specific AS Interface Descriptor(4.9.2) */ \ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ 0x01, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, \ + /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_IEEE_FLOAT, /*_nchannelsphysical*/ 0x02, \ + /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_FRONT_LEFT | AUDIO_CHANNEL_CONFIG_FRONT_RIGHT, \ + /*_stridx*/ 0x00), \ + /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ \ + TUD_AUDIO_DESC_TYPE_I_FORMAT(4, 32), \ + /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \ + TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, \ + /*_attr*/ (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_SYNCHRONOUS | \ + TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, \ + /*_interval*/ (CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED) ? 0x04 : 0x01), \ + /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */ \ + TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, \ + /*_ctrl*/ AUDIO_CTRL_NONE, \ + /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, \ + /*_lockdelay*/ 0x0000) \ + +#define TUD_AUDIO_SPEAKER_STEREO_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN \ + + TUD_AUDIO_DESC_STD_AC_LEN \ + + TUD_AUDIO_DESC_CS_AC_LEN \ + + TUD_AUDIO_DESC_CLK_SRC_LEN \ + + TUD_AUDIO_DESC_INPUT_TERM_LEN \ + + TUD_AUDIO_DESC_OUTPUT_TERM_LEN \ + + 0 * TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN \ + + TUD_AUDIO_DESC_STD_AS_INT_LEN \ + + (TUD_AUDIO_DESC_STD_AS_INT_LEN \ + + TUD_AUDIO_DESC_CS_AS_INT_LEN \ + + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN \ + + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN \ + + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN) * 1) + +#define TUD_AUDIO_SPEAKER_STEREO_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epout, _epsize) \ + /* Standard Interface Association Descriptor (IAD) */ \ + TUD_AUDIO_DESC_IAD(/*_firstitfs*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00), \ + /* Standard AC Interface Descriptor(4.7.1) */ \ + TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx), \ + /* Class-Specific AC Interface Header Descriptor(4.7.2) */ \ + TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_DESKTOP_SPEAKER, \ + /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+ \ + TUD_AUDIO_DESC_OUTPUT_TERM_LEN+0*TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN, \ + /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS), \ + /* Clock Source Descriptor(4.7.2.1) */ \ + TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ 0x04, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, \ + /*_ctrl*/ (AUDIO_CTRL_RW << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ 0x00, \ + /*_stridx*/ 0x00), \ + /* Input Terminal Descriptor(4.7.2.4) */ \ + TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ 0x01, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, \ + /*_clkid*/ 0x04, /*_nchannelslogical*/ 0x02, \ + /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_FRONT_LEFT | AUDIO_CHANNEL_CONFIG_FRONT_RIGHT, \ + /*_idxchannelnames*/ 0x00, \ + /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00), \ + /* Output Terminal Descriptor(4.7.2.5) */ \ + TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ 0x03, /*_termtype*/ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER, \ + /*_assocTerm*/ 0x00, /*_srcid*/ 0x01, /*_clkid*/ 0x04, /*_ctrl*/ 0x0000, \ + /*_stridx*/ 0x00), \ + /* Standard AS Interface Descriptor(4.9.1) */ \ + /* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */ \ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, \ + /*_stridx*/ 0x00), \ + /* Standard AS Interface Descriptor(4.9.1) */ \ + /* Interface 1, Alternate 1 - alternate interface for data streaming */ \ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, \ + /*_stridx*/ 0x00), \ + /* Class-Specific AS Interface Descriptor(4.9.2) */ \ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ 0x01, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, \ + /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x02, \ + /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_FRONT_LEFT | AUDIO_CHANNEL_CONFIG_FRONT_RIGHT, \ + /*_stridx*/ 0x00), \ + /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ \ + TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \ + /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \ + TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, \ + /*_attr*/ (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_SYNCHRONOUS | \ + TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, \ + /*_interval*/ (CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED) ? 0x04 : 0x01), \ + /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */ \ + TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, \ + /*_ctrl*/ AUDIO_CTRL_NONE, \ + /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, \ + /*_lockdelay*/ 0x0000) \ + +#define CFG_TUD_AUDIO_FUNC_1_DESC_LEN ( \ + (1 - CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP) * CFG_TUD_AUDIO_OUT * \ + (TUD_AUDIO_SPEAKER_MONO_DESC_LEN * (CFG_TUD_AUDIO_N_CHANNELS_RX == 1 ? 1 : 0)) + \ + (0 + CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP) * CFG_TUD_AUDIO_OUT * \ + (TUD_AUDIO_SPEAKER_MONO_FB_DESC_LEN * (CFG_TUD_AUDIO_N_CHANNELS_RX == 1 ? 1 : 0)) + \ + (1 - CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP) * CFG_TUD_AUDIO_OUT * \ + (TUD_AUDIO_SPEAKER_STEREO_DESC_LEN * (CFG_TUD_AUDIO_N_CHANNELS_RX == 2 ? 1 : 0))) +#define CFG_TUD_AUDIO_FUNC_1_N_AS_INT 1 +#define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64 + +#ifdef __cplusplus +} +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/apps/auracast_usb/include/usb_audio.h b/apps/auracast_usb/include/usb_audio.h new file mode 100644 index 0000000000..f1a77b3695 --- /dev/null +++ b/apps/auracast_usb/include/usb_audio.h @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_USB_AUDIO_ +#define H_USB_AUDIO_ + +#include + +typedef void (* usb_audio_sample_rate_cb_t)(uint32_t); + +/* Set default sample rate, should only be used before USB is initialized */ +void usb_desc_sample_rate_set(uint32_t sample_rate); + +/* Set callback to receive sample rate set by USB host */ +void usb_audio_sample_rate_cb_set(usb_audio_sample_rate_cb_t cb); + +/* Get current sample rate */ +uint32_t usb_audio_sample_rate_get(void); + +#endif /* H_USB_AUDIO_ */ diff --git a/apps/auracast_usb/pkg.yml b/apps/auracast_usb/pkg.yml new file mode 100644 index 0000000000..ccfc0b3df1 --- /dev/null +++ b/apps/auracast_usb/pkg.yml @@ -0,0 +1,45 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +pkg.name: apps/auracast_usb +pkg.type: app +pkg.description: Auracast sample application. + +pkg.author: "Krzysztof Kopyściński" +pkg.email: "krzysztof.kopyscinski@codecoup.pl" +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + +pkg.deps: + - "@apache-mynewt-core/sys/config" + - nimble/host + - nimble/host/util + - nimble/host/services/gap + - nimble/host/store/config + - "@apache-mynewt-core/kernel/os" + - "@apache-mynewt-core/sys/console" + - "@apache-mynewt-core/sys/log" + - "@apache-mynewt-core/sys/stats" + - "@apache-mynewt-core/sys/sysinit" + - "@apache-mynewt-core/sys/id" + - "@apache-mynewt-core/hw/usb/tinyusb" + - "@apache-mynewt-nimble/nimble/host/services/auracast" + - "@apache-mynewt-nimble/ext/liblc3" + +pkg.init: + audio_usb_init: 402 diff --git a/apps/auracast_usb/src/app_priv.h b/apps/auracast_usb/src/app_priv.h new file mode 100644 index 0000000000..d15b50e02e --- /dev/null +++ b/apps/auracast_usb/src/app_priv.h @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef H_APP_PRIV_ +#define H_APP_PRIV_ + +#include + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#define AUDIO_CHANNELS MYNEWT_VAL(AURACAST_CHAN_NUM) +#define AUDIO_SAMPLE_SIZE sizeof(int16_t) + +#define LC3_FRAME_DURATION (MYNEWT_VAL(LC3_FRAME_DURATION)) +#define LC3_SAMPLING_FREQ (MYNEWT_VAL(LC3_SAMPLING_FREQ)) +#define LC3_BITRATE (MYNEWT_VAL(LC3_BITRATE)) +#define LC3_FPDT (LC3_SAMPLING_FREQ * LC3_FRAME_DURATION / 1000000) +#define BIG_NUM_BIS (MIN(AUDIO_CHANNELS, MYNEWT_VAL(BIG_NUM_BIS))) + +struct chan { + void *encoder; + uint16_t handle; +}; + +extern struct chan chans[AUDIO_CHANNELS]; +#endif /* H_APP_PRIV_ */ diff --git a/apps/auracast_usb/src/audio_usb.c b/apps/auracast_usb/src/audio_usb.c new file mode 100644 index 0000000000..1dc6b22bdf --- /dev/null +++ b/apps/auracast_usb/src/audio_usb.c @@ -0,0 +1,236 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include "host/ble_gap.h" + +#include "app_priv.h" + +#if 1 +#define TP_PIN_ENCODE LED_1 +#define TP_PIN_DATA_CB LED_2 +#define TP_PIN_DATA_IN LED_3 +#define TP_PIN_DATA_ISR 0 +#else +#define TP_PIN_ENCODE 0 +#define TP_PIN_DATA_CB 0 +#define TP_PIN_DATA_IN 0 +#define TP_PIN_DATA_ISR 0 +#endif + +#define TP_INIT(_pin) (TP_PIN_ ## _pin ? \ + hal_gpio_init_out(TP_PIN_ ## _pin, 0) : (void)0) +#define TP_1(_pin) (TP_PIN_ ## _pin ? \ + hal_gpio_write(TP_PIN_ ## _pin, 1) : (void)0) +#define TP_0(_pin) (TP_PIN_ ## _pin ? \ + hal_gpio_write(TP_PIN_ ## _pin, 0) : (void)0) + +static uint8_t g_usb_enabled; + +static void usb_data_func(struct os_event *ev); + +struct chan chans[AUDIO_CHANNELS]; + +static struct os_event usb_data_ev = { + .ev_cb = usb_data_func, +}; + +static uint32_t frame_bytes_lc3; +static uint16_t big_sdu; + +static int16_t samples[96000 / 100 * 2 * 2]; +static uint8_t samples_out[96000 / 100 * 2]; +static int samples_idx = 0; + +static uint32_t pkt_counter = 0; + +static void +usb_data_func(struct os_event *ev) +{ + int ch_idx; + unsigned int num_bytes; + unsigned int num_samples; + unsigned int num_frames; + unsigned int frames_count; + int read; + int skip; + + if (!g_usb_enabled) { + tud_audio_clear_ep_out_ff(); + return; + } + + TP_1(DATA_CB); + + while ((num_bytes = tud_audio_available()) > 0) { + num_samples = num_bytes / AUDIO_SAMPLE_SIZE; + num_frames = num_samples / MYNEWT_VAL(AURACAST_CHAN_NUM); + num_bytes = num_frames * AUDIO_SAMPLE_SIZE * MYNEWT_VAL(AURACAST_CHAN_NUM); + + assert(samples_idx + num_samples < ARRAY_SIZE(samples)); + + TP_1(DATA_IN); + read = tud_audio_read(&samples[samples_idx], num_bytes); + TP_0(DATA_IN); + + assert(read == num_bytes); + assert(samples[ARRAY_SIZE(samples) - 1] = 0xaaaa); + + samples_idx += num_samples; + assert((samples_idx & 0x80000000) == 0); + frames_count = samples_idx / MYNEWT_VAL(AURACAST_CHAN_NUM); + + if (frames_count >= LC3_FPDT) { + pkt_counter++; + skip = 0; + + for (ch_idx = 0; ch_idx < MYNEWT_VAL(AURACAST_CHAN_NUM); ch_idx++) { + if (chans[ch_idx].handle == 0) { + skip = 1; + continue; + } + } + + if (!skip) { + memset(samples_out, 0, sizeof(samples_out)); + TP_1(ENCODE); + lc3_encode(chans[0].encoder, LC3_PCM_FORMAT_S16, + samples + 0, AUDIO_CHANNELS, + (int)frame_bytes_lc3, samples_out); + TP_0(ENCODE); + + if (AUDIO_CHANNELS == 2) { + ble_iso_tx(chans[0].handle, samples_out, big_sdu); + + TP_1(ENCODE); + memset(samples_out, 0, sizeof(samples_out)); + lc3_encode(chans[1].encoder, LC3_PCM_FORMAT_S16, + samples + 1, AUDIO_CHANNELS, + (int)frame_bytes_lc3, samples_out); + TP_0(ENCODE); + ble_iso_tx(chans[1].handle, samples_out, big_sdu); + } else { + ble_iso_tx(chans[0].handle, samples_out, big_sdu); + if (BIG_NUM_BIS == 1) { + memset(samples_out, 0, sizeof(samples_out)); + lc3_encode(chans[1].encoder, LC3_PCM_FORMAT_S16, + samples + 1, AUDIO_CHANNELS, + (int)frame_bytes_lc3, samples_out); + } + ble_iso_tx(chans[0].handle, samples_out, big_sdu); + } + + } + + if (frames_count > LC3_FPDT) { + int old_samples_idx = samples_idx; + samples_idx -= LC3_FPDT * AUDIO_CHANNELS; + memmove(samples, &samples[old_samples_idx], + (old_samples_idx - samples_idx) * AUDIO_SAMPLE_SIZE); + } else { + samples_idx = 0; + } + } + } + + TP_0(DATA_CB); +} + +bool +tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_bytes_received, + uint8_t func_id, uint8_t ep_out, + uint8_t cur_alt_setting) +{ + (void)rhport; + (void)n_bytes_received; + (void)func_id; + (void)ep_out; + (void)cur_alt_setting; + + TP_1(DATA_ISR); + + if (!usb_data_ev.ev_queued) { + os_eventq_put(os_eventq_dflt_get(), &usb_data_ev); + } + + TP_0(DATA_ISR); + + return true; +} + +void +audio_usb_init(void) +{ + /* Need to reference those explicitly, so they are always pulled by linker + * instead of weak symbols in tinyusb. + */ + (void)tud_audio_rx_done_post_read_cb; + + TP_INIT(ENCODE); + TP_INIT(DATA_CB); + TP_INIT(DATA_IN); + TP_INIT(DATA_ISR); + + usb_desc_sample_rate_set(LC3_SAMPLING_FREQ); + + assert(LC3_FPDT == lc3_frame_samples(LC3_FRAME_DURATION, + LC3_SAMPLING_FREQ)); + + memset(samples, 0xaa, sizeof(samples)); + + unsigned esize = lc3_encoder_size(LC3_FRAME_DURATION, + LC3_SAMPLING_FREQ); + for (int i = 0; i < AUDIO_CHANNELS; i++) { + chans[i].encoder = calloc(1, esize); + lc3_setup_encoder(LC3_FRAME_DURATION, LC3_SAMPLING_FREQ, + 0, chans[i].encoder); + } + +#ifdef NRF53_SERIES + nrf_clock_hfclk_div_set(NRF_CLOCK, NRF_CLOCK_HFCLK_DIV_1); +#endif + + g_usb_enabled = 1; + + frame_bytes_lc3 = lc3_frame_bytes(LC3_FRAME_DURATION, LC3_BITRATE); + big_sdu = frame_bytes_lc3 * + (1 + ((AUDIO_CHANNELS == 2) && (BIG_NUM_BIS == 1))); +} + +void +audio_usb_enable(uint8_t enabled) +{ + g_usb_enabled = enabled; +} diff --git a/apps/auracast_usb/src/main.c b/apps/auracast_usb/src/main.c new file mode 100644 index 0000000000..b8ac0a29c5 --- /dev/null +++ b/apps/auracast_usb/src/main.c @@ -0,0 +1,349 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "console/console.h" +#include "config/config.h" + +#include "nimble/ble.h" +#include "host/ble_hs.h" +#include "host/util/util.h" + +#include "services/auracast/ble_svc_auracast.h" + +#include "hal/hal_gpio.h" +#include "bsp/bsp.h" +#include "app_priv.h" + +#define BROADCAST_SID 1 +#define BROADCAST_SDU_INTVL MYNEWT_VAL(LC3_FRAME_DURATION) +#define BROADCAST_SAMPLE_RATE BLE_AUDIO_SAMPLING_RATE_48000_HZ +#define BROADCAST_MAX_SDU (BROADCAST_SDU_INTVL * \ + MYNEWT_VAL(LC3_BITRATE) / \ + (1000 * 1000 * 8)) + +#define BROADCASTER_INTERRUPT_TASK_PRIO 4 +#define BROADCASTER_INTERRUPT_TASK_STACK_SZ 512 + +static uint8_t id_addr_type; + +static struct ble_audio_base auracast_base; +static struct ble_audio_big_subgroup big_subgroup; + +static os_membuf_t bis_mem[ + OS_MEMPOOL_SIZE(MYNEWT_VAL(BIG_NUM_BIS), + sizeof(struct ble_audio_bis)) +]; +static struct os_mempool bis_pool; + +static os_membuf_t codec_spec_mem[ + OS_MEMPOOL_SIZE(MYNEWT_VAL(BIG_NUM_BIS) * 2, 19) +]; +static struct os_mempool codec_spec_pool; + +static struct os_task auracast_interrupt_task_str; +static struct os_eventq auracast_interrupt_eventq; +static os_stack_t auracast_interrupt_task_stack[BROADCASTER_INTERRUPT_TASK_STACK_SZ]; + +static uint8_t auracast_adv_instance; + +static void +auracast_interrupt_task(void *arg) +{ + while (1) { + os_eventq_run(&auracast_interrupt_eventq); + } +} + +static void +broadcast_stop_ev_cb(struct os_event *ev) +{ + ble_svc_auracast_stop(auracast_adv_instance); + ble_svc_auracast_terminate(auracast_adv_instance); +} + +static struct os_event broadcast_stop_ev = { + .ev_cb = broadcast_stop_ev_cb, +}; + +static void +auracast_gpio_irq(void *arg) +{ + os_eventq_put(&auracast_interrupt_eventq, &broadcast_stop_ev); +} + +static void +auracast_init() +{ + int rc; + + assert(MYNEWT_VAL(AURACAST_CHAN_NUM) > 0); + + rc = os_mempool_init(&bis_pool, MYNEWT_VAL(BIG_NUM_BIS), + sizeof(struct ble_audio_bis), bis_mem, + "bis_pool"); + assert(rc == 0); + + rc = os_mempool_init(&codec_spec_pool, + MYNEWT_VAL(BIG_NUM_BIS) * 2, 19, + codec_spec_mem, "codec_spec_pool"); + assert(rc == 0); +} + +static int +base_create() +{ +#if MYNEWT_VAL(BIG_NUM_BIS) > 1 + struct ble_audio_bis *bis_left; + struct ble_audio_bis *bis_right; + uint8_t codec_spec_config_left_chan[] = + BLE_AUDIO_BUILD_CODEC_CONFIG(BROADCAST_SAMPLE_RATE, + BLE_AUDIO_SELECTED_FRAME_DURATION_10_MS, + BLE_AUDIO_LOCATION_FRONT_LEFT, + BROADCAST_MAX_SDU, ); + uint8_t codec_spec_config_right_chan[] = + BLE_AUDIO_BUILD_CODEC_CONFIG(BROADCAST_SAMPLE_RATE, + BLE_AUDIO_SELECTED_FRAME_DURATION_10_MS, + BLE_AUDIO_LOCATION_FRONT_RIGHT, + BROADCAST_MAX_SDU, ); +#else + uint16_t chan_loc = BLE_AUDIO_LOCATION_FRONT_LEFT | + BLE_AUDIO_LOCATION_FRONT_RIGHT; + uint8_t codec_spec_config[] = + BLE_AUDIO_BUILD_CODEC_CONFIG(BROADCAST_SAMPLE_RATE, + BLE_AUDIO_SELECTED_FRAME_DURATION_10_MS, + chan_loc, + BROADCAST_MAX_SDU * 2, ); + + struct ble_audio_bis *bis; +#endif + if (MYNEWT_VAL(BROADCAST_ID) != 0) { + auracast_base.broadcast_id = MYNEWT_VAL(BROADCAST_ID); + } else { + ble_hs_hci_rand(&auracast_base.broadcast_id, 3); + } + auracast_base.presentation_delay = 20000; + + big_subgroup.bis_cnt = MYNEWT_VAL(BIG_NUM_BIS); + + /** LC3 */ + big_subgroup.codec_id.format = 0x06; + + big_subgroup.codec_spec_config_len = 0; +#if MYNEWT_VAL(BIG_NUM_BIS) > 1 + bis_left = os_memblock_get(&bis_pool); + if (!bis_left) { + return BLE_HS_ENOMEM; + } + + bis_left->codec_spec_config = os_memblock_get(&codec_spec_pool); + memcpy(bis_left->codec_spec_config, + codec_spec_config_left_chan, + sizeof(codec_spec_config_left_chan)); + bis_left->codec_spec_config_len = sizeof(codec_spec_config_left_chan); + bis_left->idx = 1; + + bis_right = os_memblock_get(&bis_pool); + if (!bis_right) { + return BLE_HS_ENOMEM; + } + + bis_right->codec_spec_config = os_memblock_get(&codec_spec_pool); + memcpy(bis_right->codec_spec_config, + codec_spec_config_right_chan, + sizeof(codec_spec_config_right_chan)); + bis_right->codec_spec_config_len = sizeof(codec_spec_config_right_chan); + bis_right->idx = 2; + + STAILQ_INSERT_HEAD(&big_subgroup.bises, bis_left, next); + STAILQ_INSERT_TAIL(&big_subgroup.bises, bis_right, next); +#else + bis = os_memblock_get(&bis_pool); + if (!bis) { + return BLE_HS_ENOMEM; + } + + bis->codec_spec_config = os_memblock_get(&codec_spec_pool); + memcpy(bis->codec_spec_config, + codec_spec_config, + sizeof(codec_spec_config)); + bis->codec_spec_config_len = sizeof(codec_spec_config); + STAILQ_INSERT_HEAD(&big_subgroup.bises, bis, next); +#endif + + STAILQ_INSERT_HEAD(&auracast_base.subs, &big_subgroup, next); + auracast_base.num_subgroups++; + return 0; +} + +static int +auracast_destroy_fn(struct ble_audio_base *base, void *args) +{ + struct ble_audio_bis *bis; + int i; + + STAILQ_FOREACH(bis, &big_subgroup.bises, next) { + os_memblock_put(&codec_spec_pool, bis->codec_spec_config); + os_memblock_put(&bis_pool, bis); + } + + memset(&big_subgroup, 0, sizeof(big_subgroup)); + + for (i = 0; i < MYNEWT_VAL(AURACAST_CHAN_NUM); i++) { + chans[i].handle = 0; + } + + return 0; +} + +static int +iso_event(struct ble_iso_event *event, void *arg) +{ + int i; + + switch (event->type) { + case BLE_ISO_EVENT_BIG_CREATE_COMPLETE: + console_printf("BIG created\n"); + if (event->big_created.desc.num_bis > + MYNEWT_VAL(AURACAST_CHAN_NUM)) { + return BLE_HS_EINVAL; + } + if (MYNEWT_VAL(BIG_NUM_BIS) == event->big_created.desc.num_bis) { + for (i = 0; i < MYNEWT_VAL(AURACAST_CHAN_NUM); i++) { + chans[i].handle = event->big_created.desc.conn_handle[i]; + } + } else { + for (i = 0; i < MYNEWT_VAL(AURACAST_CHAN_NUM); i++) { + chans[i].handle = event->big_created.desc.conn_handle[0]; + } + } + return 0; + case BLE_ISO_EVENT_BIG_TERMINATE_COMPLETE: + console_printf("BIG terminated\n"); + return 0; + default: + return BLE_HS_ENOTSUP; + } +} + +static int +auracast_create() +{ + const char *program_info = "NimBLE Auracast Test"; + static struct ble_iso_big_params big_params = { + .sdu_interval = MYNEWT_VAL(LC3_FRAME_DURATION), + .max_sdu = BROADCAST_MAX_SDU, + .max_transport_latency = MYNEWT_VAL(LC3_FRAME_DURATION) / 1000, + .rtn = MYNEWT_VAL(BIG_RTN), + .phy = MYNEWT_VAL(BIG_PHY), + .packing = MYNEWT_VAL(BIG_PACKING), + .framing = MYNEWT_VAL(BIG_FRAMING), + .encryption = MYNEWT_VAL(BIG_ENCRYPTION), + .broadcast_code = MYNEWT_VAL(BROADCAST_CODE), + }; + + struct ble_svc_auracast_create_params create_params = { + .base = &auracast_base, + .big_params = &big_params, + .name = MYNEWT_VAL(BROADCAST_NAME), + .program_info = program_info, + .own_addr_type = id_addr_type, + .secondary_phy = BLE_HCI_LE_PHY_2M, + .sid = BROADCAST_SID, + .frame_duration = MYNEWT_VAL(LC3_FRAME_DURATION), + .sampling_frequency = MYNEWT_VAL(LC3_SAMPLING_FREQ), + .bitrate = MYNEWT_VAL(LC3_BITRATE), + }; + + return ble_svc_auracast_create(&create_params, + &auracast_adv_instance, + auracast_destroy_fn, + NULL, + NULL); +} + +static int +auracast_start() +{ + return ble_svc_auracast_start(auracast_adv_instance, iso_event, NULL); +} + +static void +on_sync(void) +{ + int rc; + + console_printf("Bluetooth initialized\n"); + + /* Make sure we have proper identity address set (public preferred) */ + rc = ble_hs_util_ensure_addr(0); + assert(rc == 0); + + /* configure global address */ + rc = ble_hs_id_infer_auto(0, &id_addr_type); + assert(rc == 0); + + auracast_init(); + + rc = base_create(); + assert(rc == 0); + + rc = auracast_create(); + assert(rc == 0); + + rc = auracast_start(); + assert(rc == 0); +} + +/* + * main + * + * The main task for the project. This function initializes the packages, + * then starts serving events from default event queue. + * + * @return int NOTE: this function should never return! + */ +int +mynewt_main(int argc, char **argv) +{ + /* Initialize OS */ + sysinit(); + + console_printf("LE Audio Broadcast sample application\n"); + + /* Set sync callback */ + ble_hs_cfg.sync_cb = on_sync; + + os_eventq_init(&auracast_interrupt_eventq); + os_task_init(&auracast_interrupt_task_str, "auracast_interrupt_task", + auracast_interrupt_task, NULL, + BROADCASTER_INTERRUPT_TASK_PRIO, OS_WAIT_FOREVER, + auracast_interrupt_task_stack, + BROADCASTER_INTERRUPT_TASK_STACK_SZ); + + hal_gpio_irq_init(BUTTON_3, auracast_gpio_irq, NULL, + HAL_GPIO_TRIG_RISING, HAL_GPIO_PULL_UP); + hal_gpio_irq_enable(BUTTON_3); + + /* As the last thing, process events from default event queue */ + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + + return 0; +} diff --git a/apps/auracast_usb/src/usb_desc.c b/apps/auracast_usb/src/usb_desc.c new file mode 100644 index 0000000000..aed9a2f65a --- /dev/null +++ b/apps/auracast_usb/src/usb_desc.c @@ -0,0 +1,409 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USBD_PRODUCT_RELEASE_NUMBER MYNEWT_VAL(USBD_PRODUCT_RELEASE_NUMBER) + +#ifndef CONFIG_NUM +#define CONFIG_NUM 1 +#endif + +typedef enum { + USB_STRING_DESCRIPTOR_LANG = 0, + USB_STRING_DESCRIPTOR_MANUFACTURER = 1, + USB_STRING_DESCRIPTOR_PRODUCT = 2, + USB_STRING_DESCRIPTOR_INTERFACE = 3, + USB_STRING_DESCRIPTOR_CDC = 4, + USB_STRING_DESCRIPTOR_SERIAL = 16, + USB_STRING_DESCRIPTOR_MICROSOFT_OS = 0xEE, +} usb_string_descriptor_ix_t; + +#define CDC_IF_STR_IX (MYNEWT_VAL(USBD_CDC_DESCRIPTOR_STRING) == NULL ? 0 : 4) + +const tusb_desc_device_t desc_device = { + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0200, + + .bDeviceClass = 0x00, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = MYNEWT_VAL(USBD_VID), + .idProduct = MYNEWT_VAL(USBD_PID), + .bcdDevice = USBD_PRODUCT_RELEASE_NUMBER, + + .iManufacturer = USB_STRING_DESCRIPTOR_MANUFACTURER, + .iProduct = USB_STRING_DESCRIPTOR_PRODUCT, + .iSerialNumber = USB_STRING_DESCRIPTOR_SERIAL, + + .bNumConfigurations = 0x01 +}; + +/* + * Invoked when received GET DEVICE DESCRIPTOR + * Application return pointer to descriptor + */ +const uint8_t * +tud_descriptor_device_cb(void) +{ + return (const uint8_t *)&desc_device; +} + +#if MYNEWT_VAL_CHOICE(MCU_TARGET, nRF5340_APP) || MYNEWT_VAL_CHOICE(MCU_TARGET, nRF52840) +#define ISO_EP 8 +#else +#error MCU not supported +#endif + +/* + * Configuration Descriptor + */ + +enum { +#if CFG_TUD_BTH + ITF_NUM_BTH, +#if CFG_TUD_BTH_ISO_ALT_COUNT > 0 + ITF_NUM_BTH_VOICE, +#endif +#endif + +#if CFG_TUD_CDC + ITF_NUM_CDC, + ITF_NUM_CDC_DATA, +#endif + +#if CFG_TUD_MSC + ITF_NUM_MSC, +#endif + +#if CFG_TUD_HID + ITF_NUM_HID, +#endif + +#if CFG_TUD_AUDIO_IN + ITF_NUM_AUDIO_AC, + ITF_NUM_AUDIO_AS_IN, +#elif CFG_TUD_AUDIO_OUT + ITF_NUM_AUDIO_AC, + ITF_NUM_AUDIO_AS_OUT, +#elif CFG_TUD_AUDIO_IN_OUT + ITF_NUM_AUDIO_AC, + ITF_NUM_AUDIO_AS_IN, + ITF_NUM_AUDIO_AS_OUT, +#endif + + ITF_NUM_TOTAL +}; + +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + \ + CFG_TUD_CDC * TUD_CDC_DESC_LEN + \ + CFG_TUD_MSC * TUD_MSC_DESC_LEN + \ + CFG_TUD_HID * TUD_HID_DESC_LEN + \ + CFG_TUD_BTH * TUD_BTH_DESC_LEN + \ + CFG_TUD_AUDIO_IN * TUD_AUDIO_MIC_ONE_CH_DESC_LEN + \ + (1 + CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP) * CFG_TUD_AUDIO_OUT * \ + (TUD_AUDIO_SPEAKER_MONO_DESC_LEN * (CFG_TUD_AUDIO_N_CHANNELS_RX == 1 ? 1 : 0)) + \ + (1 - CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP) * CFG_TUD_AUDIO_OUT * \ + (TUD_AUDIO_SPEAKER_STEREO_DESC_LEN * (CFG_TUD_AUDIO_N_CHANNELS_RX == 2 ? 1 : 0)) + \ + 0) + +const uint8_t desc_configuration[] = { + TUD_CONFIG_DESCRIPTOR(CONFIG_NUM, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, + MYNEWT_VAL(USBD_CONFIGURATION_MAX_POWER)), + +#if CFG_TUD_BTH + TUD_BTH_DESCRIPTOR(ITF_NUM_BTH, BTH_IF_STR_IX, USBD_BTH_EVENT_EP, USBD_BTH_EVENT_EP_SIZE, + USBD_BTH_EVENT_EP_INTERVAL, USBD_BTH_DATA_IN_EP, USBD_BTH_DATA_OUT_EP, USBD_BTH_DATA_EP_SIZE, + 0, 9, 17, 25, 33, 49), +#endif + + +#if CFG_TUD_CDC + TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, CDC_IF_STR_IX, USBD_CDC_NOTIFY_EP, USBD_CDC_NOTIFY_EP_SIZE, + USBD_CDC_DATA_OUT_EP, USBD_CDC_DATA_IN_EP, USBD_CDC_DATA_EP_SIZE), +#endif + +#if CFG_TUD_MSC + /* TODO: MSC not handled yet */ + TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, MSC_IF_STR_IX, EPNUM_MSC_OUT, EPNUM_MSC_IN, + (CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED) ? 512 : 64), +#endif + +#if CFG_TUD_HID + TUD_HID_DESCRIPTOR(ITF_NUM_HID, HID_IF_STR_IX, HID_PROTOCOL_NONE, sizeof(desc_hid_report), + USBD_HID_REPORT_EP, USBD_HID_REPORT_EP_SIZE, USBD_HID_REPORT_EP_INTERVAL), +#endif + +#if CFG_TUD_AUDIO_IN_OUT +#elif CFG_TUD_AUDIO_IN + TUD_AUDIO_MIC2_DESCRIPTOR(ITF_NUM_AUDIO_AC_IN, 0, 2, 16, 0x80 | ISO_EP, 192), +#elif CFG_TUD_AUDIO_OUT && CFG_TUD_AUDIO_N_CHANNELS_RX == 2 +#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP == 1 + TUD_AUDIO_SPEAKER_STEREO_FB_DESCRIPTOR(ITF_NUM_AUDIO_AC, 0, CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX, + 8 * CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX, ISO_EP, CFG_TUD_AUDIO_EPSIZE_OUT, 0x80 | ISO_EP, 1), +#else + TUD_AUDIO_SPEAKER_STEREO_DESCRIPTOR(ITF_NUM_AUDIO_AC, 0, CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX, + 8 * CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX, ISO_EP, CFG_TUD_AUDIO_EPSIZE_OUT), +#endif +#elif CFG_TUD_AUDIO_OUT && CFG_TUD_AUDIO_N_CHANNELS_RX == 1 + #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP + TUD_AUDIO_SPEAKER_MONO_FB_DESCRIPTOR(ITF_NUM_AUDIO_AC, 0, 2, 16, ISO_EP, 100, 0x80 | ISO_EP), +#else + TUD_AUDIO_SPEAKER_MONO_DESCRIPTOR(ITF_NUM_AUDIO_AC, 0, CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX, + 8 * CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX, ISO_EP, CFG_TUD_AUDIO_EPSIZE_OUT), +#endif +#endif +}; + +/** + * Invoked when received GET CONFIGURATION DESCRIPTOR + * Application return pointer to descriptor + * Descriptor contents must exist long enough for transfer to complete + */ +const uint8_t * +tud_descriptor_configuration_cb(uint8_t index) +{ + (void)index; + + return desc_configuration; +} + +static uint16_t desc_string[MYNEWT_VAL(USBD_STRING_DESCRIPTOR_MAX_LENGTH) + 1]; + +#if CFG_TUD_AUDIO + +#if CFG_TUD_AUDIO_IN +const uint16_t tud_audio_desc_lengths[] = {TUD_AUDIO_MIC2_DESC_LEN}; +#elif CFG_TUD_AUDIO_OUT && CFG_TUD_AUDIO_N_CHANNELS_RX == 1 +#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP +const uint16_t tud_audio_desc_lengths[] = {TUD_AUDIO_SPEAKER_MONO_FB_DESC_LEN}; +#else +const uint16_t tud_audio_desc_lengths[] = {TUD_AUDIO_SPEAKER_MONO_DESC_LEN}; +#endif +#elif CFG_TUD_AUDIO_OUT && CFG_TUD_AUDIO_N_CHANNELS_RX == 2 +#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP +const uint16_t tud_audio_desc_lengths[] = {TUD_AUDIO_SPEAKER_STEREO_FB_DESC_LEN}; +#else +const uint16_t tud_audio_desc_lengths[] = {TUD_AUDIO_SPEAKER_STEREO_DESC_LEN}; +#endif +#endif + +static uint32_t g_sample_rate = CFG_TUD_AUDIO_SAMPLE_RATE; +static usb_audio_sample_rate_cb_t sample_rate_cb; + +/* Invoked when audio class specific set request received for an entity */ +bool +tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff) +{ + uint32_t new_sample_rate; + + (void) rhport; + audio_control_request_t *request = (audio_control_request_t *) p_request; + + if (request->bEntityID == 2 && request->bControlSelector == AUDIO_FU_CTRL_VOLUME && + request->bRequest == AUDIO_CS_REQ_CUR) { + /* Ignore value but accept request */ + return true; + } else if (request->bEntityID == 4 && request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ && + request->bRequest == AUDIO_CS_REQ_CUR) { + /* Ignore value but accept request */ + new_sample_rate = ((audio_control_cur_4_t *)(pBuff))->bCur; + if (new_sample_rate != g_sample_rate) { + g_sample_rate = new_sample_rate; + if (sample_rate_cb) { + sample_rate_cb(g_sample_rate); + } + } + return true; + } else { + __BKPT(1); + } + return false; +} + +bool +tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request) +{ + (void) rhport; + audio_control_request_t *request = (audio_control_request_t *) p_request; + if (request->bEntityID == 4 && request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) { + if (request->bRequest == AUDIO_CS_REQ_CUR) { + audio_control_cur_4_t curf = {g_sample_rate}; + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &curf, sizeof(curf)); + } else if (request->bRequest == AUDIO_CS_REQ_RANGE) { +#if 1 + audio_control_range_4_n_t(1) rangef = { + .wNumSubRanges = 1, + .subrange[0] = {g_sample_rate, g_sample_rate, 0}, + }; +#else + audio_control_range_4_n_t(2) rangef = { + .wNumSubRanges = 2, + .subrange[0] = {16000, 16000, 0}, + .subrange[1] = {48000, 48000, 0}, + }; +#endif + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &rangef, sizeof(rangef)); + } + } else if (request->bEntityID == 5 && request->bControlSelector == AUDIO_CX_CTRL_CONTROL) { + if (request->bRequest == AUDIO_CS_REQ_CUR) { + audio_control_cur_1_t cur_clk = {1}; + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &cur_clk, sizeof(cur_clk)); + } + } else if (request->bEntityID == 4 && request->bControlSelector == AUDIO_CS_CTRL_CLK_VALID && + request->bRequest == AUDIO_CS_REQ_CUR) { + audio_control_cur_1_t cur_valid = {.bCur = 1}; + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &cur_valid, sizeof(cur_valid)); + } else if (request->bEntityID == 2 && request->bControlSelector == AUDIO_FU_CTRL_MUTE && + request->bRequest == AUDIO_CS_REQ_CUR) { + audio_control_cur_1_t mute = {.bCur = 0}; + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute, sizeof(mute)); + } else if (request->bEntityID == 2 && request->bControlSelector == AUDIO_FU_CTRL_VOLUME) { + if (request->bRequest == AUDIO_CS_REQ_RANGE) { + audio_control_range_2_n_t(1) range_vol = { + .wNumSubRanges = 1, + .subrange[0] = {0, 1000, 10} + }; + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &range_vol, sizeof(range_vol)); + } else if (request->bRequest == AUDIO_CS_REQ_CUR) { + audio_control_cur_2_t cur_vol = {.bCur = 1280}; + return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &cur_vol, sizeof(cur_vol)); + } + } else { + __BKPT(1); + } + return false; +} + +#endif + +/* Function converts ASCII string to USB descriptor format */ +static uint16_t * +string_to_usb_desc_string(const char *str, uint16_t *desc, uint8_t desc_size) +{ + int i; + int char_num = strlen(str); + + assert(char_num < desc_size); + if (char_num >= desc_size) { + char_num = desc_size - 1; + } + + /* Encode length in first byte, type in second byte */ + desc[0] = tu_htole16(tu_u16(TUSB_DESC_STRING, 2 * (char_num + 1))); + + /* Copy characters 8bit to 16 bits */ + for (i = 0; i < char_num; ++i) { + desc[1 + i] = tu_htole16(str[i]); + } + + return desc; +} + +/* LANGID string descriptors */ +static const uint16_t usbd_lang_id[2] = { + (TUSB_DESC_STRING << 8) + 4, /* Size of this descriptor */ + tu_htole16(MYNEWT_VAL(USBD_LANGID)) +}; + +static struct { + uint16_t major; + uint16_t minor; + uint16_t revision; + uint32_t build; +} img_version = { + 1, 0, 0, 1 +}; + +static char serial_number[11]; + +static uint16_t * +serial_to_usb_desc_string(uint16_t *desc, size_t size) +{ + if (serial_number[0] == 0) { + uint64_t serial = 1; + /* It probably means that serial was not provisioned return 0s */ + if (serial > 9999999999) { + serial = 0; + } + snprintf(serial_number, 11, "%010" PRIu64, serial); + } + return string_to_usb_desc_string(serial_number, desc, size); +} + +/* + * Invoked when received GET STRING DESCRIPTOR request + * Application return pointer to descriptor, whose contents must exist long enough for transfer to complete + */ +const uint16_t * +tud_descriptor_string_cb(uint8_t index, uint16_t langid) +{ + const uint16_t *ret = NULL; + char interface[100]; + +#if MYNEWT_VAL(USBD_WINDOWS_COMP_ID) + if (index == USB_STRING_DESCRIPTOR_MICROSOFT_OS) { + ret = (const uint16_t *)microsoft_os_string_descriptor; + } +#endif + if (index == USB_STRING_DESCRIPTOR_LANG) { + ret = usbd_lang_id; + } else if (index == USB_STRING_DESCRIPTOR_SERIAL) { + ret = serial_to_usb_desc_string(desc_string, ARRAY_SIZE(desc_string)); + } else if (index == USB_STRING_DESCRIPTOR_MANUFACTURER) { + ret = string_to_usb_desc_string(MYNEWT_VAL(USBD_VENDOR_STRING), desc_string, ARRAY_SIZE(desc_string)); + } else if (index == USB_STRING_DESCRIPTOR_PRODUCT) { + ret = string_to_usb_desc_string(MYNEWT_VAL(USBD_PRODUCT_STRING), desc_string, ARRAY_SIZE(desc_string)); + } else if (index == USB_STRING_DESCRIPTOR_INTERFACE) { + snprintf(interface, sizeof(interface), "%s, (%u.%u.%u.%lu)", MYNEWT_VAL(USBD_PRODUCT_STRING), + img_version.major, img_version.minor, img_version.revision, img_version.build); + ret = string_to_usb_desc_string(interface, desc_string, ARRAY_SIZE(desc_string)); + } + return ret; +} + +void +usb_desc_sample_rate_set(uint32_t sample_rate) +{ + g_sample_rate = sample_rate; +} + +void +usb_audio_sample_rate_cb_set(usb_audio_sample_rate_cb_t cb) +{ + sample_rate_cb = cb; +} + +uint32_t +usb_audio_sample_rate_get(void) +{ + return g_sample_rate; +} diff --git a/apps/auracast_usb/syscfg.usb.yml b/apps/auracast_usb/syscfg.usb.yml new file mode 100644 index 0000000000..b540c2ed67 --- /dev/null +++ b/apps/auracast_usb/syscfg.usb.yml @@ -0,0 +1,169 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +syscfg.defs.TINYUSB: + USBD_STRING_DESCRIPTOR_MAX_LENGTH: + description: Maximum length of string descriptor in characters + value: 31 + USBD_EP0_SIZE: + description: > + Endpoint 0 size + value: 64 + USBD_VID: + description: Vendor ID + value: + restrictions: + - $notnull + USBD_PID: + description: Product ID + value: + restrictions: + - $notnull + USBD_PRODUCT_RELEASE_NUMBER: + description: Product release number needed for USB descriptor + value: 0x100 + USBD_CONFIGURATION_MAX_POWER: + description: > + Maximum power consumption of the USB device from the bus in this specific + configuration when the device is fully operational. Expressed in 2 mA units + (i.e., 50 = 100 mA). + value: 100 + USBD_LANGID: + description: Language ID as specified by usb.org + value: 0x0409 + USBD_VENDOR_STRING: + description: Manufacturer identification string + value: '"Codecoup"' + USBD_PRODUCT_STRING: + description: Device friendly name + value: '"DK 22050"' + USBD_CDC: + description: Enable CDC device + value: 0 + USBD_CDC_CONSOLE: + description: Enable CDC device function for console in TinyUSB stack. + value: + USBD_HID: + description: Enable HID device + value: 0 + USBD_MSC: + description: Enable MSC device + value: 0 + USBD_BTH: + description: Enable BT HCI device + value: 0 + USBD_AUDIO: + description: Enable Audio device + value: 0 + USBD_AUDIO_IN: + description: Enable Audio device + value: 0 + USBD_AUDIO_OUT: + description: Enable Audio device + value: 1 + USBD_AUDIO_IN_OUT: + description: Enable Audio device + value: 0 + USB_AUDIO_OUT_CHANNELS: + description: Number of audio channels on USB stream + value: 2 + USB_AUDIO_OUT_SAMPLE_RATE: + description: USB sample rate + value: 22050 + + USBD_CDC_DATA_OUT_EP: + description: CDC data out endpoint number + value: + USBD_CDC_DATA_IN_EP: + description: CDC data out endpoint number + value: + USBD_CDC_DATA_EP_SIZE: + description: CDC data endpoint size + value: + USBD_CDC_NOTIFY_EP: + description: CDC notify endpoint number + value: + USBD_CDC_NOTIFY_EP_SIZE: + description: CDC notify endpoint size + value: + + USBD_CDC_DESCRIPTOR_STRING: + description: String for CDC interface + value: NULL + + USBD_MSC_DESCRIPTOR_STRING: + description: String for MSC interface + value: NULL + + USBD_HID_DESCRIPTOR_STRING: + description: String for HID interface + value: NULL + + USBD_BTH_DESCRIPTOR_STRING: + description: String for BT descriptor + value: NULL + + USBD_HID_REPORT_EP: + description: HID report endpoint number + value: + USBD_HID_REPORT_EP_SIZE: + description: HID report endpoint size + value: + USBD_HID_REPORT_EP_INTERVAL: + description: HID report endpoint interval + value: + + USBD_HID_REPORT_ID_KEYBOARD: + description: HID keyboard report ID + value: + USBD_HID_CAPS_LOCK_LED_PIN: + description: Caps Lock LED + value: -1 + USBD_HID_CAPS_LOCK_LED_ON_VALUE: + description: Value to set to pin to turn led on + value: 0 + USBD_HID_NUM_LOCK_LED_PIN: + description: Num Lock LED + value: -1 + USBD_HID_NUM_LOCK_LED_ON_VALUE: + description: Value to set to pin to turn led on + value: 0 + USBD_HID_REPORT_ID_MOUSE: + description: HID keyboard report ID + value: + + USBD_BTH_EVENT_EP: + description: BTH event endpoint number + value: + USBD_BTH_EVENT_EP_SIZE: + description: BTH event endpoint size + value: + USBD_BTH_EVENT_EP_INTERVAL: + description: BTH event endpoint interval + value: + USBD_BTH_DATA_IN_EP: + description: BTH data in endpoint number + value: + USBD_BTH_DATA_OUT_EP: + description: BTH data out endpoint number + value: + USBD_BTH_DATA_EP_SIZE: + description: BTH data endpoints size + value: + +syscfg.restrictions: + - '!USBD_STD_DESCRIPTORS' diff --git a/apps/auracast_usb/syscfg.yml b/apps/auracast_usb/syscfg.yml new file mode 100644 index 0000000000..64b48221c0 --- /dev/null +++ b/apps/auracast_usb/syscfg.yml @@ -0,0 +1,115 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +syscfg.defs: + AURACAST_CHAN_NUM: 2 + + BROADCAST_ID: + description: Broadcast ID value, will use random number if 0 + value: 0x000000 + BROADCAST_NAME: + description: Broadcast name + value: '"NimBLE Auracast"' + BROADCAST_CODE: + description: Broadcast code used for encrpytion + value: '"listen2nimble"' + + LC3_FRAME_DURATION: + description: LC3 frame duration + value: 10000 + LC3_SAMPLING_FREQ: + description: LC3 sampling frequency + value: 48000 + LC3_BITRATE: + description: LC3 bitrate + value: 96000 + + BIG_PHY: + description: + value: 3 + BIG_NUM_BIS: + description: Max number of BISes used + value: 2 + BIG_RTN: + description: BIG RTN (number of retransmissions) + value: 0 + BIG_PACKING: + description: Arrangement of BIS subevents (0 = sequential, 1 = interleaved) + value: 0 + BIG_FRAMING: + description: + value: 0 + BIG_ENCRYPTION: + description: BIS encryption + value: 1 + +syscfg.vals: + CONSOLE_IMPLEMENTATION: full + LOG_IMPLEMENTATION: full + STATS_IMPLEMENTATION: full + + # Disable not used GAP roles (we only do non-connectable + # advertising here) + BLE_ROLE_BROADCASTER: 1 + BLE_ROLE_CENTRAL: 0 + BLE_ROLE_OBSERVER: 0 + BLE_ROLE_PERIPHERAL: 0 + + # Enable Extended Advertising + BLE_EXT_ADV: 1 + + # Enable Periodic Advertising + BLE_PERIODIC_ADV: 1 + + # Max advertising data size + BLE_EXT_ADV_MAX_SIZE: 261 + + # Number of multi-advertising instances. Note that due + # to historical reasonds total number of advertising + # instances is BLE_MULTI_ADV_INSTANCES + 1 as instance + # 0 is always available + BLE_MULTI_ADV_INSTANCES: 1 + + # Controller uses msys pool for storing advertising data and scan responses. + # Since we advertise a lot of data (~6k in total) at the same time we need + # to increase block count. + MSYS_1_BLOCK_COUNT: 32 + + BLE_PHY_2M: 1 + + BLE_VERSION: 54 + BLE_ISO: 1 + BLE_ISO_BROADCASTER: 1 + BLE_MAX_BIG: 1 + BLE_MAX_BIS: 2 + + BLE_PHY_NRF52_HEADERMASK_WORKAROUND: 1 + + USBD_VID: 0xc0de + USBD_PID: 0x150b + USBD_VENDOR_STRING: '"Codecoup"' + USBD_PRODUCT_STRING: '"NimBLE ISO Broadcaster"' + USBD_AUDIO_OUT: 1 + USB_AUDIO_OUT_CHANNELS: 2 + USB_AUDIO_OUT_SAMPLE_RATE: MYNEWT_VAL(LC3_SAMPLING_FREQ) + +syscfg.vals.TINYUSB: + USBD_STACK_SIZE: 500 + USBD_STD_DESCRIPTORS: 0 + +$import: + - "@apache-mynewt-nimble/apps/auracast_usb/syscfg.usb.yml" \ No newline at end of file