Android内核之创建PCM设备snd_pcm_new:用法实例(七十八)

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

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

人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.

更多原创,欢迎关注:Android系统攻城狮

欢迎关注Android系统攻城狮

🌻1. 前言

本篇目的:Android内核进阶之创建PCM设备snd_pcm_new:用法实例

🌻2. Android内核进阶之创建PCM设备snd_pcm_new介绍

  1. 基本概念
    snd_pcm_new是ALSA核心提供的工厂函数,负责生成pcm逻辑设备实例,关联声卡、指定流方向、命名设备节点,为后续硬件寄存器映射与DMA缓冲做准备。

  2. 功能
    支持捕获流SNDRV_PCM_STREAM_CAPTURE、播放流SNDRV_PCM_STREAM_PLAYBACK、双向同开;可携带私有数据指针;自动填充snd_pcm结构体;一次注册即可在userspace生成pcmC0D0p/pcmC0D0c节点。

  3. 使用限制
    必须已有有效snd_card实例;命名长度不超16字节;需在snd_card_register前调用;非原子上下文;错误码需手工检查。

  4. 性能特性
    内存分配单次小于2 KB;无锁遍历声卡设备链表;dev节点随声卡生命周期;支持同时创建16路pcm;DMA预留延后到hw_params阶段。

  5. 使用场景
    嵌入式SoC新增I2S播放端口、USB声卡扩展第二路录音通道、虚拟环路测试设备。

🌻3. 代码实例

🌻3.1 创建单播放PCM设备
  1. 应用场景
    嵌入式板卡通过I2S外接DAC,仅需播放,无录音需求。

  2. 用法实例

#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <linux/module.h>

static struct snd_card *card;
static struct snd_pcm *pcm;

/* 简易回调占位,实际项目替换为硬件操作 */
static int i2s_pcm_open(struct snd_pcm_substream *s)
{
    return 0;
}
static int i2s_pcm_hw_params(struct snd_pcm_substream *s,
                             struct snd_pcm_hw_params *p)
{
    return 0;
}
static int i2s_pcm_trigger(struct snd_pcm_substream *s, int cmd)
{
    return 0;
}
static snd_pcm_uframes_t i2s_pcm_pointer(struct snd_pcm_substream *s)
{
    return 0;
}

static struct snd_pcm_ops i2s_pcm_ops = {
    .open      = i2s_pcm_open,
    .ioctl     = snd_pcm_lib_ioctl,
    .hw_params = i2s_pcm_hw_params,
    .trigger   = i2s_pcm_trigger,
    .pointer   = i2s_pcm_pointer,
};

static int __init single_play_init(void)
{
    int err;

    /* 1. 创建声卡实例 */
    err = snd_card_new(NULL, -1, "I2S-Card", THIS_MODULE, 0, &card);
    if (err < 0)
        return err;

    /* 2. 新建PCM设备:编号0,仅播放(1,0) */
    err = snd_pcm_new(card, "I2S-Play", 0, 1, 0, &pcm);
    if (err < 0)
        goto fail;

    /* 3. 绑定播放流操作集 */
    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &i2s_pcm_ops);

    /* 4. 设置节点名称 */
    strcpy(pcm->name, "I2S Playback");

    /* 5. 注册声卡,生成pcmC0D0p */
    err = snd_card_register(card);
    if (err < 0)
        goto fail;

    return 0;

fail:
    snd_card_free(card);
    return err;
}

static void __exit single_play_exit(void)
{
    snd_card_free(card); /* 6. 卸载时释放声卡 */
}

module_init(single_play_init);
module_exit(single_play_exit);
MODULE_LICENSE("GPL");

代码功能:
创建仅支持播放的PCM逻辑设备,userspace出现pcmC0D0p节点,应用可写入音频数据。

🌻3.2 创建纯捕获PCM设备
  1. 应用场景
    数字麦克风芯片只提供录音数据,无需播放。

  2. 用法实例

#include <sound/core.h>
#include <sound/pcm.h>
#include <linux/module.h>

static struct snd_card *card;
static struct snd_pcm *pcm;

static int dmic_pcm_open(struct snd_pcm_substream *s) { return 0; }
static int dmic_pcm_hw_params(struct snd_pcm_substream *s,
                              struct snd_pcm_hw_params *p) { return 0; }
static int dmic_pcm_trigger(struct snd_pcm_substream *s, int cmd) { return 0; }
static snd_pcm_uframes_t dmic_pcm_pointer(struct snd_pcm_substream *s) { return 0; }

static struct snd_pcm_ops dmic_pcm_ops = {
    .open      = dmic_pcm_open,
    .ioctl     = snd_pcm_lib_ioctl,
    .hw_params = dmic_pcm_hw_params,
    .trigger   = dmic_pcm_trigger,
    .pointer   = dmic_pcm_pointer,
};

static int __init single_cap_init(void)
{
    int err;

    /* 1. 新建声卡 */
    err = snd_card_new(NULL, -1, "DMIC-Card", THIS_MODULE, 0, &card);
    if (err < 0)
        return err;

    /* 2. 新建PCM:编号1,仅捕获(0,1) */
    err = snd_pcm_new(card, "DMIC-Cap", 1, 0, 1, &pcm);
    if (err < 0)
        goto fail;

    /* 3. 绑定捕获流操作集 */
    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &dmic_pcm_ops);

    strcpy(pcm->name, "DMIC Capture");

    /* 4. 注册声卡,生成pcmC0D1c */
    err = snd_card_register(card);
    if (err < 0)
        goto fail;

    return 0;

fail:
    snd_card_free(card);
    return err;
}

static void __exit single_cap_exit(void)
{
    snd_card_free(card);
}

module_init(single_cap_init);
module_exit(single_cap_exit);
MODULE_LICENSE("GPL");

代码功能:
生成仅支持录音的PCM设备,userspace可见pcmC0D1c节点,录音应用可从该设备读取数据。

🌻3.3 创建双向全双工PCM设备
  1. 应用场景
    USB音频芯片同时提供播放与录音,需要一对节点。

  2. 用法实例

#include <sound/core.h>
#include <sound/pcm.h>
#include <linux/module.h>

static struct snd_card *card;
static struct snd_pcm *pcm;

/* 播放侧回调 */
static int usb_play_open(struct snd_pcm_substream *s) { return 0; }
static int usb_play_hw_params(struct snd_pcm_substream *s,
                              struct snd_pcm_hw_params *p) { return 0; }
static int usb_play_trigger(struct snd_pcm_substream *s, int cmd) { return 0; }
static snd_pcm_uframes_t usb_play_pointer(struct snd_pcm_substream *s) { return 0; }

/* 捕获侧回调 */
static int usb_cap_open(struct snd_pcm_substream *s) { return 0; }
static int usb_cap_hw_params(struct snd_pcm_substream *s,
                             struct snd_pcm_hw_params *p) { return 0; }
static int usb_cap_trigger(struct snd_pcm_substream *s, int cmd) { return 0; }
static snd_pcm_uframes_t usb_cap_pointer(struct snd_pcm_substream *s) { return 0; }

static struct snd_pcm_ops usb_play_ops = {
    .open      = usb_play_open,
    .ioctl     = snd_pcm_lib_ioctl,
    .hw_params = usb_play_hw_params,
    .trigger   = usb_play_trigger,
    .pointer   = usb_play_pointer,
};

static struct snd_pcm_ops usb_cap_ops = {
    .open      = usb_cap_open,
    .ioctl     = snd_pcm_lib_ioctl,
    .hw_params = usb_cap_hw_params,
    .trigger   = usb_cap_trigger,
    .pointer   = usb_cap_pointer,
};

static int __init duplex_init(void)
{
    int err;

    /* 1. 创建声卡 */
    err = snd_card_new(NULL, -1, "USB-Card", THIS_MODULE, 0, &card);
    if (err < 0)
        return err;

    /* 2. 新建PCM:编号2,播放1路+捕获1路 */
    err = snd_pcm_new(card, "USB-Dup", 2, 1, 1, &pcm);
    if (err < 0)
        goto fail;

    /* 3. 绑定双向操作集 */
    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &usb_play_ops);
    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &usb_cap_ops);

    strcpy(pcm->name, "USB Duplex");

    /* 4. 注册声卡,生成pcmC0D2p与pcmC0D2c */
    err = snd_card_register(card);
    if (err < 0)
        goto fail;

    return 0;

fail:
    snd_card_free(card);
    return err;
}

static void __exit duplex_exit(void)
{
    snd_card_free(card);
}

module_init(duplex_init);
module_exit(duplex_exit);
MODULE_LICENSE("GPL");

代码功能:
同一PCM设备号下同时开启播放与捕获流,userspace得到pcmC0D2p、pcmC0D2c两个节点,可独立进行放音和录音。

🌻3.4 用法总结

代码关键字功能描述典型应用
snd_pcm_new 1 0单播放I2S输出
snd_pcm_new 0 1单捕获DMIC录音
snd_pcm_new 1 1全双工USB声卡
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Android系统攻城狮

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值