blob: 681ad7cea7b63514e02c6c24092e4a196a0779d3 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <assert.h>
#include <syscfg/syscfg.h>
#include <bsp/bsp.h>
#include <string.h>
#include <tusb.h>
#include <device/usbd.h>
#include <class/audio/audio.h>
#include <os/util.h>
#include <usb_audio.h>
#define USBD_PRODUCT_RELEASE_NUMBER MYNEWT_VAL(USBD_PRODUCT_RELEASE_NUMBER)
#ifndef CONFIG_NUM
#define CONFIG_NUM 1
#endif
typedef enum {
USB_STRING_DESCRIPTOR_LANG = 0,
USB_STRING_DESCRIPTOR_MANUFACTURER = 1,
USB_STRING_DESCRIPTOR_PRODUCT = 2,
USB_STRING_DESCRIPTOR_INTERFACE = 3,
USB_STRING_DESCRIPTOR_CDC = 4,
USB_STRING_DESCRIPTOR_SERIAL = 16,
USB_STRING_DESCRIPTOR_MICROSOFT_OS = 0xEE,
} usb_string_descriptor_ix_t;
#define CDC_IF_STR_IX (MYNEWT_VAL(USBD_CDC_DESCRIPTOR_STRING) == NULL ? 0 : 4)
const tusb_desc_device_t desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = 0x00,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.idVendor = MYNEWT_VAL(USBD_VID),
.idProduct = MYNEWT_VAL(USBD_PID),
.bcdDevice = USBD_PRODUCT_RELEASE_NUMBER,
.iManufacturer = USB_STRING_DESCRIPTOR_MANUFACTURER,
.iProduct = USB_STRING_DESCRIPTOR_PRODUCT,
.iSerialNumber = USB_STRING_DESCRIPTOR_SERIAL,
.bNumConfigurations = 0x01
};
/*
* Invoked when received GET DEVICE DESCRIPTOR
* Application return pointer to descriptor
*/
const uint8_t *
tud_descriptor_device_cb(void)
{
return (const uint8_t *)&desc_device;
}
#if MYNEWT_VAL_CHOICE(MCU_TARGET, nRF5340_APP) || MYNEWT_VAL_CHOICE(MCU_TARGET, nRF52840)
#define ISO_EP 8
#else
#error MCU not supported
#endif
/*
* Configuration Descriptor
*/
enum {
#if CFG_TUD_BTH
ITF_NUM_BTH,
#if CFG_TUD_BTH_ISO_ALT_COUNT > 0
ITF_NUM_BTH_VOICE,
#endif
#endif
#if CFG_TUD_CDC
ITF_NUM_CDC,
ITF_NUM_CDC_DATA,
#endif
#if CFG_TUD_MSC
ITF_NUM_MSC,
#endif
#if CFG_TUD_HID
ITF_NUM_HID,
#endif
#if CFG_TUD_AUDIO_IN
ITF_NUM_AUDIO_AC,
ITF_NUM_AUDIO_AS_IN,
#elif CFG_TUD_AUDIO_OUT
ITF_NUM_AUDIO_AC,
ITF_NUM_AUDIO_AS_OUT,
#elif CFG_TUD_AUDIO_IN_OUT
ITF_NUM_AUDIO_AC,
ITF_NUM_AUDIO_AS_IN,
ITF_NUM_AUDIO_AS_OUT,
#endif
ITF_NUM_TOTAL
};
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + \
CFG_TUD_CDC * TUD_CDC_DESC_LEN + \
CFG_TUD_MSC * TUD_MSC_DESC_LEN + \
CFG_TUD_HID * TUD_HID_DESC_LEN + \
CFG_TUD_BTH * TUD_BTH_DESC_LEN + \
CFG_TUD_AUDIO_IN * TUD_AUDIO_MIC_ONE_CH_DESC_LEN + \
(1 + CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP) * CFG_TUD_AUDIO_OUT * \
(TUD_AUDIO_SPEAKER_MONO_DESC_LEN * (CFG_TUD_AUDIO_N_CHANNELS_RX == 1 ? 1 : 0)) + \
(1 - CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP) * CFG_TUD_AUDIO_OUT * \
(TUD_AUDIO_SPEAKER_STEREO_DESC_LEN * (CFG_TUD_AUDIO_N_CHANNELS_RX == 2 ? 1 : 0)) + \
0)
const uint8_t desc_configuration[] = {
TUD_CONFIG_DESCRIPTOR(CONFIG_NUM, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP,
MYNEWT_VAL(USBD_CONFIGURATION_MAX_POWER)),
#if CFG_TUD_BTH
TUD_BTH_DESCRIPTOR(ITF_NUM_BTH, BTH_IF_STR_IX, USBD_BTH_EVENT_EP, USBD_BTH_EVENT_EP_SIZE,
USBD_BTH_EVENT_EP_INTERVAL, USBD_BTH_DATA_IN_EP, USBD_BTH_DATA_OUT_EP, USBD_BTH_DATA_EP_SIZE,
0, 9, 17, 25, 33, 49),
#endif
#if CFG_TUD_CDC
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, CDC_IF_STR_IX, USBD_CDC_NOTIFY_EP, USBD_CDC_NOTIFY_EP_SIZE,
USBD_CDC_DATA_OUT_EP, USBD_CDC_DATA_IN_EP, USBD_CDC_DATA_EP_SIZE),
#endif
#if CFG_TUD_MSC
/* TODO: MSC not handled yet */
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, MSC_IF_STR_IX, EPNUM_MSC_OUT, EPNUM_MSC_IN,
(CFG_TUSB_RHPORT0_MODE & OPT_MODE_HIGH_SPEED) ? 512 : 64),
#endif
#if CFG_TUD_HID
TUD_HID_DESCRIPTOR(ITF_NUM_HID, HID_IF_STR_IX, HID_PROTOCOL_NONE, sizeof(desc_hid_report),
USBD_HID_REPORT_EP, USBD_HID_REPORT_EP_SIZE, USBD_HID_REPORT_EP_INTERVAL),
#endif
#if CFG_TUD_AUDIO_IN_OUT
#elif CFG_TUD_AUDIO_IN
TUD_AUDIO_MIC2_DESCRIPTOR(ITF_NUM_AUDIO_AC_IN, 0, 2, 16, 0x80 | ISO_EP, 192),
#elif CFG_TUD_AUDIO_OUT && CFG_TUD_AUDIO_N_CHANNELS_RX == 2
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP == 1
TUD_AUDIO_SPEAKER_STEREO_FB_DESCRIPTOR(ITF_NUM_AUDIO_AC, 0, CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX,
8 * CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX, ISO_EP, CFG_TUD_AUDIO_EPSIZE_OUT, 0x80 | ISO_EP, 1),
#else
TUD_AUDIO_SPEAKER_STEREO_DESCRIPTOR(ITF_NUM_AUDIO_AC, 0, CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX,
8 * CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX, ISO_EP, CFG_TUD_AUDIO_EPSIZE_OUT),
#endif
#elif CFG_TUD_AUDIO_OUT && CFG_TUD_AUDIO_N_CHANNELS_RX == 1
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
TUD_AUDIO_SPEAKER_MONO_FB_DESCRIPTOR(ITF_NUM_AUDIO_AC, 0, 2, 16, ISO_EP, 100, 0x80 | ISO_EP),
#else
TUD_AUDIO_SPEAKER_MONO_DESCRIPTOR(ITF_NUM_AUDIO_AC, 0, CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX,
8 * CFG_TUD_AUDIO_N_BYTES_PER_SAMPLE_RX, ISO_EP, CFG_TUD_AUDIO_EPSIZE_OUT),
#endif
#endif
};
/**
* Invoked when received GET CONFIGURATION DESCRIPTOR
* Application return pointer to descriptor
* Descriptor contents must exist long enough for transfer to complete
*/
const uint8_t *
tud_descriptor_configuration_cb(uint8_t index)
{
(void)index;
return desc_configuration;
}
static uint16_t desc_string[MYNEWT_VAL(USBD_STRING_DESCRIPTOR_MAX_LENGTH) + 1];
#if CFG_TUD_AUDIO
#if CFG_TUD_AUDIO_IN
const uint16_t tud_audio_desc_lengths[] = {TUD_AUDIO_MIC2_DESC_LEN};
#elif CFG_TUD_AUDIO_OUT && CFG_TUD_AUDIO_N_CHANNELS_RX == 1
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
const uint16_t tud_audio_desc_lengths[] = {TUD_AUDIO_SPEAKER_MONO_FB_DESC_LEN};
#else
const uint16_t tud_audio_desc_lengths[] = {TUD_AUDIO_SPEAKER_MONO_DESC_LEN};
#endif
#elif CFG_TUD_AUDIO_OUT && CFG_TUD_AUDIO_N_CHANNELS_RX == 2
#if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
const uint16_t tud_audio_desc_lengths[] = {TUD_AUDIO_SPEAKER_STEREO_FB_DESC_LEN};
#else
const uint16_t tud_audio_desc_lengths[] = {TUD_AUDIO_SPEAKER_STEREO_DESC_LEN};
#endif
#endif
static uint32_t g_sample_rate = CFG_TUD_AUDIO_SAMPLE_RATE;
static usb_audio_sample_rate_cb_t sample_rate_cb;
/* Invoked when audio class specific set request received for an entity */
bool
tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
{
uint32_t new_sample_rate;
(void) rhport;
audio_control_request_t *request = (audio_control_request_t *) p_request;
if (request->bEntityID == 2 && request->bControlSelector == AUDIO_FU_CTRL_VOLUME &&
request->bRequest == AUDIO_CS_REQ_CUR) {
/* Ignore value but accept request */
return true;
} else if (request->bEntityID == 4 && request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ &&
request->bRequest == AUDIO_CS_REQ_CUR) {
/* Ignore value but accept request */
new_sample_rate = ((audio_control_cur_4_t *)(pBuff))->bCur;
if (new_sample_rate != g_sample_rate) {
g_sample_rate = new_sample_rate;
if (sample_rate_cb) {
sample_rate_cb(g_sample_rate);
}
}
return true;
} else {
__BKPT(1);
}
return false;
}
bool
tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request)
{
(void) rhport;
audio_control_request_t *request = (audio_control_request_t *) p_request;
if (request->bEntityID == 4 && request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) {
if (request->bRequest == AUDIO_CS_REQ_CUR) {
audio_control_cur_4_t curf = {g_sample_rate};
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &curf, sizeof(curf));
} else if (request->bRequest == AUDIO_CS_REQ_RANGE) {
#if 1
audio_control_range_4_n_t(1) rangef = {
.wNumSubRanges = 1,
.subrange[0] = {g_sample_rate, g_sample_rate, 0},
};
#else
audio_control_range_4_n_t(2) rangef = {
.wNumSubRanges = 2,
.subrange[0] = {16000, 16000, 0},
.subrange[1] = {48000, 48000, 0},
};
#endif
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &rangef, sizeof(rangef));
}
} else if (request->bEntityID == 5 && request->bControlSelector == AUDIO_CX_CTRL_CONTROL) {
if (request->bRequest == AUDIO_CS_REQ_CUR) {
audio_control_cur_1_t cur_clk = {1};
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &cur_clk, sizeof(cur_clk));
}
} else if (request->bEntityID == 4 && request->bControlSelector == AUDIO_CS_CTRL_CLK_VALID &&
request->bRequest == AUDIO_CS_REQ_CUR) {
audio_control_cur_1_t cur_valid = {.bCur = 1};
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &cur_valid, sizeof(cur_valid));
} else if (request->bEntityID == 2 && request->bControlSelector == AUDIO_FU_CTRL_MUTE &&
request->bRequest == AUDIO_CS_REQ_CUR) {
audio_control_cur_1_t mute = {.bCur = 0};
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute, sizeof(mute));
} else if (request->bEntityID == 2 && request->bControlSelector == AUDIO_FU_CTRL_VOLUME) {
if (request->bRequest == AUDIO_CS_REQ_RANGE) {
audio_control_range_2_n_t(1) range_vol = {
.wNumSubRanges = 1,
.subrange[0] = {0, 1000, 10}
};
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &range_vol, sizeof(range_vol));
} else if (request->bRequest == AUDIO_CS_REQ_CUR) {
audio_control_cur_2_t cur_vol = {.bCur = 1280};
return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &cur_vol, sizeof(cur_vol));
}
} else {
__BKPT(1);
}
return false;
}
#endif
/* Function converts ASCII string to USB descriptor format */
static uint16_t *
string_to_usb_desc_string(const char *str, uint16_t *desc, uint8_t desc_size)
{
int i;
int char_num = strlen(str);
assert(char_num < desc_size);
if (char_num >= desc_size) {
char_num = desc_size - 1;
}
/* Encode length in first byte, type in second byte */
desc[0] = tu_htole16(tu_u16(TUSB_DESC_STRING, 2 * (char_num + 1)));
/* Copy characters 8bit to 16 bits */
for (i = 0; i < char_num; ++i) {
desc[1 + i] = tu_htole16(str[i]);
}
return desc;
}
/* LANGID string descriptors */
static const uint16_t usbd_lang_id[2] = {
(TUSB_DESC_STRING << 8) + 4, /* Size of this descriptor */
tu_htole16(MYNEWT_VAL(USBD_LANGID))
};
static struct {
uint16_t major;
uint16_t minor;
uint16_t revision;
uint32_t build;
} img_version = {
1, 0, 0, 1
};
static char serial_number[11];
static uint16_t *
serial_to_usb_desc_string(uint16_t *desc, size_t size)
{
if (serial_number[0] == 0) {
uint64_t serial = MYNEWT_VAL(USBD_SERIAL_ID);
snprintf(serial_number, 11, "%010" PRIu64, serial);
}
return string_to_usb_desc_string(serial_number, desc, size);
}
/*
* Invoked when received GET STRING DESCRIPTOR request
* Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
*/
const uint16_t *
tud_descriptor_string_cb(uint8_t index, uint16_t langid)
{
const uint16_t *ret = NULL;
char interface[100];
#if MYNEWT_VAL(USBD_WINDOWS_COMP_ID)
if (index == USB_STRING_DESCRIPTOR_MICROSOFT_OS) {
ret = (const uint16_t *)microsoft_os_string_descriptor;
}
#endif
if (index == USB_STRING_DESCRIPTOR_LANG) {
ret = usbd_lang_id;
} else if (index == USB_STRING_DESCRIPTOR_SERIAL) {
ret = serial_to_usb_desc_string(desc_string, ARRAY_SIZE(desc_string));
} else if (index == USB_STRING_DESCRIPTOR_MANUFACTURER) {
ret = string_to_usb_desc_string(MYNEWT_VAL(USBD_VENDOR_STRING), desc_string, ARRAY_SIZE(desc_string));
} else if (index == USB_STRING_DESCRIPTOR_PRODUCT) {
ret = string_to_usb_desc_string(MYNEWT_VAL(USBD_PRODUCT_STRING), desc_string, ARRAY_SIZE(desc_string));
} else if (index == USB_STRING_DESCRIPTOR_INTERFACE) {
snprintf(interface, sizeof(interface), "%s, (%u.%u.%u.%lu)", MYNEWT_VAL(USBD_PRODUCT_STRING),
img_version.major, img_version.minor, img_version.revision, img_version.build);
ret = string_to_usb_desc_string(interface, desc_string, ARRAY_SIZE(desc_string));
}
return ret;
}
void
usb_desc_sample_rate_set(uint32_t sample_rate)
{
g_sample_rate = sample_rate;
}