345 lines
14 KiB
C
345 lines
14 KiB
C
|
/*
|
||
|
* Copyright (c) 2022, sakumisu
|
||
|
*
|
||
|
* SPDX-License-Identifier: Apache-2.0
|
||
|
*/
|
||
|
#include "usbd_core.h"
|
||
|
#include "usbd_audio.h"
|
||
|
|
||
|
struct audio_entity_param {
|
||
|
uint32_t wCur;
|
||
|
uint32_t wMin;
|
||
|
uint32_t wMax;
|
||
|
uint32_t wRes;
|
||
|
};
|
||
|
|
||
|
struct usbd_audio_priv {
|
||
|
struct audio_entity_info *table;
|
||
|
uint8_t num;
|
||
|
uint16_t uac_version;
|
||
|
} g_usbd_audio[CONFIG_USBDEV_MAX_BUS];
|
||
|
|
||
|
static int audio_class_endpoint_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
|
||
|
{
|
||
|
uint8_t control_selector;
|
||
|
uint32_t sampling_freq = 0;
|
||
|
uint8_t ep;
|
||
|
|
||
|
control_selector = HI_BYTE(setup->wValue);
|
||
|
ep = LO_BYTE(setup->wIndex);
|
||
|
|
||
|
switch (control_selector) {
|
||
|
case AUDIO_EP_CONTROL_SAMPLING_FEQ:
|
||
|
switch (setup->bRequest) {
|
||
|
case AUDIO_REQUEST_SET_CUR:
|
||
|
memcpy((uint8_t *)&sampling_freq, *data, *len);
|
||
|
USB_LOG_DBG("Set ep:0x%02x %d Hz\r\n", ep, (int)sampling_freq);
|
||
|
usbd_audio_set_sampling_freq(busid, ep, sampling_freq);
|
||
|
break;
|
||
|
case AUDIO_REQUEST_GET_CUR:
|
||
|
case AUDIO_REQUEST_GET_MIN:
|
||
|
case AUDIO_REQUEST_GET_MAX:
|
||
|
case AUDIO_REQUEST_GET_RES:
|
||
|
sampling_freq = usbd_audio_get_sampling_freq(busid, ep);
|
||
|
memcpy(*data, &sampling_freq, 3);
|
||
|
USB_LOG_DBG("Get ep:0x%02x %d Hz\r\n", ep, (int)sampling_freq);
|
||
|
*len = 3;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
default:
|
||
|
USB_LOG_WRN("Unhandled Audio Class control selector 0x%02x\r\n", control_selector);
|
||
|
return -1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int audio_class_interface_request_handler(uint8_t busid, struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
|
||
|
{
|
||
|
USB_LOG_DBG("Audio Class request: "
|
||
|
"bRequest 0x%02x\r\n",
|
||
|
setup->bRequest);
|
||
|
|
||
|
uint8_t entity_id;
|
||
|
uint8_t ep;
|
||
|
uint8_t subtype = 0x01;
|
||
|
uint8_t control_selector;
|
||
|
uint8_t ch;
|
||
|
uint8_t mute;
|
||
|
uint16_t volume;
|
||
|
int volume_db = 0;
|
||
|
uint32_t sampling_freq = 0;
|
||
|
|
||
|
const char *mute_string[2] = { "off", "on" };
|
||
|
|
||
|
entity_id = HI_BYTE(setup->wIndex);
|
||
|
control_selector = HI_BYTE(setup->wValue);
|
||
|
ch = LO_BYTE(setup->wValue);
|
||
|
|
||
|
ARG_UNUSED(mute_string);
|
||
|
|
||
|
for (uint8_t i = 0; i < g_usbd_audio[busid].num; i++) {
|
||
|
if (g_usbd_audio[busid].table[i].bEntityId == entity_id) {
|
||
|
subtype = g_usbd_audio[busid].table[i].bDescriptorSubtype;
|
||
|
ep = g_usbd_audio[busid].table[i].ep;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (subtype == 0x01) {
|
||
|
USB_LOG_ERR("Do not find subtype for 0x%02x\r\n", entity_id);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
USB_LOG_DBG("Audio entity_id:%02x, subtype:%02x, cs:%02x\r\n", entity_id, subtype, control_selector);
|
||
|
|
||
|
switch (subtype) {
|
||
|
case AUDIO_CONTROL_FEATURE_UNIT:
|
||
|
switch (control_selector) {
|
||
|
case AUDIO_FU_CONTROL_MUTE:
|
||
|
if (g_usbd_audio[busid].uac_version < 0x0200) {
|
||
|
switch (setup->bRequest) {
|
||
|
case AUDIO_REQUEST_SET_CUR:
|
||
|
mute = (*data)[0];
|
||
|
usbd_audio_set_mute(busid, ep, ch, mute);
|
||
|
break;
|
||
|
case AUDIO_REQUEST_GET_CUR:
|
||
|
(*data)[0] = usbd_audio_get_mute(busid, ep, ch);
|
||
|
*len = 1;
|
||
|
break;
|
||
|
default:
|
||
|
USB_LOG_WRN("Unhandled Audio Class bRequest 0x%02x in cs 0x%02x\r\n", setup->bRequest, control_selector);
|
||
|
return -1;
|
||
|
}
|
||
|
} else {
|
||
|
switch (setup->bRequest) {
|
||
|
case AUDIO_REQUEST_CUR:
|
||
|
if (setup->bmRequestType & USB_REQUEST_DIR_MASK) {
|
||
|
(*data)[0] = usbd_audio_get_mute(busid, ep, ch);
|
||
|
*len = 1;
|
||
|
} else {
|
||
|
mute = (*data)[0];
|
||
|
usbd_audio_set_mute(busid, ep, ch, mute);
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
//USB_LOG_WRN("Unhandled Audio Class bRequest 0x%02x in cs 0x%02x\r\n", setup->bRequest, control_selector);
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case AUDIO_FU_CONTROL_VOLUME:
|
||
|
if (g_usbd_audio[busid].uac_version < 0x0200) {
|
||
|
switch (setup->bRequest) {
|
||
|
case AUDIO_REQUEST_SET_CUR:
|
||
|
memcpy(&volume, *data, *len);
|
||
|
if (volume < 0x8000) {
|
||
|
volume_db = volume / 256;
|
||
|
} else if (volume > 0x8000) {
|
||
|
volume_db = (0xffff - volume + 1) / -256;
|
||
|
}
|
||
|
volume_db += 128; /* 0 ~ 255 */
|
||
|
USB_LOG_DBG("Set ep:0x%02x ch:%d volume:0x%04x\r\n", ep, ch, volume);
|
||
|
usbd_audio_set_volume(busid, ep, ch, volume_db);
|
||
|
break;
|
||
|
case AUDIO_REQUEST_GET_CUR:
|
||
|
volume_db = usbd_audio_get_volume(busid, ep, ch);
|
||
|
volume_db -= 128;
|
||
|
if (volume_db >= 0) {
|
||
|
volume = volume_db * 256;
|
||
|
} else {
|
||
|
volume = volume_db * 256 + 0xffff + 1;
|
||
|
}
|
||
|
memcpy(*data, &volume, 2);
|
||
|
*len = 2;
|
||
|
break;
|
||
|
case AUDIO_REQUEST_GET_MIN:
|
||
|
(*data)[0] = 0x00; /* -2560/256 dB */
|
||
|
(*data)[1] = 0xdb;
|
||
|
*len = 2;
|
||
|
break;
|
||
|
case AUDIO_REQUEST_GET_MAX:
|
||
|
(*data)[0] = 0x00; /* 0 dB */
|
||
|
(*data)[1] = 0x00;
|
||
|
*len = 2;
|
||
|
break;
|
||
|
case AUDIO_REQUEST_GET_RES:
|
||
|
(*data)[0] = 0x00; /* -256/256 dB */
|
||
|
(*data)[1] = 0x01;
|
||
|
*len = 2;
|
||
|
break;
|
||
|
default:
|
||
|
USB_LOG_WRN("Unhandled Audio Class bRequest 0x%02x in cs 0x%02x\r\n", setup->bRequest, control_selector);
|
||
|
return -1;
|
||
|
}
|
||
|
} else {
|
||
|
switch (setup->bRequest) {
|
||
|
case AUDIO_REQUEST_CUR:
|
||
|
if (setup->bmRequestType & USB_REQUEST_DIR_MASK) {
|
||
|
volume_db = usbd_audio_get_volume(busid, ep, ch);
|
||
|
volume = volume_db;
|
||
|
memcpy(*data, &volume, 2);
|
||
|
*len = 2;
|
||
|
} else {
|
||
|
memcpy(&volume, *data, *len);
|
||
|
volume_db = volume;
|
||
|
USB_LOG_DBG("Set ep:0x%02x ch:%d volume:0x%02x\r\n", ep, ch, volume);
|
||
|
usbd_audio_set_volume(busid, ep, ch, volume_db);
|
||
|
}
|
||
|
break;
|
||
|
case AUDIO_REQUEST_RANGE:
|
||
|
if (setup->bmRequestType & USB_REQUEST_DIR_MASK) {
|
||
|
*((uint16_t *)(*data + 0)) = 1;
|
||
|
*((uint16_t *)(*data + 2)) = 0;
|
||
|
*((uint16_t *)(*data + 4)) = 100;
|
||
|
*((uint16_t *)(*data + 6)) = 1;
|
||
|
*len = 8;
|
||
|
} else {
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
//USB_LOG_WRN("Unhandled Audio Class bRequest 0x%02x in cs 0x%02x\r\n", setup->bRequest, control_selector);
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
USB_LOG_WRN("Unhandled Audio Class cs 0x%02x \r\n", control_selector);
|
||
|
return -1;
|
||
|
}
|
||
|
break;
|
||
|
case AUDIO_CONTROL_CLOCK_SOURCE:
|
||
|
switch (control_selector) {
|
||
|
case AUDIO_CS_CONTROL_SAM_FREQ:
|
||
|
switch (setup->bRequest) {
|
||
|
case AUDIO_REQUEST_CUR:
|
||
|
if (setup->bmRequestType & USB_REQUEST_DIR_MASK) {
|
||
|
sampling_freq = usbd_audio_get_sampling_freq(busid, ep);
|
||
|
memcpy(*data, &sampling_freq, 4);
|
||
|
USB_LOG_DBG("Get ep:0x%02x %d Hz\r\n", ep, (int)sampling_freq);
|
||
|
*len = 4;
|
||
|
} else {
|
||
|
memcpy(&sampling_freq, *data, setup->wLength);
|
||
|
USB_LOG_DBG("Set ep:0x%02x %d Hz\r\n", ep, (int)sampling_freq);
|
||
|
usbd_audio_set_sampling_freq(busid, ep, sampling_freq);
|
||
|
}
|
||
|
break;
|
||
|
case AUDIO_REQUEST_RANGE:
|
||
|
if (setup->bmRequestType & USB_REQUEST_DIR_MASK) {
|
||
|
uint8_t *sampling_freq_table = NULL;
|
||
|
uint16_t num;
|
||
|
|
||
|
usbd_audio_get_sampling_freq_table(busid, ep, &sampling_freq_table);
|
||
|
num = (uint16_t)((uint16_t)(sampling_freq_table[1] << 8) | ((uint16_t)sampling_freq_table[0]));
|
||
|
memcpy(*data, sampling_freq_table, (12 * num + 2));
|
||
|
*len = (12 * num + 2);
|
||
|
} else {
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
//USB_LOG_WRN("Unhandled Audio Class bRequest 0x%02x in cs 0x%02x\r\n", setup->bRequest, control_selector);
|
||
|
return -1;
|
||
|
}
|
||
|
break;
|
||
|
case AUDIO_CS_CONTROL_CLOCK_VALID:
|
||
|
if (setup->bmRequestType & USB_REQUEST_DIR_MASK) {
|
||
|
(*data)[0] = 1;
|
||
|
*len = 1;
|
||
|
} else {
|
||
|
return -1;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
//USB_LOG_WRN("Unhandled Audio Class cs 0x%02x \r\n", control_selector);
|
||
|
return -1;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void audio_notify_handler(uint8_t busid, uint8_t event, void *arg)
|
||
|
{
|
||
|
switch (event) {
|
||
|
case USBD_EVENT_RESET:
|
||
|
|
||
|
break;
|
||
|
|
||
|
case USBD_EVENT_SET_INTERFACE: {
|
||
|
struct usb_interface_descriptor *intf = (struct usb_interface_descriptor *)arg;
|
||
|
if (intf->bAlternateSetting) {
|
||
|
usbd_audio_open(busid, intf->bInterfaceNumber);
|
||
|
} else {
|
||
|
usbd_audio_close(busid, intf->bInterfaceNumber);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
struct usbd_interface *usbd_audio_init_intf(uint8_t busid,
|
||
|
struct usbd_interface *intf,
|
||
|
uint16_t uac_version,
|
||
|
struct audio_entity_info *table,
|
||
|
uint8_t num)
|
||
|
{
|
||
|
if (uac_version < 0x0200) {
|
||
|
intf->class_interface_handler = audio_class_interface_request_handler;
|
||
|
intf->class_endpoint_handler = audio_class_endpoint_request_handler;
|
||
|
intf->vendor_handler = NULL;
|
||
|
intf->notify_handler = audio_notify_handler;
|
||
|
} else {
|
||
|
intf->class_interface_handler = audio_class_interface_request_handler;
|
||
|
intf->class_endpoint_handler = NULL;
|
||
|
intf->vendor_handler = NULL;
|
||
|
intf->notify_handler = audio_notify_handler;
|
||
|
}
|
||
|
|
||
|
g_usbd_audio[busid].uac_version = uac_version;
|
||
|
g_usbd_audio[busid].table = table;
|
||
|
g_usbd_audio[busid].num = num;
|
||
|
|
||
|
return intf;
|
||
|
}
|
||
|
|
||
|
__WEAK void usbd_audio_set_volume(uint8_t busid, uint8_t ep, uint8_t ch, int volume)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
__WEAK int usbd_audio_get_volume(uint8_t busid, uint8_t ep, uint8_t ch)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
__WEAK void usbd_audio_set_mute(uint8_t busid, uint8_t ep, uint8_t ch, bool mute)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
__WEAK bool usbd_audio_get_mute(uint8_t busid, uint8_t ep, uint8_t ch)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
__WEAK void usbd_audio_set_sampling_freq(uint8_t busid, uint8_t ep, uint32_t sampling_freq)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
__WEAK uint32_t usbd_audio_get_sampling_freq(uint8_t busid, uint8_t ep)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
__WEAK void usbd_audio_get_sampling_freq_table(uint8_t busid, uint8_t ep, uint8_t **sampling_freq_table)
|
||
|
{
|
||
|
}
|