Merge pull request #593 from hathach/edpt_ISO_xfer

Edpt iso xfer
This commit is contained in:
Ha Thach 2021-04-16 01:59:47 +07:00 committed by GitHub
commit c611199632
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 3872 additions and 1112 deletions

View File

@ -0,0 +1,37 @@
# use BOARD-Directory name for project id
get_filename_component(PROJECT ${CMAKE_CURRENT_SOURCE_DIR} NAME)
set(PROJECT ${BOARD}-${PROJECT})
# TOP is absolute path to root directory of TinyUSB git repo
set(TOP "../../..")
get_filename_component(TOP "${TOP}" REALPATH)
# Check for -DFAMILY=
if(FAMILY STREQUAL "rp2040")
cmake_minimum_required(VERSION 3.12)
include(${TOP}/hw/bsp/${FAMILY}/pico_sdk_import.cmake)
project(${PROJECT})
add_executable(${PROJECT})
include(${TOP}/hw/bsp/${FAMILY}/family.cmake)
# Example source
target_sources(${PROJECT} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
)
# Example include
target_include_directories(${PROJECT} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
# Example defines
target_compile_definitions(${PROJECT} PUBLIC
CFG_TUSB_OS=OPT_OS_PICO
)
else()
message(FATAL_ERROR "Invalid FAMILY specified")
endif()

View File

@ -0,0 +1,12 @@
include ../../../tools/top.mk
include ../../make.mk
INC += \
src \
$(TOP)/hw \
# Example source
EXAMPLE_SOURCE += $(wildcard src/*.c)
SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
include ../../rules.mk

View File

@ -0,0 +1,458 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2020 Reinhard Panhuber
*
* 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.
*
*/
/* plot_audio_samples.py requires following modules:
* $ sudo apt install libportaudio
* $ pip3 install sounddevice matplotlib
*
* Then run
* $ python3 plot_audio_samples.py
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "bsp/board.h"
#include "tusb.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+
#ifndef AUDIO_SAMPLE_RATE
#define AUDIO_SAMPLE_RATE 48000
#endif
/* Blink pattern
* - 250 ms : device not mounted
* - 1000 ms : device mounted
* - 2500 ms : device is suspended
*/
enum {
BLINK_NOT_MOUNTED = 250,
BLINK_MOUNTED = 1000,
BLINK_SUSPENDED = 2500,
};
static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
// Audio controls
// Current states
bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
uint32_t sampFreq;
uint8_t clkValid;
// Range states
audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX+1]; // Volume range state
audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
// Audio test data
uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ/2]; // Ensure half word aligned
void led_blinking_task(void);
void audio_task(void);
/*------------- MAIN -------------*/
int main(void)
{
board_init();
tusb_init();
// Init values
sampFreq = AUDIO_SAMPLE_RATE;
clkValid = 1;
sampleFreqRng.wNumSubRanges = 1;
sampleFreqRng.subrange[0].bMin = AUDIO_SAMPLE_RATE;
sampleFreqRng.subrange[0].bMax = AUDIO_SAMPLE_RATE;
sampleFreqRng.subrange[0].bRes = 0;
while (1)
{
tud_task(); // tinyusb device task
led_blinking_task();
audio_task();
}
return 0;
}
//--------------------------------------------------------------------+
// Device callbacks
//--------------------------------------------------------------------+
// Invoked when device is mounted
void tud_mount_cb(void)
{
blink_interval_ms = BLINK_MOUNTED;
}
// Invoked when device is unmounted
void tud_umount_cb(void)
{
blink_interval_ms = BLINK_NOT_MOUNTED;
}
// Invoked when usb bus is suspended
// remote_wakeup_en : if host allow us to perform remote wakeup
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
void tud_suspend_cb(bool remote_wakeup_en)
{
(void) remote_wakeup_en;
blink_interval_ms = BLINK_SUSPENDED;
}
// Invoked when usb bus is resumed
void tud_resume_cb(void)
{
blink_interval_ms = BLINK_MOUNTED;
}
//--------------------------------------------------------------------+
// AUDIO Task
//--------------------------------------------------------------------+
void audio_task(void)
{
// Yet to be filled - e.g. put meas data into TX FIFOs etc.
asm("nop");
}
//--------------------------------------------------------------------+
// Application Callback API Implementations
//--------------------------------------------------------------------+
// Invoked when audio class specific set request received for an EP
bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
{
(void) rhport;
(void) pBuff;
// We do not support any set range requests here, only current value requests
TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
// Page 91 in UAC2 specification
uint8_t channelNum = TU_U16_LOW(p_request->wValue);
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
uint8_t ep = TU_U16_LOW(p_request->wIndex);
(void) channelNum; (void) ctrlSel; (void) ep;
return false; // Yet not implemented
}
// Invoked when audio class specific set request received for an interface
bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
{
(void) rhport;
(void) pBuff;
// We do not support any set range requests here, only current value requests
TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
// Page 91 in UAC2 specification
uint8_t channelNum = TU_U16_LOW(p_request->wValue);
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
uint8_t itf = TU_U16_LOW(p_request->wIndex);
(void) channelNum; (void) ctrlSel; (void) itf;
return false; // Yet not implemented
}
// 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)
{
(void) rhport;
// Page 91 in UAC2 specification
uint8_t channelNum = TU_U16_LOW(p_request->wValue);
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
uint8_t itf = TU_U16_LOW(p_request->wIndex);
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
(void) itf;
// We do not support any set range requests here, only current value requests
TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
// If request is for our feature unit
if ( entityID == 2 )
{
switch ( ctrlSel )
{
case AUDIO_FU_CTRL_MUTE:
// Request uses format layout 1
TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_1_t));
mute[channelNum] = ((audio_control_cur_1_t*) pBuff)->bCur;
TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
return true;
case AUDIO_FU_CTRL_VOLUME:
// Request uses format layout 2
TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_2_t));
volume[channelNum] = ((audio_control_cur_2_t*) pBuff)->bCur;
TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
return true;
// Unknown/Unsupported control
default:
TU_BREAKPOINT();
return false;
}
}
return false; // Yet not implemented
}
// Invoked when audio class specific get request received for an EP
bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
(void) rhport;
// Page 91 in UAC2 specification
uint8_t channelNum = TU_U16_LOW(p_request->wValue);
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
uint8_t ep = TU_U16_LOW(p_request->wIndex);
(void) channelNum; (void) ctrlSel; (void) ep;
// return tud_control_xfer(rhport, p_request, &tmp, 1);
return false; // Yet not implemented
}
// Invoked when audio class specific get request received for an interface
bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
(void) rhport;
// Page 91 in UAC2 specification
uint8_t channelNum = TU_U16_LOW(p_request->wValue);
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
uint8_t itf = TU_U16_LOW(p_request->wIndex);
(void) channelNum; (void) ctrlSel; (void) itf;
return false; // Yet not implemented
}
// Invoked when audio class specific get request received for an entity
bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
(void) rhport;
// Page 91 in UAC2 specification
uint8_t channelNum = TU_U16_LOW(p_request->wValue);
uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
// uint8_t itf = TU_U16_LOW(p_request->wIndex); // Since we have only one audio function implemented, we do not need the itf value
uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
// Input terminal (Microphone input)
if (entityID == 1)
{
switch ( ctrlSel )
{
case AUDIO_TE_CTRL_CONNECTOR:
{
// The terminal connector control only has a get request with only the CUR attribute.
audio_desc_channel_cluster_t ret;
// Those are dummy values for now
ret.bNrChannels = 1;
ret.bmChannelConfig = 0;
ret.iChannelNames = 0;
TU_LOG2(" Get terminal connector\r\n");
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
}
break;
// Unknown/Unsupported control selector
default:
TU_BREAKPOINT();
return false;
}
}
// Feature unit
if (entityID == 2)
{
switch ( ctrlSel )
{
case AUDIO_FU_CTRL_MUTE:
// Audio control mute cur parameter block consists of only one byte - we thus can send it right away
// There does not exist a range parameter block for mute
TU_LOG2(" Get Mute of channel: %u\r\n", channelNum);
return tud_control_xfer(rhport, p_request, &mute[channelNum], 1);
case AUDIO_FU_CTRL_VOLUME:
switch ( p_request->bRequest )
{
case AUDIO_CS_REQ_CUR:
TU_LOG2(" Get Volume of channel: %u\r\n", channelNum);
return tud_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
case AUDIO_CS_REQ_RANGE:
TU_LOG2(" Get Volume range of channel: %u\r\n", channelNum);
// Copy values - only for testing - better is version below
audio_control_range_2_n_t(1)
ret;
ret.wNumSubRanges = 1;
ret.subrange[0].bMin = -90; // -90 dB
ret.subrange[0].bMax = 90; // +90 dB
ret.subrange[0].bRes = 1; // 1 dB steps
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
// Unknown/Unsupported control
default:
TU_BREAKPOINT();
return false;
}
break;
// Unknown/Unsupported control
default:
TU_BREAKPOINT();
return false;
}
}
// Clock Source unit
if ( entityID == 4 )
{
switch ( ctrlSel )
{
case AUDIO_CS_CTRL_SAM_FREQ:
// channelNum is always zero in this case
switch ( p_request->bRequest )
{
case AUDIO_CS_REQ_CUR:
TU_LOG2(" Get Sample Freq.\r\n");
return tud_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
case AUDIO_CS_REQ_RANGE:
TU_LOG2(" Get Sample Freq. range\r\n");
return tud_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng));
// Unknown/Unsupported control
default:
TU_BREAKPOINT();
return false;
}
break;
case AUDIO_CS_CTRL_CLK_VALID:
// Only cur attribute exists for this request
TU_LOG2(" Get Sample Freq. valid\r\n");
return tud_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid));
// Unknown/Unsupported control
default:
TU_BREAKPOINT();
return false;
}
}
TU_LOG2(" Unsupported entity: %d\r\n", entityID);
return false; // Yet not implemented
}
bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
{
(void) rhport;
(void) itf;
(void) ep_in;
(void) cur_alt_setting;
for (uint8_t cnt=0; cnt < CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO; cnt++)
{
tud_audio_write_support_ff(cnt, i2s_dummy_buffer[cnt], AUDIO_SAMPLE_RATE/1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX);
}
return true;
}
bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
{
(void) rhport;
(void) n_bytes_copied;
(void) itf;
(void) ep_in;
(void) cur_alt_setting;
uint16_t dataVal;
// Generate dummy data
for (uint16_t cnt = 0; cnt < CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO; cnt++)
{
uint16_t * p_buff = i2s_dummy_buffer[cnt]; // 2 bytes per sample
dataVal = 1;
for (uint16_t cnt2 = 0; cnt2 < AUDIO_SAMPLE_RATE/1000; cnt2++)
{
for (uint8_t cnt3 = 0; cnt3 < CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX; cnt3++)
{
*p_buff++ = dataVal;
}
dataVal++;
}
}
return true;
}
bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
(void) rhport;
(void) p_request;
return true;
}
//--------------------------------------------------------------------+
// BLINKING TASK
//--------------------------------------------------------------------+
void led_blinking_task(void)
{
static uint32_t start_ms = 0;
static bool led_state = false;
// Blink every interval ms
if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time
start_ms += blink_interval_ms;
board_led_write(led_state);
led_state = 1 - led_state; // toggle
}

View File

@ -0,0 +1,34 @@
import sounddevice as sd
import matplotlib.pyplot as plt
import numpy as np
import platform
if __name__ == '__main__':
# If you got "ValueError: No input device matching", that is because your PC name example device
# differently from tested list below. Uncomment the next line to see full list and try to pick correct one
# print(sd.query_devices())
fs = 48000 # Sample rate
duration = 100e-3 # Duration of recording
if platform.system() == 'Windows':
# WDM-KS is needed since there are more than one MicNode device APIs (at least in Windows)
device = 'Microphone (MicNode_4_Ch), Windows WDM-KS'
elif platform.system() == 'Darwin':
device = 'MicNode_4_Ch'
else:
device ='default'
myrecording = sd.rec(int(duration * fs), samplerate=fs, channels=4, dtype='int16', device=device)
print('Waiting...')
sd.wait() # Wait until recording is finished
print('Done!')
time = np.arange(0, duration, 1 / fs) # time vector
plt.plot(time, myrecording)
plt.xlabel('Time [s]')
plt.ylabel('Amplitude')
plt.title('MicNode 4 Channel')
plt.show()

View File

@ -0,0 +1,118 @@
/*
* 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_
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------
// COMMON CONFIGURATION
//--------------------------------------------------------------------
// defined by compiler flags for flexibility
#ifndef CFG_TUSB_MCU
#error CFG_TUSB_MCU must be defined
#endif
#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX
#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED)
#else
#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE
#endif
#ifndef CFG_TUSB_OS
#define CFG_TUSB_OS OPT_OS_NONE
#endif
#ifndef CFG_TUSB_DEBUG
#define CFG_TUSB_DEBUG 0
#endif
// CFG_TUSB_DEBUG is defined by compiler in DEBUG build
// #define CFG_TUSB_DEBUG 0
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
* Tinyusb use follows macros to declare transferring memory so that they can be put
* into those specific section.
* e.g
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
*/
#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
//--------------------------------------------------------------------
#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE 64
#endif
//------------- CLASS -------------//
#define CFG_TUD_CDC 0
#define CFG_TUD_MSC 0
#define CFG_TUD_HID 0
#define CFG_TUD_MIDI 0
#define CFG_TUD_AUDIO 1
#define CFG_TUD_VENDOR 0
//--------------------------------------------------------------------
// AUDIO CLASS DRIVER CONFIGURATION
//--------------------------------------------------------------------
// Have a look into audio_device.h for all configurations
#define CFG_TUD_AUDIO_FUNC_1_DESC_LEN TUD_AUDIO_MIC_FOUR_CH_DESC_LEN
#define CFG_TUD_AUDIO_FUNC_1_N_AS_INT 1
#define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64
#define CFG_TUD_AUDIO_ENABLE_EP_IN 1
#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX 2 // This value is not required by the driver, it parses this information from the descriptor once the alternate interface is set by the host - we use it for the setup
#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 4 // This value is not required by the driver, it parses this information from the descriptor once the alternate interface is set by the host - we use it for the setup
#define CFG_TUD_AUDIO_EP_SZ_IN (48 + 1) * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX // 48 Samples (48 kHz) x 2 Bytes/Sample x CFG_TUD_AUDIO_N_CHANNELS_TX Channels - the Windows driver always needs an extra sample per channel of space more, otherwise it complains... found by trial and error
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX CFG_TUD_AUDIO_EP_SZ_IN
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ CFG_TUD_AUDIO_EP_SZ_IN
#define CFG_TUD_AUDIO_ENABLE_ENCODING 1
#define CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING 1
#define CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX 2 // One I2S stream contains two channels, each stream is saved within one support FIFO - this value is currently fixed, the driver does not support a changing value
#define CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO (CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX / CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX)
#define CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ (CFG_TUD_AUDIO_EP_SZ_IN / CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO)
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_CONFIG_H_ */

View File

@ -0,0 +1,160 @@
/*
* 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.
*
*/
#include "tusb.h"
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
*
* Auto ProductID layout's Bitmap:
* [MSB] AUDIO | MIDI | HID | MSC | CDC [LSB]
*/
#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
_PID_MAP(MIDI, 3) | _PID_MAP(AUDIO, 4) | _PID_MAP(VENDOR, 5) )
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
tusb_desc_device_t const desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
// Use Interface Association Descriptor (IAD) for CDC
// As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = 0xCafe,
.idProduct = USB_PID,
.bcdDevice = 0x0100,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01
};
// Invoked when received GET DEVICE DESCRIPTOR
// Application return pointer to descriptor
uint8_t const * tud_descriptor_device_cb(void)
{
return (uint8_t const *) &desc_device;
}
//--------------------------------------------------------------------+
// Configuration Descriptor
//--------------------------------------------------------------------+
enum
{
ITF_NUM_AUDIO_CONTROL = 0,
ITF_NUM_AUDIO_STREAMING,
ITF_NUM_TOTAL
};
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_AUDIO * TUD_AUDIO_MIC_FOUR_CH_DESC_LEN)
#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX
// LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
// 0 control, 1 In, 2 Bulk, 3 Iso, 4 In etc ...
#define EPNUM_AUDIO 0x03
#else
#define EPNUM_AUDIO 0x01
#endif
uint8_t const desc_configuration[] =
{
// Interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
// Interface number, string index, EP Out & EP In address, EP size
TUD_AUDIO_MIC_FOUR_CH_DESCRIPTOR(/*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_stridx*/ 0, /*_nBytesPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, /*_nBitsUsedPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX*8, /*_epin*/ 0x80 | EPNUM_AUDIO, /*_epsize*/ CFG_TUD_AUDIO_EP_SZ_IN)
};
// Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
{
(void) index; // for multiple configurations
return desc_configuration;
}
//--------------------------------------------------------------------+
// String Descriptors
//--------------------------------------------------------------------+
// array of pointer to string descriptors
char const* string_desc_arr [] =
{
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"PaniRCorp", // 1: Manufacturer
"MicNode_4_Ch", // 2: Product
"123458", // 3: Serials, should use chip ID
"UAC2", // 4: Audio Interface
};
static uint16_t _desc_str[32];
// Invoked when received GET STRING DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
{
(void) langid;
uint8_t chr_count;
if ( index == 0)
{
memcpy(&_desc_str[1], string_desc_arr[0], 2);
chr_count = 1;
}else
{
// Convert ASCII string into UTF-16
if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL;
const char* str = string_desc_arr[index];
// Cap at max char
chr_count = strlen(str);
if ( chr_count > 31 ) chr_count = 31;
for(uint8_t i=0; i<chr_count; i++)
{
_desc_str[1+i] = str[i];
}
}
// first byte is length (including header), second byte is string type
_desc_str[0] = (TUSB_DESC_STRING << 8 ) | (2*chr_count + 2);
return _desc_str;
}

View File

@ -23,6 +23,14 @@
*
*/
/* plot_audio_samples.py requires following modules:
* $ sudo apt install libportaudio
* $ pip3 install sounddevice matplotlib
*
* Then run
* $ python3 plot_audio_samples.py
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@ -34,6 +42,10 @@
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+
#ifndef AUDIO_SAMPLE_RATE
#define AUDIO_SAMPLE_RATE 48000
#endif
/* Blink pattern
* - 250 ms : device not mounted
* - 1000 ms : device mounted
@ -49,17 +61,17 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
// Audio controls
// Current states
bool mute[CFG_TUD_AUDIO_N_CHANNELS_TX + 1]; // +1 for master channel 0
uint16_t volume[CFG_TUD_AUDIO_N_CHANNELS_TX + 1]; // +1 for master channel 0
bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
uint32_t sampFreq;
uint8_t clkValid;
// Range states
audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_N_CHANNELS_TX+1]; // Volume range state
audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX+1]; // Volume range state
audio_control_range_4_n_t(1) sampleFreqRng; // Sample frequency range state
// Audio test data
uint16_t test_buffer_audio[CFG_TUD_AUDIO_TX_FIFO_SIZE/2];
uint16_t test_buffer_audio[CFG_TUD_AUDIO_EP_SZ_IN/2];
uint16_t startVal = 0;
void led_blinking_task(void);
@ -73,12 +85,12 @@ int main(void)
tusb_init();
// Init values
sampFreq = 44100;
sampFreq = AUDIO_SAMPLE_RATE;
clkValid = 1;
sampleFreqRng.wNumSubRanges = 1;
sampleFreqRng.subrange[0].bMin = 44100;
sampleFreqRng.subrange[0].bMax = 44100;
sampleFreqRng.subrange[0].bMin = AUDIO_SAMPLE_RATE;
sampleFreqRng.subrange[0].bMax = AUDIO_SAMPLE_RATE;
sampleFreqRng.subrange[0].bRes = 0;
while (1)
@ -203,7 +215,6 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
mute[channelNum] = ((audio_control_cur_1_t*) pBuff)->bCur;
TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
return true;
case AUDIO_FU_CTRL_VOLUME:
@ -213,8 +224,7 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
volume[channelNum] = ((audio_control_cur_2_t*) pBuff)->bCur;
TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
return true;
return true;
// Unknown/Unsupported control
default:
@ -271,96 +281,110 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
// Input terminal (Microphone input)
if (entityID == 1)
{
switch (ctrlSel)
switch ( ctrlSel )
{
case AUDIO_TE_CTRL_CONNECTOR:;
// The terminal connector control only has a get request with only the CUR attribute.
case AUDIO_TE_CTRL_CONNECTOR:
{
// The terminal connector control only has a get request with only the CUR attribute.
audio_desc_channel_cluster_t ret;
audio_desc_channel_cluster_t ret;
// Those are dummy values for now
ret.bNrChannels = 1;
ret.bmChannelConfig = 0;
ret.iChannelNames = 0;
// Those are dummy values for now
ret.bNrChannels = 1;
ret.bmChannelConfig = 0;
ret.iChannelNames = 0;
TU_LOG2(" Get terminal connector\r\n");
TU_LOG2(" Get terminal connector\r\n");
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
}
break;
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*)&ret, sizeof(ret));
// Unknown/Unsupported control selector
default: TU_BREAKPOINT(); return false;
// Unknown/Unsupported control selector
default:
TU_BREAKPOINT();
return false;
}
}
// Feature unit
if (entityID == 2)
{
switch (ctrlSel)
switch ( ctrlSel )
{
case AUDIO_FU_CTRL_MUTE:
// Audio control mute cur parameter block consists of only one byte - we thus can send it right away
// There does not exist a range parameter block for mute
TU_LOG2(" Get Mute of channel: %u\r\n", channelNum);
return tud_control_xfer(rhport, p_request, &mute[channelNum], 1);
// Audio control mute cur parameter block consists of only one byte - we thus can send it right away
// There does not exist a range parameter block for mute
TU_LOG2(" Get Mute of channel: %u\r\n", channelNum);
return tud_control_xfer(rhport, p_request, &mute[channelNum], 1);
case AUDIO_FU_CTRL_VOLUME:
switch ( p_request->bRequest )
{
case AUDIO_CS_REQ_CUR:
TU_LOG2(" Get Volume of channel: %u\r\n", channelNum);
return tud_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
switch (p_request->bRequest)
{
case AUDIO_CS_REQ_CUR:
TU_LOG2(" Get Volume of channel: %u\r\n", channelNum);
return tud_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
case AUDIO_CS_REQ_RANGE:
TU_LOG2(" Get Volume range of channel: %u\r\n", channelNum);
case AUDIO_CS_REQ_RANGE:
TU_LOG2(" Get Volume range of channel: %u\r\n", channelNum);
// Copy values - only for testing - better is version below
audio_control_range_2_n_t(1) ret;
// Copy values - only for testing - better is version below
audio_control_range_2_n_t(1)
ret;
ret.wNumSubRanges = 1;
ret.subrange[0].bMin = -90; // -90 dB
ret.subrange[0].bMax = 90; // +90 dB
ret.subrange[0].bRes = 1; // 1 dB steps
ret.wNumSubRanges = 1;
ret.subrange[0].bMin = -90; // -90 dB
ret.subrange[0].bMax = 90; // +90 dB
ret.subrange[0].bRes = 1; // 1 dB steps
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*)&ret, sizeof(ret));
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
// Unknown/Unsupported control
default: TU_BREAKPOINT(); return false;
}
// Unknown/Unsupported control
default:
TU_BREAKPOINT();
return false;
}
break;
// Unknown/Unsupported control
default: TU_BREAKPOINT(); return false;
// Unknown/Unsupported control
default:
TU_BREAKPOINT();
return false;
}
}
// Clock Source unit
if (entityID == 4)
if ( entityID == 4 )
{
switch (ctrlSel)
switch ( ctrlSel )
{
case AUDIO_CS_CTRL_SAM_FREQ:
// channelNum is always zero in this case
switch ( p_request->bRequest )
{
case AUDIO_CS_REQ_CUR:
TU_LOG2(" Get Sample Freq.\r\n");
return tud_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
// channelNum is always zero in this case
case AUDIO_CS_REQ_RANGE:
TU_LOG2(" Get Sample Freq. range\r\n");
return tud_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng));
switch (p_request->bRequest)
{
case AUDIO_CS_REQ_CUR:
TU_LOG2(" Get Sample Freq.\r\n");
return tud_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
case AUDIO_CS_REQ_RANGE:
TU_LOG2(" Get Sample Freq. range\r\n");
return tud_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng));
// Unknown/Unsupported control
default:
TU_BREAKPOINT();
return false;
}
break;
// Unknown/Unsupported control
default: TU_BREAKPOINT(); return false;
}
case AUDIO_CS_CTRL_CLK_VALID:
// Only cur attribute exists for this request
TU_LOG2(" Get Sample Freq. valid\r\n");
return tud_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid));
case AUDIO_CS_CTRL_CLK_VALID:
// Only cur attribute exists for this request
TU_LOG2(" Get Sample Freq. valid\r\n");
return tud_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid));
// Unknown/Unsupported control
default: TU_BREAKPOINT(); return false;
// Unknown/Unsupported control
default:
TU_BREAKPOINT();
return false;
}
}
@ -375,7 +399,7 @@ bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, u
(void) ep_in;
(void) cur_alt_setting;
tud_audio_write ((uint8_t *)test_buffer_audio, CFG_TUD_AUDIO_TX_FIFO_SIZE);
tud_audio_write ((uint8_t *)test_buffer_audio, CFG_TUD_AUDIO_EP_SZ_IN);
return true;
}
@ -388,7 +412,7 @@ bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uin
(void) ep_in;
(void) cur_alt_setting;
for (size_t cnt = 0; cnt < CFG_TUD_AUDIO_TX_FIFO_SIZE/2; cnt++)
for (size_t cnt = 0; cnt < CFG_TUD_AUDIO_EP_SZ_IN/2; cnt++)
{
test_buffer_audio[cnt] = startVal++;
}

View File

@ -0,0 +1,34 @@
import sounddevice as sd
import matplotlib.pyplot as plt
import numpy as np
import platform
if __name__ == '__main__':
# If you got "ValueError: No input device matching", that is because your PC name example device
# differently from tested list below. Uncomment the next line to see full list and try to pick correct one
# print(sd.query_devices())
fs = 48000 # Sample rate
duration = 100e-3 # Duration of recording
if platform.system() == 'Windows':
# MME is needed since there are more than one MicNode device APIs (at least in Windows)
device = 'Microphone (MicNode) MME'
elif platform.system() == 'Darwin':
device = 'MicNode'
else:
device ='default'
myrecording = sd.rec(int(duration * fs), samplerate=fs, channels=1, dtype='int16', device=device)
print('Waiting...')
sd.wait() # Wait until recording is finished
print('Done!')
time = np.arange(0, duration, 1 / fs) # time vector
plt.plot(time, myrecording)
plt.xlabel('Time [s]')
plt.ylabel('Amplitude')
plt.title('MicNode')
plt.show()

View File

@ -91,25 +91,18 @@ extern "C" {
// AUDIO CLASS DRIVER CONFIGURATION
//--------------------------------------------------------------------
// Audio format type
#define CFG_TUD_AUDIO_USE_TX_FIFO 1
#define CFG_TUD_AUDIO_FORMAT_TYPE_TX AUDIO_FORMAT_TYPE_I
#define CFG_TUD_AUDIO_FORMAT_TYPE_RX AUDIO_FORMAT_TYPE_UNDEFINED
// Have a look into audio_device.h for all configurations
// Audio format type I specifications
#define CFG_TUD_AUDIO_FORMAT_TYPE_I_TX AUDIO_DATA_FORMAT_TYPE_I_PCM
#define CFG_TUD_AUDIO_N_CHANNELS_TX 1
#define CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX 2
#define CFG_TUD_AUDIO_FUNC_1_DESC_LEN TUD_AUDIO_MIC_ONE_CH_DESC_LEN
#define CFG_TUD_AUDIO_FUNC_1_N_AS_INT 1 // 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_FUNC_1_CTRL_BUF_SZ 64 // Size of control request buffer
// 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 48*CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX*CFG_TUD_AUDIO_N_CHANNELS_TX // 48 Samples (48 kHz) x 2 Bytes/Sample x 1 Channels
#define CFG_TUD_AUDIO_TX_FIFO_SIZE 48*2 // 48 Samples (48 kHz) x 2 Bytes/Sample (1/2 word)
// 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
#define CFG_TUD_AUDIO_ENABLE_EP_IN 1
#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX 2 // Driver gets this info from the descriptors - we define it here to use it to setup the descriptors and to do calculations with it below
#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 1 // Driver gets this info from the descriptors - we define it here to use it to setup the descriptors and to do calculations with it below - be aware: for different number of channels you need another descriptor!
#define CFG_TUD_AUDIO_EP_SZ_IN 48 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX // 48 Samples (48 kHz) x 2 Bytes/Sample x 1 Channel
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX CFG_TUD_AUDIO_EP_SZ_IN // Maximum EP IN size for all AS alternate settings used
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ CFG_TUD_AUDIO_EP_SZ_IN + 1
#ifdef __cplusplus
}

View File

@ -79,7 +79,7 @@ enum
ITF_NUM_TOTAL
};
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_AUDIO * TUD_AUDIO_MIC_DESC_LEN)
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_AUDIO * TUD_AUDIO_MIC_ONE_CH_DESC_LEN)
#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX
// LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
@ -89,20 +89,13 @@ enum
#define EPNUM_AUDIO 0x01
#endif
// These variables are required by the audio driver in audio_device.c
// List of audio descriptor lengths which is required by audio driver - you need as many entries as CFG_TUD_AUDIO - unfortunately this is not possible to determine otherwise
const uint16_t tud_audio_desc_lengths[] = {TUD_AUDIO_MIC_DESC_LEN};
// TAKE CARE - THE NUMBER OF AUDIO STREAMING INTERFACES PER AUDIO FUNCTION MUST NOT EXCEED CFG_TUD_AUDIO_N_AS_INT - IF IT DOES INCREASE CFG_TUD_AUDIO_N_AS_INT IN tusb_config.h!
uint8_t const desc_configuration[] =
{
// Interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
// Interface number, string index, EP Out & EP In address, EP size
TUD_AUDIO_MIC_DESCRIPTOR(/*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_stridx*/ 0, /*_nBytesPerSample*/ 3, /*_nBitsUsedPerSample*/ 24, /*_epin*/ 0x80 | EPNUM_AUDIO, /*_epsize*/ 48*4)
TUD_AUDIO_MIC_ONE_CH_DESCRIPTOR(/*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_stridx*/ 0, /*_nBytesPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, /*_nBitsUsedPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX*8, /*_epin*/ 0x80 | EPNUM_AUDIO, /*_epsize*/ CFG_TUD_AUDIO_EP_SZ_IN)
};
// Invoked when received GET CONFIGURATION DESCRIPTOR

View File

@ -34,6 +34,10 @@
// MACRO CONSTANT TYPEDEF PROTOTYPES
//--------------------------------------------------------------------+
#ifndef AUDIO_SAMPLE_RATE
#define AUDIO_SAMPLE_RATE 48000
#endif
/* Blink pattern
* - 25 ms : streaming data
* - 250 ms : device not mounted
@ -68,8 +72,8 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
// Audio controls
// Current states
int8_t mute[CFG_TUD_AUDIO_N_CHANNELS_TX + 1]; // +1 for master channel 0
int16_t volume[CFG_TUD_AUDIO_N_CHANNELS_TX + 1]; // +1 for master channel 0
int8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0
// Buffer for microphone data
int16_t mic_buf[1000];

View File

@ -35,6 +35,8 @@ extern "C" {
// COMMON CONFIGURATION
//--------------------------------------------------------------------
#include "usb_descriptors.h"
// defined by compiler flags for flexibility
#ifndef CFG_TUSB_MCU
#error CFG_TUSB_MCU must be defined
@ -91,43 +93,36 @@ extern "C" {
//--------------------------------------------------------------------
// AUDIO CLASS DRIVER CONFIGURATION
//--------------------------------------------------------------------
#define CFG_TUD_AUDIO_IN_PATH (CFG_TUD_AUDIO)
#define CFG_TUD_AUDIO_OUT_PATH (CFG_TUD_AUDIO)
#ifndef AUDIO_SAMPLE_RATE
#define AUDIO_SAMPLE_RATE 48000
#endif
#define CFG_TUD_AUDIO_IN_PATH (CFG_TUD_AUDIO)
#define CFG_TUD_AUDIO_OUT_PATH (CFG_TUD_AUDIO)
// 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
//#define CFG_TUD_AUDIO_FUNC_1_DESC_LEN 220 // This equals TUD_AUDIO_HEADSET_STEREO_DESC_LEN, however, including it from usb_descriptors.h is not possible due to some strange include hassle
#define CFG_TUD_AUDIO_FUNC_1_DESC_LEN TUD_AUDIO_HEADSET_STEREO_DESC_LEN
// 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 1
#define CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX 2
#define CFG_TUD_AUDIO_N_CHANNELS_RX 2
#define CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX 2
#define CFG_TUD_AUDIO_RX_ITEMSIZE 2
#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP 0
#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX 1
#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX 2
#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX 2
#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX 2
#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP 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_EPSIZE_IN (CFG_TUD_AUDIO_IN_PATH * (48 + 1) * (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_PATH * 1)
#define CFG_TUD_AUDIO_TX_FIFO_SIZE (CFG_TUD_AUDIO_IN_PATH ? ((CFG_TUD_AUDIO_EPSIZE_IN)) : 0)
#define CFG_TUD_AUDIO_ENABLE_EP_IN 1
#define CFG_TUD_AUDIO_EP_SZ_IN (CFG_TUD_AUDIO_IN_PATH * (48 + 1) * (CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX) * (CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)) // 48 Samples (48 kHz) x 2 Bytes/Sample x n Channels
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ CFG_TUD_AUDIO_EP_SZ_IN
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX CFG_TUD_AUDIO_EP_SZ_IN // Maximum EP IN size for all AS alternate settings used
// 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_OUT (CFG_TUD_AUDIO_OUT_PATH * ((48 + CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP) * (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_RX_FIFO_COUNT (CFG_TUD_AUDIO_OUT_PATH * 1)
#define CFG_TUD_AUDIO_RX_FIFO_SIZE (CFG_TUD_AUDIO_OUT_PATH ? (3 * (CFG_TUD_AUDIO_EPSIZE_OUT / CFG_TUD_AUDIO_RX_FIFO_COUNT)) : 0)
#define CFG_TUD_AUDIO_ENABLE_EP_OUT 1
#define CFG_TUD_AUDIO_EP_OUT_SZ (CFG_TUD_AUDIO_OUT_PATH * ((48 + CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP) * (CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX) * (CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX))) // N Samples (N kHz) x 2 Bytes/Sample x n Channels
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ CFG_TUD_AUDIO_EP_OUT_SZ*3
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX CFG_TUD_AUDIO_EP_OUT_SZ // Maximum EP IN size for all AS alternate settings used
// 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
#define CFG_TUD_AUDIO_FUNC_1_N_AS_INT 1
// Size of control request buffer
#define CFG_TUD_AUDIO_CTRL_BUF_SIZE 64
#define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64
#ifdef __cplusplus
}

View File

@ -87,18 +87,13 @@ uint8_t const * tud_descriptor_device_cb(void)
#define EPNUM_AUDIO 0x01
#endif
// These variables are required by the audio driver in audio_device.c
// List of audio descriptor lengths which is required by audio driver - you need as many entries as CFG_TUD_AUDIO
const uint16_t tud_audio_desc_lengths[] = {TUD_AUDIO_HEADSET_STEREO_DESC_LEN};
uint8_t const desc_configuration[] =
{
// Interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
// Interface number, string index, EP Out & EP In address, EP size
TUD_AUDIO_HEADSET_STEREO_DESCRIPTOR(2, 2, 16, EPNUM_AUDIO, CFG_TUD_AUDIO_EPSIZE_OUT, EPNUM_AUDIO | 0x80, CFG_TUD_AUDIO_EPSIZE_IN)
TUD_AUDIO_HEADSET_STEREO_DESCRIPTOR(2, 2, 16, EPNUM_AUDIO, CFG_TUD_AUDIO_EP_OUT_SZ, EPNUM_AUDIO | 0x80, CFG_TUD_AUDIO_EP_SZ_IN)
};
// Invoked when received GET CONFIGURATION DESCRIPTOR

View File

@ -26,7 +26,7 @@
#ifndef _USB_DESCRIPTORS_H_
#define _USB_DESCRIPTORS_H_
#include "tusb.h"
// #include "tusb.h"
// Unit numbers are arbitrary selected
#define UAC2_ENTITY_CLOCK 0x04

View File

@ -469,44 +469,28 @@ typedef enum
/// Additional Audio Device Class Codes - Source: Audio Data Formats
/// A.1 - Audio Class-Format Type Codes UAC2
//typedef enum
//{
// AUDIO_FORMAT_TYPE_UNDEFINED = 0x00,
// AUDIO_FORMAT_TYPE_I = 0x01,
// AUDIO_FORMAT_TYPE_II = 0x02,
// AUDIO_FORMAT_TYPE_III = 0x03,
// AUDIO_FORMAT_TYPE_IV = 0x04,
// AUDIO_EXT_FORMAT_TYPE_I = 0x81,
// AUDIO_EXT_FORMAT_TYPE_II = 0x82,
// AUDIO_EXT_FORMAT_TYPE_III = 0x83,
//} audio_format_type_t;
typedef enum
{
AUDIO_FORMAT_TYPE_UNDEFINED = 0x00,
AUDIO_FORMAT_TYPE_I = 0x01,
AUDIO_FORMAT_TYPE_II = 0x02,
AUDIO_FORMAT_TYPE_III = 0x03,
AUDIO_FORMAT_TYPE_IV = 0x04,
AUDIO_EXT_FORMAT_TYPE_I = 0x81,
AUDIO_EXT_FORMAT_TYPE_II = 0x82,
AUDIO_EXT_FORMAT_TYPE_III = 0x83,
} audio_format_type_t;
#define AUDIO_FORMAT_TYPE_UNDEFINED 0x00
#define AUDIO_FORMAT_TYPE_I 0x01
#define AUDIO_FORMAT_TYPE_II 0x02
#define AUDIO_FORMAT_TYPE_III 0x03
#define AUDIO_FORMAT_TYPE_IV 0x04
#define AUDIO_EXT_FORMAT_TYPE_I 0x81
#define AUDIO_EXT_FORMAT_TYPE_II 0x82
#define AUDIO_EXT_FORMAT_TYPE_III 0x83
/// A.2.1 - Audio Class-Audio Data Format Type I UAC2
//typedef enum
//{
// AUDIO_DATA_FORMAT_TYPE_I_PCM = (uint32_t) (1 << 0),
// AUDIO_DATA_FORMAT_TYPE_I_PCM8 = (uint32_t) (1 << 1),
// AUDIO_DATA_FORMAT_TYPE_I_IEEE_FLOAT = (uint32_t) (1 << 2),
// AUDIO_DATA_FORMAT_TYPE_I_ALAW = (uint32_t) (1 << 3),
// AUDIO_DATA_FORMAT_TYPE_I_MULAW = (uint32_t) (1 << 4),
// AUDIO_DATA_FORMAT_TYPE_I_RAW_DATA = 0x100000000,
//} audio_data_format_type_I_t;
#define AUDIO_DATA_FORMAT_TYPE_I_PCM ((uint32_t) (1 << 0))
#define AUDIO_DATA_FORMAT_TYPE_I_PCM8 ((uint32_t) (1 << 1))
#define AUDIO_DATA_FORMAT_TYPE_I_IEEE_FLOAT ((uint32_t) (1 << 2))
#define AUDIO_DATA_FORMAT_TYPE_I_ALAW ((uint32_t) (1 << 3))
#define AUDIO_DATA_FORMAT_TYPE_I_MULAW ((uint32_t) (1 << 4))
#define AUDIO_DATA_FORMAT_TYPE_I_RAW_DATA 0x100000000
// A.2.1 - Audio Class-Audio Data Format Type I UAC2
typedef enum
{
AUDIO_DATA_FORMAT_TYPE_I_PCM = (uint32_t) (1 << 0),
AUDIO_DATA_FORMAT_TYPE_I_PCM8 = (uint32_t) (1 << 1),
AUDIO_DATA_FORMAT_TYPE_I_IEEE_FLOAT = (uint32_t) (1 << 2),
AUDIO_DATA_FORMAT_TYPE_I_ALAW = (uint32_t) (1 << 3),
AUDIO_DATA_FORMAT_TYPE_I_MULAW = (uint32_t) (1 << 4),
AUDIO_DATA_FORMAT_TYPE_I_RAW_DATA = 0x100000000,
} audio_data_format_type_I_t;
/// All remaining definitions are taken from the descriptor descriptions in the UAC2 main specification
@ -901,7 +885,7 @@ typedef struct TU_ATTR_PACKED {
} subrange[numSubRanges] ; \
}
/// 5.2.3.2 2-byte Control RANGE Parameter Block
/// 5.2.3.2 2-byte Control RANGE Parameter Block
#define audio_control_range_2_n_t(numSubRanges) \
struct TU_ATTR_PACKED { \
uint16_t wNumSubRanges; \
@ -912,7 +896,7 @@ typedef struct TU_ATTR_PACKED {
} subrange[numSubRanges]; \
}
// 5.2.3.3 4-byte Control RANGE Parameter Block
// 5.2.3.3 4-byte Control RANGE Parameter Block
#define audio_control_range_4_n_t(numSubRanges) \
struct TU_ATTR_PACKED { \
uint16_t wNumSubRanges; \
@ -923,12 +907,12 @@ typedef struct TU_ATTR_PACKED {
} subrange[numSubRanges]; \
}
/** @} */
/** @} */
#ifdef __cplusplus
}
}
#endif
#endif
/** @} */
/** @} */

File diff suppressed because it is too large Load Diff

View File

@ -38,116 +38,306 @@
// Class Driver Configuration
//--------------------------------------------------------------------+
// 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 waste a few bytes)
#ifndef CFG_TUD_AUDIO_N_AS_INT
#define CFG_TUD_AUDIO_N_AS_INT 0
// All sizes are in bytes!
#ifndef CFG_TUD_AUDIO_FUNC_1_DESC_LEN
#error You must tell the driver the length of the audio function descriptor including IAD descriptor
#endif
#if CFG_TUD_AUDIO > 1
#ifndef CFG_TUD_AUDIO_FUNC_2_DESC_LEN
#error You must tell the driver the length of the audio function descriptor including IAD descriptor
#endif
#endif
#if CFG_TUD_AUDIO > 2
#ifndef CFG_TUD_AUDIO_FUNC_3_DESC_LEN
#error You must tell the driver the length of the audio function descriptor including IAD descriptor
#endif
#endif
// 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
#ifndef CFG_TUD_AUDIO_FUNC_1_N_AS_INT
#error You must tell the driver the number of Standard AS Interface Descriptors you have defined in the audio function descriptor!
#endif
#if CFG_TUD_AUDIO > 1
#ifndef CFG_TUD_AUDIO_FUNC_2_N_AS_INT
#error You must tell the driver the number of Standard AS Interface Descriptors you have defined in the audio function descriptor!
#endif
#endif
#if CFG_TUD_AUDIO > 2
#ifndef CFG_TUD_AUDIO_FUNC_3_N_AS_INT
#error You must tell the driver the number of Standard AS Interface Descriptors you have defined in the audio function descriptor!
#endif
#endif
// Size of control buffer used to receive and send control messages via EP0 - has to be big enough to hold your biggest request structure e.g. range requests with multiple intervals defined or cluster descriptors
#ifndef CFG_TUD_AUDIO_CTRL_BUF_SIZE
#ifndef CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ
#error You must define an audio class control request buffer size!
#endif
// Use of TX/RX FIFOs - If sizes are not zero, audio.c implements FIFOs for RX and TX (whatever defined).
// For RX: the input stream gets decoded into its corresponding channels, where for each channel a FIFO is setup to hold its data -> see: audio_rx_done_cb().
// For TX: the output stream is composed from CFG_TUD_AUDIO_N_CHANNELS_TX channels, where for each channel a FIFO is defined.
// Further, it implements encoding and decoding of the individual channels (parameterized by the defines below).
// If you don't use the FIFOs you need to handle encoding and decoding on your own in audio_rx_done_cb() and audio_tx_done_cb(). This, however, allows for optimizations.
#ifndef CFG_TUD_AUDIO_TX_FIFO_SIZE
#define CFG_TUD_AUDIO_TX_FIFO_SIZE 0 // Buffer size per channel
#if CFG_TUD_AUDIO > 1
#ifndef CFG_TUD_AUDIO_FUNC_2_CTRL_BUF_SZ
#error You must define an audio class control request buffer size!
#endif
#endif
#ifndef CFG_TUD_AUDIO_RX_FIFO_SIZE
#define CFG_TUD_AUDIO_RX_FIFO_SIZE 0 // Buffer size per channel
#if CFG_TUD_AUDIO > 2
#ifndef CFG_TUD_AUDIO_FUNC_3_CTRL_BUF_SZ
#error You must define an audio class control request buffer size!
#endif
#endif
// End point sizes - Limits: Full Speed <= 1023, High Speed <= 1024
#ifndef CFG_TUD_AUDIO_EPSIZE_IN
#define CFG_TUD_AUDIO_EPSIZE_IN 0 // TX
// End point sizes IN BYTES - Limits: Full Speed <= 1023, High Speed <= 1024
#ifndef CFG_TUD_AUDIO_ENABLE_EP_IN
#define CFG_TUD_AUDIO_ENABLE_EP_IN 0 // TX
#endif
#ifndef CFG_TUD_AUDIO_EPSIZE_OUT
#define CFG_TUD_AUDIO_EPSIZE_OUT 0 // RX
#ifndef CFG_TUD_AUDIO_ENABLE_EP_OUT
#define CFG_TUD_AUDIO_ENABLE_EP_OUT 0 // RX
#endif
// Maximum EP sizes for all alternate AS interface settings - used for checks and buffer allocation
#if CFG_TUD_AUDIO_ENABLE_EP_IN
#ifndef CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX
#error You must tell the driver the biggest EP IN size!
#endif
#if CFG_TUD_AUDIO > 1
#ifndef CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX
#error You must tell the driver the biggest EP IN size!
#endif
#endif
#if CFG_TUD_AUDIO > 2
#ifndef CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX
#error You must tell the driver the biggest EP IN size!
#endif
#endif
#endif // CFG_TUD_AUDIO_ENABLE_EP_IN
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
#ifndef CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX
#error You must tell the driver the biggest EP OUT size!
#endif
#if CFG_TUD_AUDIO > 1
#ifndef CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX
#error You must tell the driver the biggest EP OUT size!
#endif
#endif
#if CFG_TUD_AUDIO > 2
#ifndef CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX
#error You must tell the driver the biggest EP OUT size!
#endif
#endif
#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT
// Software EP FIFO buffer sizes - must be >= max EP SIZEs!
#ifndef CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ
#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ 0
#endif
#ifndef CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ
#define CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ 0
#endif
#ifndef CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ
#define CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ 0
#endif
#ifndef CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ
#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ 0
#endif
#ifndef CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ
#define CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ 0
#endif
#ifndef CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ
#define CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ 0
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_IN
#if CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ < CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX
#error EP software buffer size MUST BE at least as big as maximum EP size
#endif
#if CFG_TUD_AUDIO > 1
#if CFG_TUD_AUDIO_FUNC_2_EP_IN_SW_BUF_SZ < CFG_TUD_AUDIO_FUNC_2_EP_IN_SZ_MAX
#error EP software buffer size MUST BE at least as big as maximum EP size
#endif
#endif
#if CFG_TUD_AUDIO > 2
#if CFG_TUD_AUDIO_FUNC_3_EP_IN_SW_BUF_SZ < CFG_TUD_AUDIO_FUNC_3_EP_IN_SZ_MAX
#error EP software buffer size MUST BE at least as big as maximum EP size
#endif
#endif
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
#if CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ < CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX
#error EP software buffer size MUST BE at least as big as maximum EP size
#endif
#if CFG_TUD_AUDIO > 1
#if CFG_TUD_AUDIO_FUNC_2_EP_OUT_SW_BUF_SZ < CFG_TUD_AUDIO_FUNC_2_EP_OUT_SZ_MAX
#error EP software buffer size MUST BE at least as big as maximum EP size
#endif
#endif
#if CFG_TUD_AUDIO > 2
#if CFG_TUD_AUDIO_FUNC_3_EP_OUT_SW_BUF_SZ < CFG_TUD_AUDIO_FUNC_3_EP_OUT_SZ_MAX
#error EP software buffer size MUST BE at least as big as maximum EP size
#endif
#endif
#endif
// Enable/disable feedback EP (required for asynchronous RX applications)
#ifndef CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP 0 // Feedback
#define CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP 0 // Feedback - 0 or 1
#endif
// Audio interrupt control EP size - disabled if 0
#ifndef CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
#define CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN 0 // Audio interrupt control
#define CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN 0 // Audio interrupt control - if required - 6 Bytes according to UAC 2 specification (p. 74)
#endif
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
#ifndef CFG_TUD_AUDIO_INT_CTR_BUFSIZE
#define CFG_TUD_AUDIO_INT_CTR_BUFSIZE 6 // Buffer size of audio control interrupt EP - 6 Bytes according to UAC 2 specification (p. 74)
#ifndef CFG_TUD_AUDIO_INT_CTR_EP_IN_SW_BUFFER_SIZE
#define CFG_TUD_AUDIO_INT_CTR_EP_IN_SW_BUFFER_SIZE 6 // Buffer size of audio control interrupt EP - 6 Bytes according to UAC 2 specification (p. 74)
#endif
// Use software encoding/decoding
// The software coding feature of the driver is not mandatory. It is useful if, for instance, you have two I2S streams which need to be interleaved
// into a single PCM stream as SAMPLE_1 | SAMPLE_2 | SAMPLE_3 | SAMPLE_4.
//
// Currently, only PCM type I encoding/decoding is supported!
//
// If the coding feature is to be used, support FIFOs need to be configured. Their sizes and numbers are defined below.
// Encoding/decoding is done in software and thus time consuming. If you can encode/decode your stream more efficiently do not use the
// support FIFOs but write/read directly into/from the EP_X_SW_BUFFER_FIFOs using
// - tud_audio_n_write() or
// - tud_audio_n_read().
// To write/read to/from the support FIFOs use
// - tud_audio_n_write_support_ff() or
// - tud_audio_n_read_support_ff().
//
// The encoding/decoding format type done is defined below.
//
// The encoding/decoding starts when the private callback functions
// - audio_tx_done_cb()
// - audio_rx_done_cb()
// are invoked. If support FIFOs are used, the corresponding encoding/decoding functions are called from there.
// Once encoding/decoding is done the result is put directly into the EP_X_SW_BUFFER_FIFOs. You can use the public callback functions
// - tud_audio_tx_done_pre_load_cb() or tud_audio_tx_done_post_load_cb()
// - tud_audio_rx_done_pre_read_cb() or tud_audio_rx_done_post_read_cb()
// if you want to get informed what happened.
//
// If you don't use the support FIFOs you may use the public callback functions
// - tud_audio_tx_done_pre_load_cb() or tud_audio_tx_done_post_load_cb()
// - tud_audio_rx_done_pre_read_cb() or tud_audio_rx_done_post_read_cb()
// to write/read from/into the EP_X_SW_BUFFER_FIFOs at the right time.
//
// If you need a different encoding which is not support so far implement it in the
// - audio_tx_done_cb()
// - audio_rx_done_cb()
// functions.
// Enable encoding/decodings - for these to work, support FIFOs need to be setup in appropriate numbers and size
// The actual coding parameters of active AS alternate interface is parsed from the descriptors
// The item size of the FIFO is always fixed to one i.e. bytes! Furthermore, the actively used FIFO depth is reconfigured such that the depth is a multiple of the current sample size in order to avoid samples to get split up in case of a wrap in the FIFO ring buffer (depth = (max_depth / sampe_sz) * sampe_sz)!
// This is important to remind in case you use DMAs! If the sample sizes changes, the DMA MUST BE RECONFIGURED just like the FIFOs for a different depth!!!
// For PCM encoding/decoding
#ifndef CFG_TUD_AUDIO_ENABLE_ENCODING
#define CFG_TUD_AUDIO_ENABLE_ENCODING 0
#endif
#ifndef CFG_TUD_AUDIO_ENABLE_DECODING
#define CFG_TUD_AUDIO_ENABLE_DECODING 0
#endif
// This enabling allows to save the current coding parameters e.g. # of bytes per sample etc. - TYPE_I includes common PCM encoding
#ifndef CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
#define CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING 0
#endif
#ifndef CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
#define CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING 0
#endif
// Type I Coding parameters not given within UAC2 descriptors
// It would be possible to allow for a more flexible setting and not fix this parameter as done below. However, this is most often not needed and kept for later if really necessary. The more flexible setting could be implemented within set_interface(), however, how the values are saved per alternate setting is to be determined!
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING && CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
#ifndef CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX
#error You must tell the driver the number of channels per FIFO for the interleaved encoding! E.g. for an I2S interface having two channels, CHANNEL_PER_FIFO = 2 as the I2S stream having two channels is usually saved within one FIFO
#endif
#if CFG_TUD_AUDIO > 1
#ifndef CFG_TUD_AUDIO_FUNC_2_CHANNEL_PER_FIFO_TX
#error You must tell the driver the number of channels per FIFO for the interleaved encoding! E.g. for an I2S interface having two channels, CHANNEL_PER_FIFO = 2 as the I2S stream having two channels is usually saved within one FIFO
#endif
#endif
#if CFG_TUD_AUDIO > 2
#ifndef CFG_TUD_AUDIO_FUNC_3_CHANNEL_PER_FIFO_TX
#error You must tell the driver the number of channels per FIFO for the interleaved encoding! E.g. for an I2S interface having two channels, CHANNEL_PER_FIFO = 2 as the I2S stream having two channels is usually saved within one FIFO
#endif
#endif
#endif
#ifndef CFG_TUD_AUDIO_N_CHANNELS_TX
#define CFG_TUD_AUDIO_N_CHANNELS_TX 1
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING && CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
#ifndef CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_RX
#error You must tell the driver the number of channels per FIFO for the interleaved encoding! E.g. for an I2S interface having two channels, CHANNEL_PER_FIFO = 2 as the I2S stream having two channels is usually saved within one FIFO
#endif
#ifndef CFG_TUD_AUDIO_N_CHANNELS_RX
#define CFG_TUD_AUDIO_N_CHANNELS_RX 1
#if CFG_TUD_AUDIO > 1
#ifndef CFG_TUD_AUDIO_FUNC_2_CHANNEL_PER_FIFO_RX
#error You must tell the driver the number of channels per FIFO for the interleaved encoding! E.g. for an I2S interface having two channels, CHANNEL_PER_FIFO = 2 as the I2S stream having two channels is usually saved within one FIFO
#endif
// Audio data format types
#ifndef CFG_TUD_AUDIO_FORMAT_TYPE_TX
#define CFG_TUD_AUDIO_FORMAT_TYPE_TX AUDIO_FORMAT_TYPE_UNDEFINED // If this option is used, an encoding function has to be implemented in audio_device.c
#endif
#ifndef CFG_TUD_AUDIO_FORMAT_TYPE_RX
#define CFG_TUD_AUDIO_FORMAT_TYPE_RX AUDIO_FORMAT_TYPE_UNDEFINED // If this option is used, a decoding function has to be implemented in audio_device.c
#if CFG_TUD_AUDIO > 2
#ifndef CFG_TUD_AUDIO_FUNC_3_CHANNEL_PER_FIFO_RX
#error You must tell the driver the number of channels per FIFO for the interleaved encoding! E.g. for an I2S interface having two channels, CHANNEL_PER_FIFO = 2 as the I2S stream having two channels is usually saved within one FIFO
#endif
// Audio data format type I specifications
#if CFG_TUD_AUDIO_FORMAT_TYPE_TX == AUDIO_FORMAT_TYPE_I
// Type definitions - for possible formats see: audio_data_format_type_I_t and further in UAC2 specifications.
#ifndef CFG_TUD_AUDIO_FORMAT_TYPE_I_TX
#define CFG_TUD_AUDIO_FORMAT_TYPE_I_TX AUDIO_DATA_FORMAT_TYPE_I_PCM
#endif
#ifndef CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX // bSubslotSize
#define CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX 1
#endif
#ifndef CFG_TUD_AUDIO_TX_ITEMSIZE
#if CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX == 1
#define CFG_TUD_AUDIO_TX_ITEMSIZE 1
#elif CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX == 2
#define CFG_TUD_AUDIO_TX_ITEMSIZE 2
#else
#define CFG_TUD_AUDIO_TX_ITEMSIZE 4
#endif
#endif
#if CFG_TUD_AUDIO_TX_ITEMSIZE < CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_TX
#error FIFO element size (ITEMSIZE) must not be smaller then sample size
// Remaining types not support so far
// Number of support FIFOs to set up - multiple channels can be handled by one FIFO - very common is two channels per FIFO stemming from one I2S interface
#ifndef CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO
#define CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO 0
#endif
#ifndef CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO
#define CFG_TUD_AUDIO_FUNC_2_N_TX_SUPP_SW_FIFO 0
#endif
#ifndef CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO
#define CFG_TUD_AUDIO_FUNC_3_N_TX_SUPP_SW_FIFO 0
#endif
#ifndef CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO
#define CFG_TUD_AUDIO_FUNC_1_N_RX_SUPP_SW_FIFO 0
#endif
#ifndef CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO
#define CFG_TUD_AUDIO_FUNC_2_N_RX_SUPP_SW_FIFO 0
#endif
#ifndef CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO
#define CFG_TUD_AUDIO_FUNC_3_N_RX_SUPP_SW_FIFO 0
#endif
#if CFG_TUD_AUDIO_FORMAT_TYPE_RX == AUDIO_FORMAT_TYPE_I
#ifndef CFG_TUD_AUDIO_FORMAT_TYPE_I_RX
#define CFG_TUD_AUDIO_FORMAT_TYPE_I_RX AUDIO_DATA_FORMAT_TYPE_I_PCM
// Size of support FIFOs IN BYTES - if size > 0 there are as many FIFOs set up as CFG_TUD_AUDIO_FUNC_X_N_TX_SUPP_SW_FIFO and CFG_TUD_AUDIO_FUNC_X_N_RX_SUPP_SW_FIFO
#ifndef CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ
#define CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ 0 // FIFO size - minimum size: ceil(f_s/1000) * max(# of TX channels) / (# of TX support FIFOs) * max(# of bytes per sample)
#endif
#ifndef CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ
#define CFG_TUD_AUDIO_FUNC_2_TX_SUPP_SW_FIFO_SZ 0
#endif
#ifndef CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ
#define CFG_TUD_AUDIO_FUNC_3_TX_SUPP_SW_FIFO_SZ 0
#endif
#ifndef CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX // bSubslotSize
#define CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX 1
#ifndef CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ
#define CFG_TUD_AUDIO_FUNC_1_RX_SUPP_SW_FIFO_SZ 0 // FIFO size - minimum size: ceil(f_s/1000) * max(# of RX channels) / (# of RX support FIFOs) * max(# of bytes per sample)
#endif
#if CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX == 1
#define CFG_TUD_AUDIO_RX_ITEMSIZE 1
#elif CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX == 2
#define CFG_TUD_AUDIO_RX_ITEMSIZE 2
#else
#define CFG_TUD_AUDIO_RX_ITEMSIZE 4
#ifndef CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ
#define CFG_TUD_AUDIO_FUNC_2_RX_SUPP_SW_FIFO_SZ 0
#endif
#ifndef CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ
#define CFG_TUD_AUDIO_FUNC_3_RX_SUPP_SW_FIFO_SZ 0
#endif
//static_assert(sizeof(tud_audio_desc_lengths) != CFG_TUD_AUDIO, "Supply audio function descriptor pack length!");
@ -170,69 +360,70 @@ extern "C" {
//--------------------------------------------------------------------+
bool tud_audio_n_mounted (uint8_t itf);
#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
#if CFG_TUD_AUDIO_RX_FIFO_COUNT > 1
uint16_t tud_audio_n_available (uint8_t itf, uint8_t channelId);
uint16_t tud_audio_n_read (uint8_t itf, uint8_t channelId, void* buffer, uint16_t bufsize);
void tud_audio_n_read_flush (uint8_t itf, uint8_t channelId);
#else
uint16_t tud_audio_n_available (uint8_t itf);
uint16_t tud_audio_n_read (uint8_t itf, void* buffer, uint16_t bufsize);
void tud_audio_n_read_flush (uint8_t itf);
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
uint16_t tud_audio_n_available (uint8_t itf);
uint16_t tud_audio_n_read (uint8_t itf, void* buffer, uint16_t bufsize);
bool tud_audio_n_clear_ep_out_ff (uint8_t itf); // Delete all content in the EP OUT FIFO
#endif
/* This function is intended for later use once EP buffers (at least for ISO EPs) are implemented as ring buffers
#if CFG_TUD_AUDIO_EPSIZE_IN && !CFG_TUD_AUDIO_TX_FIFO_SIZE
uint16_t tud_audio_n_write_ep_in_buffer(uint8_t itf, const void * data, uint16_t len)
#endif
*/
#ifndef CFG_TUD_AUDIO_TX_FIFO_COUNT
#define CFG_TUD_AUDIO_TX_FIFO_COUNT 1
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
bool tud_audio_n_clear_rx_support_ff (uint8_t itf, uint8_t channelId); // Delete all content in the support RX FIFOs
uint16_t tud_audio_n_available_support_ff (uint8_t itf, uint8_t channelId);
uint16_t tud_audio_n_read_support_ff (uint8_t itf, uint8_t channelId, void* buffer, uint16_t bufsize);
#endif
#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
#if CFG_TUD_AUDIO_TX_FIFO_COUNT > 1
uint16_t tud_audio_n_write (uint8_t itf, uint8_t channelId, const void * data, uint16_t len);
#else
uint16_t tud_audio_n_write (uint8_t itf, const void * data, uint16_t len);
#endif
uint16_t tud_audio_n_write_flush(uint8_t itf);
#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
uint16_t tud_audio_n_write (uint8_t itf, const void * data, uint16_t len);
bool tud_audio_n_clear_ep_in_ff (uint8_t itf); // Delete all content in the EP IN FIFO
#endif
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0
uint16_t tud_audio_int_ctr_n_available (uint8_t itf);
uint16_t tud_audio_int_ctr_n_read (uint8_t itf, void* buffer, uint16_t bufsize);
void tud_audio_int_ctr_n_read_flush (uint8_t itf);
uint16_t tud_audio_int_ctr_n_write (uint8_t itf, uint8_t const* buffer, uint16_t bufsize);
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
uint16_t tud_audio_n_flush_tx_support_ff (uint8_t itf); // Force all content in the support TX FIFOs to be written into EP SW FIFO
bool tud_audio_n_clear_tx_support_ff (uint8_t itf, uint8_t channelId);
uint16_t tud_audio_n_write_support_ff (uint8_t itf, uint8_t channelId, const void * data, uint16_t len);
#endif
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
uint16_t tud_audio_int_ctr_n_write (uint8_t itf, uint8_t const* buffer, uint16_t len);
#endif
//--------------------------------------------------------------------+
// Application API (Interface0)
//--------------------------------------------------------------------+
static inline bool tud_audio_mounted (void);
static inline bool tud_audio_mounted (void);
#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
static inline uint16_t tud_audio_available (void);
static inline uint16_t tud_audio_read (void* buffer, uint16_t bufsize);
static inline void tud_audio_read_flush (void);
// RX API
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
static inline uint16_t tud_audio_available (void);
static inline bool tud_audio_clear_ep_out_ff (void); // Delete all content in the EP OUT FIFO
static inline uint16_t tud_audio_read (void* buffer, uint16_t bufsize);
#endif
#if CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
#if CFG_TUD_AUDIO_TX_FIFO_COUNT > 1
static inline uint16_t tud_audio_write (uint8_t channelId, uint8_t const* buffer, uint16_t bufsize);
#else
static inline uint16_t tud_audio_write (uint8_t const* buffer, uint16_t bufsize);
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
static inline bool tud_audio_clear_rx_support_ff (uint8_t channelId);
static inline uint16_t tud_audio_available_support_ff (uint8_t channelId);
static inline uint16_t tud_audio_read_support_ff (uint8_t channelId, void* buffer, uint16_t bufsize);
#endif
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0
static inline uint32_t tud_audio_int_ctr_available (void);
static inline uint32_t tud_audio_int_ctr_read (void* buffer, uint32_t bufsize);
static inline void tud_audio_int_ctr_read_flush (void);
static inline uint32_t tud_audio_int_ctr_write (uint8_t const* buffer, uint32_t bufsize);
// TX API
#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
static inline uint16_t tud_audio_write (const void * data, uint16_t len);
static inline bool tud_audio_clear_ep_in_ff (void);
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
static inline uint16_t tud_audio_flush_tx_support_ff (void);
static inline uint16_t tud_audio_clear_tx_support_ff (uint8_t channelId);
static inline uint16_t tud_audio_write_support_ff (uint8_t channelId, const void * data, uint16_t len);
#endif
// INT CTR API
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
static inline uint16_t tud_audio_int_ctr_write (uint8_t const* buffer, uint16_t len);
#endif
// Buffer control EP data and schedule a transmit
@ -247,26 +438,28 @@ bool tud_audio_buffer_and_schedule_control_xfer(uint8_t rhport, tusb_control_req
// Application Callback API (weak is optional)
//--------------------------------------------------------------------+
#if CFG_TUD_AUDIO_EPSIZE_IN
#if CFG_TUD_AUDIO_ENABLE_EP_IN
TU_ATTR_WEAK bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting);
TU_ATTR_WEAK bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting);
#endif
#if CFG_TUD_AUDIO_EPSIZE_OUT
TU_ATTR_WEAK bool tud_audio_rx_done_cb(uint8_t rhport, uint8_t * buffer, uint16_t bufsize);
#if CFG_TUD_AUDIO_ENABLE_EP_OUT
TU_ATTR_WEAK bool tud_audio_rx_done_pre_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t itf, uint8_t ep_out, uint8_t cur_alt_setting);
TU_ATTR_WEAK bool tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t itf, uint8_t ep_out, uint8_t cur_alt_setting);
#endif
#if CFG_TUD_AUDIO_EPSIZE_OUT > 0 && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
TU_ATTR_WEAK bool tud_audio_fb_done_cb(uint8_t rhport);
// User code should call this function with feedback value in 16.16 format for FS and HS.
// Value will be corrected for FS to 10.14 format automatically.
// (see Universal Serial Bus Specification Revision 2.0 5.12.4.2).
// Feedback value will be sent at FB endpoint interval till it's changed.
bool tud_audio_fb_set(uint8_t rhport, uint32_t feedback);
bool tud_audio_n_fb_set(uint8_t itf, uint32_t feedback);
static inline bool tud_audio_fb_set(uint32_t feedback);
#endif
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
TU_ATTR_WEAK bool tud_audio_int_ctr_done_cb(uint8_t rhport, uint16_t * n_bytes_copied);
TU_ATTR_WEAK bool tud_audio_int_ctr_done_cb(uint8_t rhport, uint16_t n_bytes_copied);
#endif
// Invoked when audio set interface request received
@ -302,93 +495,103 @@ static inline bool tud_audio_mounted(void)
return tud_audio_n_mounted(0);
}
#if CFG_TUD_AUDIO_EPSIZE_IN
#if CFG_TUD_AUDIO_TX_FIFO_SIZE && CFG_TUD_AUDIO_TX_FIFO_COUNT > 1
static inline uint16_t tud_audio_write (uint8_t channelId, uint8_t const* buffer, uint16_t n_bytes) // Short version if only one audio function is used
{
return tud_audio_n_write(0, channelId, buffer, n_bytes);
}
#else
static inline uint16_t tud_audio_write (uint8_t const* buffer, uint16_t n_bytes) // Short version if only one audio function is used
{
return tud_audio_n_write(0, buffer, n_bytes);
}
#endif
// RX API
static inline uint16_t tud_audio_write_flush (void) // Short version if only one audio function is used
{
#if CFG_TUD_AUDIO_TX_FIFO_SIZE
return tud_audio_n_write_flush(0);
#else
return 0;
#endif
}
#endif // CFG_TUD_AUDIO_EPSIZE_IN && CFG_TUD_AUDIO_TX_FIFO_SIZE
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && !CFG_TUD_AUDIO_ENABLE_DECODING
#if CFG_TUD_AUDIO_EPSIZE_OUT && CFG_TUD_AUDIO_RX_FIFO_SIZE
#if CFG_TUD_AUDIO_RX_FIFO_COUNT > 1
static inline uint16_t tud_audio_available(uint8_t channelId)
{
return tud_audio_n_available(0, channelId);
}
static inline uint16_t tud_audio_read(uint8_t channelId, void* buffer, uint16_t bufsize)
{
return tud_audio_n_read(0, channelId, buffer, bufsize);
}
static inline void tud_audio_read_flush(uint8_t channelId)
{
tud_audio_n_read_flush(0, channelId);
}
#else
static inline uint16_t tud_audio_available(void)
{
return tud_audio_n_available(0);
}
static inline uint16_t tud_audio_read(void *buffer, uint16_t bufsize)
static inline uint16_t tud_audio_read(void* buffer, uint16_t bufsize)
{
return tud_audio_n_read(0, buffer, bufsize);
}
static inline void tud_audio_read_flush(void)
static inline bool tud_audio_clear_ep_out_ff(void)
{
tud_audio_n_read_flush(0);
return tud_audio_n_clear_ep_out_ff(0);
}
#endif
#endif
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN > 0
static inline uint16_t tud_audio_int_ctr_available(void)
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
static inline bool tud_audio_clear_rx_support_ff(uint8_t channelId)
{
return tud_audio_int_ctr_n_available(0);
return tud_audio_n_clear_rx_support_ff(0, channelId);
}
static inline uint16_t tud_audio_int_ctr_read(void* buffer, uint16_t bufsize)
static inline uint16_t tud_audio_available_support_ff(uint8_t channelId)
{
return tud_audio_int_ctr_n_read(0, buffer, bufsize);
return tud_audio_n_available_support_ff(0, channelId);
}
static inline void tud_audio_int_ctr_read_flush(void)
static inline uint16_t tud_audio_read_support_ff(uint8_t channelId, void* buffer, uint16_t bufsize)
{
return tud_audio_int_ctr_n_read_flush(0);
return tud_audio_n_read_support_ff(0, channelId, buffer, bufsize);
}
static inline uint16_t tud_audio_int_ctr_write(uint8_t const* buffer, uint16_t bufsize)
#endif
// TX API
#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
static inline uint16_t tud_audio_write(const void * data, uint16_t len)
{
return tud_audio_int_ctr_n_write(0, buffer, bufsize);
return tud_audio_n_write(0, data, len);
}
static inline bool tud_audio_clear_ep_in_ff(void)
{
return tud_audio_n_clear_ep_in_ff(0);
}
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
static inline uint16_t tud_audio_flush_tx_support_ff(void)
{
return tud_audio_n_flush_tx_support_ff(0);
}
static inline uint16_t tud_audio_clear_tx_support_ff(uint8_t channelId)
{
return tud_audio_n_clear_tx_support_ff(0, channelId);
}
static inline uint16_t tud_audio_write_support_ff(uint8_t channelId, const void * data, uint16_t len)
{
return tud_audio_n_write_support_ff(0, channelId, data, len);
}
#endif
#if CFG_TUD_AUDIO_INT_CTR_EPSIZE_IN
static inline uint16_t tud_audio_int_ctr_write(uint8_t const* buffer, uint16_t len)
{
return tud_audio_int_ctr_n_write(0, buffer, len);
}
#endif
#if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
static inline bool tud_audio_fb_set(uint32_t feedback)
{
return tud_audio_n_fb_set(0, feedback);
}
#endif
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
void audiod_init (void);
void audiod_reset (uint8_t rhport);
uint16_t audiod_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool audiod_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
bool audiod_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes);
void audiod_init (void);
void audiod_reset (uint8_t rhport);
uint16_t audiod_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool audiod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
bool audiod_xfer_cb (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes);
#ifdef __cplusplus
}

View File

@ -244,8 +244,8 @@ void cdcd_init(void)
tu_fifo_config(&p_cdc->tx_ff, p_cdc->tx_ff_buf, TU_ARRAY_SIZE(p_cdc->tx_ff_buf), 1, true);
#if CFG_FIFO_MUTEX
tu_fifo_config_mutex(&p_cdc->rx_ff, osal_mutex_create(&p_cdc->rx_ff_mutex));
tu_fifo_config_mutex(&p_cdc->tx_ff, osal_mutex_create(&p_cdc->tx_ff_mutex));
tu_fifo_config_mutex(&p_cdc->rx_ff, NULL, osal_mutex_create(&p_cdc->rx_ff_mutex));
tu_fifo_config_mutex(&p_cdc->tx_ff, osal_mutex_create(&p_cdc->tx_ff_mutex), NULL);
#endif
}
}

View File

@ -378,8 +378,8 @@ void midid_init(void)
tu_fifo_config(&midi->tx_ff, midi->tx_ff_buf, CFG_TUD_MIDI_TX_BUFSIZE, 1, false); // OBVS.
#if CFG_FIFO_MUTEX
tu_fifo_config_mutex(&midi->rx_ff, osal_mutex_create(&midi->rx_ff_mutex));
tu_fifo_config_mutex(&midi->tx_ff, osal_mutex_create(&midi->tx_ff_mutex));
tu_fifo_config_mutex(&midi->rx_ff, NULL, osal_mutex_create(&midi->rx_ff_mutex));
tu_fifo_config_mutex(&midi->tx_ff, osal_mutex_create(&midi->tx_ff_mutex), NULL);
#endif
}
}

View File

@ -146,8 +146,8 @@ void vendord_init(void)
tu_fifo_config(&p_itf->tx_ff, p_itf->tx_ff_buf, CFG_TUD_VENDOR_TX_BUFSIZE, 1, false);
#if CFG_FIFO_MUTEX
tu_fifo_config_mutex(&p_itf->rx_ff, osal_mutex_create(&p_itf->rx_ff_mutex));
tu_fifo_config_mutex(&p_itf->tx_ff, osal_mutex_create(&p_itf->tx_ff_mutex));
tu_fifo_config_mutex(&p_itf->rx_ff, NULL, osal_mutex_create(&p_itf->rx_ff_mutex));
tu_fifo_config_mutex(&p_itf->tx_ff, osal_mutex_create(&p_itf->tx_ff_mutex), NULL);
#endif
}
}

View File

@ -39,34 +39,38 @@
// implement mutex lock and unlock
#if CFG_FIFO_MUTEX
static void tu_fifo_lock(tu_fifo_t *f)
static inline void _ff_lock(tu_fifo_mutex_t mutex)
{
if (f->mutex)
{
osal_mutex_lock(f->mutex, OSAL_TIMEOUT_WAIT_FOREVER);
}
if (mutex) osal_mutex_lock(mutex, OSAL_TIMEOUT_WAIT_FOREVER);
}
static void tu_fifo_unlock(tu_fifo_t *f)
static inline void _ff_unlock(tu_fifo_mutex_t mutex)
{
if (f->mutex)
{
osal_mutex_unlock(f->mutex);
}
if (mutex) osal_mutex_unlock(mutex);
}
#else
#define tu_fifo_lock(_ff)
#define tu_fifo_unlock(_ff)
#define _ff_lock(_mutex)
#define _ff_unlock(_mutex)
#endif
/** \enum tu_fifo_copy_mode_t
* \brief Write modes intended to allow special read and write functions to be able to copy data to and from USB hardware FIFOs as needed for e.g. STM32s and others
*/
typedef enum
{
TU_FIFO_COPY_INC, ///< Copy from/to an increasing source/destination address - default mode
TU_FIFO_COPY_CST_FULL_WORDS, ///< Copy from/to a constant source/destination address - required for e.g. STM32 to write into USB hardware FIFO
} tu_fifo_copy_mode_t;
bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable)
{
if (depth > 0x8000) return false; // Maximum depth is 2^15 items
tu_fifo_lock(f);
_ff_lock(f->mutex_wr);
_ff_lock(f->mutex_rd);
f->buffer = (uint8_t*) buffer;
f->depth = depth;
@ -78,66 +82,236 @@ bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_si
f->rd_idx = f->wr_idx = 0;
tu_fifo_unlock(f);
_ff_unlock(f->mutex_wr);
_ff_unlock(f->mutex_rd);
return true;
}
// Static functions are intended to work on local variables
static inline uint16_t _ff_mod(uint16_t idx, uint16_t depth)
{
while ( idx >= depth) idx -= depth;
return idx;
}
// send one item to FIFO WITHOUT updating write pointer
static inline void _ff_push(tu_fifo_t* f, void const * data, uint16_t wRel)
// Intended to be used to read from hardware USB FIFO in e.g. STM32 where all data is read from a constant address
// Code adapted from dcd_synopsis.c
// TODO generalize with configurable 1 byte or 4 byte each read
static void _ff_push_const_addr(uint8_t * ff_buf, const void * app_buf, uint16_t len)
{
memcpy(f->buffer + (wRel * f->item_size), data, f->item_size);
volatile uint32_t * rx_fifo = (volatile uint32_t *) app_buf;
// Reading full available 32 bit words from const app address
uint16_t full_words = len >> 2;
while(full_words--)
{
tu_unaligned_write32(ff_buf, *rx_fifo);
ff_buf += 4;
}
// Read the remaining 1-3 bytes from const app address
uint8_t const bytes_rem = len & 0x03;
if ( bytes_rem )
{
uint32_t tmp32 = *rx_fifo;
memcpy(ff_buf, &tmp32, bytes_rem);
}
}
// Intended to be used to write to hardware USB FIFO in e.g. STM32
// where all data is written to a constant address in full word copies
static void _ff_pull_const_addr(void * app_buf, const uint8_t * ff_buf, uint16_t len)
{
volatile uint32_t * tx_fifo = (volatile uint32_t *) app_buf;
// Pushing full available 32 bit words to const app address
uint16_t full_words = len >> 2;
while(full_words--)
{
*tx_fifo = tu_unaligned_read32(ff_buf);
ff_buf += 4;
}
// Write the remaining 1-3 bytes into const app address
uint8_t const bytes_rem = len & 0x03;
if ( bytes_rem )
{
uint32_t tmp32 = 0;
memcpy(&tmp32, ff_buf, bytes_rem);
*tx_fifo = tmp32;
}
}
// send one item to FIFO WITHOUT updating write pointer
static inline void _ff_push(tu_fifo_t* f, void const * app_buf, uint16_t rel)
{
memcpy(f->buffer + (rel * f->item_size), app_buf, f->item_size);
}
// send n items to FIFO WITHOUT updating write pointer
static void _ff_push_n(tu_fifo_t* f, void const * data, uint16_t n, uint16_t wRel)
static void _ff_push_n(tu_fifo_t* f, void const * app_buf, uint16_t n, uint16_t rel, tu_fifo_copy_mode_t copy_mode)
{
if(wRel + n <= f->depth) // Linear mode only
{
memcpy(f->buffer + (wRel * f->item_size), data, n*f->item_size);
}
else // Wrap around
{
uint16_t nLin = f->depth - wRel;
uint16_t const nLin = f->depth - rel;
uint16_t const nWrap = n - nLin;
// Write data to linear part of buffer
memcpy(f->buffer + (wRel * f->item_size), data, nLin*f->item_size);
uint16_t nLin_bytes = nLin * f->item_size;
uint16_t nWrap_bytes = nWrap * f->item_size;
// Write data wrapped around
memcpy(f->buffer, ((uint8_t const*) data) + nLin*f->item_size, (n - nLin) * f->item_size);
// current buffer of fifo
uint8_t* ff_buf = f->buffer + (rel * f->item_size);
switch (copy_mode)
{
case TU_FIFO_COPY_INC:
if(n <= nLin)
{
// Linear only
memcpy(ff_buf, app_buf, n*f->item_size);
}
else
{
// Wrap around
// Write data to linear part of buffer
memcpy(ff_buf, app_buf, nLin_bytes);
// Write data wrapped around
memcpy(f->buffer, ((uint8_t const*) app_buf) + nLin_bytes, nWrap_bytes);
}
break;
case TU_FIFO_COPY_CST_FULL_WORDS:
// Intended for hardware buffers from which it can be read word by word only
if(n <= nLin)
{
// Linear only
_ff_push_const_addr(ff_buf, app_buf, n*f->item_size);
}
else
{
// Wrap around case
// Write full words to linear part of buffer
uint16_t nLin_4n_bytes = nLin_bytes & 0xFFFC;
_ff_push_const_addr(ff_buf, app_buf, nLin_4n_bytes);
ff_buf += nLin_4n_bytes;
// There could be odd 1-3 bytes before the wrap-around boundary
volatile uint32_t * rx_fifo = (volatile uint32_t *) app_buf;
uint8_t rem = nLin_bytes & 0x03;
if (rem > 0)
{
uint8_t remrem = tu_min16(nWrap_bytes, 4-rem);
nWrap_bytes -= remrem;
uint32_t tmp32 = *rx_fifo;
uint8_t * src_u8 = ((uint8_t *) &tmp32);
// Write 1-3 bytes before wrapped boundary
while(rem--) *ff_buf++ = *src_u8++;
// Read more bytes to beginning to complete a word
ff_buf = f->buffer;
while(remrem--) *ff_buf++ = *src_u8++;
}
else
{
ff_buf = f->buffer; // wrap around to beginning
}
// Write data wrapped part
if (nWrap_bytes > 0) _ff_push_const_addr(ff_buf, app_buf, nWrap_bytes);
}
break;
}
}
// get one item from FIFO WITHOUT updating read pointer
static inline void _ff_pull(tu_fifo_t* f, void * p_buffer, uint16_t rRel)
static inline void _ff_pull(tu_fifo_t* f, void * app_buf, uint16_t rel)
{
memcpy(p_buffer, f->buffer + (rRel * f->item_size), f->item_size);
memcpy(app_buf, f->buffer + (rel * f->item_size), f->item_size);
}
// get n items from FIFO WITHOUT updating read pointer
static void _ff_pull_n(tu_fifo_t* f, void * p_buffer, uint16_t n, uint16_t rRel)
static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rel, tu_fifo_copy_mode_t copy_mode)
{
if(rRel + n <= f->depth) // Linear mode only
{
memcpy(p_buffer, f->buffer + (rRel * f->item_size), n*f->item_size);
}
else // Wrap around
{
uint16_t nLin = f->depth - rRel;
uint16_t const nLin = f->depth - rel;
uint16_t const nWrap = n - nLin; // only used if wrapped
// Read data from linear part of buffer
memcpy(p_buffer, f->buffer + (rRel * f->item_size), nLin*f->item_size);
uint16_t nLin_bytes = nLin * f->item_size;
uint16_t nWrap_bytes = nWrap * f->item_size;
// Read data wrapped part
memcpy((uint8_t*)p_buffer + nLin*f->item_size, f->buffer, (n - nLin) * f->item_size);
// current buffer of fifo
uint8_t* ff_buf = f->buffer + (rel * f->item_size);
switch (copy_mode)
{
case TU_FIFO_COPY_INC:
if ( n <= nLin )
{
// Linear only
memcpy(app_buf, ff_buf, n*f->item_size);
}
else
{
// Wrap around
// Read data from linear part of buffer
memcpy(app_buf, ff_buf, nLin_bytes);
// Read data wrapped part
memcpy((uint8_t*) app_buf + nLin_bytes, f->buffer, nWrap_bytes);
}
break;
case TU_FIFO_COPY_CST_FULL_WORDS:
if ( n <= nLin )
{
// Linear only
_ff_pull_const_addr(app_buf, ff_buf, n*f->item_size);
}
else
{
// Wrap around case
// Read full words from linear part of buffer
uint16_t nLin_4n_bytes = nLin_bytes & 0xFFFC;
_ff_pull_const_addr(app_buf, ff_buf, nLin_4n_bytes);
ff_buf += nLin_4n_bytes;
// There could be odd 1-3 bytes before the wrap-around boundary
volatile uint32_t * tx_fifo = (volatile uint32_t *) app_buf;
uint8_t rem = nLin_bytes & 0x03;
if (rem > 0)
{
uint8_t remrem = tu_min16(nWrap_bytes, 4-rem);
nWrap_bytes -= remrem;
uint32_t tmp32=0;
uint8_t * dst_u8 = (uint8_t *)&tmp32;
// Read 1-3 bytes before wrapped boundary
while(rem--) *dst_u8++ = *ff_buf++;
// Read more bytes from beginning to complete a word
ff_buf = f->buffer;
while(remrem--) *dst_u8++ = *ff_buf++;
*tx_fifo = tmp32;
}
else
{
ff_buf = f->buffer; // wrap around to beginning
}
// Read data wrapped part
if (nWrap_bytes > 0) _ff_pull_const_addr(app_buf, ff_buf, nWrap_bytes);
}
break;
default: break;
}
}
@ -179,7 +353,7 @@ static uint16_t get_relative_pointer(tu_fifo_t* f, uint16_t p, uint16_t offset)
return _ff_mod(advance_pointer(f, p, offset), f->depth);
}
// Works on local copies of w and r
// Works on local copies of w and r - return only the difference and as such can be used to determine an overflow
static inline uint16_t _tu_fifo_count(tu_fifo_t* f, uint16_t wAbs, uint16_t rAbs)
{
uint16_t cnt = wAbs-rAbs;
@ -246,7 +420,7 @@ static bool _tu_fifo_peek_at(tu_fifo_t* f, uint16_t offset, void * p_buffer, uin
// Works on local copies of w and r
// Must be protected by mutexes since in case of an overflow read pointer gets modified
static uint16_t _tu_fifo_peek_at_n(tu_fifo_t* f, uint16_t offset, void * p_buffer, uint16_t n, uint16_t wAbs, uint16_t rAbs)
static uint16_t _tu_fifo_peek_at_n(tu_fifo_t* f, uint16_t offset, void * p_buffer, uint16_t n, uint16_t wAbs, uint16_t rAbs, tu_fifo_copy_mode_t copy_mode)
{
uint16_t cnt = _tu_fifo_count(f, wAbs, rAbs);
@ -263,15 +437,12 @@ static uint16_t _tu_fifo_peek_at_n(tu_fifo_t* f, uint16_t offset, void * p_buffe
// Check if we can read something at and after offset - if too less is available we read what remains
cnt -= offset;
if (cnt < n) {
if (cnt == 0) return 0;
n = cnt;
}
if (cnt < n) n = cnt;
uint16_t rRel = get_relative_pointer(f, rAbs, offset);
// Peek data
_ff_pull_n(f, p_buffer, n, rRel);
_ff_pull_n(f, p_buffer, n, rRel, copy_mode);
return n;
}
@ -282,12 +453,67 @@ static inline uint16_t _tu_fifo_remaining(tu_fifo_t* f, uint16_t wAbs, uint16_t
return f->depth - _tu_fifo_count(f, wAbs, rAbs);
}
static uint16_t _tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n, tu_fifo_copy_mode_t copy_mode)
{
if ( n == 0 ) return 0;
_ff_lock(f->mutex_wr);
uint16_t w = f->wr_idx, r = f->rd_idx;
uint8_t const* buf8 = (uint8_t const*) data;
if (!f->overwritable)
{
// Not overwritable limit up to full
n = tu_min16(n, _tu_fifo_remaining(f, w, r));
}
else if (n >= f->depth)
{
// Only copy last part
buf8 = buf8 + (n - f->depth) * f->item_size;
n = f->depth;
// We start writing at the read pointer's position since we fill the complete
// buffer and we do not want to modify the read pointer within a write function!
// This would end up in a race condition with read functions!
w = r;
}
uint16_t wRel = get_relative_pointer(f, w, 0);
// Write data
_ff_push_n(f, buf8, n, wRel, copy_mode);
// Advance pointer
f->wr_idx = advance_pointer(f, w, n);
_ff_unlock(f->mutex_wr);
return n;
}
static uint16_t _tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n, tu_fifo_copy_mode_t copy_mode)
{
_ff_lock(f->mutex_rd);
// Peek the data
n = _tu_fifo_peek_at_n(f, 0, buffer, n, f->wr_idx, f->rd_idx, copy_mode); // f->rd_idx might get modified in case of an overflow so we can not use a local variable
// Advance read pointer
f->rd_idx = advance_pointer(f, f->rd_idx, n);
_ff_unlock(f->mutex_rd);
return n;
}
/******************************************************************************/
/*!
@brief Get number of items in FIFO.
As this function only reads the read and write pointers once, this function is
reentrant and thus thread and ISR save without any mutexes.
reentrant and thus thread and ISR save without any mutexes. In case an
overflow occurred, this function return f.depth at maximum. Overflows are
checked and corrected for in the read functions!
@param[in] f
Pointer to the FIFO buffer to manipulate
@ -297,7 +523,7 @@ static inline uint16_t _tu_fifo_remaining(tu_fifo_t* f, uint16_t wAbs, uint16_t
/******************************************************************************/
uint16_t tu_fifo_count(tu_fifo_t* f)
{
return _tu_fifo_count(f, f->wr_idx, f->rd_idx);
return tu_min16(_tu_fifo_count(f, f->wr_idx, f->rd_idx), f->depth);
}
/******************************************************************************/
@ -361,7 +587,7 @@ uint16_t tu_fifo_remaining(tu_fifo_t* f)
BE AWARE - THIS FUNCTION MIGHT NOT GIVE A CORRECT ANSWERE IN CASE WRITE POINTER "OVERFLOWS"
Only one overflow is allowed for this function to work e.g. if depth = 100, you must not
write more than 2*depth-1 items in one rush without updating write pointer. Otherwise
write pointer wraps and you pointer states are messed up. This can only happen if you
write pointer wraps and your pointer states are messed up. This can only happen if you
use DMAs, write functions do not allow such an error. Avoid such nasty things!
All reading functions (read, peek) check for overflows and correct read pointer on their own such
@ -383,9 +609,9 @@ bool tu_fifo_overflowed(tu_fifo_t* f)
// Only use in case tu_fifo_overflow() returned true!
void tu_fifo_correct_read_pointer(tu_fifo_t* f)
{
tu_fifo_lock(f);
_ff_lock(f->mutex_rd);
_tu_fifo_correct_read_pointer(f, f->wr_idx);
tu_fifo_unlock(f);
_ff_unlock(f->mutex_rd);
}
/******************************************************************************/
@ -406,7 +632,7 @@ void tu_fifo_correct_read_pointer(tu_fifo_t* f)
/******************************************************************************/
bool tu_fifo_read(tu_fifo_t* f, void * buffer)
{
tu_fifo_lock(f); // TODO: Here we may distinguish for read and write pointer mutexes!
_ff_lock(f->mutex_rd);
// Peek the data
bool ret = _tu_fifo_peek_at(f, 0, buffer, f->wr_idx, f->rd_idx); // f->rd_idx might get modified in case of an overflow so we can not use a local variable
@ -414,7 +640,7 @@ bool tu_fifo_read(tu_fifo_t* f, void * buffer)
// Advance pointer
f->rd_idx = advance_pointer(f, f->rd_idx, ret);
tu_fifo_unlock(f);
_ff_unlock(f->mutex_rd);
return ret;
}
@ -428,24 +654,20 @@ bool tu_fifo_read(tu_fifo_t* f, void * buffer)
Pointer to the FIFO buffer to manipulate
@param[in] buffer
The pointer to data location
@param[in] count
@param[in] n
Number of element that buffer can afford
@returns number of items read from the FIFO
*/
/******************************************************************************/
uint16_t tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t count)
uint16_t tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n)
{
tu_fifo_lock(f); // TODO: Here we may distinguish for read and write pointer mutexes!
return _tu_fifo_read_n(f, buffer, n, TU_FIFO_COPY_INC);
}
// Peek the data
count = _tu_fifo_peek_at_n(f, 0, buffer, count, f->wr_idx, f->rd_idx); // f->rd_idx might get modified in case of an overflow so we can not use a local variable
// Advance read pointer
f->rd_idx = advance_pointer(f, f->rd_idx, count);
tu_fifo_unlock(f);
return count;
uint16_t tu_fifo_read_n_const_addr_full_words(tu_fifo_t* f, void * buffer, uint16_t n)
{
return _tu_fifo_read_n(f, buffer, n, TU_FIFO_COPY_CST_FULL_WORDS);
}
/******************************************************************************/
@ -465,9 +687,9 @@ uint16_t tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t count)
/******************************************************************************/
bool tu_fifo_peek_at(tu_fifo_t* f, uint16_t offset, void * p_buffer)
{
tu_fifo_lock(f); // TODO: Here we may distinguish for read and write pointer mutexes!
_ff_lock(f->mutex_rd);
bool ret = _tu_fifo_peek_at(f, offset, p_buffer, f->wr_idx, f->rd_idx);
tu_fifo_unlock(f);
_ff_unlock(f->mutex_rd);
return ret;
}
@ -490,9 +712,9 @@ bool tu_fifo_peek_at(tu_fifo_t* f, uint16_t offset, void * p_buffer)
/******************************************************************************/
uint16_t tu_fifo_peek_at_n(tu_fifo_t* f, uint16_t offset, void * p_buffer, uint16_t n)
{
tu_fifo_lock(f); // TODO: Here we may distinguish for read and write pointer mutexes!
bool ret = _tu_fifo_peek_at_n(f, offset, p_buffer, n, f->wr_idx, f->rd_idx);
tu_fifo_unlock(f);
_ff_lock(f->mutex_rd);
bool ret = _tu_fifo_peek_at_n(f, offset, p_buffer, n, f->wr_idx, f->rd_idx, TU_FIFO_COPY_INC);
_ff_unlock(f->mutex_rd);
return ret;
}
@ -514,7 +736,7 @@ uint16_t tu_fifo_peek_at_n(tu_fifo_t* f, uint16_t offset, void * p_buffer, uint1
/******************************************************************************/
bool tu_fifo_write(tu_fifo_t* f, const void * data)
{
tu_fifo_lock(f);
_ff_lock(f->mutex_wr);
uint16_t w = f->wr_idx;
@ -528,7 +750,7 @@ bool tu_fifo_write(tu_fifo_t* f, const void * data)
// Advance pointer
f->wr_idx = advance_pointer(f, w, 1);
tu_fifo_unlock(f);
_ff_unlock(f->mutex_wr);
return true;
}
@ -547,43 +769,29 @@ bool tu_fifo_write(tu_fifo_t* f, const void * data)
@return Number of written elements
*/
/******************************************************************************/
uint16_t tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t count)
uint16_t tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n)
{
if ( count == 0 ) return 0;
return _tu_fifo_write_n(f, data, n, TU_FIFO_COPY_INC);
}
tu_fifo_lock(f);
/******************************************************************************/
/*!
@brief This function will write n elements into the array index specified by
the write pointer and increment the write index. The source address will
not be incremented which is useful for reading from registers.
uint16_t w = f->wr_idx, r = f->rd_idx;
uint8_t const* buf8 = (uint8_t const*) data;
if (!f->overwritable)
{
// Not overwritable limit up to full
count = tu_min16(count, _tu_fifo_remaining(f, w, r));
}
else if (count > f->depth)
{
// Only copy last part
buf8 = buf8 + (count - f->depth) * f->item_size;
count = f->depth;
// We start writing at the read pointer's position since we fill the complete
// buffer and we do not want to modify the read pointer within a write function!
// This would end up in a race condition with read functions!
f->wr_idx = r;
}
uint16_t wRel = get_relative_pointer(f, w, 0);
// Write data
_ff_push_n(f, buf8, count, wRel);
// Advance pointer
f->wr_idx = advance_pointer(f, w, count);
tu_fifo_unlock(f);
return count;
@param[in] f
Pointer to the FIFO buffer to manipulate
@param[in] data
The pointer to data to add to the FIFO
@param[in] count
Number of element
@return Number of written elements
*/
/******************************************************************************/
uint16_t tu_fifo_write_n_const_addr_full_words(tu_fifo_t* f, const void * data, uint16_t n)
{
return _tu_fifo_write_n(f, data, n, TU_FIFO_COPY_CST_FULL_WORDS);
}
/******************************************************************************/
@ -596,12 +804,15 @@ uint16_t tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t count)
/******************************************************************************/
bool tu_fifo_clear(tu_fifo_t *f)
{
tu_fifo_lock(f);
_ff_lock(f->mutex_wr);
_ff_lock(f->mutex_rd);
f->rd_idx = f->wr_idx = 0;
f->max_pointer_idx = 2*f->depth-1;
f->non_used_index_space = UINT16_MAX - f->max_pointer_idx;
tu_fifo_unlock(f);
_ff_unlock(f->mutex_wr);
_ff_unlock(f->mutex_rd);
return true;
}
@ -613,15 +824,17 @@ bool tu_fifo_clear(tu_fifo_t *f)
Pointer to the FIFO buffer to manipulate
@param[in] overwritable
Overwritable mode the fifo is set to
*/
*/
/******************************************************************************/
bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable)
{
tu_fifo_lock(f);
_ff_lock(f->mutex_wr);
_ff_lock(f->mutex_rd);
f->overwritable = overwritable;
tu_fifo_unlock(f);
_ff_unlock(f->mutex_wr);
_ff_unlock(f->mutex_rd);
return true;
}
@ -667,3 +880,146 @@ void tu_fifo_advance_read_pointer(tu_fifo_t *f, uint16_t n)
{
f->rd_idx = advance_pointer(f, f->rd_idx, n);
}
/******************************************************************************/
/*!
@brief Get linear read info
Returns the length and pointer from which bytes can be read in a linear manner.
This is of major interest for DMA transmissions. If returned length is zero the
corresponding pointer is invalid. The returned length is limited to the number
of ITEMS n which the user wants to write into the buffer.
The write pointer does NOT get advanced, use tu_fifo_advance_read_pointer() to
do so! If the length returned is less than n i.e. len<n, then a wrap occurs
and you need to execute this function a second time to get a pointer to the
wrapped part!
@param[in] f
Pointer to FIFO
@param[in] offset
Number of ITEMS to ignore before start writing
@param[out] **ptr
Pointer to start writing to
@param[in] n
Number of ITEMS to read from buffer
@return len
Length of linear part IN ITEMS, if zero corresponding pointer ptr is invalid
*/
/******************************************************************************/
uint16_t tu_fifo_get_linear_read_info(tu_fifo_t *f, uint16_t offset, void **ptr, uint16_t n)
{
// Operate on temporary values in case they change in between
uint16_t w = f->wr_idx, r = f->rd_idx;
uint16_t cnt = _tu_fifo_count(f, w, r);
// Check overflow and correct if required
if (cnt > f->depth)
{
_ff_lock(f->mutex_rd);
_tu_fifo_correct_read_pointer(f, w);
_ff_unlock(f->mutex_rd);
r = f->rd_idx;
cnt = f->depth;
}
// Skip beginning of buffer
if (cnt == 0 || offset >= cnt) return 0;
// Check if we can read something at and after offset - if too less is available we read what remains
cnt -= offset;
if (cnt < n) n = cnt;
// Get relative pointers
w = get_relative_pointer(f, w, 0);
r = get_relative_pointer(f, r, offset);
// Check if there is a wrap around necessary
uint16_t len;
if (w > r) {
len = w - r;
}
else
{
len = f->depth - r; // Also the case if FIFO was full
}
// Limit to required length
len = tu_min16(n, len);
// Copy pointer to buffer to start reading from
*ptr = &f->buffer[r];
return len;
}
/******************************************************************************/
/*!
@brief Get linear write info
Returns the length and pointer from which bytes can be written into buffer array in a linear manner.
This is of major interest for DMA transmissions not using circular mode. If returned length is zero the
corresponding pointer is invalid. The returned length is limited to the number of BYTES n which the user
wants to write into the buffer.
The write pointer does NOT get advanced, use tu_fifo_advance_write_pointer() to do so! If the length
returned is less than n i.e. len<n, then a wrap occurs and you need to execute this function a second
time to get a pointer to the wrapped part!
@param[in] f
Pointer to FIFO
@param[in] offset
Number of ITEMS to ignore before start writing
@param[out] **ptr
Pointer to start writing to
@param[in] n
Number of ITEMS to write into buffer
@return len
Length of linear part IN ITEMS, if zero corresponding pointer ptr is invalid
*/
/******************************************************************************/
uint16_t tu_fifo_get_linear_write_info(tu_fifo_t *f, uint16_t offset, void **ptr, uint16_t n)
{
uint16_t w = f->wr_idx, r = f->rd_idx;
uint16_t free = _tu_fifo_remaining(f, w, r);
if (!f->overwritable)
{
// Not overwritable limit up to full
n = tu_min16(n, free);
}
else if (n >= f->depth)
{
// If overwrite is allowed it must be less than or equal to 2 x buffer length, otherwise the overflow can not be resolved by the read functions
TU_VERIFY(n <= 2*f->depth);
n = f->depth;
// We start writing at the read pointer's position since we fill the complete
// buffer and we do not want to modify the read pointer within a write function!
// This would end up in a race condition with read functions!
w = r;
}
// Check if there is room to write to
if (free == 0 || offset >= free) return 0;
// Get relative pointers
w = get_relative_pointer(f, w, offset);
r = get_relative_pointer(f, r, 0);
uint16_t len;
if (w < r)
{
len = r-w;
}
else
{
len = f->depth - w;
}
// Limit to required length
len = tu_min16(n, len);
// Copy pointer to buffer to start reading from
*ptr = &f->buffer[w];
return len;
}

View File

@ -2,6 +2,7 @@
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
* Copyright (c) 2020 Reinhard Panhuber - rework to unmasked pointers
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -52,10 +53,10 @@ extern "C" {
#endif
#if CFG_FIFO_MUTEX
#include "osal/osal.h"
#define tu_fifo_mutex_t osal_mutex_t
#endif
/** \struct tu_fifo_t
* \brief Simple Circular FIFO
*/
@ -66,14 +67,15 @@ typedef struct
uint16_t item_size ; ///< size of each item
bool overwritable ;
uint16_t max_pointer_idx ; ///< maximum absolute pointer index
uint16_t non_used_index_space ; ///< required for non-power-of-two buffer length
uint16_t max_pointer_idx ; ///< maximum absolute pointer index
volatile uint16_t wr_idx ; ///< write pointer
volatile uint16_t rd_idx ; ///< read pointer
#if CFG_FIFO_MUTEX
tu_fifo_mutex_t mutex;
tu_fifo_mutex_t mutex_wr;
tu_fifo_mutex_t mutex_rd;
#endif
} tu_fifo_t;
@ -85,29 +87,33 @@ typedef struct
.item_size = sizeof(_type), \
.overwritable = _overwritable, \
.max_pointer_idx = 2*(_depth)-1, \
.non_used_index_space = UINT16_MAX - (2*(_depth)-1) \
.non_used_index_space = UINT16_MAX - (2*(_depth)-1), \
}
#define TU_FIFO_DEF(_name, _depth, _type, _overwritable) \
uint8_t _name##_buf[_depth*sizeof(_type)]; \
tu_fifo_t _name = TU_FIFO_INIT(_name##_buf, _depth, _type, _overwritable)
bool tu_fifo_set_overwritable(tu_fifo_t *f, bool overwritable);
bool tu_fifo_clear(tu_fifo_t *f);
bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable);
#if CFG_FIFO_MUTEX
static inline void tu_fifo_config_mutex(tu_fifo_t *f, tu_fifo_mutex_t mutex_hdl)
static inline void tu_fifo_config_mutex(tu_fifo_t *f, tu_fifo_mutex_t write_mutex_hdl, tu_fifo_mutex_t read_mutex_hdl)
{
f->mutex = mutex_hdl;
f->mutex_wr = write_mutex_hdl;
f->mutex_rd = read_mutex_hdl;
}
#endif
bool tu_fifo_write (tu_fifo_t* f, void const * p_data);
uint16_t tu_fifo_write_n (tu_fifo_t* f, void const * p_data, uint16_t count);
uint16_t tu_fifo_write_n (tu_fifo_t* f, void const * p_data, uint16_t n);
uint16_t tu_fifo_write_n_const_addr_full_words (tu_fifo_t* f, const void * data, uint16_t n);
bool tu_fifo_read (tu_fifo_t* f, void * p_buffer);
uint16_t tu_fifo_read_n (tu_fifo_t* f, void * p_buffer, uint16_t count);
uint16_t tu_fifo_read_n (tu_fifo_t* f, void * p_buffer, uint16_t n);
uint16_t tu_fifo_read_n_const_addr_full_words (tu_fifo_t* f, void * buffer, uint16_t n);
bool tu_fifo_peek_at (tu_fifo_t* f, uint16_t pos, void * p_buffer);
uint16_t tu_fifo_peek_at_n (tu_fifo_t* f, uint16_t pos, void * p_buffer, uint16_t n);
@ -116,7 +122,7 @@ uint16_t tu_fifo_count (tu_fifo_t* f);
bool tu_fifo_empty (tu_fifo_t* f);
bool tu_fifo_full (tu_fifo_t* f);
uint16_t tu_fifo_remaining (tu_fifo_t* f);
bool tu_fifo_overflowed (tu_fifo_t* f);
bool tu_fifo_overflowed (tu_fifo_t* f);
void tu_fifo_correct_read_pointer (tu_fifo_t* f);
// Pointer modifications intended to be used in combinations with DMAs.
@ -124,6 +130,13 @@ void tu_fifo_correct_read_pointer (tu_fifo_t* f);
void tu_fifo_advance_write_pointer (tu_fifo_t *f, uint16_t n);
void tu_fifo_advance_read_pointer (tu_fifo_t *f, uint16_t n);
// If you want to read/write from/to the FIFO by use of a DMA, you may need to conduct two copies to handle a possible wrapping part
// This functions deliver a pointer to start reading/writing from/to and a valid linear length along which no wrap occurs.
// In case not all of your data is available within one read/write, update the read/write pointer by
// tu_fifo_advance_read_pointer()/tu_fifo_advance_write_pointer and conduct a second read/write operation
uint16_t tu_fifo_get_linear_read_info (tu_fifo_t *f, uint16_t offset, void **ptr, uint16_t n);
uint16_t tu_fifo_get_linear_write_info (tu_fifo_t *f, uint16_t offset, void **ptr, uint16_t n);
static inline bool tu_fifo_peek(tu_fifo_t* f, void * p_buffer)
{
return tu_fifo_peek_at(f, 0, p_buffer);

View File

@ -32,6 +32,7 @@
#define _TUSB_DCD_H_
#include "common/tusb_common.h"
#include "common/tusb_fifo.h"
#ifdef __cplusplus
extern "C" {
@ -124,20 +125,24 @@ void dcd_disconnect(uint8_t rhport) TU_ATTR_WEAK;
void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request) TU_ATTR_WEAK;
// Configure endpoint's registers according to descriptor
bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc);
bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc);
// Close an endpoint.
// Since it is weak, caller must TU_ASSERT this function's existence before calling it.
void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr) TU_ATTR_WEAK;
void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr) TU_ATTR_WEAK;
// Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack
bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes);
bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes);
// Submit an transfer using fifo, When complete dcd_event_xfer_complete() is invoked to notify the stack
// This API is optional, may be useful for register-based for transferring data.
bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes) TU_ATTR_WEAK;
// Stall endpoint
void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr);
void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr);
// clear stall, data toggle is also reset to DATA0
void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr);
void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr);
//--------------------------------------------------------------------+
// Event API (implemented by stack)

View File

@ -29,9 +29,9 @@
#if TUSB_OPT_DEVICE_ENABLED
#include "tusb.h"
#include "usbd.h"
#include "device/usbd.h"
#include "device/usbd_pvt.h"
#include "dcd.h"
#include "device/dcd.h"
#ifndef CFG_TUD_TASK_QUEUE_SZ
#define CFG_TUD_TASK_QUEUE_SZ 16
@ -1241,6 +1241,39 @@ bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
}
}
// The number of bytes has to be given explicitly to allow more flexible control of how many
// bytes should be written and second to keep the return value free to give back a boolean
// success message. If total_bytes is too big, the FIFO will copy only what is available
// into the USB buffer!
bool usbd_edpt_iso_xfer(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
{
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
TU_LOG2(" Queue ISO EP %02X with %u bytes ... ", ep_addr, total_bytes);
// Attempt to transfer on a busy endpoint, sound like an race condition !
TU_ASSERT(_usbd_dev.ep_status[epnum][dir].busy == 0);
// Set busy first since the actual transfer can be complete before dcd_edpt_xfer() could return
// and usbd task can preempt and clear the busy
_usbd_dev.ep_status[epnum][dir].busy = true;
if (dcd_edpt_xfer_fifo(rhport, ep_addr, ff, total_bytes))
{
TU_LOG2("OK\r\n");
return true;
}else
{
// DCD error, mark endpoint as ready to allow next transfer
_usbd_dev.ep_status[epnum][dir].busy = false;
_usbd_dev.ep_status[epnum][dir].claimed = 0;
TU_LOG2("failed\r\n");
TU_BREAKPOINT();
return false;
}
}
bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;

View File

@ -353,6 +353,10 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
#define TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN (6+(2+1)*4)
#define TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL(_unitid, _srcid, _ctrlch0master, _ctrlch1, _ctrlch2, _stridx) \
TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_FEATURE_UNIT, _unitid, _srcid, U32_TO_U8S_LE(_ctrlch0master), U32_TO_U8S_LE(_ctrlch1), U32_TO_U8S_LE(_ctrlch2), _stridx
// 4 - Channels
#define TUD_AUDIO_DESC_FEATURE_UNIT_FOUR_CHANNEL_LEN (6+(4+1)*4)
#define TUD_AUDIO_DESC_FEATURE_UNIT_FOUR_CHANNEL(_unitid, _srcid, _ctrlch0master, _ctrlch1, _ctrlch2, _ctrlch3, _ctrlch4, _stridx) \
TUD_AUDIO_DESC_FEATURE_UNIT_FOUR_CHANNEL_LEN, TUSB_DESC_CS_INTERFACE, AUDIO_CS_AC_INTERFACE_FEATURE_UNIT, _unitid, _srcid, U32_TO_U8S_LE(_ctrlch0master), U32_TO_U8S_LE(_ctrlch1), U32_TO_U8S_LE(_ctrlch2), U32_TO_U8S_LE(_ctrlch3), U32_TO_U8S_LE(_ctrlch4), _stridx
// For more channels, add definitions here
@ -389,7 +393,7 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
// AUDIO simple descriptor (UAC2) for 1 microphone input
// - 1 Input Terminal, 1 Feature Unit (Mute and Volume Control), 1 Output Terminal, 1 Clock Source
#define TUD_AUDIO_MIC_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN\
#define TUD_AUDIO_MIC_ONE_CH_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\
@ -403,9 +407,9 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
+ TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN\
+ TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN)
#define TUD_AUDIO_MIC_DESC_N_AS_INT 1 // Number of AS interfaces
#define TUD_AUDIO_MIC_ONE_CH_DESC_N_AS_INT 1 // Number of AS interfaces
#define TUD_AUDIO_MIC_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epin, _epsize) \
#define TUD_AUDIO_MIC_ONE_CH_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epin, _epsize) \
/* Standard Interface Association Descriptor (IAD) */\
TUD_AUDIO_DESC_IAD(/*_firstitfs*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00),\
/* Standard AC Interface Descriptor(4.7.1) */\
@ -435,6 +439,55 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
/* 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)
// AUDIO simple descriptor (UAC2) for 4 microphone input
// - 1 Input Terminal, 1 Feature Unit (Mute and Volume Control), 1 Output Terminal, 1 Clock Source
#define TUD_AUDIO_MIC_FOUR_CH_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_FOUR_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_MIC_FOUR_CH_DESC_N_AS_INT 1 // Number of AS interfaces
#define TUD_AUDIO_MIC_FOUR_CH_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epin, _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_MICROPHONE, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+TUD_AUDIO_DESC_OUTPUT_TERM_LEN+TUD_AUDIO_DESC_FEATURE_UNIT_FOUR_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*/ 0x01, /*_stridx*/ 0x00),\
/* Input Terminal Descriptor(4.7.2.4) */\
TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ 0x01, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x03, /*_clkid*/ 0x04, /*_nchannelslogical*/ 0x04, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 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_USB_STREAMING, /*_assocTerm*/ 0x01, /*_srcid*/ 0x02, /*_clkid*/ 0x04, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\
/* Feature Unit Descriptor(4.7.2.8) */\
TUD_AUDIO_DESC_FEATURE_UNIT_FOUR_CHANNEL(/*_unitid*/ 0x02, /*_srcid*/ 0x01, /*_ctrlch0master*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch2*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch3*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch4*/ 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*/ 0x03, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x04, /*_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*/ _epin, /*_attr*/ (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ASYNCHRONOUS | 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)
// AUDIO simple descriptor (UAC2) for mono speaker
// - 1 Input Terminal, 2 Feature Unit (Mute and Volume Control), 3 Output Terminal, 4 Clock Source

View File

@ -72,6 +72,9 @@ void usbd_edpt_close(uint8_t rhport, uint8_t ep_addr);
// Submit a usb transfer
bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes);
// Submit a usb ISO transfer by use of a FIFO (ring buffer) - all bytes in FIFO get transmitted
bool usbd_edpt_iso_xfer(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes);
// Claim an endpoint before submitting a transfer.
// If caller does not make any transfer, it must release endpoint for others.
bool usbd_edpt_claim(uint8_t rhport, uint8_t ep_addr);

View File

@ -143,7 +143,7 @@ static inline bool osal_queue_receive(osal_queue_t qhdl, void* data)
// TODO: revisit... docs say that mutexes are never used from IRQ context,
// however osal_queue_recieve may be. therefore my assumption is that
// the fifo mutex is not populated for queues used from an IRQ context
assert(!qhdl->ff.mutex);
//assert(!qhdl->ff.mutex);
_osal_q_lock(qhdl);
bool success = tu_fifo_read(&qhdl->ff, data);
@ -157,7 +157,7 @@ static inline bool osal_queue_send(osal_queue_t qhdl, void const * data, bool in
// TODO: revisit... docs say that mutexes are never used from IRQ context,
// however osal_queue_recieve may be. therefore my assumption is that
// the fifo mutex is not populated for queues used from an IRQ context
assert(!qhdl->ff.mutex);
//assert(!qhdl->ff.mutex);
_osal_q_lock(qhdl);
bool success = tu_fifo_write(&qhdl->ff, data);

View File

@ -27,6 +27,7 @@
*/
#include "tusb_option.h"
#include "common/tusb_fifo.h"
#if CFG_TUSB_MCU == OPT_MCU_ESP32S2 && TUSB_OPT_DEVICE_ENABLED
@ -59,6 +60,7 @@
typedef struct {
uint8_t *buffer;
// tu_fifo_t * ff; // TODO support dcd_edpt_xfer_fifo API
uint16_t total_len;
uint16_t queued_len;
uint16_t max_size;
@ -319,6 +321,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to
xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
xfer->buffer = buffer;
// xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API
xfer->total_len = total_bytes;
xfer->queued_len = 0;
xfer->short_packet = false;
@ -354,6 +357,56 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to
return true;
}
#if 0 // TODO support dcd_edpt_xfer_fifo API
bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
{
(void)rhport;
// USB buffers always work in bytes so to avoid unnecessary divisions we demand item_size = 1
TU_ASSERT(ff->item_size == 1);
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
xfer->buffer = NULL;
xfer->ff = ff;
xfer->total_len = total_bytes;
xfer->queued_len = 0;
xfer->short_packet = false;
uint16_t num_packets = (total_bytes / xfer->max_size);
uint8_t short_packet_size = total_bytes % xfer->max_size;
// Zero-size packet is special case.
if (short_packet_size > 0 || (total_bytes == 0)) {
num_packets++;
}
ESP_LOGV(TAG, "Transfer <-> EP%i, %s, pkgs: %i, bytes: %i",
epnum, ((dir == TUSB_DIR_IN) ? "USB0.HOST (in)" : "HOST->DEV (out)"),
num_packets, total_bytes);
// IN and OUT endpoint xfers are interrupt-driven, we just schedule them
// here.
if (dir == TUSB_DIR_IN) {
// A full IN transfer (multiple packets, possibly) triggers XFRC.
USB0.in_ep_reg[epnum].dieptsiz = (num_packets << USB_D_PKTCNT0_S) | total_bytes;
USB0.in_ep_reg[epnum].diepctl |= USB_D_EPENA1_M | USB_D_CNAK1_M; // Enable | CNAK
// Enable fifo empty interrupt only if there are something to put in the fifo.
if(total_bytes != 0) {
USB0.dtknqr4_fifoemptymsk |= (1 << epnum);
}
} else {
// Each complete packet for OUT xfers triggers XFRC.
USB0.out_ep_reg[epnum].doeptsiz |= USB_PKTCNT0_M | ((xfer->max_size & USB_XFERSIZE0_V) << USB_XFERSIZE0_S);
USB0.out_ep_reg[epnum].doepctl |= USB_EPENA0_M | USB_CNAK0_M;
}
return true;
}
#endif
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
{
(void)rhport;
@ -462,35 +515,46 @@ static void receive_packet(xfer_ctl_t *xfer, /* usb_out_endpoint_t * out_ep, */
to_recv_size = (xfer_size > xfer->max_size) ? xfer->max_size : xfer_size;
}
uint8_t to_recv_rem = to_recv_size % 4;
uint16_t to_recv_size_aligned = to_recv_size - to_recv_rem;
// Do not assume xfer buffer is aligned.
uint8_t *base = (xfer->buffer + xfer->queued_len);
// This for loop always runs at least once- skip if less than 4 bytes
// to collect.
if (to_recv_size >= 4) {
for (uint16_t i = 0; i < to_recv_size_aligned; i += 4) {
uint32_t tmp = (*rx_fifo);
base[i] = tmp & 0x000000FF;
base[i + 1] = (tmp & 0x0000FF00) >> 8;
base[i + 2] = (tmp & 0x00FF0000) >> 16;
base[i + 3] = (tmp & 0xFF000000) >> 24;
}
// Common buffer read
#if 0 // TODO support dcd_edpt_xfer_fifo API
if (xfer->ff)
{
// Ring buffer
tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void *) rx_fifo, to_recv_size);
}
else
#endif
{
uint8_t to_recv_rem = to_recv_size % 4;
uint16_t to_recv_size_aligned = to_recv_size - to_recv_rem;
// Do not read invalid bytes from RX FIFO.
if (to_recv_rem != 0) {
uint32_t tmp = (*rx_fifo);
uint8_t *last_32b_bound = base + to_recv_size_aligned;
// Do not assume xfer buffer is aligned.
uint8_t *base = (xfer->buffer + xfer->queued_len);
last_32b_bound[0] = tmp & 0x000000FF;
if (to_recv_rem > 1) {
last_32b_bound[1] = (tmp & 0x0000FF00) >> 8;
// This for loop always runs at least once- skip if less than 4 bytes
// to collect.
if (to_recv_size >= 4) {
for (uint16_t i = 0; i < to_recv_size_aligned; i += 4) {
uint32_t tmp = (*rx_fifo);
base[i] = tmp & 0x000000FF;
base[i + 1] = (tmp & 0x0000FF00) >> 8;
base[i + 2] = (tmp & 0x00FF0000) >> 16;
base[i + 3] = (tmp & 0xFF000000) >> 24;
}
}
if (to_recv_rem > 2) {
last_32b_bound[2] = (tmp & 0x00FF0000) >> 16;
// Do not read invalid bytes from RX FIFO.
if (to_recv_rem != 0) {
uint32_t tmp = (*rx_fifo);
uint8_t *last_32b_bound = base + to_recv_size_aligned;
last_32b_bound[0] = tmp & 0x000000FF;
if (to_recv_rem > 1) {
last_32b_bound[1] = (tmp & 0x0000FF00) >> 8;
}
if (to_recv_rem > 2) {
last_32b_bound[2] = (tmp & 0x00FF0000) >> 16;
}
}
}
@ -510,38 +574,48 @@ static void transmit_packet(xfer_ctl_t *xfer, volatile usb_in_endpoint_t *in_ep,
xfer->queued_len = xfer->total_len - remaining;
uint16_t to_xfer_size = (remaining > xfer->max_size) ? xfer->max_size : remaining;
uint8_t to_xfer_rem = to_xfer_size % 4;
uint16_t to_xfer_size_aligned = to_xfer_size - to_xfer_rem;
// Buffer might not be aligned to 32b, so we need to force alignment
// by copying to a temp var.
uint8_t *base = (xfer->buffer + xfer->queued_len);
#if 0 // TODO support dcd_edpt_xfer_fifo API
if (xfer->ff)
{
tu_fifo_read_n_const_addr_full_words(xfer->ff, (void *) tx_fifo, to_xfer_size);
}
else
#endif
{
uint8_t to_xfer_rem = to_xfer_size % 4;
uint16_t to_xfer_size_aligned = to_xfer_size - to_xfer_rem;
// Buffer might not be aligned to 32b, so we need to force alignment
// by copying to a temp var.
uint8_t *base = (xfer->buffer + xfer->queued_len);
// This for loop always runs at least once- skip if less than 4 bytes
// to send off.
if (to_xfer_size >= 4) {
for (uint16_t i = 0; i < to_xfer_size_aligned; i += 4) {
uint32_t tmp = base[i] | (base[i + 1] << 8) |
(base[i + 2] << 16) | (base[i + 3] << 24);
(*tx_fifo) = tmp;
}
}
// Do not read beyond end of buffer if not divisible by 4.
if (to_xfer_rem != 0) {
uint32_t tmp = 0;
uint8_t *last_32b_bound = base + to_xfer_size_aligned;
tmp |= last_32b_bound[0];
if (to_xfer_rem > 1) {
tmp |= (last_32b_bound[1] << 8);
}
if (to_xfer_rem > 2) {
tmp |= (last_32b_bound[2] << 16);
}
// This for loop always runs at least once- skip if less than 4 bytes
// to send off.
if (to_xfer_size >= 4) {
for (uint16_t i = 0; i < to_xfer_size_aligned; i += 4) {
uint32_t tmp = base[i] | (base[i + 1] << 8) |
(base[i + 2] << 16) | (base[i + 3] << 24);
(*tx_fifo) = tmp;
}
}
// Do not read beyond end of buffer if not divisible by 4.
if (to_xfer_rem != 0) {
uint32_t tmp = 0;
uint8_t *last_32b_bound = base + to_xfer_size_aligned;
tmp |= last_32b_bound[0];
if (to_xfer_rem > 1) {
tmp |= (last_32b_bound[1] << 8);
}
if (to_xfer_rem > 2) {
tmp |= (last_32b_bound[2] << 16);
}
(*tx_fifo) = tmp;
}
}
static void read_rx_fifo(void)

View File

@ -25,6 +25,7 @@
*/
#include "tusb_option.h"
#include "common/tusb_fifo.h"
#if CFG_TUSB_MCU == OPT_MCU_SAMG
@ -43,6 +44,7 @@
typedef struct
{
uint8_t* buffer;
// tu_fifo_t* ff; // TODO support dcd_edpt_xfer_fifo API
uint16_t total_len;
volatile uint16_t actual_len;
uint16_t epsize;
@ -59,6 +61,7 @@ void xfer_epsize_set(xfer_desc_t* xfer, uint16_t epsize)
void xfer_begin(xfer_desc_t* xfer, uint8_t * buffer, uint16_t total_bytes)
{
xfer->buffer = buffer;
// xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API
xfer->total_len = total_bytes;
xfer->actual_len = 0;
}
@ -66,6 +69,7 @@ void xfer_begin(xfer_desc_t* xfer, uint8_t * buffer, uint16_t total_bytes)
void xfer_end(xfer_desc_t* xfer)
{
xfer->buffer = NULL;
// xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API
xfer->total_len = 0;
xfer->actual_len = 0;
}
@ -293,6 +297,14 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
return true;
}
#if 0 // TODO support dcd_edpt_xfer_fifo API
bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
{
(void) rhport;
return true;
}
#endif
// Stall endpoint
void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
{
@ -402,7 +414,16 @@ void dcd_int_handler(uint8_t rhport)
if (xact_len)
{
// write to EP fifo
xact_ep_write(epnum, xfer->buffer, xact_len);
#if 0 // TODO support dcd_edpt_xfer_fifo
if (xfer->ff)
{
tu_fifo_read_n_const_addr_full_words(xfer->ff, (void *) &UDP->UDP_FDR[epnum], xact_len);
}
else
#endif
{
xact_ep_write(epnum, xfer->buffer, xact_len);
}
// TX ready for transfer
csr_set(epnum, UDP_CSR_TXPKTRDY_Msk);
@ -428,7 +449,17 @@ void dcd_int_handler(uint8_t rhport)
uint16_t const xact_len = (uint16_t) ((UDP->UDP_CSR[epnum] & UDP_CSR_RXBYTECNT_Msk) >> UDP_CSR_RXBYTECNT_Pos);
// Read from EP fifo
xact_ep_read(epnum, xfer->buffer, xact_len);
#if 0 // TODO support dcd_edpt_xfer_fifo API
if (xfer->ff)
{
tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void *) &UDP->UDP_FDR[epnum], xact_len);
}
else
#endif
{
xact_ep_read(epnum, xfer->buffer, xact_len);
}
xfer_packet_done(xfer);
if ( 0 == xfer_packet_len(xfer) )

View File

@ -34,6 +34,7 @@
*/
#include "tusb_option.h"
#include "common/tusb_fifo.h"
#if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_NUC120)
@ -76,6 +77,7 @@ static bool active_ep0_xfer;
static struct xfer_ctl_t
{
uint8_t *data_ptr; /* data_ptr tracks where to next copy data to (for OUT) or from (for IN) */
// tu_fifo_t * ff; /* pointer to FIFO required for dcd_edpt_xfer_fifo() */ // TODO support dcd_edpt_xfer_fifo API
union {
uint16_t in_remaining_bytes; /* for IN endpoints, we track how many bytes are left to transfer */
uint16_t out_bytes_so_far; /* but for OUT endpoints, we track how many bytes we've transferred so far */
@ -142,7 +144,17 @@ static void dcd_in_xfer(struct xfer_ctl_t *xfer, USBD_EP_T *ep)
{
uint16_t bytes_now = tu_min16(xfer->in_remaining_bytes, xfer->max_packet_size);
memcpy((uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), xfer->data_ptr, bytes_now);
#if 0 // TODO support dcd_edpt_xfer_fifo API
if (xfer->ff)
{
tu_fifo_read_n(xfer->ff, (void *) (USBD_BUF_BASE + ep->BUFSEG), bytes_now);
}
else
#endif
{
memcpy((uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), xfer->data_ptr, bytes_now);
}
ep->MXPLD = bytes_now;
}
@ -267,6 +279,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to
/* store away the information we'll needing now and later */
xfer->data_ptr = buffer;
// xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API
xfer->in_remaining_bytes = total_bytes;
xfer->total_bytes = total_bytes;
@ -286,6 +299,36 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to
return true;
}
#if 0 // TODO support dcd_edpt_xfer_fifo API
bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
{
(void) rhport;
/* mine the data for the information we need */
tusb_dir_t dir = tu_edpt_dir(ep_addr);
USBD_EP_T *ep = ep_entry(ep_addr, false);
struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP];
/* store away the information we'll needing now and later */
xfer->data_ptr = NULL; // Indicates a FIFO shall be used
xfer->ff = ff;
xfer->in_remaining_bytes = total_bytes;
xfer->total_bytes = total_bytes;
if (TUSB_DIR_IN == dir)
{
dcd_in_xfer(xfer, ep);
}
else
{
xfer->out_bytes_so_far = 0;
ep->MXPLD = xfer->max_packet_size;
}
return true;
}
#endif
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
@ -389,9 +432,19 @@ void dcd_int_handler(uint8_t rhport)
if (out_ep)
{
/* copy the data from the PC to the previously provided buffer */
memcpy(xfer->data_ptr, (uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), available_bytes);
#if 0 // // TODO support dcd_edpt_xfer_fifo API
if (xfer->ff)
{
tu_fifo_write_n(xfer->ff, (const void *) (USBD_BUF_BASE + ep->BUFSEG), available_bytes);
}
else
#endif
{
memcpy(xfer->data_ptr, (uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), available_bytes);
xfer->data_ptr += available_bytes;
}
xfer->out_bytes_so_far += available_bytes;
xfer->data_ptr += available_bytes;
/* when the transfer is finished, alert TinyUSB; otherwise, accept more data */
if ( (xfer->total_bytes == xfer->out_bytes_so_far) || (available_bytes < xfer->max_packet_size) )
@ -403,6 +456,7 @@ void dcd_int_handler(uint8_t rhport)
{
/* update the bookkeeping to reflect the data that has now been sent to the PC */
xfer->in_remaining_bytes -= available_bytes;
xfer->data_ptr += available_bytes;
/* if more data to send, send it; otherwise, alert TinyUSB that we've finished */

View File

@ -34,6 +34,7 @@
*/
#include "tusb_option.h"
#include "common/tusb_fifo.h"
#if TUSB_OPT_DEVICE_ENABLED && ( (CFG_TUSB_MCU == OPT_MCU_NUC121) || (CFG_TUSB_MCU == OPT_MCU_NUC126) )
@ -78,6 +79,7 @@ static bool active_ep0_xfer;
static struct xfer_ctl_t
{
uint8_t *data_ptr; /* data_ptr tracks where to next copy data to (for OUT) or from (for IN) */
// tu_fifo_t * ff; // TODO support dcd_edpt_xfer_fifo API
union {
uint16_t in_remaining_bytes; /* for IN endpoints, we track how many bytes are left to transfer */
uint16_t out_bytes_so_far; /* but for OUT endpoints, we track how many bytes we've transferred so far */
@ -144,7 +146,17 @@ static void dcd_in_xfer(struct xfer_ctl_t *xfer, USBD_EP_T *ep)
{
uint16_t bytes_now = tu_min16(xfer->in_remaining_bytes, xfer->max_packet_size);
memcpy((uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), xfer->data_ptr, bytes_now);
#if 0 // TODO support dcd_edpt_xfer_fifo API
if (xfer->ff)
{
tu_fifo_read_n(xfer->ff, (void *) (USBD_BUF_BASE + ep->BUFSEG), bytes_now);
}
else
#endif
{
memcpy((uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), xfer->data_ptr, bytes_now);
}
ep->MXPLD = bytes_now;
}
@ -273,6 +285,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to
/* store away the information we'll needing now and later */
xfer->data_ptr = buffer;
// xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API
xfer->in_remaining_bytes = total_bytes;
xfer->total_bytes = total_bytes;
@ -292,6 +305,36 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to
return true;
}
#if 0 // TODO support dcd_edpt_xfer_fifo API
bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
{
(void) rhport;
/* mine the data for the information we need */
tusb_dir_t dir = tu_edpt_dir(ep_addr);
USBD_EP_T *ep = ep_entry(ep_addr, false);
struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP];
/* store away the information we'll needing now and later */
xfer->data_ptr = NULL; // Indicates a FIFO shall be used
xfer->ff = ff;
xfer->in_remaining_bytes = total_bytes;
xfer->total_bytes = total_bytes;
if (TUSB_DIR_IN == dir)
{
dcd_in_xfer(xfer, ep);
}
else
{
xfer->out_bytes_so_far = 0;
ep->MXPLD = xfer->max_packet_size;
}
return true;
}
#endif
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
@ -400,9 +443,19 @@ void dcd_int_handler(uint8_t rhport)
if (out_ep)
{
/* copy the data from the PC to the previously provided buffer */
memcpy(xfer->data_ptr, (uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), available_bytes);
#if 0 // TODO support dcd_edpt_xfer_fifo API
if (xfer->ff)
{
tu_fifo_write_n(xfer->ff, (const void *) (USBD_BUF_BASE + ep->BUFSEG), available_bytes);
}
else
#endif
{
memcpy(xfer->data_ptr, (uint8_t *)(USBD_BUF_BASE + ep->BUFSEG), available_bytes);
xfer->data_ptr += available_bytes;
}
xfer->out_bytes_so_far += available_bytes;
xfer->data_ptr += available_bytes;
/* when the transfer is finished, alert TinyUSB; otherwise, accept more data */
if ( (xfer->total_bytes == xfer->out_bytes_so_far) || (available_bytes < xfer->max_packet_size) )

View File

@ -34,6 +34,7 @@
*/
#include "tusb_option.h"
#include "common/tusb_fifo.h"
#if TUSB_OPT_DEVICE_ENABLED && (CFG_TUSB_MCU == OPT_MCU_NUC505)
@ -94,6 +95,7 @@ static uint32_t bufseg_addr;
static struct xfer_ctl_t
{
uint8_t *data_ptr; /* data_ptr tracks where to next copy data to (for OUT) or from (for IN) */
// tu_fifo_t* ff; // TODO support dcd_edpt_xfer_fifo API
union {
uint16_t in_remaining_bytes; /* for IN endpoints, we track how many bytes are left to transfer */
uint16_t out_bytes_so_far; /* but for OUT endpoints, we track how many bytes we've transferred so far */
@ -163,8 +165,7 @@ static USBD_EP_T *ep_entry(uint8_t ep_addr, bool add)
/* perform a non-control IN endpoint transfer; this is called by the ISR */
static void dcd_userEP_in_xfer(struct xfer_ctl_t *xfer, USBD_EP_T *ep)
{
uint16_t bytes_now = tu_min16(xfer->in_remaining_bytes, xfer->max_packet_size);
uint16_t countdown = bytes_now;
uint16_t const bytes_now = tu_min16(xfer->in_remaining_bytes, xfer->max_packet_size);
/* precompute what amount of data will be left */
xfer->in_remaining_bytes -= bytes_now;
@ -180,20 +181,29 @@ static void dcd_userEP_in_xfer(struct xfer_ctl_t *xfer, USBD_EP_T *ep)
}
/* provided buffers are thankfully 32-bit aligned, allowing most data to be transfered as 32-bit */
while (countdown > 3)
#if 0 // TODO support dcd_edpt_xfer_fifo API
if (xfer->ff)
{
uint32_t u32;
memcpy(&u32, xfer->data_ptr, 4);
ep->EPDAT = u32;
xfer->data_ptr += 4; countdown -= 4;
tu_fifo_read_n_const_addr_full_words(xfer->ff, (void *) (&ep->EPDAT_BYTE), bytes_now);
}
else
#endif
{
uint16_t countdown = bytes_now;
while (countdown > 3)
{
uint32_t u32;
memcpy(&u32, xfer->data_ptr, 4);
ep->EPDAT = u32;
xfer->data_ptr += 4; countdown -= 4;
}
while (countdown--) ep->EPDAT_BYTE = *xfer->data_ptr++;
}
while (countdown--)
ep->EPDAT_BYTE = *xfer->data_ptr++;
/* for short packets, we must nudge the peripheral to say 'that's all folks' */
if (bytes_now != xfer->max_packet_size)
ep->EPRSPCTL = USBD_EPRSPCTL_SHORTTXEN_Msk;
if (bytes_now != xfer->max_packet_size) ep->EPRSPCTL = USBD_EPRSPCTL_SHORTTXEN_Msk;
}
/* called by dcd_init() as well as by the ISR during a USB bus reset */
@ -385,6 +395,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to
/* store away the information we'll needing now and later */
xfer->data_ptr = buffer;
// xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API
xfer->in_remaining_bytes = total_bytes;
xfer->total_bytes = total_bytes;
@ -402,6 +413,38 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t to
return true;
}
#if 0 // TODO support dcd_edpt_xfer_fifo API
bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
{
(void) rhport;
TU_ASSERT(0x80 != ep_addr && 0x00 != ep_addr); // Must not be used for control stuff
/* mine the data for the information we need */
tusb_dir_t dir = tu_edpt_dir(ep_addr);
USBD_EP_T *ep = ep_entry(ep_addr, false);
struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP];
/* store away the information we'll needing now and later */
xfer->data_ptr = NULL; // Indicates a FIFO shall be used
xfer->ff = ff;
xfer->in_remaining_bytes = total_bytes;
xfer->total_bytes = total_bytes;
if (TUSB_DIR_IN == dir)
{
ep->EPINTEN = USBD_EPINTEN_BUFEMPTYIEN_Msk;
}
else
{
xfer->out_bytes_so_far = 0;
ep->EPINTEN = USBD_EPINTEN_RXPKIEN_Msk;
}
return true;
}
#endif
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
@ -615,12 +658,25 @@ void dcd_int_handler(uint8_t rhport)
#else
uint16_t const available_bytes = ep->EPDATCNT & USBD_EPDATCNT_DATCNT_Msk;
/* copy the data from the PC to the previously provided buffer */
for (int count = 0; (count < available_bytes) && (xfer->out_bytes_so_far < xfer->total_bytes); count++, xfer->out_bytes_so_far++)
*xfer->data_ptr++ = ep->EPDAT_BYTE;
#if 0 // TODO support dcd_edpt_xfer_fifo API
if (xfer->ff)
{
tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void *) &ep->EPDAT_BYTE, tu_min16(available_bytes, xfer->total_bytes - xfer->out_bytes_so_far));
}
else
#endif
{
for (int count = 0; (count < available_bytes) && (xfer->out_bytes_so_far < xfer->total_bytes); count++, xfer->out_bytes_so_far++)
{
*xfer->data_ptr++ = ep->EPDAT_BYTE;
}
}
/* when the transfer is finished, alert TinyUSB; otherwise, continue accepting more data */
if ( (xfer->total_bytes == xfer->out_bytes_so_far) || (available_bytes < xfer->max_packet_size) )
{
dcd_event_xfer_complete(0, ep_addr, xfer->out_bytes_so_far, XFER_RESULT_SUCCESS, true);
}
#endif
}

View File

@ -35,7 +35,8 @@
#include "pico/fix/rp2040_usb_device_enumeration.h"
#endif
#include "osal/osal.h"
#include "common/tusb_fifo.h"
#include "device/dcd.h"
/*------------------------------------------------------------------*/

View File

@ -102,6 +102,7 @@
*/
#include "tusb_option.h"
#include "common/tusb_fifo.h"
#if defined(STM32F102x6) || defined(STM32F102xB) || \
defined(STM32F103x6) || defined(STM32F103xB) || \
@ -165,6 +166,7 @@ TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) % 8) == 0, "BTABLE base must be aligne
typedef struct
{
uint8_t * buffer;
// tu_fifo_t * ff; // TODO support dcd_edpt_xfer_fifo API
uint16_t total_len;
uint16_t queued_len;
uint16_t pma_ptr;
@ -197,6 +199,9 @@ static void dcd_pma_free(uint8_t ep_addr);
static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, size_t wNBytes);
static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, size_t wNBytes);
//static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wNBytes);
//static bool dcd_read_packet_memory_ff(tu_fifo_t * ff, uint16_t src, uint16_t wNBytes);
// Using a function due to better type checks
// This seems better than having to do type casts everywhere else
static inline void reg16_clear_bits(__IO uint16_t *reg, uint16_t mask) {
@ -476,8 +481,17 @@ static void dcd_ep_ctr_rx_handler(uint32_t wIstr)
if (count != 0U)
{
dcd_read_packet_memory(&(xfer->buffer[xfer->queued_len]),
*pcd_ep_rx_address_ptr(USB,EPindex), count);
#if 0 // TODO support dcd_edpt_xfer_fifo API
if (xfer->ff)
{
dcd_read_packet_memory_ff(xfer->ff, *pcd_ep_rx_address_ptr(USB,EPindex), count);
}
else
#endif
{
dcd_read_packet_memory(&(xfer->buffer[xfer->queued_len]), *pcd_ep_rx_address_ptr(USB,EPindex), count);
}
xfer->queued_len = (uint16_t)(xfer->queued_len + count);
}
@ -804,7 +818,17 @@ static void dcd_transmit_packet(xfer_ctl_t * xfer, uint16_t ep_ix)
len = xfer->max_packet_size;
}
uint16_t oldAddr = *pcd_ep_tx_address_ptr(USB,ep_ix);
dcd_write_packet_memory(oldAddr, &(xfer->buffer[xfer->queued_len]), len);
#if 0 // TODO support dcd_edpt_xfer_fifo API
if (xfer->ff)
{
dcd_write_packet_memory_ff(xfer->ff, oldAddr, len);
}
else
#endif
{
dcd_write_packet_memory(oldAddr, &(xfer->buffer[xfer->queued_len]), len);
}
xfer->queued_len = (uint16_t)(xfer->queued_len + len);
pcd_set_ep_tx_cnt(USB,ep_ix,len);
@ -821,6 +845,7 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
xfer_ctl_t * xfer = xfer_ctl_ptr(epnum,dir);
xfer->buffer = buffer;
// xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API
xfer->total_len = total_bytes;
xfer->queued_len = 0;
@ -847,6 +872,39 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
return true;
}
#if 0 // TODO support dcd_edpt_xfer_fifo API
bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
{
(void) rhport;
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
xfer_ctl_t * xfer = xfer_ctl_ptr(epnum,dir);
xfer->buffer = NULL;
// xfer->ff = ff; // TODO support dcd_edpt_xfer_fifo API
xfer->total_len = total_bytes;
xfer->queued_len = 0;
if ( dir == TUSB_DIR_OUT )
{
if(total_bytes > xfer->max_packet_size)
{
pcd_set_ep_rx_cnt(USB,epnum,xfer->max_packet_size);
} else {
pcd_set_ep_rx_cnt(USB,epnum,total_bytes);
}
pcd_set_ep_rx_status(USB, epnum, USB_EP_RX_VALID);
}
else // IN
{
dcd_transmit_packet(xfer,epnum);
}
return true;
}
#endif
void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
{
(void)rhport;
@ -920,8 +978,55 @@ static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, si
return true;
}
#if 0 // TODO support dcd_edpt_xfer_fifo API
/**
* @brief Copy a buffer from user memory area to packet memory area (PMA).
* @brief Copy from FIFO to packet memory area (PMA).
* Uses byte-access of system memory and 16-bit access of packet memory
* @param wNBytes no. of bytes to be copied.
* @retval None
*/
// THIS FUNCTION IS UNTESTED
static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wNBytes)
{
// Since we copy from a ring buffer FIFO, a wrap might occur making it necessary to conduct two copies
// Check for first linear part
void * src;
uint16_t len = tu_fifo_get_linear_read_info(ff, 0, &src, wNBytes); // We want to read from the FIFO
TU_VERIFY(len && dcd_write_packet_memory(dst, src, len)); // and write it into the PMA
tu_fifo_advance_read_pointer(ff, len);
// Check for wrapped part
if (len < wNBytes)
{
// Get remaining wrapped length
uint16_t len2 = tu_fifo_get_linear_read_info(ff, 0, &src, wNBytes - len);
TU_VERIFY(len2);
// Update destination pointer
dst += len;
// Since PMA is accessed 16-bit wise we need to handle the case when a 16 bit value was split
if (len % 2) // If len is uneven there is a byte left to copy
{
// Since PMA can accessed only 16 bit-wise we copy the last byte again
tu_fifo_backward_read_pointer(ff, 1); // Move one byte back and copy two bytes for the PMA
tu_fifo_read_n(ff, (void *) &pma[PMA_STRIDE*(dst>>1)], 2); // Since EP FIFOs must be of item size 1 this is safe to do
dst++;
len2--;
}
TU_VERIFY(dcd_write_packet_memory(dst, src, len2));
tu_fifo_advance_write_pointer(ff, len2);
}
return true;
}
#endif
/**
* @brief Copy a buffer from packet memory area (PMA) to user memory area.
* Uses byte-access of system memory and 16-bit access of packet memory
* @param wNBytes no. of bytes to be copied.
* @retval None
@ -955,5 +1060,52 @@ static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, size_t wN
return true;
}
#if 0 // TODO support dcd_edpt_xfer_fifo API
/**
* @brief Copy a buffer from user packet memory area (PMA) to FIFO.
* Uses byte-access of system memory and 16-bit access of packet memory
* @param wNBytes no. of bytes to be copied.
* @retval None
*/
// THIS FUNCTION IS UNTESTED
static bool dcd_read_packet_memory_ff(tu_fifo_t * ff, uint16_t src, uint16_t wNBytes)
{
// Since we copy into a ring buffer FIFO, a wrap might occur making it necessary to conduct two copies
// Check for first linear part
void * dst;
uint16_t len = tu_fifo_get_linear_write_info(ff, 0, &dst, wNBytes);
TU_VERIFY(len && dcd_read_packet_memory(dst, src, len));
tu_fifo_advance_write_pointer(ff, len);
// Check for wrapped part
if (len < wNBytes)
{
// Get remaining wrapped length
uint16_t len2 = tu_fifo_get_linear_write_info(ff, 0, &dst, wNBytes - len);
TU_VERIFY(len2);
// Update source pointer
src += len;
// Since PMA is accessed 16-bit wise we need to handle the case when a 16 bit value was split
if (len % 2) // If len is uneven there is a byte left to copy
{
uint32_t temp = pma[PMA_STRIDE*(src>>1)];
*((uint8_t *)dst++) = ((temp >> 8) & 0xFF);
src++;
len2--;
}
TU_VERIFY(dcd_read_packet_memory(dst, src, len2));
tu_fifo_advance_write_pointer(ff, len2);
}
return true;
}
#endif
#endif

View File

@ -28,6 +28,7 @@
*/
#include "tusb_option.h"
#include "common/tusb_fifo.h"
// Since TinyUSB doesn't use SOF for now, and this interrupt too often (1ms interval)
// We disable SOF for now until needed later on
@ -135,6 +136,7 @@ static TU_ATTR_ALIGNED(4) uint32_t _setup_packet[2];
typedef struct {
uint8_t * buffer;
tu_fifo_t * ff;
uint16_t total_len;
uint16_t max_size;
uint8_t interval;
@ -644,6 +646,7 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
xfer->buffer = buffer;
xfer->ff = NULL;
xfer->total_len = total_bytes;
// EP0 can only handle one packet
@ -668,6 +671,35 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
return true;
}
// The number of bytes has to be given explicitly to allow more flexible control of how many
// bytes should be written and second to keep the return value free to give back a boolean
// success message. If total_bytes is too big, the FIFO will copy only what is available
// into the USB buffer!
bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
{
// USB buffers always work in bytes so to avoid unnecessary divisions we demand item_size = 1
TU_ASSERT(ff->item_size == 1);
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
xfer->buffer = NULL;
xfer->ff = ff;
xfer->total_len = total_bytes;
uint16_t num_packets = (total_bytes / xfer->max_size);
uint8_t const short_packet_size = total_bytes % xfer->max_size;
// Zero-size packet is special case.
if(short_packet_size > 0 || (total_bytes == 0)) num_packets++;
// Schedule packets to be sent within interrupt
edpt_schedule_packets(rhport, epnum, dir, num_packets, total_bytes);
return true;
}
static void dcd_edpt_disable (uint8_t rhport, uint8_t ep_addr, bool stall)
{
(void) rhport;
@ -867,10 +899,19 @@ static void handle_rxflvl_ints(uint8_t rhport, USB_OTG_OUTEndpointTypeDef * out_
xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
// Read packet off RxFIFO
read_fifo_packet(rhport, xfer->buffer, bcnt);
if (xfer->ff)
{
// Ring buffer
tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void *) rx_fifo, bcnt);
}
else
{
// Linear buffer
read_fifo_packet(rhport, xfer->buffer, bcnt);
// Increment pointer to xfer data
xfer->buffer += bcnt;
// Increment pointer to xfer data
xfer->buffer += bcnt;
}
// Truncate transfer length in case of short packet
if(bcnt < xfer->max_size) {
@ -966,22 +1007,30 @@ static void handle_epin_ints(uint8_t rhport, USB_OTG_DeviceTypeDef * dev, USB_OT
uint16_t remaining_packets = (in_ep[n].DIEPTSIZ & USB_OTG_DIEPTSIZ_PKTCNT_Msk) >> USB_OTG_DIEPTSIZ_PKTCNT_Pos;
// Process every single packet (only whole packets can be written to fifo)
for(uint16_t i = 0; i < remaining_packets; i++){
uint16_t remaining_bytes = (in_ep[n].DIEPTSIZ & USB_OTG_DIEPTSIZ_XFRSIZ_Msk) >> USB_OTG_DIEPTSIZ_XFRSIZ_Pos;
for(uint16_t i = 0; i < remaining_packets; i++)
{
uint16_t const remaining_bytes = (in_ep[n].DIEPTSIZ & USB_OTG_DIEPTSIZ_XFRSIZ_Msk) >> USB_OTG_DIEPTSIZ_XFRSIZ_Pos;
// Packet can not be larger than ep max size
uint16_t packet_size = tu_min16(remaining_bytes, xfer->max_size);
uint16_t const packet_size = tu_min16(remaining_bytes, xfer->max_size);
// It's only possible to write full packets into FIFO. Therefore DTXFSTS register of current
// EP has to be checked if the buffer can take another WHOLE packet
if(packet_size > ((in_ep[n].DTXFSTS & USB_OTG_DTXFSTS_INEPTFSAV_Msk) << 2)){
break;
}
if(packet_size > ((in_ep[n].DTXFSTS & USB_OTG_DTXFSTS_INEPTFSAV_Msk) << 2)) break;
// Push packet to Tx-FIFO
write_fifo_packet(rhport, n, xfer->buffer, packet_size);
if (xfer->ff)
{
usb_fifo_t tx_fifo = FIFO_BASE(rhport, n);
tu_fifo_read_n_const_addr_full_words(xfer->ff, (void *) tx_fifo, packet_size);
}
else
{
write_fifo_packet(rhport, n, xfer->buffer, packet_size);
// Increment pointer to xfer data
xfer->buffer += packet_size;
// Increment pointer to xfer data
xfer->buffer += packet_size;
}
}
// Turn off TXFE if all bytes are written.

View File

@ -25,6 +25,7 @@
*/
#include "tusb_option.h"
#include "common/tusb_fifo.h"
#if CFG_TUSB_MCU == OPT_MCU_NONE
@ -104,6 +105,16 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
return false;
}
// Submit a transfer where is managed by FIFO, When complete dcd_event_xfer_complete() is invoked to notify the stack - optional, however, must be listed in usbd.c
bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
{
(void) rhport;
(void) ep_addr;
(void) ff;
(void) total_bytes;
return false;
}
// Stall endpoint
void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
{

View File

@ -26,6 +26,7 @@
*/
#include "tusb_option.h"
#include "common/tusb_fifo.h"
#if TUSB_OPT_DEVICE_ENABLED && ( CFG_TUSB_MCU == OPT_MCU_MSP430x5xx )
@ -48,6 +49,7 @@ uint8_t _setup_packet[8];
typedef struct
{
uint8_t * buffer;
// tu_fifo_t * ff; // TODO support dcd_edpt_xfer_fifo API
uint16_t total_len;
uint16_t queued_len;
uint16_t max_size;
@ -306,6 +308,7 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
xfer->buffer = buffer;
// xfer->ff = NULL; // TODO support dcd_edpt_xfer_fifo API
xfer->total_len = total_bytes;
xfer->queued_len = 0;
xfer->short_packet = false;
@ -344,6 +347,36 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
return true;
}
#if 0 // TODO support dcd_edpt_xfer_fifo API
bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
{
(void) rhport;
uint8_t const epnum = tu_edpt_number(ep_addr);
uint8_t const dir = tu_edpt_dir(ep_addr);
xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir);
xfer->buffer = NULL;
xfer->ff = ff;
xfer->total_len = total_bytes;
xfer->queued_len = 0;
xfer->short_packet = false;
ep_regs_t ep_regs = EP_REGS(epnum, dir);
if(dir == TUSB_DIR_OUT)
{
ep_regs[BCTX] &= ~NAK;
}
else
{
USBIEPIFG |= (1 << epnum);
}
return true;
}
#endif
void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
{
(void) rhport;
@ -443,22 +476,32 @@ static void receive_packet(uint8_t ep_num)
to_recv_size = (xfer_size > xfer->max_size) ? xfer->max_size : xfer_size;
}
uint8_t * base = (xfer->buffer + xfer->queued_len);
if(ep_num == 0)
#if 0 // TODO support dcd_edpt_xfer_fifo API
if (xfer->ff)
{
volatile uint8_t * ep0out_buf = &USBOEP0BUF;
for(uint16_t i = 0; i < to_recv_size; i++)
{
base[i] = ep0out_buf[i];
}
volatile uint8_t * ep_buf = (ep_num == 0) ? &USBOEP0BUF : (&USBSTABUFF + (ep_regs[BBAX] << 3));
tu_fifo_write_n(xfer->ff, (const void *) ep_buf, to_recv_size);
}
else
#endif
{
volatile uint8_t * ep_buf = &USBSTABUFF + (ep_regs[BBAX] << 3);
for(uint16_t i = 0; i < to_recv_size ; i++)
uint8_t * base = (xfer->buffer + xfer->queued_len);
if(ep_num == 0)
{
base[i] = ep_buf[i];
volatile uint8_t * ep0out_buf = &USBOEP0BUF;
for(uint16_t i = 0; i < to_recv_size; i++)
{
base[i] = ep0out_buf[i];
}
}
else
{
volatile uint8_t * ep_buf = &USBSTABUFF + (ep_regs[BBAX] << 3);
for(uint16_t i = 0; i < to_recv_size ; i++)
{
base[i] = ep_buf[i];
}
}
}
@ -499,7 +542,6 @@ static void transmit_packet(uint8_t ep_num)
}
// Then actually commit to transmit a packet.
uint8_t * base = (xfer->buffer + xfer->queued_len);
uint16_t remaining = xfer->total_len - xfer->queued_len;
uint8_t xfer_size = (xfer->max_size < xfer->total_len) ? xfer->max_size : remaining;
@ -513,6 +555,7 @@ static void transmit_packet(uint8_t ep_num)
if(ep_num == 0)
{
volatile uint8_t * ep0in_buf = &USBIEP0BUF;
uint8_t * base = (xfer->buffer + xfer->queued_len);
for(uint16_t i = 0; i < xfer_size; i++)
{
ep0in_buf[i] = base[i];
@ -526,9 +569,19 @@ static void transmit_packet(uint8_t ep_num)
ep_regs_t ep_regs = EP_REGS(ep_num, TUSB_DIR_IN);
volatile uint8_t * ep_buf = &USBSTABUFF + (ep_regs[BBAX] << 3);
for(int i = 0; i < xfer_size; i++)
#if 0 // TODO support dcd_edpt_xfer_fifo API
if (xfer->ff)
{
ep_buf[i] = base[i];
tu_fifo_read_n(xfer->ff, (void *) ep_buf, xfer_size);
}
else
#endif
{
uint8_t * base = (xfer->buffer + xfer->queued_len);
for(int i = 0; i < xfer_size; i++)
{
ep_buf[i] = base[i];
}
}
ep_regs[BCTX] = (ep_regs[BCTX] & 0x80) + (xfer_size & 0x7F);