高通AudioHAL分析

本文详细解析了AudioFlinger与HAL层交互的工作流程,从out_write函数出发,深入探讨了start_output_stream函数如何根据usecase选择正确的设备ID,及select_devices函数在设备选择中的作用,最后分析了enable_snd_device和enable_audio_route函数如何开启设备通路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这篇文章主要介绍一下AudioFlinger到hal层的一些工作流程。我们知道AudioFlinger从FIFO种读取到数据之后会调用hal的out_write函数去写入数据,我们就以这个函数为入口展开分析。

  1. out_write函数
    这个函数首先是有一些out->devices的判断,然后会判断是否是standby,如果是会调用start_output_stream去打开输出设备。然后如果pcm状态正常的情况下会调用pcm_write函数向adsp中写入数据。pcm_write是kernel的函数,我们这里只分析hal层,所以接下来我们去看一下start_output_stream函数打开设备的流程。
  2. start_output_stream函数
    这个函数首先调用platform_get_pcm_device_id函数去获取device id,这个id的定义是取决于kernel的,也就是说hal层把这个id传给kernel,kernel根据对应的id去打开相应的通路。platform_get_pcm_device_id函数在platform.c中,主要是从pcm_device_table中根据相应的usecase去获取对应的通路id。定义如下:
static int pcm_device_table[AUDIO_USECASE_MAX][2] = {
    [USECASE_AUDIO_PLAYBACK_DEEP_BUFFER] = {DEEP_BUFFER_PCM_DEVICE,
                                            DEEP_BUFFER_PCM_DEVICE},
    [USECASE_AUDIO_PLAYBACK_WITH_HAPTICS] = {AUDIO_HAPTICS_PCM_DEVICE,
                                             AUDIO_HAPTICS_PCM_DEVICE},
    [USECASE_AUDIO_PLAYBACK_LOW_LATENCY] = {LOWLATENCY_PCM_DEVICE,
                                           LOWLATENCY_PCM_DEVICE},
    [USECASE_AUDIO_PLAYBACK_ULL]         = {MULTIMEDIA3_PCM_DEVICE,
                                            MULTIMEDIA3_PCM_DEVICE},
    [USECASE_AUDIO_PLAYBACK_MULTI_CH] = {MULTIMEDIA2_PCM_DEVICE,
                                         MULTIMEDIA2_PCM_DEVICE},
    [USECASE_AUDIO_PLAYBACK_HIFI] = {MULTIMEDIA2_PCM_DEVICE,
                                     MULTIMEDIA2_PCM_DEVICE},
    [USECASE_AUDIO_PLAYBACK_TTS] = {MULTIMEDIA2_PCM_DEVICE,
                                        MULTIMEDIA2_PCM_DEVICE},
    [USECASE_AUDIO_PLAYBACK_OFFLOAD] =
                     {PLAYBACK_OFFLOAD_DEVICE, PLAYBACK_OFFLOAD_DEVICE},
    [USECASE_AUDIO_PLAYBACK_OFFLOAD2] =
                     {PLAYBACK_OFFLOAD_DEVICE2, PLAYBACK_OFFLOAD_DEVICE2},
    [USECASE_AUDIO_PLAYBACK_OFFLOAD3] =
                     {PLAYBACK_OFFLOAD_DEVICE3, PLAYBACK_OFFLOAD_DEVICE3},
    [USECASE_AUDIO_PLAYBACK_OFFLOAD4] =
                     {PLAYBACK_OFFLOAD_DEVICE4, PLAYBACK_OFFLOAD_DEVICE4},
    [USECASE_AUDIO_PLAYBACK_OFFLOAD5] =
                     {PLAYBACK_OFFLOAD_DEVICE5, PLAYBACK_OFFLOAD_DEVICE5},
    [USECASE_AUDIO_PLAYBACK_OFFLOAD6] =
                     {PLAYBACK_OFFLOAD_DEVICE6, PLAYBACK_OFFLOAD_DEVICE6},
    [USECASE_AUDIO_PLAYBACK_OFFLOAD7] =
                     {PLAYBACK_OFFLOAD_DEVICE7, PLAYBACK_OFFLOAD_DEVICE7},
    [USECASE_AUDIO_PLAYBACK_OFFLOAD8] =
                     {PLAYBACK_OFFLOAD_DEVICE8, PLAYBACK_OFFLOAD_DEVICE8},
    [USECASE_AUDIO_PLAYBACK_OFFLOAD9] =
                     {PLAYBACK_OFFLOAD_DEVICE9, PLAYBACK_OFFLOAD_DEVICE9},

。。。
};

然后DEEP_BUFFER_PCM_DEVICE等分别对应定义好的一个int值。
然后会调用到select_devices函数中。
3. select_devices函数

int select_devices(struct audio_device *adev, audio_usecase_t uc_id)
{
    ALOGD("%s for use case (%s)", __func__, use_case_table[uc_id]);

    usecase = get_usecase_from_list(adev, uc_id);
    if (usecase == NULL) {
        ALOGE("%s: Could not find the usecase(%d)", __func__, uc_id);
        return -EINVAL;
    }
        if (usecase->type == PCM_PLAYBACK) {
            if (usecase->stream.out == NULL) {
                ALOGE("%s: stream.out is NULL", __func__);
                return -EINVAL;
            }
            usecase->devices = usecase->stream.out->devices;
            in_snd_device = SND_DEVICE_NONE;
            if (out_snd_device == SND_DEVICE_NONE) {
                struct stream_out *voip_out = adev->primary_output;
                struct stream_in *voip_in = get_voice_communication_input(adev);
                if (usecase->devices & AUDIO_DEVICE_OUT_BUS)
                    out_snd_device = audio_extn_auto_hal_get_output_snd_device(adev, uc_id);
                else
                    out_snd_device = platform_get_output_snd_device(adev->platform,
                                                                    usecase->stream.out);
                voip_usecase = get_usecase_from_list(adev, USECASE_AUDIO_PLAYBACK_VOIP);

                if (voip_usecase)
                    voip_out = voip_usecase->stream.out;

                if (usecase->stream.out == voip_out && voip_in != NULL)
                    select_devices(adev, voip_in->usecase);
            }
        } 
        /* Enable new sound devices */
        if (out_snd_device != SND_DEVICE_NONE) {
            check_usecases_codec_backend(adev, usecase, out_snd_device);
            if (platform_check_codec_asrc_support(adev->platform))
               check_and_set_asrc_mode(adev, usecase, out_snd_device);
            enable_snd_device(adev, out_snd_device);
        }
        enable_audio_route(adev, usecase);

        audio_extn_qdsp_set_device(usecase);
    }

这个函数会调用get_usecase_from_list函数把device和usecase等信息封装到audio_usecase结构体中,如果usecase->type是播放的话会去判断输出设备是否是AUDIO_DEVICE_OUT_BUS(这个是车机的device,因为我们是车机,所以就看一下这个if调用的函数),然后调用audio_extn_auto_hal_get_output_snd_device函数,再到auto_hal_get_output_snd_device函数,这个函数会根据device和usecase去选择hal层对应的snd_device。这个snd_device和上面传过来的device不同,传过来的device是framework上定义的一个名称,这个snd_device才是真正的hal层的device,比如framework传过来的是通过offload模式(媒体播放)设备是AUDIO_DEVICE_OUT_BUS去播放设备,在这里会根据这个usecase和device把snd_device转换成SND_DEVICE_OUT_BUS_MEDIA,如果framework传过来的是NAV(导航)模式AUDIO_DEVICE_OUT_BUS设备,那么snd_device对应的就是SND_DEVICE_OUT_BUS_NAV。同样的,如果设备不是AUDIO_DEVICE_OUT_BUS会调用platform_get_output_snd_device,但是这个函数也是做上面的一些操作,只不过这个函数相对会复杂一些,除了AUDIO_DEVICE_OUT_BUS之外所有的device转换都是从这里做的,比如headset,手机上的听筒等。最后调用enable_snd_device去打开输出设备通路,以及调用enable_audio_route去使能audio route。
4. enable_snd_device函数
这个函数首先调用platform_get_snd_device_name_extn函数使用上一步得到的snd_device去获取device_name,这个device_name就是mixer_path中所定义的名字,还是以SND_DEVICE_OUT_BUS_MEDIA为例进行分析,这里从device_table数组中得到的device_name是"bus-speaker"。然后继续往下走,会走到audio_route_apply_and_update_path函数,再到audio_route_update_path函数,这里首先根据上面获取到的device_name去解析mixer_path,然后拿到mixer_path中定义的"bus-speaker"所对应的通路,然后调用tinyalsa的mixer.c中的mixer_ctl_set_value函数去打开设备通路。
5. enable_audio_route函数
这个函数会调用platform_add_backend_name函数在通路的设备上添加设备名,用于不同的usecase定义,也就是把"headphones-44.1","speaker-and-headphones"等添加到usecase的设备名称中,主要是为了应对同样的usecase不同的设备配置不同的问题。比如同样的offload模式,有如下两种不同的定义

    <path name="compress-offload-playback">
        <ctl name="TERT_TDM_RX_0 Channels" value="Six" />
        <ctl name="TERT_TDM_RX_0 Audio Mixer MultiMedia4" value="1" />
    </path>
    
    <path name="compress-offload-playback bt-sco">
        <ctl name="SLIMBUS_7_RX Audio Mixer MultiMedia4" value="1" />
    </path>

通过上面的例子我们可以看到,同样的offload模式,蓝牙的和默认的定义是完全不同的。最后这个函数和enable_snd_device函数一样也是调用audio_route_apply_and_update_path函数通过tinyalsa去打开对应的usecase。到这里hal层的打开usecase通路以及打开device输出设备的流程就大体分析完成了

### Qualcomm 8295 音频 HAL 实现与信息 #### 高通8295音频HAL架构概述 高通8295平台上的音频硬件抽象层(HAL)遵循Android标准的HAL设计模式,通过`hw_module_t`和`hw_device_t`两个核心数据结构来管理硬件资源。对于特定于高通芯片组的功能,则会在此基础上扩展额外的支持[^1]。 #### 核心数据结构解析 - **hw_module_t**: 定义了模块级别的接口,包含了版本号、ID以及名称等基本信息,并提供了一套操作该模块的方法指针表。例如,在audio模块中可以看到如下定义: ```c struct audio_module HAL_MODULE_INFO_SYM = { .common = { .tag = HARDWARE_MODULE_TAG, .module_api_version = AUDIO_MODULE_API_VERSION_0_1, .hal_api_version = HARDWARE_HAL_API_VERSION, .id = AUDIO_HARDWARE_MODULE_ID, .name = "Default audio HW HAL", .author = "The Android Open Source Project", .methods = &hal_module_methods, }, }; ``` - **hw_device_t**: 表示具体的设备实例,通常由`hw_module_t::methods->open()`函数创建并返回给调用者。它同样包含一组用于控制此设备的操作方法。 #### 查询和加载动态共享库的过程 当应用程序请求打开某个类型的硬件服务时(比如播放声音),系统会先尝试找到对应的`.so`文件——即实现了相应功能的具体实现库。这个过程涉及到读取配置文件以获取路径名,接着利用dlopen/dlsym API装载目标库入内存空间内执行初始化工作[^2]。 #### GPS案例中的HAL使用方式 虽然这里讨论的是音频而非GPS,但是两者之间存在相似之处。以上层应用的角度来看,无论是哪种传感器或外围装置,都可通过一致的方式访问底层物理组件:首先获得指向对应HW Module对象的引用;之后再依据需求开启相应的Device Instance;最后按照既定协议发送指令集完成交互逻辑[^3]。 #### AudioFlinger的角色 在具体到音频处理方面,AudioFlinger扮演着至关重要的角色。其负责协调多个音轨之间的混合运算,并且承担起了加载HW Modules的任务。因此,在启动顺序上有必要确保AudioFlinger早于其他依赖项被建立起来以便正常运作[^4]。 ```cpp // 示例代码片段展示如何在C++环境中加载HW Module status_t loadHwModule(const char* id, const hw_module_t** module){ return hw_get_module(id, module); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值