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
相关推荐
清空mega2 小时前
Android Studio移动应用基础教程(前言)
android·ide·android studio
2501_937145412 小时前
2025IPTV 源码优化版实测:双架构兼容 + 可视化运维
android·源码·源代码管理·机顶盒
zhoutanooi4 小时前
安卓bp文件编译学习
android·学习
aramae5 小时前
MySQL数据库入门指南
android·数据库·经验分享·笔记·mysql
百锦再6 小时前
选择Rust的理由:从内存管理到抛弃抽象
android·java·开发语言·后端·python·rust·go
whatever who cares6 小时前
在Java/Android中,List的属性和方法
android·java
油炸小波8 小时前
09-微服务原理篇(XXLJOB-幂等-MySQL)
android·mysql·微服务
果子没有六分钟9 小时前
setprop debug.hwui.profile visual_bars有什么作用
android
键来大师12 小时前
Android15 源码关闭Selinux
android·java·framework·rk3588