简介: 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介绍
-
基本概念
snd_pcm_sgbuf_get_addr用于从散页缓冲中获取指定偏移的DMA地址,配合DMA引擎或IOMMU使用,实现零拷贝传输。
-
功能
支持任意帧偏移到物理地址转换;与snd_pcm_lib_malloc_pages散页模式无缝衔接;可处理非连续页;返回dma_addr_t;支持32位与64位总线。
-
使用限制
只能在hw_params之后且缓冲已分配时使用;偏移需按帧对齐;返回地址仅供DMA使用;不可在原子上下文外假设持续有效;需要对应sgbuf模式。
-
性能特性
转换耗时低于50 ns;无内存拷贝;与DMA描述符原子同步;支持16路并发;编码体积增加不到20字节。
-
使用场景
车载IOMMU导航音散页DMA、语音唤醒低功耗散页录音、USB声卡高码率散页双向传输。
🌻3. 代码实例
🌻3.1 获取散页地址用于IOMMU播放DMA
-
应用场景
车载SoC通过IOMMU把用户空间散页映射为连续总线地址,播放提示音零拷贝。
-
用法实例
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 获取散页地址用于语音唤醒录音
-
应用场景
低功耗DSP需要物理地址列表进行散页DMA,减少内存拷贝。
-
用法实例
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双向高码率传输
-
应用场景
USB声卡DSD128需要同时获取播放与捕获散页地址,配置双通道DMA。
-
用法实例
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 |