summaryrefslogtreecommitdiff
path: root/src/class/audio/audio_host.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/class/audio/audio_host.h')
-rw-r--r--src/class/audio/audio_host.h361
1 files changed, 361 insertions, 0 deletions
diff --git a/src/class/audio/audio_host.h b/src/class/audio/audio_host.h
new file mode 100644
index 000000000..95911ab3b
--- /dev/null
+++ b/src/class/audio/audio_host.h
@@ -0,0 +1,361 @@
+/*
+ * SPDX-FileCopyrightText: Copyright (c) 2026 Zhenjiang Zhang
+ * SPDX-FileCopyrightText: Copyright (c) 2026 HiFiPhile (Zixun LI)
+ * SPDX-License-Identifier: MIT
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef TUSB_AUDIO_HOST_H_
+#define TUSB_AUDIO_HOST_H_
+
+#include "audio.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Class Driver Configuration
+//--------------------------------------------------------------------+
+
+// Audio Class protocol versions compiled into the host driver. Multiple
+// versions can be enabled so UAC1 and UAC2 devices can be mounted together.
+#define TUH_AUDIO_PROTOCOL_UAC1 TU_BIT(0)
+#define TUH_AUDIO_PROTOCOL_UAC2 TU_BIT(1)
+
+#ifndef CFG_TUH_AUDIO_PROTOCOLS
+ #define CFG_TUH_AUDIO_PROTOCOLS TUH_AUDIO_PROTOCOL_UAC1
+#endif
+
+#if !(CFG_TUH_AUDIO_PROTOCOLS & (TUH_AUDIO_PROTOCOL_UAC1 | TUH_AUDIO_PROTOCOL_UAC2))
+ #error CFG_TUH_AUDIO_PROTOCOLS must enable UAC1 and/or UAC2
+#endif
+
+#if CFG_TUH_AUDIO_PROTOCOLS & ~(TUH_AUDIO_PROTOCOL_UAC1 | TUH_AUDIO_PROTOCOL_UAC2)
+ #error CFG_TUH_AUDIO_PROTOCOLS contains an unsupported protocol bit
+#endif
+
+// Maximum number of Audio devices
+#ifndef CFG_TUH_AUDIO_MAX
+ #define CFG_TUH_AUDIO_MAX 1
+#endif
+// Maximum discrete sampling frequencies retained per rate source. UAC1 uses
+// one rate source per alternate setting; UAC2 alternate settings may share a
+// Clock Source.
+#ifndef CFG_TUH_AUDIO_MAX_SAM_FREQ
+ #define CFG_TUH_AUDIO_MAX_SAM_FREQ 5
+#endif
+// Maximum supported nonzero-bandwidth Audio Streaming alternate settings per
+// logical stream.
+#ifndef CFG_TUH_AUDIO_MAX_AS
+ #define CFG_TUH_AUDIO_MAX_AS 4
+#endif
+
+// Maximum size of one capture (IN) isochronous transfer the driver submits.
+// Configurations needing a larger per-poll-interval packet are rejected.
+// 256 covers 2-ch 48 kHz S16_LE (192 B) and common endpoint padding (208 B).
+#ifndef CFG_TUH_AUDIO_EPIN_BUFSIZE
+ #define CFG_TUH_AUDIO_EPIN_BUFSIZE 256
+#endif
+
+// Maximum size of one playback (OUT) isochronous transfer the driver submits.
+// Configurations needing a larger per-poll-interval packet are rejected.
+#ifndef CFG_TUH_AUDIO_EPOUT_BUFSIZE
+ #define CFG_TUH_AUDIO_EPOUT_BUFSIZE 256
+#endif
+
+// Depth in bytes of the per-stream data FIFO. The FIFO decouples the
+// application's read/write calls from the endpoint's isochronous polling cadence
+// and absorbs rate differences. Capture overwrites the oldest frames when full.
+// 1024 bytes hold 4 default (256 B) packets.
+#ifndef CFG_TUH_AUDIO_STREAM_BUFSIZE
+ #define CFG_TUH_AUDIO_STREAM_BUFSIZE 1024
+#endif
+
+//--------------------------------------------------------------------+
+// Types
+//--------------------------------------------------------------------+
+
+// Fixed transfer direction of a logical stream.
+typedef enum {
+ TUH_AUDIO_STREAM_PLAYBACK = 0, // Host -> Device (OUT)
+ TUH_AUDIO_STREAM_CAPTURE = 1, // Device -> Host (IN)
+ TUH_AUDIO_STREAM_DIRECTION_COUNT
+} tuh_audio_direction_t;
+
+// Asynchronous stream operation and transport events.
+typedef enum {
+ TUH_AUDIO_EVENT_START_COMPLETE = 0,
+ TUH_AUDIO_EVENT_STOP_COMPLETE,
+ TUH_AUDIO_EVENT_XFER_FAILED
+} tuh_audio_event_t;
+
+// Discrete Type-I PCM sample format. UAC1 requires bSamFreqType > 0; UAC2
+// configurations are built from the directly connected Clock Source RANGE.
+typedef enum {
+ TUH_AUDIO_FORMAT_S8 = 0, // signed 8-bit
+ TUH_AUDIO_FORMAT_S16_LE, // signed 16-bit little-endian
+ TUH_AUDIO_FORMAT_S24_3LE, // signed 24-bit packed in 3 bytes, LE
+ TUH_AUDIO_FORMAT_S24_LE, // signed 24-bit in 32-bit container, LE
+ TUH_AUDIO_FORMAT_S32_LE, // signed 32-bit little-endian
+ TUH_AUDIO_FORMAT_COUNT
+} tuh_audio_format_t;
+
+// One complete supported discrete configuration tuple.
+// Each entry is a full (format, sample_rate, channels) combination,
+// avoiding invalid mixes between independent format/rate/channel lists.
+// dir is constant for all configs of a given (dev_idx, stream_idx) and
+// equals the result of tuh_audio_stream_direction().
+typedef struct {
+ uint32_t sample_rate;
+ tuh_audio_direction_t dir;
+ tuh_audio_format_t format;
+ uint8_t channels;
+} tuh_audio_stream_config_t;
+
+// Feature Unit channel zero selects the master channel.
+#define TUH_AUDIO_CHANNEL_MASTER 0
+
+// Volume values are signed 1/256 dB. INT16_MIN represents silence.
+#define TUH_AUDIO_VOLUME_SILENCE INT16_MIN
+
+// One continuous volume range. This matches UAC1 MIN/MAX/RES and the common
+// UAC2 RANGE response containing one subrange.
+typedef struct {
+ int16_t min;
+ int16_t max;
+ uint16_t res;
+} tuh_audio_volume_range_t;
+
+// Audio Control descriptors reported during enumeration. Descriptor pointers
+// are valid only for the duration of tuh_audio_descriptor_cb().
+typedef struct {
+ const tusb_desc_interface_t *desc_audio_control;
+ const uint8_t *desc_cs_audio_control;
+ uint16_t desc_cs_audio_control_len;
+} tuh_audio_descriptor_cb_t;
+
+//--------------------------------------------------------------------+
+// Stream Enumeration
+//--------------------------------------------------------------------+
+
+// Number of logical audio streams exposed by one mounted device. The
+// application iterates stream indices [0, tuh_audio_stream_count()) and
+// inspects each with tuh_audio_stream_exists()/tuh_audio_stream_direction().
+uint8_t tuh_audio_stream_count(uint8_t dev_idx);
+
+// True if (dev_idx, stream_idx) identifies an existing stream.
+bool tuh_audio_stream_exists(uint8_t dev_idx, uint8_t stream_idx);
+
+// Fixed transfer direction of the stream.
+tuh_audio_direction_t tuh_audio_stream_direction(uint8_t dev_idx, uint8_t stream_idx);
+
+//--------------------------------------------------------------------+
+// Configuration Enumeration
+//--------------------------------------------------------------------+
+
+// Number of supported discrete configurations of the stream.
+uint8_t tuh_audio_config_count(uint8_t dev_idx, uint8_t stream_idx);
+
+// Active configuration index of the stream, or TUSB_INDEX_INVALID_8 if none.
+uint8_t tuh_audio_active_config(uint8_t dev_idx, uint8_t stream_idx);
+
+// Retrieve one discrete configuration tuple into *config.
+bool tuh_audio_config_get(uint8_t dev_idx, uint8_t stream_idx, uint8_t config_idx, tuh_audio_stream_config_t *config);
+
+//--------------------------------------------------------------------+
+// Configuration (ALSA hw_params analogue)
+//--------------------------------------------------------------------+
+
+// Synchronously configure the stream with the discrete configuration identified by
+// config_idx. The driver:
+// 1. resolves the AS interface and alternate setting,
+// 2. initializes the FIFO and packet scheduler,
+// 3. opens / reconfigures only the selected endpoint.
+bool tuh_audio_configure(uint8_t dev_idx, uint8_t stream_idx, uint8_t config_idx);
+
+//--------------------------------------------------------------------+
+// Stream Control / Frame-based Data
+//--------------------------------------------------------------------+
+
+// Start transferring data with the configuration selected by configure().
+// UAC1 activates the alternate setting before setting an endpoint frequency;
+// UAC2 sets a writable Clock Source before activating the alternate setting.
+// Startup is asynchronous: true means that the first request was submitted.
+// Completion is reported through tuh_audio_event_cb(); no event is emitted
+// when this function returns false.
+bool tuh_audio_start(uint8_t dev_idx, uint8_t stream_idx);
+// Stop transferring and asynchronously deactivate the Audio Streaming
+// interface (alt 0). true means that the deactivation request was submitted.
+// Completion is reported through tuh_audio_event_cb(); no event is emitted
+// when this function returns false.
+bool tuh_audio_stop(uint8_t dev_idx, uint8_t stream_idx);
+
+// Frame-based transfer. One frame = channels * bytes per sample.
+// tuh_audio_write() is valid only for TUH_AUDIO_STREAM_PLAYBACK streams,
+// tuh_audio_read() only for TUH_AUDIO_STREAM_CAPTURE streams.
+// Both functions are non-blocking and return immediately.
+// Returns the number of frames actually written/read (0 on any error,
+// including wrong direction, unconfigured/stopped stream, or full/empty FIFO).
+uint32_t tuh_audio_write(uint8_t dev_idx, uint8_t stream_idx, const void *buffer, uint32_t frame_count);
+uint32_t tuh_audio_read(uint8_t dev_idx, uint8_t stream_idx, void *buffer, uint32_t frame_count);
+
+// Number of frames that can be queued immediately for playback.
+uint32_t tuh_audio_write_available(uint8_t dev_idx, uint8_t stream_idx);
+// Number of captured frames that can be read immediately.
+uint32_t tuh_audio_read_available(uint8_t dev_idx, uint8_t stream_idx);
+
+//--------------------------------------------------------------------+
+// Helpers
+//--------------------------------------------------------------------+
+
+// Container size in bytes of one sample for a given format.
+static inline uint8_t tuh_audio_format_bytes(tuh_audio_format_t format) {
+ switch (format) {
+ case TUH_AUDIO_FORMAT_S8:
+ return 1;
+ case TUH_AUDIO_FORMAT_S16_LE:
+ return 2;
+ case TUH_AUDIO_FORMAT_S24_3LE:
+ return 3;
+ case TUH_AUDIO_FORMAT_S24_LE:
+ case TUH_AUDIO_FORMAT_S32_LE:
+ return 4;
+ default:
+ return 0;
+ }
+}
+
+// Size in bytes of one frame (all channels) for a configuration.
+static inline uint32_t tuh_audio_config_frame_size(const tuh_audio_stream_config_t *config) {
+ TU_ASSERT(config != NULL);
+ return (uint32_t)tuh_audio_format_bytes(config->format) * config->channels;
+}
+
+//--------------------------------------------------------------------+
+// Device Info
+//--------------------------------------------------------------------+
+
+// Check if Audio device is mounted
+bool tuh_audio_mounted(uint8_t idx);
+// Get device address of Audio device
+uint8_t tuh_audio_get_dev_addr(uint8_t idx);
+// True when the stream's Feature Unit supports master mute control.
+bool tuh_audio_mute_supported(uint8_t idx, uint8_t stream_idx);
+// Get the cached volume range. The driver reads the master channel when it
+// supports volume, otherwise the first logical channel with volume control.
+// This typed API assumes logical channels use the same range; applications
+// needing per-channel ranges can use tuh_audio_control_xfer().
+bool tuh_audio_volume_range_get(uint8_t idx, uint8_t stream_idx, tuh_audio_volume_range_t *range);
+
+//--------------------------------------------------------------------+
+// Control Request API
+//--------------------------------------------------------------------+
+
+// Submit a class-specific request to an entity on the Audio Control interface.
+// request is the protocol-specific UAC request code. buffer contains the raw
+// little-endian control payload. For an asynchronous transfer, buffer must
+// remain valid until complete_cb is invoked.
+bool tuh_audio_control_xfer(uint8_t idx, uint8_t entity_id, tusb_dir_t direction, uint8_t request,
+ uint8_t control_selector, uint8_t channel, void *buffer, uint16_t length,
+ tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+
+// Master mute and volume controls. Capability and range information is cached
+// before tuh_audio_mount_cb() is invoked. Volume channel 0 selects the master;
+// a SET falls back to writing every logical channel when the master is not
+// writable and all logical channels advertise write access. The completion
+// callback is invoked once after the entire operation. A nonzero volume
+// channel directly selects that 1-based Feature Unit logical channel.
+// Per-channel capability is not cached; an unsupported channel is reported by
+// the control transfer.
+//
+// Volume SET accepts TUH_AUDIO_VOLUME_SILENCE or a value within the cached
+// range; finite values are rounded to the nearest resolution step measured
+// from the range minimum.
+bool tuh_audio_mute_set(uint8_t idx, uint8_t stream_idx, bool mute, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+bool tuh_audio_mute_get(uint8_t idx, uint8_t stream_idx, bool *mute, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
+bool tuh_audio_volume_set(uint8_t idx, uint8_t stream_idx, uint8_t channel, int16_t volume, tuh_xfer_cb_t complete_cb,
+ uintptr_t user_data);
+bool tuh_audio_volume_get(uint8_t idx, uint8_t stream_idx, uint8_t channel, int16_t *volume, tuh_xfer_cb_t complete_cb,
+ uintptr_t user_data);
+
+//--------------------------------------------------------------------+
+// Synchronous control requests block until the transfer completes and return
+// its result. actual_len may be NULL when the received length is not needed.
+// Only use when audio streaming is stopped, otherwise the stream's isochronous
+// transfers may be disrupted and creating audible artifacts !
+//--------------------------------------------------------------------+
+tusb_xfer_result_t tuh_audio_control_xfer_sync(uint8_t idx, uint8_t entity_id, tusb_dir_t direction, uint8_t request,
+ uint8_t control_selector, uint8_t channel, void *buffer, uint16_t length,
+ uint32_t *actual_len);
+
+TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_audio_mute_set_sync(uint8_t idx, uint8_t stream_idx,
+ bool mute) {
+ TU_API_SYNC(tuh_audio_mute_set, idx, stream_idx, mute);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_audio_mute_get_sync(uint8_t idx, uint8_t stream_idx,
+ bool *mute) {
+ TU_API_SYNC(tuh_audio_mute_get, idx, stream_idx, mute);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_audio_volume_set_sync(uint8_t idx, uint8_t stream_idx,
+ uint8_t channel, int16_t volume) {
+ TU_API_SYNC(tuh_audio_volume_set, idx, stream_idx, channel, volume);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline tusb_xfer_result_t tuh_audio_volume_get_sync(uint8_t idx, uint8_t stream_idx,
+ uint8_t channel, int16_t *volume) {
+ TU_API_SYNC(tuh_audio_volume_get, idx, stream_idx, channel, volume);
+}
+
+//--------------------------------------------------------------------+
+// Callbacks (Weak is optional)
+//--------------------------------------------------------------------+
+
+// Invoked after the Audio Control and Streaming descriptors have been
+// validated during enumeration, before tuh_audio_mount_cb(). The interface is
+// not mounted yet and control requests must not be submitted from this
+// callback. Applications may inspect or copy descriptors needed for later raw
+// entity control requests.
+void tuh_audio_descriptor_cb(uint8_t idx, const tuh_audio_descriptor_cb_t *desc_cb_data);
+
+// Invoked when device with Audio interface is mounted
+void tuh_audio_mount_cb(uint8_t idx);
+
+// Invoked when device with Audio interface is un-mounted
+void tuh_audio_umount_cb(uint8_t idx);
+
+// Invoked when an isochronous IN transfer completes successfully: the
+// received data is already queued into the stream's capture FIFO.
+void tuh_audio_capture_cb(uint8_t idx, uint8_t stream_idx, uint16_t xferred_bytes);
+
+// Invoked after a successful isochronous OUT transfer, before the next packet
+// is prepared. After this callback returns, the driver submits queued audio
+// from the stream FIFO, or silence when a complete packet is unavailable.
+void tuh_audio_playback_cb(uint8_t idx, uint8_t stream_idx, uint16_t xferred_bytes);
+
+// Reports completion of asynchronous start/stop operations and unrecoverable
+// transfer failures. START_COMPLETE is emitted after the complete activation
+// sequence and initial endpoint transfers are submitted. XFER_FAILED means the
+// HCD could not submit a transfer or completed it unsuccessfully; it is not a
+// notification for an individual dropped isochronous packet. The driver stops
+// the stream before reporting START_COMPLETE failure or XFER_FAILED.
+void tuh_audio_event_cb(uint8_t idx, uint8_t stream_idx, tuh_audio_event_t event, tusb_xfer_result_t result);
+
+//--------------------------------------------------------------------+
+// Internal Class Driver API
+//--------------------------------------------------------------------+
+bool audioh_init(void);
+bool audioh_deinit(void);
+uint16_t audioh_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_interface_t *desc_itf, uint16_t max_len);
+bool audioh_set_config(uint8_t dev_addr, uint8_t itf_num);
+bool audioh_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
+void audioh_close(uint8_t daddr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TUSB_AUDIO_HOST_H_ */