diff options
Diffstat (limited to 'src/class/audio/audio_host.h')
| -rw-r--r-- | src/class/audio/audio_host.h | 361 |
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_ */ |
