esp32-sip-client-with-headset/main/usb_audio/usb_audio_host.c

357 lines
10 KiB
C

/**
* USB Audio Host - USB Headset Unterstützung
*
* Verwendet ESP32-S3 USB OTG im Host-Modus für USB Audio Class Geräte
*/
#include <string.h>
#include "usb_audio_host.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/ringbuf.h"
#include "usb/usb_host.h"
static const char* TAG = "USB_AUDIO";
// USB Audio Class Definitionen
#define USB_CLASS_AUDIO 0x01
#define USB_SUBCLASS_AUDIOCONTROL 0x01
#define USB_SUBCLASS_AUDIOSTREAMING 0x02
#define USB_CLASS_HID 0x03
// State
static bool s_initialized = false;
static bool s_host_installed = false;
static usb_audio_state_t s_state = USB_AUDIO_STATE_NOT_CONNECTED;
static usb_audio_info_t s_device_info;
static usb_host_client_handle_t s_client_handle = NULL;
static usb_device_handle_t s_device_handle = NULL;
static TaskHandle_t s_usb_task_handle = NULL;
// Audio Buffers
#define AUDIO_RINGBUF_SIZE (16 * 1024)
static RingbufHandle_t s_audio_in_ringbuf = NULL; // Vom USB Mic
static RingbufHandle_t s_audio_out_ringbuf = NULL; // Zum USB Speaker
// Callbacks
static usb_audio_state_callback_t s_state_callback = NULL;
static usb_audio_data_callback_t s_data_callback = NULL;
static usb_button_callback_t s_button_callback = NULL;
static void notify_state_change(usb_audio_state_t new_state)
{
s_state = new_state;
if (s_state_callback) {
s_state_callback(new_state);
}
}
static void usb_host_client_event_callback(const usb_host_client_event_msg_t *event_msg, void *arg)
{
switch (event_msg->event) {
case USB_HOST_CLIENT_EVENT_NEW_DEV:
ESP_LOGI(TAG, "Neues USB-Gerät gefunden: Adresse %d", event_msg->new_dev.address);
// Gerät öffnen
if (usb_host_device_open(s_client_handle, event_msg->new_dev.address,
&s_device_handle) == ESP_OK) {
// Device Descriptor holen
const usb_device_desc_t *dev_desc;
if (usb_host_get_device_descriptor(s_device_handle, &dev_desc) == ESP_OK) {
s_device_info.vendor_id = dev_desc->idVendor;
s_device_info.product_id = dev_desc->idProduct;
ESP_LOGI(TAG, "USB Gerät: VID=0x%04X PID=0x%04X",
dev_desc->idVendor, dev_desc->idProduct);
// Configuration Descriptor analysieren
const usb_config_desc_t *config_desc;
if (usb_host_get_active_config_descriptor(s_device_handle, &config_desc) == ESP_OK) {
// Interfaces durchgehen
int offset = 0;
const usb_intf_desc_t *intf_desc;
while ((intf_desc = usb_parse_interface_descriptor(
config_desc, offset, 0, &offset)) != NULL) {
if (intf_desc->bInterfaceClass == USB_CLASS_AUDIO) {
if (intf_desc->bInterfaceSubClass == USB_SUBCLASS_AUDIOSTREAMING) {
ESP_LOGI(TAG, "Audio Streaming Interface gefunden");
s_device_info.has_speaker = true;
s_device_info.has_microphone = true;
}
} else if (intf_desc->bInterfaceClass == USB_CLASS_HID) {
ESP_LOGI(TAG, "HID Interface gefunden (Tasten)");
s_device_info.has_hid = true;
}
}
if (s_device_info.has_speaker || s_device_info.has_microphone) {
notify_state_change(USB_AUDIO_STATE_CONNECTED);
// Standard Audio-Format
s_device_info.sample_rate = 16000;
s_device_info.channels = 1;
s_device_info.bit_depth = 16;
ESP_LOGI(TAG, "USB Audio Headset erkannt");
}
}
}
}
break;
case USB_HOST_CLIENT_EVENT_DEV_GONE:
ESP_LOGI(TAG, "USB-Gerät entfernt");
if (s_device_handle) {
usb_host_device_close(s_client_handle, s_device_handle);
s_device_handle = NULL;
}
memset(&s_device_info, 0, sizeof(s_device_info));
notify_state_change(USB_AUDIO_STATE_NOT_CONNECTED);
break;
default:
break;
}
}
static void usb_host_task(void *arg)
{
ESP_LOGI(TAG, "USB Host Task gestartet");
while (s_initialized) {
// USB Host Events verarbeiten
usb_host_lib_handle_events(pdMS_TO_TICKS(100), NULL);
// Client Events verarbeiten
if (s_client_handle) {
usb_host_client_handle_events(s_client_handle, pdMS_TO_TICKS(100));
}
// Wenn verbunden: Audio-Daten verarbeiten
if (s_state == USB_AUDIO_STATE_STREAMING && s_audio_in_ringbuf) {
// Audio vom USB lesen und Callback aufrufen
size_t item_size;
uint8_t* data = xRingbufferReceive(s_audio_in_ringbuf, &item_size, 0);
if (data && item_size > 0) {
if (s_data_callback) {
s_data_callback(data, item_size);
}
vRingbufferReturnItem(s_audio_in_ringbuf, data);
}
}
vTaskDelay(pdMS_TO_TICKS(10));
}
ESP_LOGI(TAG, "USB Host Task beendet");
vTaskDelete(NULL);
}
esp_err_t usb_audio_host_init(void)
{
if (s_initialized) {
return ESP_OK;
}
ESP_LOGI(TAG, "Initialisiere USB Audio Host");
// Audio Buffers erstellen
s_audio_in_ringbuf = xRingbufferCreate(AUDIO_RINGBUF_SIZE, RINGBUF_TYPE_BYTEBUF);
s_audio_out_ringbuf = xRingbufferCreate(AUDIO_RINGBUF_SIZE, RINGBUF_TYPE_BYTEBUF);
if (!s_audio_in_ringbuf || !s_audio_out_ringbuf) {
ESP_LOGE(TAG, "Ringbuffer erstellen fehlgeschlagen");
return ESP_ERR_NO_MEM;
}
// USB Host Library installieren
usb_host_config_t host_config = {
.skip_phy_setup = false,
.intr_flags = ESP_INTR_FLAG_LEVEL1,
};
esp_err_t ret = usb_host_install(&host_config);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "USB Host install failed: %s", esp_err_to_name(ret));
return ret;
}
s_host_installed = true;
// Client registrieren
usb_host_client_config_t client_config = {
.is_synchronous = false,
.max_num_event_msg = 5,
.async = {
.client_event_callback = usb_host_client_event_callback,
.callback_arg = NULL,
},
};
ret = usb_host_client_register(&client_config, &s_client_handle);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "USB Client register failed: %s", esp_err_to_name(ret));
return ret;
}
s_initialized = true;
// USB Host Task starten
xTaskCreate(usb_host_task, "usb_host", 4096, NULL, 5, &s_usb_task_handle);
ESP_LOGI(TAG, "USB Audio Host initialisiert");
return ESP_OK;
}
esp_err_t usb_audio_host_deinit(void)
{
if (!s_initialized) return ESP_OK;
ESP_LOGI(TAG, "Deinitalisiere USB Audio Host");
s_initialized = false;
// Warten auf Task-Ende
if (s_usb_task_handle) {
vTaskDelay(pdMS_TO_TICKS(200));
}
// Gerät schließen
if (s_device_handle && s_client_handle) {
usb_host_device_close(s_client_handle, s_device_handle);
s_device_handle = NULL;
}
// Client deregistrieren
if (s_client_handle) {
usb_host_client_deregister(s_client_handle);
s_client_handle = NULL;
}
// USB Host deinstallieren
if (s_host_installed) {
usb_host_uninstall();
s_host_installed = false;
}
// Buffers freigeben
if (s_audio_in_ringbuf) {
vRingbufferDelete(s_audio_in_ringbuf);
s_audio_in_ringbuf = NULL;
}
if (s_audio_out_ringbuf) {
vRingbufferDelete(s_audio_out_ringbuf);
s_audio_out_ringbuf = NULL;
}
return ESP_OK;
}
usb_audio_state_t usb_audio_host_get_state(void)
{
return s_state;
}
bool usb_audio_host_is_connected(void)
{
return s_state >= USB_AUDIO_STATE_CONNECTED;
}
esp_err_t usb_audio_host_get_info(usb_audio_info_t* info)
{
if (!info) return ESP_ERR_INVALID_ARG;
if (s_state == USB_AUDIO_STATE_NOT_CONNECTED) return ESP_ERR_INVALID_STATE;
memcpy(info, &s_device_info, sizeof(usb_audio_info_t));
return ESP_OK;
}
esp_err_t usb_audio_host_start_stream(void)
{
if (s_state < USB_AUDIO_STATE_CONNECTED) {
return ESP_ERR_INVALID_STATE;
}
ESP_LOGI(TAG, "Starte USB Audio Stream");
// TODO: Echte USB Audio Stream Konfiguration
// Dies würde das Konfigurieren der Audio-Interfaces,
// Setzen der Samplerate und Starten der Isochronen Transfers beinhalten
s_state = USB_AUDIO_STATE_STREAMING;
notify_state_change(USB_AUDIO_STATE_STREAMING);
return ESP_OK;
}
esp_err_t usb_audio_host_stop_stream(void)
{
if (s_state != USB_AUDIO_STATE_STREAMING) {
return ESP_OK;
}
ESP_LOGI(TAG, "Stoppe USB Audio Stream");
s_state = USB_AUDIO_STATE_CONNECTED;
notify_state_change(USB_AUDIO_STATE_CONNECTED);
return ESP_OK;
}
esp_err_t usb_audio_host_send(const uint8_t* data, size_t len)
{
if (s_state != USB_AUDIO_STATE_STREAMING || !s_audio_out_ringbuf) {
return ESP_ERR_INVALID_STATE;
}
if (xRingbufferSend(s_audio_out_ringbuf, data, len, 0) != pdTRUE) {
ESP_LOGW(TAG, "Audio buffer overflow");
return ESP_ERR_NO_MEM;
}
return ESP_OK;
}
esp_err_t usb_audio_host_set_volume(uint8_t volume)
{
if (s_state < USB_AUDIO_STATE_CONNECTED) {
return ESP_ERR_INVALID_STATE;
}
ESP_LOGI(TAG, "Setze Lautstärke: %d%%", volume);
// TODO: USB Audio Class Volume Control implementieren
return ESP_OK;
}
esp_err_t usb_audio_host_set_mute(bool mute)
{
if (s_state < USB_AUDIO_STATE_CONNECTED) {
return ESP_ERR_INVALID_STATE;
}
ESP_LOGI(TAG, "Mute: %s", mute ? "an" : "aus");
// TODO: USB Audio Class Mute Control implementieren
return ESP_OK;
}
void usb_audio_host_register_state_callback(usb_audio_state_callback_t callback)
{
s_state_callback = callback;
}
void usb_audio_host_register_data_callback(usb_audio_data_callback_t callback)
{
s_data_callback = callback;
}
void usb_audio_host_register_button_callback(usb_button_callback_t callback)
{
s_button_callback = callback;
}