mirror of https://github.com/ARMmbed/mbed-os.git
1002 lines
32 KiB
C++
1002 lines
32 KiB
C++
/*
|
|
* Copyright (c) 2018-2019, Arm Limited and affiliates.
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "stdint.h"
|
|
#include "USBAudio.h"
|
|
#include "USBAudio_Types.h"
|
|
#include "EndpointResolver.h"
|
|
#include "usb_phy_api.h"
|
|
|
|
#define SAMPLE_SIZE 2
|
|
#define XFER_FREQUENCY_HZ 1000
|
|
#define WRITE_READY_UNBLOCK (1 << 0)
|
|
#define READ_READY_UNBLOCK (1 << 1)
|
|
|
|
class USBAudio::AsyncWrite: public AsyncOp {
|
|
public:
|
|
AsyncWrite(USBAudio *audio, uint8_t *buf, uint32_t size):
|
|
audio(audio), tx_buf(buf), tx_size(size), result(false)
|
|
{
|
|
|
|
}
|
|
|
|
virtual ~AsyncWrite()
|
|
{
|
|
|
|
}
|
|
|
|
virtual bool process()
|
|
{
|
|
if (audio->_tx_state != Opened) {
|
|
result = false;
|
|
return true;
|
|
}
|
|
|
|
uint32_t actual_size = 0;
|
|
audio->write_nb(tx_buf, tx_size, &actual_size);
|
|
tx_size -= actual_size;
|
|
tx_buf += actual_size;
|
|
if (tx_size == 0) {
|
|
result = true;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
USBAudio *audio;
|
|
uint8_t *tx_buf;
|
|
uint32_t tx_size;
|
|
bool result;
|
|
};
|
|
|
|
class USBAudio::AsyncRead: public AsyncOp {
|
|
public:
|
|
AsyncRead(USBAudio *audio, uint8_t *buf, uint32_t size, uint32_t *size_read, bool read_all)
|
|
: audio(audio), rx_buf(buf), rx_size(size), rx_actual(size_read), all(read_all), result(false)
|
|
{
|
|
|
|
}
|
|
|
|
virtual ~AsyncRead()
|
|
{
|
|
|
|
}
|
|
|
|
virtual bool process()
|
|
{
|
|
if (audio->_rx_state != Opened) {
|
|
result = false;
|
|
return true;
|
|
}
|
|
|
|
uint32_t actual_size = 0;
|
|
audio->read_nb(rx_buf, rx_size, &actual_size);
|
|
rx_buf += actual_size;
|
|
*rx_actual += actual_size;
|
|
rx_size -= actual_size;
|
|
if ((!all && *rx_actual > 0) || (rx_size == 0)) {
|
|
// Wake thread if request is done
|
|
result = true;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
USBAudio *audio;
|
|
uint8_t *rx_buf;
|
|
uint32_t rx_size;
|
|
uint32_t *rx_actual;
|
|
bool all;
|
|
bool result;
|
|
};
|
|
|
|
static void stub_volume()
|
|
{
|
|
|
|
}
|
|
|
|
static void stub_handler(USBAudio::AudioEvent event)
|
|
{
|
|
(void)event;
|
|
}
|
|
|
|
USBAudio::USBAudio(bool connect, uint32_t frequency_rx, uint8_t channel_count_rx, uint32_t frequency_tx, uint8_t channel_count_tx, uint32_t buffer_ms, uint16_t vendor_id, uint16_t product_id, uint16_t product_release):
|
|
USBDevice(get_usb_phy(), vendor_id, product_id, product_release)
|
|
{
|
|
_init(frequency_rx, channel_count_rx, frequency_tx, channel_count_tx, buffer_ms);
|
|
|
|
// connect or init device
|
|
if (connect) {
|
|
USBDevice::connect();
|
|
} else {
|
|
USBDevice::init();
|
|
}
|
|
}
|
|
|
|
USBAudio::USBAudio(USBPhy *phy, uint32_t frequency_rx, uint8_t channel_count_rx, uint32_t frequency_tx, uint8_t channel_count_tx, uint32_t buffer_ms, uint16_t vendor_id, uint16_t product_id, uint16_t product_release):
|
|
USBDevice(phy, vendor_id, product_id, product_release)
|
|
{
|
|
_init(frequency_rx, channel_count_rx, frequency_tx, channel_count_tx, buffer_ms);
|
|
}
|
|
|
|
void USBAudio::_init(uint32_t frequency_rx, uint8_t channel_count_rx, uint32_t frequency_tx, uint8_t channel_count_tx, uint32_t buffer_ms)
|
|
{
|
|
_connected = false;
|
|
|
|
_volume = 0;
|
|
_mute = 0;
|
|
_vol_cur = 0x0080;
|
|
_vol_min = 0x0000;
|
|
_vol_max = 0x0100;
|
|
_vol_res = 0x0004;
|
|
|
|
_update_vol = mbed::callback(stub_volume);
|
|
_tx_done = mbed::callback(stub_handler);
|
|
_rx_done = mbed::callback(stub_handler);
|
|
|
|
_rx_overflow = 0;
|
|
_tx_underflow = 0;
|
|
|
|
_tx_freq = frequency_tx;
|
|
_rx_freq = frequency_rx;
|
|
|
|
_tx_channel_count = channel_count_tx;
|
|
_rx_channel_count = channel_count_rx;
|
|
|
|
_tx_idle = true;
|
|
_tx_frame_fract = 0;
|
|
_tx_whole_frames_per_xfer = _tx_freq / XFER_FREQUENCY_HZ;
|
|
_tx_fract_frames_per_xfer = _tx_freq % XFER_FREQUENCY_HZ;
|
|
|
|
uint32_t max_frames = _tx_whole_frames_per_xfer + (_tx_fract_frames_per_xfer ? 1 : 0);
|
|
_tx_packet_size_max = max_frames * SAMPLE_SIZE * _tx_channel_count;
|
|
_rx_packet_size_max = (_rx_freq + 1000 - 1) / 1000 * _rx_channel_count * 2;
|
|
|
|
_tx_packet_buf = new uint8_t[_tx_packet_size_max]();
|
|
_rx_packet_buf = new uint8_t[_rx_packet_size_max]();
|
|
|
|
_tx_queue.resize(buffer_ms * _tx_channel_count * SAMPLE_SIZE * _tx_freq / XFER_FREQUENCY_HZ);
|
|
_rx_queue.resize(buffer_ms * _rx_channel_count * SAMPLE_SIZE * _rx_freq / XFER_FREQUENCY_HZ);
|
|
|
|
_tx_state = Closed;
|
|
_rx_state = Closed;
|
|
|
|
EndpointResolver resolver(endpoint_table());
|
|
resolver.endpoint_ctrl(64);
|
|
_episo_out = resolver.endpoint_out(USB_EP_TYPE_ISO, _tx_packet_size_max);
|
|
_episo_in = resolver.endpoint_in(USB_EP_TYPE_ISO, _rx_packet_size_max);
|
|
MBED_ASSERT(resolver.valid());
|
|
|
|
_channel_config_rx = (_rx_channel_count == 1) ? CHANNEL_M : CHANNEL_L + CHANNEL_R;
|
|
_channel_config_tx = (_tx_channel_count == 1) ? CHANNEL_M : CHANNEL_L + CHANNEL_R;
|
|
|
|
_build_configuration_desc();
|
|
}
|
|
|
|
USBAudio::~USBAudio()
|
|
{
|
|
disconnect();
|
|
deinit();
|
|
|
|
delete[] _tx_packet_buf;
|
|
delete[] _rx_packet_buf;
|
|
}
|
|
|
|
void USBAudio::connect()
|
|
{
|
|
lock();
|
|
|
|
if (!_connected) {
|
|
USBDevice::connect();
|
|
_connected = true;
|
|
_receive_change(Closed);
|
|
_send_change(Closed);
|
|
}
|
|
|
|
unlock();
|
|
}
|
|
|
|
void USBAudio::disconnect()
|
|
{
|
|
lock();
|
|
|
|
if (_connected) {
|
|
_connected = false;
|
|
USBDevice::disconnect();
|
|
_receive_change(Powerdown);
|
|
_send_change(Powerdown);
|
|
}
|
|
|
|
unlock();
|
|
}
|
|
|
|
bool USBAudio::read(uint8_t *buf, uint32_t size)
|
|
{
|
|
lock();
|
|
|
|
uint32_t actual;
|
|
AsyncRead read(this, buf, size, &actual, true);
|
|
_read_list.add(&read);
|
|
|
|
unlock();
|
|
|
|
read.wait(NULL);
|
|
return read.result;
|
|
}
|
|
|
|
void USBAudio::read_nb(uint8_t *buf, uint32_t size, uint32_t *actual)
|
|
{
|
|
|
|
lock();
|
|
|
|
uint32_t available = _rx_queue.size();
|
|
uint32_t copy_size = available > size ? size : available;
|
|
_rx_queue.read(buf, copy_size);
|
|
*actual = copy_size;
|
|
|
|
unlock();
|
|
}
|
|
|
|
uint32_t USBAudio::read_overflows(bool clear)
|
|
{
|
|
lock();
|
|
|
|
uint32_t overflows = _rx_overflow;
|
|
if (clear) {
|
|
_rx_overflow = 0;
|
|
}
|
|
|
|
unlock();
|
|
return overflows;
|
|
}
|
|
|
|
|
|
bool USBAudio::read_ready()
|
|
{
|
|
lock();
|
|
|
|
bool ready = _rx_state == Opened;
|
|
|
|
unlock();
|
|
|
|
return ready;
|
|
}
|
|
|
|
void USBAudio::read_wait_ready()
|
|
{
|
|
_flags.wait_any(READ_READY_UNBLOCK, osWaitForever, false);
|
|
}
|
|
|
|
bool USBAudio::write(uint8_t *buf, uint32_t size)
|
|
{
|
|
lock();
|
|
|
|
AsyncWrite write(this, buf, size);
|
|
_write_list.add(&write);
|
|
|
|
unlock();
|
|
|
|
write.wait(NULL);
|
|
return write.result;
|
|
}
|
|
|
|
void USBAudio::write_nb(uint8_t *buf, uint32_t size, uint32_t *actual)
|
|
{
|
|
lock();
|
|
|
|
uint32_t available = _tx_queue.free();
|
|
uint32_t copy_size = available > size ? size : available;
|
|
_tx_queue.write(buf, copy_size);
|
|
*actual = copy_size;
|
|
_send_isr_start();
|
|
|
|
unlock();
|
|
}
|
|
|
|
uint32_t USBAudio::write_underflows(bool clear)
|
|
{
|
|
lock();
|
|
|
|
uint32_t underflows = _tx_underflow;
|
|
if (clear) {
|
|
_tx_underflow = 0;
|
|
}
|
|
|
|
unlock();
|
|
return underflows;
|
|
}
|
|
|
|
bool USBAudio::write_ready()
|
|
{
|
|
lock();
|
|
|
|
bool ready = _tx_state == Opened;
|
|
|
|
unlock();
|
|
|
|
return ready;
|
|
}
|
|
|
|
void USBAudio::write_wait_ready()
|
|
{
|
|
_flags.wait_any(WRITE_READY_UNBLOCK, osWaitForever, false);
|
|
}
|
|
|
|
|
|
float USBAudio::get_volume()
|
|
{
|
|
lock();
|
|
|
|
float ret = _mute ? 0.0f : _volume;
|
|
|
|
unlock();
|
|
return ret;
|
|
}
|
|
|
|
void USBAudio::attach(mbed::Callback<void()> &cb)
|
|
{
|
|
lock();
|
|
|
|
_update_vol = cb;
|
|
if (!_update_vol) {
|
|
_update_vol = stub_volume;
|
|
}
|
|
|
|
unlock();
|
|
}
|
|
|
|
void USBAudio::attach_tx(mbed::Callback<void(AudioEvent)> &cb)
|
|
{
|
|
lock();
|
|
|
|
_tx_done = cb;
|
|
if (!_tx_done) {
|
|
_tx_done = mbed::callback(stub_handler);
|
|
}
|
|
|
|
unlock();
|
|
}
|
|
|
|
void USBAudio::attach_rx(mbed::Callback<void(AudioEvent)> &cb)
|
|
{
|
|
lock();
|
|
|
|
_rx_done = cb;
|
|
if (!_rx_done) {
|
|
_rx_done = mbed::callback(stub_handler);
|
|
}
|
|
|
|
unlock();
|
|
}
|
|
|
|
void USBAudio::callback_state_change(DeviceState new_state)
|
|
{
|
|
assert_locked();
|
|
|
|
if (_connected && (new_state != Configured)) {
|
|
_receive_change(Closed);
|
|
_send_change(Closed);
|
|
}
|
|
}
|
|
|
|
void USBAudio::callback_request(const setup_packet_t *setup)
|
|
{
|
|
assert_locked();
|
|
|
|
RequestResult result = PassThrough;
|
|
uint8_t *data = NULL;
|
|
uint32_t size = 0;
|
|
|
|
// Process class-specific requests
|
|
if (setup->bmRequestType.Type == CLASS_TYPE) {
|
|
|
|
// Feature Unit: Interface = 0, ID = 2
|
|
if (setup->wIndex == 0x0200) {
|
|
|
|
// Master Channel
|
|
if ((setup->wValue & 0xff) == 0) {
|
|
|
|
switch (setup->wValue >> 8) {
|
|
case MUTE_CONTROL:
|
|
switch (setup->bRequest) {
|
|
case REQUEST_GET_CUR:
|
|
size = 1;
|
|
data = &_mute;
|
|
result = Send;
|
|
break;
|
|
|
|
case REQUEST_SET_CUR:
|
|
size = 1;
|
|
data = _control_receive;
|
|
result = Receive;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case VOLUME_CONTROL:
|
|
switch (setup->bRequest) {
|
|
case REQUEST_GET_CUR:
|
|
size = 2;
|
|
data = (uint8_t *)&_vol_cur;
|
|
result = Send;
|
|
break;
|
|
case REQUEST_GET_MIN:
|
|
size = 2;
|
|
data = (uint8_t *)&_vol_min;
|
|
result = Send;
|
|
break;
|
|
case REQUEST_GET_MAX:
|
|
size = 2;
|
|
data = (uint8_t *)&_vol_max;
|
|
result = Send;
|
|
break;
|
|
case REQUEST_GET_RES:
|
|
size = 2;
|
|
data = (uint8_t *)&_vol_res;
|
|
result = Send;
|
|
break;
|
|
|
|
case REQUEST_SET_CUR:
|
|
size = 2;
|
|
data = _control_receive;
|
|
result = Receive;
|
|
break;
|
|
case REQUEST_SET_MIN:
|
|
size = 2;
|
|
data = _control_receive;
|
|
result = Receive;
|
|
break;
|
|
case REQUEST_SET_MAX:
|
|
size = 2;
|
|
data = _control_receive;
|
|
result = Receive;
|
|
break;
|
|
case REQUEST_SET_RES:
|
|
size = 2;
|
|
data = _control_receive;
|
|
result = Receive;
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
complete_request(result, data, size);
|
|
}
|
|
|
|
void USBAudio::callback_request_xfer_done(const setup_packet_t *setup, bool aborted)
|
|
{
|
|
assert_locked();
|
|
|
|
if (aborted) {
|
|
complete_request_xfer_done(false);
|
|
return;
|
|
}
|
|
|
|
if (setup->bmRequestType.dataTransferDirection == DEVICE_TO_HOST) {
|
|
complete_request_xfer_done(true);
|
|
return;
|
|
}
|
|
|
|
if ((setup->wLength == 1) || (setup->wLength == 2)) {
|
|
uint16_t data = (_control_receive[0] << 0) | (_control_receive[1] << 8);
|
|
data &= ((setup->wLength == 1) ? 0xFF : 0xFFFF);
|
|
switch (setup->wValue >> 8) {
|
|
case MUTE_CONTROL:
|
|
switch (setup->bRequest) {
|
|
case REQUEST_SET_CUR:
|
|
_mute = data & 0xff;
|
|
_update_vol.call();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case VOLUME_CONTROL:
|
|
switch (setup->bRequest) {
|
|
case REQUEST_SET_CUR:
|
|
_vol_cur = data;
|
|
_volume = (float)_vol_cur / (float)_vol_max;
|
|
_update_vol.call();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
complete_request_xfer_done(true);
|
|
return;
|
|
}
|
|
|
|
complete_request_xfer_done(false);
|
|
}
|
|
|
|
void USBAudio::callback_set_configuration(uint8_t configuration)
|
|
{
|
|
assert_locked();
|
|
|
|
bool ret = false;
|
|
if (configuration == DEFAULT_CONFIGURATION) {
|
|
endpoint_remove_all();
|
|
|
|
// Configure isochronous endpoint
|
|
endpoint_add(_episo_out, _rx_packet_size_max, USB_EP_TYPE_ISO, static_cast<ep_cb_t>(&USBAudio::_receive_isr));
|
|
endpoint_add(_episo_in, _tx_packet_size_max, USB_EP_TYPE_ISO, static_cast<ep_cb_t>(&USBAudio::_send_isr));
|
|
|
|
// activate readings on this endpoint
|
|
read_start(_episo_out, _rx_packet_buf, _rx_packet_size_max);
|
|
ret = true;
|
|
}
|
|
complete_set_configuration(ret);
|
|
}
|
|
|
|
void USBAudio::callback_set_interface(uint16_t interface, uint8_t alternate)
|
|
{
|
|
assert_locked();
|
|
|
|
bool ret = false;
|
|
if (interface == 0 && alternate == 0) {
|
|
ret = true;
|
|
}
|
|
if (interface == 1 && (alternate == 0 || alternate == 1)) {
|
|
_receive_change(alternate == 1 ? Opened : Closed);
|
|
ret = true;
|
|
}
|
|
if (interface == 2 && (alternate == 0 || alternate == 1)) {
|
|
_send_change(alternate == 1 ? Opened : Closed);
|
|
ret = true;
|
|
}
|
|
complete_set_interface(ret);
|
|
}
|
|
|
|
const uint8_t *USBAudio::configuration_desc(uint8_t index)
|
|
{
|
|
if (index != 0) {
|
|
return NULL;
|
|
}
|
|
return _config_descriptor;
|
|
}
|
|
|
|
const uint8_t *USBAudio::string_iinterface_desc()
|
|
{
|
|
static const uint8_t stringIinterfaceDescriptor[] = {
|
|
0x0c, //bLength
|
|
STRING_DESCRIPTOR, //bDescriptorType 0x03
|
|
'A', 0, 'u', 0, 'd', 0, 'i', 0, 'o', 0 //bString iInterface - Audio
|
|
};
|
|
return stringIinterfaceDescriptor;
|
|
}
|
|
|
|
const uint8_t *USBAudio::string_iproduct_desc()
|
|
{
|
|
static const uint8_t stringIproductDescriptor[] = {
|
|
0x16, //bLength
|
|
STRING_DESCRIPTOR, //bDescriptorType 0x03
|
|
'M', 0, 'b', 0, 'e', 0, 'd', 0, ' ', 0, 'A', 0, 'u', 0, 'd', 0, 'i', 0, 'o', 0 //bString iProduct - Mbed Audio
|
|
};
|
|
return stringIproductDescriptor;
|
|
}
|
|
|
|
#define TOTAL_DESCRIPTOR_LENGTH ((1 * CONFIGURATION_DESCRIPTOR_LENGTH) \
|
|
+ (5 * INTERFACE_DESCRIPTOR_LENGTH) \
|
|
+ (1 * CONTROL_INTERFACE_DESCRIPTOR_LENGTH + 1) \
|
|
+ (2 * INPUT_TERMINAL_DESCRIPTOR_LENGTH) \
|
|
+ (1 * FEATURE_UNIT_DESCRIPTOR_LENGTH) \
|
|
+ (2 * OUTPUT_TERMINAL_DESCRIPTOR_LENGTH) \
|
|
+ (2 * STREAMING_INTERFACE_DESCRIPTOR_LENGTH) \
|
|
+ (2 * FORMAT_TYPE_I_DESCRIPTOR_LENGTH) \
|
|
+ (2 * (ENDPOINT_DESCRIPTOR_LENGTH + 2)) \
|
|
+ (2 * STREAMING_ENDPOINT_DESCRIPTOR_LENGTH) )
|
|
|
|
#define TOTAL_CONTROL_INTF_LENGTH (CONTROL_INTERFACE_DESCRIPTOR_LENGTH + 1 + \
|
|
2*INPUT_TERMINAL_DESCRIPTOR_LENGTH + \
|
|
FEATURE_UNIT_DESCRIPTOR_LENGTH + \
|
|
2*OUTPUT_TERMINAL_DESCRIPTOR_LENGTH)
|
|
|
|
void USBAudio::_build_configuration_desc()
|
|
{
|
|
uint8_t config_descriptor_temp[] = {
|
|
// Configuration 1
|
|
CONFIGURATION_DESCRIPTOR_LENGTH, // bLength
|
|
CONFIGURATION_DESCRIPTOR, // bDescriptorType
|
|
LSB(TOTAL_DESCRIPTOR_LENGTH), // wTotalLength (LSB)
|
|
MSB(TOTAL_DESCRIPTOR_LENGTH), // wTotalLength (MSB)
|
|
0x03, // bNumInterfaces
|
|
DEFAULT_CONFIGURATION, // bConfigurationValue
|
|
0x00, // iConfiguration
|
|
0x80, // bmAttributes
|
|
50, // bMaxPower
|
|
|
|
// Interface 0, Alternate Setting 0, Audio Control
|
|
INTERFACE_DESCRIPTOR_LENGTH, // bLength
|
|
INTERFACE_DESCRIPTOR, // bDescriptorType
|
|
0x00, // bInterfaceNumber
|
|
0x00, // bAlternateSetting
|
|
0x00, // bNumEndpoints
|
|
AUDIO_CLASS, // bInterfaceClass
|
|
SUBCLASS_AUDIOCONTROL, // bInterfaceSubClass
|
|
0x00, // bInterfaceProtocol
|
|
0x00, // iInterface
|
|
|
|
|
|
// Audio Control Interface
|
|
CONTROL_INTERFACE_DESCRIPTOR_LENGTH + 1,// bLength
|
|
INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType
|
|
CONTROL_HEADER, // bDescriptorSubtype
|
|
LSB(0x0100), // bcdADC (LSB)
|
|
MSB(0x0100), // bcdADC (MSB)
|
|
LSB(TOTAL_CONTROL_INTF_LENGTH), // wTotalLength
|
|
MSB(TOTAL_CONTROL_INTF_LENGTH), // wTotalLength
|
|
0x02, // bInCollection
|
|
0x01, // baInterfaceNr
|
|
0x02, // baInterfaceNr
|
|
|
|
// Audio Input Terminal (Speaker)
|
|
INPUT_TERMINAL_DESCRIPTOR_LENGTH, // bLength
|
|
INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType
|
|
CONTROL_INPUT_TERMINAL, // bDescriptorSubtype
|
|
0x01, // bTerminalID
|
|
LSB(TERMINAL_USB_STREAMING), // wTerminalType
|
|
MSB(TERMINAL_USB_STREAMING), // wTerminalType
|
|
0x00, // bAssocTerminal
|
|
_rx_channel_count, // bNrChannels
|
|
(uint8_t)(LSB(_channel_config_rx)), // wChannelConfig
|
|
(uint8_t)(MSB(_channel_config_rx)), // wChannelConfig
|
|
0x00, // iChannelNames
|
|
0x00, // iTerminal
|
|
|
|
// Audio Feature Unit (Speaker)
|
|
FEATURE_UNIT_DESCRIPTOR_LENGTH, // bLength
|
|
INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType
|
|
CONTROL_FEATURE_UNIT, // bDescriptorSubtype
|
|
0x02, // bUnitID
|
|
0x01, // bSourceID
|
|
0x01, // bControlSize
|
|
CONTROL_MUTE |
|
|
CONTROL_VOLUME, // bmaControls(0)
|
|
0x00, // bmaControls(1)
|
|
0x00, // iTerminal
|
|
|
|
// Audio Output Terminal (Speaker)
|
|
OUTPUT_TERMINAL_DESCRIPTOR_LENGTH, // bLength
|
|
INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType
|
|
CONTROL_OUTPUT_TERMINAL, // bDescriptorSubtype
|
|
0x03, // bTerminalID
|
|
LSB(TERMINAL_SPEAKER), // wTerminalType
|
|
MSB(TERMINAL_SPEAKER), // wTerminalType
|
|
0x00, // bAssocTerminal
|
|
0x02, // bSourceID
|
|
0x00, // iTerminal
|
|
|
|
|
|
// Audio Input Terminal (Microphone)
|
|
INPUT_TERMINAL_DESCRIPTOR_LENGTH, // bLength
|
|
INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType
|
|
CONTROL_INPUT_TERMINAL, // bDescriptorSubtype
|
|
0x04, // bTerminalID
|
|
LSB(TERMINAL_MICROPHONE), // wTerminalType
|
|
MSB(TERMINAL_MICROPHONE), // wTerminalType
|
|
0x00, // bAssocTerminal
|
|
_tx_channel_count, // bNrChannels
|
|
(uint8_t)(LSB(_channel_config_tx)), // wChannelConfig
|
|
(uint8_t)(MSB(_channel_config_tx)), // wChannelConfig
|
|
0x00, // iChannelNames
|
|
0x00, // iTerminal
|
|
|
|
// Audio Output Terminal (Microphone)
|
|
OUTPUT_TERMINAL_DESCRIPTOR_LENGTH, // bLength
|
|
INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType
|
|
CONTROL_OUTPUT_TERMINAL, // bDescriptorSubtype
|
|
0x05, // bTerminalID
|
|
LSB(TERMINAL_USB_STREAMING), // wTerminalType
|
|
MSB(TERMINAL_USB_STREAMING), // wTerminalType
|
|
0x00, // bAssocTerminal
|
|
0x04, // bSourceID
|
|
0x00, // iTerminal
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Interface 1, Alternate Setting 0, Audio Streaming - Zero Bandwith
|
|
INTERFACE_DESCRIPTOR_LENGTH, // bLength
|
|
INTERFACE_DESCRIPTOR, // bDescriptorType
|
|
0x01, // bInterfaceNumber
|
|
0x00, // bAlternateSetting
|
|
0x00, // bNumEndpoints
|
|
AUDIO_CLASS, // bInterfaceClass
|
|
SUBCLASS_AUDIOSTREAMING, // bInterfaceSubClass
|
|
0x00, // bInterfaceProtocol
|
|
0x00, // iInterface
|
|
|
|
// Interface 1, Alternate Setting 1, Audio Streaming - Operational
|
|
INTERFACE_DESCRIPTOR_LENGTH, // bLength
|
|
INTERFACE_DESCRIPTOR, // bDescriptorType
|
|
0x01, // bInterfaceNumber
|
|
0x01, // bAlternateSetting
|
|
0x01, // bNumEndpoints
|
|
AUDIO_CLASS, // bInterfaceClass
|
|
SUBCLASS_AUDIOSTREAMING, // bInterfaceSubClass
|
|
0x00, // bInterfaceProtocol
|
|
0x00, // iInterface
|
|
|
|
// Audio Streaming Interface
|
|
STREAMING_INTERFACE_DESCRIPTOR_LENGTH, // bLength
|
|
INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType
|
|
STREAMING_GENERAL, // bDescriptorSubtype
|
|
0x01, // bTerminalLink
|
|
0x00, // bDelay
|
|
LSB(FORMAT_PCM), // wFormatTag
|
|
MSB(FORMAT_PCM), // wFormatTag
|
|
|
|
// Audio Type I Format
|
|
FORMAT_TYPE_I_DESCRIPTOR_LENGTH, // bLength
|
|
INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType
|
|
STREAMING_FORMAT_TYPE, // bDescriptorSubtype
|
|
FORMAT_TYPE_I, // bFormatType
|
|
_rx_channel_count, // bNrChannels
|
|
0x02, // bSubFrameSize
|
|
16, // bBitResolution
|
|
0x01, // bSamFreqType
|
|
(uint8_t)(LSB(_rx_freq)), // tSamFreq
|
|
(uint8_t)((_rx_freq >> 8) & 0xff), // tSamFreq
|
|
(uint8_t)((_rx_freq >> 16) & 0xff), // tSamFreq
|
|
|
|
// Endpoint - Standard Descriptor
|
|
ENDPOINT_DESCRIPTOR_LENGTH + 2, // bLength
|
|
ENDPOINT_DESCRIPTOR, // bDescriptorType
|
|
_episo_out, // bEndpointAddress
|
|
E_ISOCHRONOUS, // bmAttributes
|
|
(uint8_t)(LSB(_rx_packet_size_max)), // wMaxPacketSize
|
|
(uint8_t)(MSB(_rx_packet_size_max)), // wMaxPacketSize
|
|
0x01, // bInterval
|
|
0x00, // bRefresh
|
|
0x00, // bSynchAddress
|
|
|
|
// Endpoint - Audio Streaming
|
|
STREAMING_ENDPOINT_DESCRIPTOR_LENGTH, // bLength
|
|
ENDPOINT_DESCRIPTOR_TYPE, // bDescriptorType
|
|
ENDPOINT_GENERAL, // bDescriptor
|
|
0x00, // bmAttributes
|
|
0x00, // bLockDelayUnits
|
|
LSB(0x0000), // wLockDelay
|
|
MSB(0x0000), // wLockDelay
|
|
|
|
|
|
// Interface 1, Alternate Setting 0, Audio Streaming - Zero Bandwith
|
|
INTERFACE_DESCRIPTOR_LENGTH, // bLength
|
|
INTERFACE_DESCRIPTOR, // bDescriptorType
|
|
0x02, // bInterfaceNumber
|
|
0x00, // bAlternateSetting
|
|
0x00, // bNumEndpoints
|
|
AUDIO_CLASS, // bInterfaceClass
|
|
SUBCLASS_AUDIOSTREAMING, // bInterfaceSubClass
|
|
0x00, // bInterfaceProtocol
|
|
0x00, // iInterface
|
|
|
|
// Interface 1, Alternate Setting 1, Audio Streaming - Operational
|
|
INTERFACE_DESCRIPTOR_LENGTH, // bLength
|
|
INTERFACE_DESCRIPTOR, // bDescriptorType
|
|
0x02, // bInterfaceNumber
|
|
0x01, // bAlternateSetting
|
|
0x01, // bNumEndpoints
|
|
AUDIO_CLASS, // bInterfaceClass
|
|
SUBCLASS_AUDIOSTREAMING, // bInterfaceSubClass
|
|
0x00, // bInterfaceProtocol
|
|
0x00, // iInterface
|
|
|
|
// Audio Streaming Interface
|
|
STREAMING_INTERFACE_DESCRIPTOR_LENGTH, // bLength
|
|
INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType
|
|
SUBCLASS_AUDIOCONTROL, // bDescriptorSubtype
|
|
0x05, // bTerminalLink (output terminal microphone)
|
|
0x01, // bDelay
|
|
0x01, // wFormatTag
|
|
0x00, // wFormatTag
|
|
|
|
// Audio Type I Format
|
|
FORMAT_TYPE_I_DESCRIPTOR_LENGTH, // bLength
|
|
INTERFACE_DESCRIPTOR_TYPE, // bDescriptorType
|
|
SUBCLASS_AUDIOSTREAMING, // bDescriptorSubtype
|
|
FORMAT_TYPE_I, // bFormatType
|
|
_tx_channel_count, // bNrChannels
|
|
0x02, // bSubFrameSize
|
|
0x10, // bBitResolution
|
|
0x01, // bSamFreqType
|
|
(uint8_t)(LSB(_tx_freq)), // tSamFreq
|
|
(uint8_t)((_tx_freq >> 8) & 0xff), // tSamFreq
|
|
(uint8_t)((_tx_freq >> 16) & 0xff), // tSamFreq
|
|
|
|
// Endpoint - Standard Descriptor
|
|
ENDPOINT_DESCRIPTOR_LENGTH + 2, // bLength
|
|
ENDPOINT_DESCRIPTOR, // bDescriptorType
|
|
_episo_in, // bEndpointAddress
|
|
E_ISOCHRONOUS, // bmAttributes
|
|
(uint8_t)(LSB(_tx_packet_size_max)), // wMaxPacketSize
|
|
(uint8_t)(MSB(_tx_packet_size_max)), // wMaxPacketSize
|
|
0x01, // bInterval
|
|
0x00, // bRefresh
|
|
0x00, // bSynchAddress
|
|
|
|
// Endpoint - Audio Streaming
|
|
STREAMING_ENDPOINT_DESCRIPTOR_LENGTH, // bLength
|
|
ENDPOINT_DESCRIPTOR_TYPE, // bDescriptorType
|
|
ENDPOINT_GENERAL, // bDescriptor
|
|
0x00, // bmAttributes
|
|
0x00, // bLockDelayUnits
|
|
LSB(0x0000), // wLockDelay
|
|
MSB(0x0000), // wLockDelay
|
|
};
|
|
|
|
MBED_ASSERT(sizeof(config_descriptor_temp) == sizeof(_config_descriptor));
|
|
memcpy(_config_descriptor, config_descriptor_temp, sizeof(_config_descriptor));
|
|
}
|
|
|
|
void USBAudio::_receive_change(ChannelState new_state)
|
|
{
|
|
assert_locked();
|
|
|
|
ChannelState prev_state = _rx_state;
|
|
_rx_state = new_state;
|
|
if (prev_state == new_state) {
|
|
// no change
|
|
return;
|
|
}
|
|
|
|
if (prev_state == Opened) {
|
|
// Leaving the opened state
|
|
_read_list.process();
|
|
_rx_done.call(End);
|
|
}
|
|
if (new_state == Opened) {
|
|
// Entering the opened state
|
|
_read_list.process();
|
|
_rx_done.call(Start);
|
|
}
|
|
if (new_state == Closed) {
|
|
// Only block if the channel is closed
|
|
_flags.clear(READ_READY_UNBLOCK);
|
|
} else {
|
|
_flags.set(READ_READY_UNBLOCK);
|
|
}
|
|
}
|
|
|
|
void USBAudio::_receive_isr()
|
|
{
|
|
assert_locked();
|
|
|
|
uint32_t size = read_finish(_episo_out);
|
|
|
|
if (size > _rx_queue.free()) {
|
|
_rx_overflow++;
|
|
} else {
|
|
|
|
// Copy data over
|
|
_rx_queue.write(_rx_packet_buf, size);
|
|
|
|
// Signal that there is more data available
|
|
_read_list.process();
|
|
if (_rx_done) {
|
|
_rx_done.call(Transfer);
|
|
}
|
|
}
|
|
|
|
read_start(_episo_out, _rx_packet_buf, _rx_packet_size_max);
|
|
}
|
|
|
|
void USBAudio::_send_change(ChannelState new_state)
|
|
{
|
|
assert_locked();
|
|
|
|
ChannelState prev_state = _tx_state;
|
|
_tx_state = new_state;
|
|
if (prev_state == new_state) {
|
|
// no change
|
|
return;
|
|
}
|
|
|
|
if (prev_state == Opened) {
|
|
// Leaving the opened state
|
|
_write_list.process();
|
|
_tx_done.call(End);
|
|
}
|
|
if (new_state == Opened) {
|
|
// Entering the opened state
|
|
_write_list.process();
|
|
_tx_done.call(Start);
|
|
}
|
|
if (new_state == Closed) {
|
|
// Only block if the channel is closed
|
|
_flags.clear(WRITE_READY_UNBLOCK);
|
|
} else {
|
|
_flags.set(WRITE_READY_UNBLOCK);
|
|
}
|
|
}
|
|
void USBAudio::_send_isr_start()
|
|
{
|
|
assert_locked();
|
|
|
|
if (!_tx_idle) {
|
|
return;
|
|
}
|
|
|
|
_send_isr_next_sync();
|
|
}
|
|
|
|
void USBAudio::_send_isr_next_sync()
|
|
{
|
|
// Compute size to send
|
|
uint32_t fames = _tx_whole_frames_per_xfer;
|
|
if (_tx_frame_fract >= XFER_FREQUENCY_HZ) {
|
|
_tx_frame_fract -= XFER_FREQUENCY_HZ;
|
|
fames += 1;
|
|
}
|
|
uint32_t send_size = fames * _tx_channel_count * 2;
|
|
|
|
// Check if this is the initial TX packet
|
|
if (_tx_idle && !_tx_queue.full()) {
|
|
// Don't start until the TX buffer is full
|
|
return;
|
|
}
|
|
|
|
// Check if this stream was closed
|
|
if (_tx_state != Opened) {
|
|
_tx_idle = true;
|
|
return;
|
|
}
|
|
|
|
// Check for enough data to send
|
|
if (_tx_queue.size() < send_size) {
|
|
_tx_underflow++;
|
|
_tx_idle = true;
|
|
return;
|
|
}
|
|
|
|
// Copy data over
|
|
_tx_queue.read(_tx_packet_buf, send_size);
|
|
|
|
// Start the write
|
|
write_start(_episo_in, _tx_packet_buf, send_size);
|
|
_tx_idle = false;
|
|
_tx_frame_fract += _tx_fract_frames_per_xfer;
|
|
}
|
|
|
|
void USBAudio::_send_isr()
|
|
{
|
|
assert_locked();
|
|
|
|
write_finish(_episo_in);
|
|
|
|
_send_isr_next_sync();
|
|
|
|
// Signal that there is space for more data
|
|
_write_list.process();
|
|
if (_tx_done) {
|
|
_tx_done.call(Transfer);
|
|
}
|
|
}
|
|
|