blob: 95911ab3bc7a9e87bb1db39ef93aeb8a9e8d675b [file]
/*
* 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_ */