1.pcm设备

脉冲编码调制(Pulse Code Modulation,PCM),就是把一个时间连续,取值连续的模拟信号变换成时间离散,取值离散的数字信号后在信道中传输,这是基本原理。

根据此原理,在音频领域的数字音频就用pcm设备来代表,pcm也是一种音频格式,可以自定义通道数,采样率,采样精度;我们经常采用的I2S格式其实属于pcm的一种,不过I2S规定了只有2通道。

音频的采样率(rate)一般采用44.1K,16K,48K等,采样精度(format)一般都是8/16/24/32bit

在ALSA框架中,pcm就是控制音频流的,区别于control

2.PCM设备结构体

这部分重要的结构体主要有:

  • struct snd_pcm
  • struct snd_pcm_str
  • struct snd_pcm_substream

这三者的关系可以用下图来表示:

一个音频设备分播放和录音两个功能,对应到pcm就分PLAYBACK和CAPTURE,分别用结构体snd_pcm_str来表示,一个播放或者录音设备可以集成多个音频流,每个音频流用snd_pcm_substream结构体来表示

这三个结构体的逻辑链接关系如下图:

3.pcm设备注册

pcm设备注册函数为:

int snd_pcm_new(struct snd_card *card, const char *id, int device,int playback_count, int capture_count, struct snd_pcm **rpcm)
{return _snd_pcm_new(card, id, device, playback_count, capture_count,false, rpcm);
}

首先调用_snd_pcm_new来把pcm设备加入到card中,然后card在注册的时候调用pcm的注册函数,把pcm注册到系统中

3.1 创建pcm设备,加入到card中

(sound/core/pcm.c)

static int _snd_pcm_new(struct snd_card *card, const char *id, int device,int playback_count, int capture_count, bool internal,struct snd_pcm **rpcm)
{struct snd_pcm *pcm;int err;static struct snd_device_ops ops = {.dev_free = snd_pcm_dev_free,.dev_register = snd_pcm_dev_register,--------------pcm注册函数(card注册时调用).dev_disconnect = snd_pcm_dev_disconnect,};if (snd_BUG_ON(!card))return -ENXIO;if (rpcm)*rpcm = NULL;pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);if (pcm == NULL) {snd_printk(KERN_ERR "Cannot allocate PCM\n");return -ENOMEM;}pcm->card = card;pcm->device = device;pcm->internal = internal;if (id)strlcpy(pcm->id, id, sizeof(pcm->id));# snd_pcm_new_stream主要是初始化snd_pcm_substream结构体if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) {snd_pcm_free(pcm);return err;}if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)) < 0) {snd_pcm_free(pcm);return err;}mutex_init(&pcm->open_mutex);init_waitqueue_head(&pcm->open_wait);if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) {-----将pcm设备加入到card的devices链表中snd_pcm_free(pcm);return err;}if (rpcm)*rpcm = pcm;return 0;
}

3.2 进行pcm设备的注册

(sound/core/pcm.c)

static int snd_pcm_dev_register(struct snd_device *device)
{int cidx, err;struct snd_pcm_substream *substream;struct snd_pcm_notify *notify;char str[16];struct snd_pcm *pcm;struct device *dev;if (snd_BUG_ON(!device || !device->device_data))return -ENXIO;pcm = device->device_data;mutex_lock(&register_mutex);err = snd_pcm_add(pcm);if (err) {mutex_unlock(&register_mutex);return err;}for (cidx = 0; cidx < 2; cidx++) {int devtype = -1;if (pcm->streams[cidx].substream == NULL || pcm->internal)continue;switch (cidx) {case SNDRV_PCM_STREAM_PLAYBACK:-------------为device命名sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device);devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;break;case SNDRV_PCM_STREAM_CAPTURE:sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device);devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;break;}/* device pointer to use, pcm->dev takes precedence if* it is assigned, otherwise fall back to card's device* if possible */dev = pcm->dev;if (!dev)dev = snd_card_get_device_link(pcm->card);/* register pcm */err = snd_register_device_for_dev(devtype, pcm->card,pcm->device,&snd_pcm_f_ops[cidx],-----pcm设备文件操作函数pcm, str, dev);if (err < 0) {list_del(&pcm->list);mutex_unlock(&register_mutex);return err;}snd_add_device_sysfs_file(devtype, pcm->card, pcm->device,&pcm_attrs);-------pcm设备注册for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)snd_pcm_timer_init(substream);}list_for_each_entry(notify, &snd_pcm_notify_list, list)notify->n_register(pcm);mutex_unlock(&register_mutex);return 0;
}

4.pcm文件操作ops

pcm这块比较难的一点就是这些操作函数了,各种ioctl设置的参数需要对音频技术这块有深入了解,本文就不介绍了,因为core层的东西对于驱动开发来说很少改动(或者基本不改动),遇到问题再解决吧

(sound/core/pcm_native.c)

const struct file_operations snd_pcm_f_ops[2] = {{.owner =        THIS_MODULE,.write =        snd_pcm_write,.aio_write =        snd_pcm_aio_write,.open =         snd_pcm_playback_open,.release =      snd_pcm_release,.llseek =       no_llseek,.poll =         snd_pcm_playback_poll,.unlocked_ioctl =   snd_pcm_playback_ioctl,.compat_ioctl =     snd_pcm_ioctl_compat,.mmap =         snd_pcm_mmap,.fasync =       snd_pcm_fasync,.get_unmapped_area =    snd_pcm_get_unmapped_area,},{.owner =        THIS_MODULE,.read =         snd_pcm_read,.aio_read =     snd_pcm_aio_read,.open =         snd_pcm_capture_open,.release =      snd_pcm_release,.llseek =       no_llseek,.poll =         snd_pcm_capture_poll,.unlocked_ioctl =   snd_pcm_capture_ioctl,.compat_ioctl =     snd_pcm_ioctl_compat,.mmap =         snd_pcm_mmap,.fasync =       snd_pcm_fasync,.get_unmapped_area =    snd_pcm_get_unmapped_area,}
};

4.1 open函数

snd_pcm_capture_open和snd_pcm_playback_open函数最后都会调用snd_pcm_open,这里不详细介绍open的过程了,只介绍下这边引出来的另一个结构体:snd_pcm_runtime

这个结构体只是在运行的时候会动态创建,具体可以参考函数snd_pcm_attach_substream
此结构体主要是设置各种参数,保存运行时的状态等

struct snd_pcm_runtime {/* -- Status -- */struct snd_pcm_substream *trigger_master;struct timespec trigger_tstamp; /* trigger timestamp */int overrange;snd_pcm_uframes_t avail_max;snd_pcm_uframes_t hw_ptr_base;  /* Position at buffer restart */snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time */unsigned long hw_ptr_jiffies;   /* Time when hw_ptr is updated */unsigned long hw_ptr_buffer_jiffies; /* buffer time in jiffies */snd_pcm_sframes_t delay;    /* extra delay; typically FIFO size */u64 hw_ptr_wrap;                /* offset for hw_ptr due to boundary wrap-around *//* -- HW params -- */snd_pcm_access_t access;    /* access mode */snd_pcm_format_t format;    /* SNDRV_PCM_FORMAT_* */snd_pcm_subformat_t subformat;  /* subformat */unsigned int rate;      /* rate in Hz */unsigned int channels;      /* channels */snd_pcm_uframes_t period_size;  /* period size */unsigned int periods;       /* periods */snd_pcm_uframes_t buffer_size;  /* buffer size */snd_pcm_uframes_t min_align;    /* Min alignment for the format */size_t byte_align;unsigned int frame_bits;unsigned int sample_bits;unsigned int info;unsigned int rate_num;unsigned int rate_den;unsigned int no_period_wakeup: 1;/* -- SW params -- */int tstamp_mode;        /* mmap timestamp is updated */unsigned int period_step;snd_pcm_uframes_t start_threshold;snd_pcm_uframes_t stop_threshold;snd_pcm_uframes_t silence_threshold; /* Silence filling happens whennoise is nearest than this */snd_pcm_uframes_t silence_size; /* Silence filling size */snd_pcm_uframes_t boundary; /* pointers wrap point */snd_pcm_uframes_t silence_start; /* starting pointer to silence area */snd_pcm_uframes_t silence_filled; /* size filled with silence */union snd_pcm_sync_id sync; /* hardware synchronization ID *//* -- mmap -- */struct snd_pcm_mmap_status *status;struct snd_pcm_mmap_control *control;/* -- locking / scheduling -- */snd_pcm_uframes_t twake;    /* do transfer (!poll) wakeup if non-zero */wait_queue_head_t sleep;    /* poll sleep */wait_queue_head_t tsleep;   /* transfer sleep */struct fasync_struct *fasync;/* -- private section -- */void *private_data;void (*private_free)(struct snd_pcm_runtime *runtime);/* -- hardware description -- */struct snd_pcm_hardware hw;struct snd_pcm_hw_constraints hw_constraints;/* -- interrupt callbacks -- */void (*transfer_ack_begin)(struct snd_pcm_substream *substream);void (*transfer_ack_end)(struct snd_pcm_substream *substream);/* -- timer -- */unsigned int timer_resolution;  /* timer resolution */int tstamp_type;        /* timestamp type *//* -- DMA -- */           unsigned char *dma_area;    /* DMA area */dma_addr_t dma_addr;        /* physical bus address (not accessible from main CPU) */size_t dma_bytes;       /* size of DMA area */struct snd_dma_buffer *dma_buffer_p;    /* allocated buffer */#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)/* -- OSS things -- */struct snd_pcm_oss_runtime oss;
#endif#ifdef CONFIG_SND_PCM_XRUN_DEBUGstruct snd_pcm_hwptr_log *hwptr_log;
#endif
};

5.change log

date content linux
2017.12.8 origin linux3.10

linux音频子系统 - pcm设备相关推荐

  1. Linux音频子系统(2) - ALSA ASoC

    1. linux音频子系统介绍 Linux音频系统比较复杂,各层间有很多交叉,可能是最无序的子系统. 1.1 ALSA ALSA 是 Advanced Linux Sound Architecture ...

  2. linux音频子系统 - DAPM

    Dynamic Audio Power Management for Portable Devices 移动设备的动态电源管理(DAPM) 1. Description DAPM使得使用音频子系统的移 ...

  3. Linux V4L2子系统-Video设备框架分析(二)

    1.概述 在V4L2子系统中,Video设备是一个字符设备,设备节点为/dev/videoX,主设备号为81,次设备号范围为0-63.在用户空间,应用可以通过open/close/ioctl/mmap ...

  4. linux4.9下alsa架构,[Alsa]4, wm8524 Kernel音频子系统入口

    上篇说到音频子系统的环境搭建和ASoC,我们会发现这样一个问题,对于已有的,已驱动的音频Codec,我们可以很方便地用aplayer.arecorder来录放音频,但是这表象背后到底隐藏了什么不为人知 ...

  5. Linux音频驱动开发概括

    原址 1.嵌入式音频系统硬件连接 下图所示的嵌入式设备使用IIS将音频数据发送给编解码器.对编解码器的I/O寄存器的编程通过IIC总线进行. 2.音频体系结构-ALSA ALSA是Advanced L ...

  6. 嵌入式Linux音频驱动开发

    1.嵌入式音频系统硬件连接 下图所示的嵌入式设备使用IIS将音频数据发送给编解码器.对编解码器的I/O寄存器的编程通过IIC总线进行. 2.音频体系结构-ALSA ALSA是Advanced Linu ...

  7. Linux V4L2子系统分析(一)

    1.概述 Linux系统上的Video设备多种多样,如通过Camera Host控制器接口连接的摄像头,通过USB总线连接的摄像头等.为了兼容更多的硬件,Linux内核抽象了V4L2(Video fo ...

  8. 嵌入式linux 配置usb otg,嵌入式linux系统环境下USB设备的驱动实现

    0  引言 嵌入式linux系统环境以其易于移植裁减.内核小.效率高.完整.原代码开放及性能优异等特点,在嵌入式领域得到了非常广泛的应用.Linux的USB设备端的源代码中主要有USB device的 ...

  9. linux V4L2子系统——v4l2架构(3)之video_device

    linux V4L2子系统--v4l2架构(3)之video_device 备注:   1. Kernel版本:5.4   2. 使用工具:Source Insight 4.0   3. 参考博客: ...

最新文章

  1. time 和 datetime 模块
  2. android怎么用别人的工程,Android导入别人的工程
  3. WebAPI(part2)--获取元素
  4. java的接口语法_JAVA接口的基本语法
  5. 【干货】Python编程惯例
  6. ESET NOD32 Antivirus – 免费 3个月/ 3PC
  7. matlab qam调制函数,matlab实现16QAM调制解调
  8. 熊猫直播显示连接服务器失败,熊猫直播提示加载失败,请按“菜单键”刷新解决办法...
  9. 从零开始学习CANoe(六)—— CAPL 测试节点
  10. 拆解1968年的美国军用电脑,真的怀疑是“穿越”啊!
  11. 神策应用-概述认知(一)
  12. Hugging Face(1)——Transformer Models
  13. 【9】核心易中期刊推荐——图像视觉与图形可视化
  14. PostgreSQL之日期时间小结
  15. Token一般存放在哪里
  16. Python abaqus实现二维裂纹扩展(XFEM)
  17. 设计模式总结干货(很好链接站点,见解深刻,易理解入门)
  18. 阿里云服务器备份从快照到自定义镜像再将镜像导出保存下载到本地
  19. 半导体专用RIFD读卡器|读写器|读写头CK-S640系列在半导体生产领域的应用
  20. 2010 SAP全球技术研发者大会(上海)

热门文章

  1. 周志华《机器学习》第三章线性模型笔记+习题
  2. freetype的简单使用之 生成一个字体bmp
  3. Ubuntu 16.04系统安装VS Code流程详解
  4. 解密TLS协议全记录之Openssl的使用与Nginx Server的配置
  5. Oracle11安装(安装包+图文讲解)
  6. 宽带连不上网原因及解决办法
  7. week4——实验题解(csp模拟1)
  8. R 下载GEO数据总是超时
  9. English--辅音
  10. 非安全系列教程 NPM、PYPI、DockerHub 备份