在介绍PCM 之前,我们先给出创建PCM实例的框架。

  #include <sound/pcm.h>
.... /* hardware definition */
static struct snd_pcm_hardware snd_mychip_playback_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_8000_48000,
.rate_min = 8000,
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 32768,
.period_bytes_min = 4096,
.period_bytes_max = 32768,
.periods_min = 1,
.periods_max = 1024,
}; /* hardware definition */
static struct snd_pcm_hardware snd_mychip_capture_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = SNDRV_PCM_RATE_8000_48000,
.rate_min = 8000,
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 32768,
.period_bytes_min = 4096,
.period_bytes_max = 32768,
.periods_min = 1,
.periods_max = 1024,
}; /* open callback */
static int snd_mychip_playback_open(struct snd_pcm_substream *substream)
{
struct mychip *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime; runtime->hw = snd_mychip_playback_hw;
/* more hardware-initialization will be done here */
....
return 0;
} /* close callback */
static int snd_mychip_playback_close(struct snd_pcm_substream *substream)
{
struct mychip *chip = snd_pcm_substream_chip(substream);
/* the hardware-specific codes will be here */
....
return 0; } /* open callback */
static int snd_mychip_capture_open(struct snd_pcm_substream *substream)
{
struct mychip *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime; runtime->hw = snd_mychip_capture_hw;
/* more hardware-initialization will be done here */
....
return 0;
} /* close callback */
static int snd_mychip_capture_close(struct snd_pcm_substream *substream)
{
struct mychip *chip = snd_pcm_substream_chip(substream);
/* the hardware-specific codes will be here */
....
return 0; } /* hw_params callback */
static int snd_mychip_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
return snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
} /* hw_free callback */
static int snd_mychip_pcm_hw_free(struct snd_pcm_substream *substream)
{
return snd_pcm_lib_free_pages(substream);
} /* prepare callback */
static int snd_mychip_pcm_prepare(struct snd_pcm_substream *substream)
{
struct mychip *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime; /* set up the hardware with the current configuration
* for example...
*/
mychip_set_sample_format(chip, runtime->format);
mychip_set_sample_rate(chip, runtime->rate);
mychip_set_channels(chip, runtime->channels);
mychip_set_dma_setup(chip, runtime->dma_addr,
chip->buffer_size,
chip->period_size);
return 0;
} /* trigger callback */
static int snd_mychip_pcm_trigger(struct snd_pcm_substream *substream,
int cmd)
{
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
/* do something to start the PCM engine */
....
break;
case SNDRV_PCM_TRIGGER_STOP:
/* do something to stop the PCM engine */
....
break;
default:
return -EINVAL;
}
} /* pointer callback */
static snd_pcm_uframes_t
snd_mychip_pcm_pointer(struct snd_pcm_substream *substream)
{
struct mychip *chip = snd_pcm_substream_chip(substream);
unsigned int current_ptr; /* get the current hardware pointer */
current_ptr = mychip_get_hw_pointer(chip);
return current_ptr;
} /* operators */
static struct snd_pcm_ops snd_mychip_playback_ops = {
.open = snd_mychip_playback_open,
.close = snd_mychip_playback_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_mychip_pcm_hw_params,
.hw_free = snd_mychip_pcm_hw_free,
.prepare = snd_mychip_pcm_prepare,
.trigger = snd_mychip_pcm_trigger,
.pointer = snd_mychip_pcm_pointer,
}; /* operators */
static struct snd_pcm_ops snd_mychip_capture_ops = {
.open = snd_mychip_capture_open,
.close = snd_mychip_capture_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_mychip_pcm_hw_params,
.hw_free = snd_mychip_pcm_hw_free,
.prepare = snd_mychip_pcm_prepare,
.trigger = snd_mychip_pcm_trigger,
.pointer = snd_mychip_pcm_pointer,
}; /*
* definitions of capture are omitted here...
*/ /* create a pcm device */
static int snd_mychip_new_pcm(struct mychip *chip)
{
struct snd_pcm *pcm;
int err; err = snd_pcm_new(chip->card, "My Chip", 0, 1, 1, &pcm);
if (err < 0)
return err;
pcm->private_data = chip;
strcpy(pcm->name, "My Chip");
chip->pcm = pcm;
/* set operators */
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_mychip_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_mychip_capture_ops);
/* pre-allocation of buffers */
/* NOTE: this may fail */
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci),
64*1024, 64*1024);
return 0;
}

1.创建pcm实例

ALSA driver为我们提供接口snd_pcm_new来创建PCM实例。但是我们最好是写一个如上述snd_mychip_new_pcm的函数来来对构建pcm实例的过程进行封装。

/**
* snd_pcm_new - create a new PCM instance
* @card: the card instance
* @id: the id string
* @device: the device index (zero based)
* @playback_count: the number of substreams for playback
* @capture_count: the number of substreams for capture
* @rpcm: the pointer to store the new pcm instance
*/
int snd_pcm_new(struct snd_card *card, const char *id, int device, int playback_count, int capture_count, struct snd_pcm **rpcm)

第三个参数表示新创建的PCM实例的index(0,1,2,3).可以在一个card上创建多个PCM 实例。每一个PCM又可以包含多个substream.如果芯片支持多路播放,那么将有多个substream.每次open/close都作用于某个substream.在创建PCM的substream时就指定了number(0~playback_count).当App在调用alsa lib API:snd_pcm_open时,alsa core通过snd_pcm_attach_substream函数来open一个空闲的substream.

2.设置PCM的操作函数

创建完PCM函数后,就可设置PCM 的操作函数。

  snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_mychip_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_mychip_capture_ops);

操作函数即我们写driver时需要实现的功能,以供alsa-core调用。ALSA PCM的操作函数包括:

  static struct snd_pcm_ops snd_mychip_playback_ops = {
.open = snd_mychip_pcm_open,
.close = snd_mychip_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_mychip_pcm_hw_params,
.hw_free = snd_mychip_pcm_hw_free,
.prepare = snd_mychip_pcm_prepare,
.trigger = snd_mychip_pcm_trigger,
.pointer = snd_mychip_pcm_pointer,
};

每个函数都包含一个snd_pcm_substream 的指针,指向当前操作的substream.

在上面的例子中,每个操作函数里面都包含如下宏调用:

其中返回的是substream->private_data,sustream的private_data是pcm->private_data的一份拷贝。拷贝动作是在snd_pcm_open时调用的snd_pcm_attach_substream中进行。一般来说pcm的private_data是芯片专用数据,当然我们也可以overwrite以保存别的数据。

2.1 open

当open PCM的一路substream时,open函数就会被调用。

  static int snd_xxx_open(struct snd_pcm_substream *substream)
{
struct mychip *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime; runtime->hw = snd_mychip_playback_hw;
return 0;
}

在open函数内,至少应该初始化此substream的runtime->hw结构体.snd_mychip_playback_hw是预先定义的硬件描述。

也可以在open函数里为substream分配private_data.如下:

data = kmalloc(sizeof(*data), GFP_KERNEL);
substream->runtime->private_data = data;

如果芯片所支持的sample rate,samples等硬件配置有限制,也可以在open函数内设置限制。

2.2 close

当PCM的substream close时就会调用到close 函数。

如果有在open函数内分配了runtime的private_data, 那private data在close函数释放。

  static int snd_xxx_close(struct snd_pcm_substream *substream)
{
....
kfree(substream->runtime->private_data);
....
}

2.4 hw_params

当App在设置substream的buffer size, the period size, the format等硬件参数时,将会调用到hw_params函数。

在hw_params函数中可以设置许多的硬件参数,包括buffer的分配。buffer分配:

snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));

2.5 hw_free

hw_free用来释放在hw_params中释放的资源,如buffer.hw_free总是在close之前调用。

2.6 prepare

当app在调用alsa lib API:snd_pcm_prepare时,prepare函数将被调用,在此函数中可以设置format type, sample rate等参数。与在hw_params中设置参数不同的是每次app调用snd_pcm_prepare时都会去设置参数,而snd_pcm_prepare可能是在recovery undrrun时调用。

prepare函数并非原子操作,因此必须使用 schedule-related functions保证安全性。

2.7 trigger

当PCM在start,stop,pause时,会调用到trigger函数。

switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
/* do something to start the PCM engine */
break;
case SNDRV_PCM_TRIGGER_STOP:
/* do something to stop the PCM engine */
break;
default:
return -EINVAL;
}

SNDRV_PCM_TRIGGER_XXX 定义在 <sound/pcm.h>. 至少我们应该在trigger函数中实现 START 和STOP commands。

当pcm支持pause操作时,必须实现 PAUSE_PUSH and PAUSE_RELEASE commands,PAUSE_PUSH用来pause pcm,PAUSE_RELEASE用来restart pcm.

trigger函数是atomic 的,因此在其中的操作越少越好,通常只用来trigger DMA.

2.8 pointer

当PCM middleware 层(alsa-core)需要获取当前的硬件指针(hardware position)时,就会调用pointer函数。pointer函数需要返回以frame为单位的hardware position(0~buffersize-1).

pointer通常在buffer-update 过程中调用,由中断函数中的snd_pcm_period_elapsed触发。即每次硬件中断,就会调用snd_pcm_period_elapsed函数来通知alsa-core来读取当前的hardware position,计算buffer中空余空间,唤醒sleep的polling thread.

ALSA driver--PCM实例创建框架的更多相关文章

  1. ALSA driver --PCM 实例创建过程

    前面已经写过PCM 实例的创建框架,我们现在来看看PCM 实例是如何创建的. 在调用snd_pcm_new时就会创建一个snd_pcm类型的PCM 实例. struct snd_pcm { struc ...

  2. ALSA driver基本概念

    https://blog.csdn.net/zyuanyun/article/details/59180272#t6 1.Card For each soundcard, a “card” recor ...

  3. Web框架本质及第一个Django实例 Web框架

    Web框架本质及第一个Django实例   Web框架本质 我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端. 这样我们就可以自己实现Web ...

  4. [Java] 实例创建的步骤

    创建类的一个实例时,按照下面步骤进行创建: 1. 给当前类及其父.祖类的所有成员字段分配空间,并给它们赋予默认值 2. 开始执行当前类的构造器 3. 如果当前类有父类,则对父类创建一个实例:从第 2 ...

  5. DB2数据库实例创建与删除 学习笔记

    以root身份执行 $DB2HOME/instance/db2idrop -f 实例名,注意一定要加-f,否则不会删除实例下面sqllib文件.如果不幸忘了,执行db2icrt,会报sqllib文件存 ...

  6. MapReduce实例&YARN框架

    MapReduce实例&YARN框架 一个wordcount程序 统计一个相当大的数据文件中,每个单词出现的个数. 一.分析map和reduce的工作 map: 切分单词 遍历单词数据输出 r ...

  7. C 语言实例 - 创建各类三角形图案

    C 语言实例 - 创建各类三角形图案 创建三角形图案. 实例 - 使用 * 号 #include <stdio.h> int main() { int i, j, rows; printf ...

  8. Python利用元类来控制实例创建

    问题: 改变实例创建方式,以此来实现单例模式,缓存或者其他类似的特性. 解决方法: 如果想定制化创建实例的过程,可以通过定制一个元类并以某种方式重新实现它的__call__()方法. 使用元类的单例模 ...

  9. ALSA声卡笔记1---ALSA驱动框架

    1.声卡驱动程序sound.c (1)入口函数里通过register_chrdev()函数注册file_operations 结构体 (2)file_operations 结构体,里面只有open函数 ...

随机推荐

  1. .net Core 安装在linux上

    1.安装 .net Core 参考官方网站 https://dotnet.microsoft.com/learn/dotnet/hello-world-tutorial/install 2.发布应用程 ...

  2. 菜鸟教程 Missing parentheses in call to 'print'

    个人博客 地址:http://www.wenhaofan.com/article/20180618180327 >>> print "hello" SyntaxE ...

  3. SDOI2010 粟粟的书架 lg2468(可持久化,前缀和)

    题面见https://www.luogu.org/problemnew/show/P2468 然后这道题属于合二为一题,看一眼数据范围就能发现 首先我们先考虑50分,二维前缀和维护一下(反正我不记得公 ...

  4. webpack如何编译ES6打包

    前言:随着ES的普及我们越来越多的开始使用ES6的语法了,当然也随着mvvm框架的流行少不了js模块化,那js模块化又有那些呢 在很早的时候大家都用的命名空间,现在也有人用(库名.类别名.方法名) 后 ...

  5. redis 列表类型list

    列表类型(list)1.插入 左侧插入 :lpush key value1 value2 value3... 右侧插入: lpush key value1 value2 value3... 在指定元素 ...

  6. 2020算法设计竞赛 H 坐火车

    链接:https://ac.nowcoder.com/acm/contest/3005/H来源:牛客网 大致题意:让我们针对每一个数,求这个数左区间和右区间颜色相同(也就是数字相同)得对数: 比如:左 ...

  7. 终极教程【zhong】

    just for a better future! 资源教程               aiim                   综合类 前端知识体系 前端知识结构 Web前端开发大系概览 We ...

  8. linux 创建svn版本库,并在svn上配置checkstyle做代码风格检查

    一.创建SVN版本库 1.安装svn服务器 yum install subversion 2.查看版本 svnserve --version 3.建立SVN版本库目录(即你的SVN服务器里面的文件存放 ...

  9. SuperSocket与SuperSocket.ClientEngine实现Protobuf协议

    参考资料说明 SuperSocket文档 http://docs.supersocket.net/ Protobuf语言参考 https://developers.google.com/protoco ...

  10. vue-cli脚手架创建vue项目

    CLI 使用vue-cli可以快速搭建Vue开发环境以及对应的webpack配置 cnpm install -g @vue/cli // 如果需要使用旧版本的vue init功能(脚手架2),你可以全 ...