first commit
This commit is contained in:
@@ -0,0 +1,356 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// USB Audio Status
|
||||
typedef enum {
|
||||
USB_AUDIO_STATE_NOT_CONNECTED = 0,
|
||||
USB_AUDIO_STATE_CONNECTED,
|
||||
USB_AUDIO_STATE_CONFIGURED,
|
||||
USB_AUDIO_STATE_STREAMING,
|
||||
USB_AUDIO_STATE_ERROR
|
||||
} usb_audio_state_t;
|
||||
|
||||
// USB Headset Button Events (HID)
|
||||
typedef enum {
|
||||
USB_BUTTON_NONE = 0,
|
||||
USB_BUTTON_ANSWER,
|
||||
USB_BUTTON_HANGUP,
|
||||
USB_BUTTON_MUTE,
|
||||
USB_BUTTON_VOLUME_UP,
|
||||
USB_BUTTON_VOLUME_DOWN
|
||||
} usb_button_event_t;
|
||||
|
||||
// USB Audio Info
|
||||
typedef struct {
|
||||
uint16_t vendor_id;
|
||||
uint16_t product_id;
|
||||
char product_name[64];
|
||||
char manufacturer[64];
|
||||
uint32_t sample_rate;
|
||||
uint8_t channels;
|
||||
uint8_t bit_depth;
|
||||
bool has_microphone;
|
||||
bool has_speaker;
|
||||
bool has_hid; // Hat HID-Tasten
|
||||
} usb_audio_info_t;
|
||||
|
||||
// Callbacks
|
||||
typedef void (*usb_audio_state_callback_t)(usb_audio_state_t state);
|
||||
typedef void (*usb_audio_data_callback_t)(const uint8_t* data, size_t len);
|
||||
typedef void (*usb_button_callback_t)(usb_button_event_t event);
|
||||
|
||||
/**
|
||||
* Initialisiert den USB Audio Host
|
||||
*/
|
||||
esp_err_t usb_audio_host_init(void);
|
||||
|
||||
/**
|
||||
* Deinitialisiert den USB Audio Host
|
||||
*/
|
||||
esp_err_t usb_audio_host_deinit(void);
|
||||
|
||||
/**
|
||||
* Gibt den aktuellen Status zurück
|
||||
*/
|
||||
usb_audio_state_t usb_audio_host_get_state(void);
|
||||
|
||||
/**
|
||||
* Prüft ob ein USB-Headset angeschlossen ist
|
||||
*/
|
||||
bool usb_audio_host_is_connected(void);
|
||||
|
||||
/**
|
||||
* Gibt Informationen über das angeschlossene Gerät zurück
|
||||
*/
|
||||
esp_err_t usb_audio_host_get_info(usb_audio_info_t* info);
|
||||
|
||||
/**
|
||||
* Startet Audio-Streaming
|
||||
*/
|
||||
esp_err_t usb_audio_host_start_stream(void);
|
||||
|
||||
/**
|
||||
* Stoppt Audio-Streaming
|
||||
*/
|
||||
esp_err_t usb_audio_host_stop_stream(void);
|
||||
|
||||
/**
|
||||
* Sendet Audio-Daten zum USB-Headset (Speaker)
|
||||
*/
|
||||
esp_err_t usb_audio_host_send(const uint8_t* data, size_t len);
|
||||
|
||||
/**
|
||||
* Setzt die Lautstärke (0-100)
|
||||
*/
|
||||
esp_err_t usb_audio_host_set_volume(uint8_t volume);
|
||||
|
||||
/**
|
||||
* Setzt Mute
|
||||
*/
|
||||
esp_err_t usb_audio_host_set_mute(bool mute);
|
||||
|
||||
/**
|
||||
* Registriert Callbacks
|
||||
*/
|
||||
void usb_audio_host_register_state_callback(usb_audio_state_callback_t callback);
|
||||
void usb_audio_host_register_data_callback(usb_audio_data_callback_t callback);
|
||||
void usb_audio_host_register_button_callback(usb_button_callback_t callback);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user