3. 音频流接口备用设置切换

任务说明

在USB音频设备中,同一音频流接口常需支持多种采样率与通道数组合(如48kHz/2ch与96kHz/2ch)。本案例实现备用设置(Alternate Setting)切换逻辑:当主机请求切换到特定设置号时,设备端动态更新当前采样率与通道数,并确保端点带宽描述符与所选设置匹配。

核心实现

// audio_if_alt_setting.h
#ifndef AUDIO_IF_ALT_SETTING_H
#define AUDIO_IF_ALT_SETTING_H

#include <stdint.h>

#define ALT_SETTING_COUNT   2

typedef struct {
    uint8_t     alt_setting_id;    // 备用设置号(0=零带宽,1=48k/2ch,2=96k/2ch)
    uint32_t    sample_rate;       // 采样率 Hz
    uint8_t     channel_count;     // 通道数
    uint16_t    ep_max_packet;     // 端点最大包字节数(按帧计算)
} audio_alt_setting_t;

// 音频流接口当前状态
typedef struct {
    uint8_t     active_alt;        // 当前激活的备用设置号
    uint32_t    sample_rate;
    uint8_t     channel_count;
    uint16_t    ep_max_packet;
} audio_stream_state_t;

// 查询备用设置表(由具体设备实现)
const audio_alt_setting_t* audio_get_alt_table(void);
uint8_t audio_get_alt_count(void);

// 切换备用设置
int audio_switch_alt_setting(audio_stream_state_t* state, uint8_t alt_id);

#endif // AUDIO_IF_ALT_SETTING_H
// audio_if_alt_setting.c
#include "audio_if_alt_setting.h"

// 设备支持的备用设置表(实际设备可在此扩展)
static const audio_alt_setting_t alt_table[ALT_SETTING_COUNT] = {
    { .alt_setting_id = 1, .sample_rate = 48000, .channel_count = 2, .ep_max_packet = 192 },  // 48kHz, 2ch, 16bit
    { .alt_setting_id = 2, .sample_rate = 96000, .channel_count = 2, .ep_max_packet = 384 },  // 96kHz, 2ch, 16bit
};

const audio_alt_setting_t* audio_get_alt_table(void) {
    return alt_table;
}

uint8_t audio_get_alt_count(void) {
    return ALT_SETTING_COUNT;
}

int audio_switch_alt_setting(audio_stream_state_t* state, uint8_t alt_id) {
    if (state == NULL) return -1;
    if (alt_id == 0) {
        // 零带宽设置:停止传输
        state->active_alt = 0;
        state->sample_rate = 0;
        state->channel_count = 0;
        state->ep_max_packet = 0;
        return 0;
    }

    const audio_alt_setting_t* table = audio_get_alt_table();
    uint8_t count = audio_get_alt_count();

    for (uint8_t i = 0; i < count; i++) {
        if (table[i].alt_setting_id == alt_id) {
            state->active_alt = alt_id;
            state->sample_rate = table[i].sample_rate;
            state->channel_count = table[i].channel_count;
            state->ep_max_packet = table[i].ep_max_packet;
            return 0;
        }
    }
    return -2;  // 不支持的设置号
}
// 使用示例(主机请求切换设置时调用)
#include <stdio.h>
#include "audio_if_alt_setting.h"

int main(void) {
    audio_stream_state_t stream = {0};

    // 模拟主机请求切换到备用设置1(48kHz/2ch)
    int ret = audio_switch_alt_setting(&stream, 1);
    if (ret == 0) {
        printf("切换到设置1: %lu Hz, %u ch, EP包=%u\n",
               (unsigned long)stream.sample_rate, stream.channel_count, stream.ep_max_packet);
    }

    // 模拟切换到备用设置2(96kHz/2ch)
    ret = audio_switch_alt_setting(&stream, 2);
    if (ret == 0) {
        printf("切换到设置2: %lu Hz, %u ch, EP包=%u\n",
               (unsigned long)stream.sample_rate, stream.channel_count, stream.ep_max_packet);
    }

    // 模拟切换到零带宽(停止传输)
    ret = audio_switch_alt_setting(&stream, 0);
    if (ret == 0) {
        printf("切换到设置0: 传输停止\n");
    }

    // 测试非法设置号
    ret = audio_switch_alt_setting(&stream, 5);
    if (ret != 0) {
        printf("非法设置号返回: %d\n", ret);
    }
    return 0;
}

编译与运行

# 编译(Linux/macOS)
gcc -Wall -o alt_setting_test audio_if_alt_setting.c main.c

# 运行
./alt_setting_test

验证方法

程序输出应严格符合以下顺序与内容:

切换到设置1: 48000 Hz, 2 ch, EP包=192
切换到设置2: 96000 Hz, 2 ch, EP包=384
切换到设置0: 传输停止
非法设置号返回: -2

判定通过条件:

  • 设置1与设置2切换后,sample_rate、channel_count、ep_max_packet与表中定义完全一致;
  • 切换到设置0时所有状态清零;
  • 请求不存在的设置号返回-2且不改变当前状态。

USB集成说明

在实际USB音频设备中,audio_switch_alt_setting应在标准USB请求SET_INTERFACE的处理函数中被调用,传入的alt_id即请求中的bAlternateSetting值。切换成功后需同步更新端点描述符中的wMaxPacketSize,并重新配置FIFO/ DMA缓冲区以匹配新的采样率与通道数。零带宽设置(alt=0)用于释放总线带宽。