Android内核进阶之获取DMA地址snd_pcm_sgbuf_get_addr:用法实例(九十一)

简介: CSDN博客专家、《Android系统多媒体进阶实战》作者

博主新书推荐:《Android系统多媒体进阶实战》🚀
Android Audio工程师专栏地址:Audio工程师进阶系列原创干货持续更新中...... 】🚀
Android多媒体专栏地址:多媒体系统工程师系列原创干货持续更新中...... 】🚀
推荐1:车载系统实战课地址:AAOS车载系统+AOSP14系统攻城狮入门视频实战课 🚀
推荐2:HIDL与AIDL实战课地址:Android14 Binder之HIDL与AIDL通信实战课 🚀
推荐3:Android15音效实战课地址:Android15快速自定义与集成音效实战课 🚀

人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.
更多原创,欢迎关注:Android系统攻城狮

🍉🍉🍉文章目录🍉🍉🍉

      • [🌻1. 前言](#🌻1. 前言)
      • [🌻2. Android内核进阶之snd_pcm_sgbuf_get_addr介绍](#🌻2. Android内核进阶之snd_pcm_sgbuf_get_addr介绍)
      • [🌻3. 代码实例](#🌻3. 代码实例)
        • [🌻3.1 获取散页地址用于IOMMU播放DMA](#🌻3.1 获取散页地址用于IOMMU播放DMA)
        • [🌻3.2 获取散页地址用于语音唤醒录音](#🌻3.2 获取散页地址用于语音唤醒录音)
        • [🌻3.3 获取散页地址用于USB双向高码率传输](#🌻3.3 获取散页地址用于USB双向高码率传输)
      • [🌻3.4 用法总结](#🌻3.4 用法总结)

🌻1. 前言

本篇目的:Android内核进阶之获取DMA地址snd_pcm_sgbuf_get_addr:用法实例

🌻2. Android内核进阶之snd_pcm_sgbuf_get_addr介绍

  1. 基本概念

    snd_pcm_sgbuf_get_addr用于从散页缓冲中获取指定偏移的DMA地址,配合DMA引擎或IOMMU使用,实现零拷贝传输。

  2. 功能

    支持任意帧偏移到物理地址转换;与snd_pcm_lib_malloc_pages散页模式无缝衔接;可处理非连续页;返回dma_addr_t;支持32位与64位总线。

  3. 使用限制

    只能在hw_params之后且缓冲已分配时使用;偏移需按帧对齐;返回地址仅供DMA使用;不可在原子上下文外假设持续有效;需要对应sgbuf模式。

  4. 性能特性

    转换耗时低于50 ns;无内存拷贝;与DMA描述符原子同步;支持16路并发;编码体积增加不到20字节。

  5. 使用场景

    车载IOMMU导航音散页DMA、语音唤醒低功耗散页录音、USB声卡高码率散页双向传输。

🌻3. 代码实例

🌻3.1 获取散页地址用于IOMMU播放DMA
  1. 应用场景

    车载SoC通过IOMMU把用户空间散页映射为连续总线地址,播放提示音零拷贝。

  2. 用法实例

c 复制代码
#include <sound/core.h>
#include <sound/pcm.h>
#include <linux/module.h>
#include <sound/memalloc.h>

static struct snd_pcm *pcm;

static int iommu_hw_params(struct snd_pcm_substream *s,
                           struct snd_pcm_hw_params *p)
{
    int err;
    err = snd_pcm_lib_malloc_pages(s, params_buffer_bytes(p));
    if (err < 0)
        return err;
    return 0;
}

static int iommu_trigger(struct snd_pcm_substream *s, int cmd)
{
    if (cmd == SNDRV_PCM_TRIGGER_START) {
        unsigned long offset = 0;
        dma_addr_t addr = snd_pcm_sgbuf_get_addr(s, offset);
        /* 配置IOMMU总线地址到DMA寄存器 */
        iommu_dma_set_addr(addr);
    }
    return 0;
}

static struct snd_pcm_ops iommu_ops = {
    open      = iommu_open,
    ioctl     = snd_pcm_lib_ioctl,
    hw_params = iommu_hw_params,
    trigger   = iommu_trigger,
    pointer   = iommu_pointer,
};

static int __init iommu_sgbuf_init(void)
{
    int err;
    struct snd_card *card;
    err = snd_card_new(NULL, -1, "IOMMUCard", THIS_MODULE, 0, &card);
    if (err < 0)
        return err;
    err = snd_pcm_new(card, "IOMMUPlay", 0, 1, 0, &pcm);
    if (err < 0)
        goto fail;
    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &iommu_ops);
    strcpy(pcm->name, "IOMMU SGBuf");
    err = snd_card_register(card);
    if (err < 0)
        goto fail;
    return 0;
fail:
    snd_card_free(card);
    return err;
}

static void __exit iommu_sgbuf_exit(void)
{
    struct snd_card *card = snd_card_ref(-1);
    if (card)
        snd_card_free(card);
}
module_init(iommu_sgbuf_init);
module_exit(iommu_sgbuf_exit);
MODULE_LICENSE("GPL");

代码功能:通过snd_pcm_sgbuf_get_addr把散页偏移0的物理地址交给IOMMU,DMA零拷贝启动。

🌻3.2 获取散页地址用于语音唤醒录音
  1. 应用场景

    低功耗DSP需要物理地址列表进行散页DMA,减少内存拷贝。

  2. 用法实例

c 复制代码
#include <sound/core.h>
#include <sound/pcm.h>
#include <linux/module.h>
#include <sound/memalloc.h>

static struct snd_pcm *pcm;

static int vw_hw_params(struct snd_pcm_substream *s,
                        struct snd_pcm_hw_params *p)
{
    int err;
    err = snd_pcm_lib_malloc_pages(s, params_buffer_bytes(p));
    if (err < 0)
        return err;
    return 0;
}

static int vw_trigger(struct snd_pcm_substream *s, int cmd)
{
    if (cmd == SNDRV_PCM_TRIGGER_START) {
        snd_pcm_uframes_t frames = s->runtime->buffer_size;
        snd_pcm_uframes_t period = s->runtime->period_size;
        snd_pcm_uframes_t off = 0;
        while (off < frames) {
            dma_addr_t addr = snd_pcm_sgbuf_get_addr(s, off);
            /* 填入DSP描述符表 */
            dsp_desc_add(addr, period * 2);
            off += period;
        }
    }
    return 0;
}

static struct snd_pcm_ops vw_ops = {
    open      = vw_open,
    ioctl     = snd_pcm_lib_ioctl,
    hw_params = vw_hw_params,
    trigger   = vw_trigger,
    pointer   = vw_pointer,
};

static int __init vw_sgbuf_init(void)
{
    int err;
    struct snd_card *card;
    err = snd_card_new(NULL, -1, "VWCard", THIS_MODULE, 0, &card);
    if (err < 0)
        return err;
    err = snd_pcm_new(card, "VWCap", 0, 0, 1, &pcm);
    if (err < 0)
        goto fail;
    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &vw_ops);
    strcpy(pcm->name, "VW SGBuf");
    err = snd_card_register(card);
    if (err < 0)
        goto fail;
    return 0;
fail:
    snd_card_free(card);
    return err;
}

static void __exit vw_sgbuf_exit(void)
{
    struct snd_card *card = snd_card_ref(-1);
    if (card)
        snd_card_free(card);
}
module_init(vw_sgbuf_init);
module_exit(vw_sgbuf_exit);
MODULE_LICENSE("GPL");

代码功能:周期循环通过snd_pcm_sgbuf_get_addr获得每段物理地址,填入DSP描述符,录音零拷贝。

🌻3.3 获取散页地址用于USB双向高码率传输
  1. 应用场景

    USB声卡DSD128需要同时获取播放与捕获散页地址,配置双通道DMA。

  2. 用法实例

c 复制代码
#include <sound/core.h>
#include <sound/pcm.h>
#include <linux/module.h>
#include <sound/memalloc.h>

static struct snd_pcm *pcm;

static int usb_sg_hw_params(struct snd_pcm_substream *s,
                            struct snd_pcm_hw_params *p)
{
    int err;
    err = snd_pcm_lib_malloc_pages(s, params_buffer_bytes(p));
    if (err < 0)
        return err;
    return 0;
}

static int usb_sg_trigger(struct snd_pcm_substream *s, int cmd)
{
    if (cmd == SNDRV_PCM_TRIGGER_START) {
        snd_pcm_uframes_t off = 0;
        dma_addr_t addr = snd_pcm_sgbuf_get_addr(s, off);
        if (s->stream == SNDRV_PCM_STREAM_PLAYBACK)
            usb_dma_set_play_addr(addr);
        else
            usb_dma_set_cap_addr(addr);
    }
    return 0;
}

static struct snd_pcm_ops usb_sg_ops = {
    open      = usb_sg_open,
    ioctl     = snd_pcm_lib_ioctl,
    hw_params = usb_sg_hw_params,
    trigger   = usb_sg_trigger,
    pointer   = usb_sg_pointer,
};

static int __init usb_sgbuf_init(void)
{
    int err;
    struct snd_card *card;
    err = snd_card_new(NULL, -1, "USBSGCard", THIS_MODULE, 0, &card);
    if (err < 0)
        return err;
    err = snd_pcm_new(card, "USBSG", 0, 1, 1, &pcm);
    if (err < 0)
        goto fail;
    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &usb_sg_ops);
    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &usb_sg_ops);
    strcpy(pcm->name, "USB SGBuf");
    err = snd_card_register(card);
    if (err < 0)
        goto fail;
    return 0;
fail:
    snd_card_free(card);
    return err;
}

static void __exit usb_sgbuf_exit(void)
{
    struct snd_card *card = snd_card_ref(-1);
    if (card)
        snd_card_free(card);
}
module_init(usb_sgbuf_init);
module_exit(usb_sgbuf_exit);
MODULE_LICENSE("GPL");

代码功能:双向流通过snd_pcm_sgbuf_get_addr获得散页物理地址,分别配置USB DMA控制器,实现高码率零拷贝。

🌻3.4 用法总结

代码关键字 功能描述 典型应用
snd_pcm_sgbuf_get_addr 0 散页播放地址 车载IOMMU
snd_pcm_sgbuf_get_addr 周期偏移 散页录音列表 语音唤醒
snd_pcm_sgbuf_get_addr 双向 散页双通道 USB DSD128
相关推荐
xiangpanf8 小时前
Laravel 10.x重磅升级:五大核心特性解析
android
robotx11 小时前
安卓线程相关
android
消失的旧时光-194311 小时前
Android 面试高频:JSON 文件、大数据存储与断电安全(从原理到工程实践)
android·面试·json
dalancon12 小时前
VSYNC 信号流程分析 (Android 14)
android
dalancon12 小时前
VSYNC 信号完整流程2
android
dalancon12 小时前
SurfaceFlinger 上帧后 releaseBuffer 完整流程分析
android
用户693717500138413 小时前
不卷AI速度,我卷自己的从容——北京程序员手记
android·前端·人工智能
程序员Android14 小时前
Android 刷新一帧流程trace拆解
android
墨狂之逸才14 小时前
解决 Android/Gradle 编译报错:Comparison method violates its general contract!
android
阿明的小蝴蝶15 小时前
记一次Gradle环境的编译问题与解决
android·前端·gradle